概要 (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 ドキュメントコメントファイルを出力するようにしよう
- Step 2. XML ドキュメントコメントを書こう
- Step 3. XMLドキュメントコメントを「多言語化」しよう
- Step 4. プロジェクトに分割用スクリプトを仕込もう
- Step 5. NuGet パッケージへ取り込もう
Step 1. XML ドキュメントコメントファイルを出力するようにしよう
まずは、Visual C# プロジェクトで XML ドキュメントファイルを出力するように設定します。
Visual Studio の C# プロジェクトの プロパティ
→ ビルド
を表示し、XMLドキュメントファイル
にチェックを入れます。
これで、プロジェクトをビルドするたびに、.dll
ファイルと一緒に .xml
ファイルが出力されるようになります。
補足(2021/06/16)
.xml
ファイルは、出力フォルダ(.dll
のあるフォルダ)とは 別に、XML ドキュメント ファイル
で指定したパスにも出力されます。上記のように指定した場合はプロジェクトフォルダに .xml
が出力されてしまうので、それが Visual Studio に拾われてイヤだという場合は、パスとして bin\SampleLibrary.xml
などのように別の場所を指定してください。
Step 2. XML ドキュメントコメントを書こう
.xml
ファイルが出力されるようになったので、C# コードに XML ドキュメントコメントをどんどん書いていきましょう。
以下はサンプルです。
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)多言語対応の問題と解決を考える)
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 にも上げてあります。
# 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\
フォルダ配下に出力されているものとします。)
<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