LoginSignup
1
3

More than 1 year has passed since last update.

スクリプトを使ったXMLドキュメントコメントの多言語対応

Last updated at Posted at 2021-06-16

概要 (TL;DR)

  • C# ライブラリの XML ドキュメントコメントに xml:lang 属性を付けて多言語化し、自作スクリプトで言語別の XML ファイルを出力するようにしました。
  • Visual Studio の IntelliSense などで、日本語の開発環境では日本語の、それ以外では英語の XML ドキュメントコメントが、それぞれ優先して表示されるようになります。
  • 出力された XML ファイルは NuGet パッケージにも同梱できます。
  • GitHub にサンプル一式を置いてあります。

XML ドキュメントコメントとは

自前で .NET ライブラリを作る際には、XML ドキュメントコメントを記述しておくと、そのライブラリを参照した時に IntelliSence でヒントが表示されるようになって便利です。
XML ドキュメントコメントとは何ぞ?という方は、「XMLコメントを含むコードの文書化」(Microsoft)をご参照ください。

XML ドキュメントコメントの多言語化

Step 1. XML ドキュメントコメントファイルを出力するようにしよう

まずは、Visual C# プロジェクトで XML ドキュメントファイルを出力するように設定します。
Visual Studio の C# プロジェクトの プロパティビルド を表示し、XMLドキュメントファイル にチェックを入れます。

OutputXMLDocumentFile.png

これで、プロジェクトをビルドするたびに、.dll ファイルと一緒に .xml ファイルが出力されるようになります。

OutputXMLDocumentFile2.png

補足(2021/06/16)
.xml ファイルは、出力フォルダ(.dll のあるフォルダ)とは 別にXML ドキュメント ファイル で指定したパスにも出力されます。上記のように指定した場合はプロジェクトフォルダに .xml が出力されてしまうので、それが Visual Studio に拾われてイヤだという場合は、パスとして bin\SampleLibrary.xml などのように別の場所を指定してください。

Step 2. XML ドキュメントコメントを書こう

.xml ファイルが出力されるようになったので、C# コードに XML ドキュメントコメントをどんどん書いていきましょう。

以下はサンプルです。

SampleLibrary.cs
using System;
using System.Runtime.InteropServices;

namespace SampleLibrary
{
    /// <summary>
    ///     Converts an array to an array of a different type.
    /// </summary>
    public class ArrayConverter
    {
        /// <summary>
        ///     Converts float[] to byte[].
        /// </summary>
        /// <param name="source">A float array.</param>
        /// <returns>A new byte array.</returns>
        public static byte[] ToByteArray( float[] source )
        {
            var sourceFloatSpan = new Span<float>( source );
            var sourceByteSpan = MemoryMarshal.Cast<float, byte>( sourceFloatSpan );
            return sourceByteSpan.ToArray();
        }
    }
}

Step 3. XMLドキュメントコメントを「多言語化」しよう

Visual Studio の .xml ファイル出力機能は、複数の言語には対応していません。

そこで、XML ドキュメントコメントをいろんな言語で記述(多言語化)し、IntelliSense やオブジェクトブラウザでは開発環境で使用している言語のコメントが優先的に表示されるようにしたいと思います。

一般的な XML の多言語化ルールに基づいて、xml:lang 属性を使って言語を指定することにします。(参考:@IT - XMLを学ぼう(11)多言語対応の問題と解決を考える

SampleLibrary.cs
using System;
using System.Runtime.InteropServices;

namespace SampleLibrary
{
    /// <summary>
    ///     <para xml:lang="en">Converts an array to an array of a different type.</para>
    ///     <para xml:lang="ja">配列を異なる型の配列に変換します。</para>
    /// </summary>
    public class ArrayConverter
    {
        /// <summary>
        ///     <para xml:lang="en">Converts float[] to byte[].</para>
        ///     <para xml:lang="ja">float[] を byte[] に変換します。</para>
        /// </summary>
        /// <param name="source">
        ///     <para xml:lang="en">An array of floats.</para>
        ///     <para xml:lang="ja">float 配列。</para>
        /// </param>
        /// <returns>
        ///     <para xml:lang="en">A new array of bytes.</para>
        ///     <para xml:lang="ja">新しい byte 配列。</para>
        /// </returns>
        public static byte[] ToByteArray( float[] source )
        {
            var sourceFloatSpan = new Span<float>( source );
            var sourceByteSpan = MemoryMarshal.Cast<float, byte>( sourceFloatSpan );
            return sourceByteSpan.ToArray();
        }
    }
}
  • 読みやすさのために <para> タグを使っていますが、<summary><param><return> などの他のタグでも xml:lang 属性を使うことができます。
  • コメントのインデントは、あってもなくても構いません。こちらも見た目の問題です。

Step 4. プロジェクトに分割用スクリプトを仕込もう

Visual Studio は xml:lang 属性に対応していないので、このままでは単に1つの .xml ファイルにコメントが丸々出力されてしまいます。
そこで、プロジェクトに分割用スクリプトを仕込み、ビルド後にその .xml ファイルを言語別 XML に自動分割するようにします。スクリプトの言語は PowerShell です。

言語別 XML とは?

Visual Studio は XML ドキュメントコメントの「出力」については複数の言語に対応していませんが、「参照」については対応しています。
言語ごとにフォルダを作ってその中にその言語用の .xml ファイルを入れると、Visual Studio は開発環境の言語に対応した .xml ファイルを既定の .xml ファイルより優先するようになります。(既定の .xml ファイルとは、.dll と同じ場所に置かれた .xml ファイルのことです。)

言語別 XML の配置例を以下に示します。

SampleLibrary.dll
SampleLibrary.xml
ja\
  SampleLibrary.xml

このような形に .xml ファイルを配置するだけで、日本語の開発環境の Visual Studio では ja/SampleLibrary.xml が優先して参照され、そこに記述がない場合のみ既定の SampleLibrary.xml の内容が参照されるようになります。

プロジェクトに分割用スクリプトを仕込む

分割用スクリプト(SeparateDocs.ps1)は、少し長いですが、次の通りです。
GitHub にも上げてあります

SeparateDocs.ps1
# Separate Docs
# A tool to split XML document comment files based on 'xml:lang' attribute.
# usage: powershell -File separateDocs.ps1 <projectName> <projectOutDir> <xmlOutDir>

Param( [parameter(mandatory)] $projectName,$outdir,$xmldocdir )

Write-Host "Separate Docs (C) Copyright 2020 FROM/Kenji Yamazaki"

# XPath for target documents
$targets = @("//member/*", "//member/*/*")
# target languages
$langs = @("en", "ja")

$script:ErrorActionPreference = 'Stop'

$xmlpath = "${outdir}${projectName}.xml"
Copy-Item ${xmlpath} "${xmlpath}.org"

foreach( $dn in $langs ) {

    $xml = [xml] (Get-Content -Encoding UTF8 "${xmlpath}.org" )

    # namespace for 'xml:lang'
    $ns = New-Object -TypeName System.Xml.XmlNamespaceManager -ArgumentList $xml.NameTable
    $ns.AddNamespace('xml', 'http://www.w3.org/XML/1998/namespace')

    foreach( $target in $targets )
    {
        foreach( $item in $xml.SelectNodes("${target}[@xml:lang!='${dn}']", $ns) )
        {
            $item.ParentNode.RemoveChild( $item ) | Out-Null
        }
    }

    if( ${dn} -eq "en" )
    {
        # en for default
        $docfolder = ${xmldocdir}
    }
    else
    {
        # other into subfolder
        $docfolder = "${xmldocdir}\${dn}"
    }

    if( !( Test-Path $docfolder ) )
    {
        mkdir $docfolder | Out-Null
    }

    $outpath = "${docfolder}\${projectName}.xml"
    $xml.Save( $outpath )

    Write-Host "--> xml:lang='${dn}', ${outpath} done."

    # feedback xml from generatedDir to outdir 
    if( ${dn} -eq "en" )
    {
        # en for default
        Copy-Item ${outpath} ${xmlpath}
    }
    else
    {
        $feedbackDir = "${outdir}\${dn}"
        if( !( Test-Path $feedbackDir ) )
        {
            mkdir $feedbackDir | Out-Null
        }
        Copy-Item ${outpath} "${feedbackDir}\${projectName}.xml"
    }
}

このスクリプトを実行すると、Visual Studio がビルドで出力する一本の .xml ファイルから、既定(en, 英語)の .xml ファイルと、日本語(ja)の .xml ファイルが抽出されます。日本語の .xml ファイルは、ja\ フォルダ内に出力されます。

このスクリプトの使い方は次の通りです。

powershell -File separateDocs.ps1 <プロジェクト名> <入力フォルダ> <出力フォルダ>

<プロジェクト名> には、Visual C# プロジェクトの名前を指定します。このプロジェクト名は、Visual Studio が出力する XML ファイルの名前でもあります。
<入力フォルダ> には、Visual Sutdio が出力する .xml ファイルの存在するフォルダを指定します。
<出力フォルダ> には、スクリプトが抽出した各 .xml ファイルを出力するフォルダを指定します。

例えば、次のようなコマンドラインを C# プロジェクトの「ビルド後イベント」に設定すれば、ビルド後にスクリプトが自動実行され、プロジェクトフォルダの Generated\ フォルダ内に言語別 XML が出力されるようになります。

ビルド後イベントスクリプト
powershell -ExecutionPolicy RemoteSigned -File "$(ProjectDir)separateDocs.ps1" $(ProjectName) $(OutDir) $(ProjectDir)Generated

スクリプトの制約とカスタマイズ

前掲した separateDocs.ps1 スクリプトには、今のところ次のような制約があります。

  • 英語(en)と日本語(ja)を抽出します。英語は既定の言語となります。
  • xml:lang 属性が付与されていないタグは、両方の XML ファイルに出力されます。
  • xml:lang 属性は、第一階層と第二階層の XML タグで有効です。

言語を増やしたい場合は、スクリプトの最初のほうにある $langs 変数に追記してください。(英語(en)は必須です。)
xml:lang が有効な XML タグ階層を増やしたい場合は、同じく $targets 変数に追記してください。

Step 5. NuGet パッケージへ取り込もう

分割スクリプトが出力する言語別 XML ファイルを NuGet パッケージに含めるには、次のように、 <Content> タグを .csproj に追加します。(ここでは、言語別 XML ファイルが Generated\ フォルダ配下に出力されているものとします。)

SampleLibrary.csproj
<ItemGroup>
    <Content Include="Generated\*\*.xml" Pack="true" PackagePath="lib\$(TargetFramework)" />
</ItemGroup>

例えば、ターゲットフレームワークが .NET 5.0 である場合には、生成される Nuget パッケージのファイル構成は次のようになります。

lib\
    net5.0\
        ja\
            SampleLibrary.xml   ... 日本語用 XML
        SampleLibrary.dll
        SampleLibrary.xml       ... 既定の言語(英語)用 XML
1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3