多言語対応したXMLドキュメントコメントファイルを配置できる、NuGetパッケージを作成してみよう！

こんにちはー！[Visual Studio Advent Calendar 2016](http://qiita.com/advent-calendar/2016/vs)の15日目担当のニアです。

今回は、多言語対応したXMLドキュメントコメント（※以降、単に「コメント」と表記します）ファイルを配置できる、NuGetパッケージを作成し、**Visual Studioの言語設定に合わせて、IntelliSenseやオブジェクトブラウザーに表示されるそのコメントを切り替えられるようにする**方法を紹介していきます。

![IntelliSenseにコメント表示](https://qiita-image-store.s3.amazonaws.com/0/78881/3a4b45c1-1ab6-4359-9c98-f0d460bde9ed.png)

![IntelliSenseにローカライズしたコメント表示](https://qiita-image-store.s3.amazonaws.com/0/78881/e6836d7e-407f-e83e-a7d8-89aded3e6d4d.png)

>ちなみに上図は、私が現在開発しているAndroid Wear用のウォッチフェイスアプリ向けクラスライブラリ（Xamarin用）「[Chronoir_net.Chronoface.Utility](https://chronoir.net/chronoface-utility/)」です。


#1. Visual StudioのIntelliSenseやオブジェクトブラウザーに表示されるコメントを多言語対応にする仕組み

その前に、Visual Studioの言語設定によって、コメントが切り替わる仕組みについて説明します。

##1.1. XMLドキュメントコメントとIntelliSense

C#やVisual Basic.NETでは、ソースコードにXMLドキュメントで表したコメントを記述すると、IntelliSenseやオブジェクトブラウザーにそのコメントが表示されます。

```csharp:C#での例
using System;

/// <summary>
/// 	Tax class.
/// </summary>
public class Tax {

	/// <summary>
	///		Gets and sets the consumption tax rate.
	/// </summary>
	public static int TaxRate { get; set; } = 8;

	/// <summary>
	/// 	Calculates the tax inclusive price from the specified base price.
	/// </summary>
	/// <param name="price">Base price</param>
	/// <returns>Tax inclusive price</returns>
	public static int TaxIn( int price ) =>
		price + ( int )Math.Round( price * ( TaxRate / 100.0 ) );
}
```

![IntelliSenseにコメント表示](https://qiita-image-store.s3.amazonaws.com/0/78881/323c718c-52b1-a38e-95d6-6d013501bb35.png)

##1.2. 配布するクラスライブラリで、コメントを表示する

配布するクラスライブラリでコメントを表示するには、**Visual Studioで生成したコメントファイルを、そのクラスライブラリと同じディレクトリに配置**します。

![コメントファイルをクラスライブラリと同じディレクトリに配置](https://qiita-image-store.s3.amazonaws.com/0/78881/06e06a38-7c8a-79d1-1b13-76dc9e142f13.png)

##1.3. コメントを多言語対応にする

先ほど示したサンプルクラスでは、コメントが英語ですが、これを**Visual Studioの言語設定に合わせてローカライズしたものを表示**したい場合、**言語コード（例 : 「ja」（日本語）、「en」（英語））のフォルダーを作成して、その中にローカライズしたコメントファイルを配置**し、**クラスライブラリの直前のフォルダー名を「ターゲットにするフレームワーク名」（例 : 「net452」（.NET Framework 4.5.2）、「monoandroid60」（Mono.Android 6.0））**にします。

![コメントのローカライズ](https://qiita-image-store.s3.amazonaws.com/0/78881/f39bbcec-493d-d0fb-342b-288d5dc45598.png)

上図の場合、Visual Studioの言語設定が日本語の時に、日本語用のドキュメントコメントが表示されます。

![IntelliSenseにローカライズしたコメント表示](https://qiita-image-store.s3.amazonaws.com/0/78881/671ba413-0ea1-f0ae-0507-fac11aca2abc.png)


#2. 多言語対応したコメントファイルを配置できる、NuGetパッケージの作成

それでは早速、NuGetパッケージを作成していきます。

##2.1. 用意するもの

* 配布するクラスライブラリ及びそのプロジェクトファイル
* 既定の言語用のコメントファイル
* ローカライズした言語のコメントファイル
* nuspecファイル

>◆ 本記事では、クラスライブラリのサンプルとして以下のコードを使用しています。

>```csharp:Sample.dllのコード（Sample.cs）
namespace Sample {
	/// <summary>
	/// 	Tax class.
	/// </summary>
	public class Tax {

>		/// <summary>
		///		Gets and sets the consumption tax rate.
		/// </summary>
		public static int TaxRate { get; set; } = 8;

>		/// <summary>
		/// 	Calculates the tax inclusive price from the specified base price.
		/// </summary>
		/// <param name="price">Base price</param>
		/// <returns>Tax inclusive price</returns>
		public static int TaxIn( int price ) =>
			price + ( int )Math.Round( price * ( TaxRate / 100.0 ) );
	}
}
```

プロジェクトの設定で、構成から「**Relese**」を選択し、「**XMLドキュメントファイル**」のチェックボックスにチェックを入れます。また、そのファイル名はクラスライブラリと同じ名前にします。

![プロジェクトの設定](https://qiita-image-store.s3.amazonaws.com/0/78881/4d88c974-abc7-9ec3-afef-bc8fbfbf9cf7.png)

アセンブリ情報を入力し、クラスライブラリをビルドします。

![アセンブリ情報](https://qiita-image-store.s3.amazonaws.com/0/78881/b76bb2d8-c6b7-153b-959b-fbb36452d700.png)

出力フォルダーにて、ローカライズする言語コードのフォルダーを作成します。本記事では、日本語（ja）とドイツ語（de）用のコメントファイルを作成しています。

>◆ 言語コードの例

>|言語|言語コード|
|---|---|
|日本語|ja|
|英語|en|
|ドイツ語|de|
|フランス語|fr|
|イタリア語|it|
|スペイン語|es|
|ポルトガル語|pl|
|ロシア語|ru|
|中国語（簡体字）|zh-cn|
|中国語（繁体字）|zh-tw|
|韓国語|ko|

![ローカライズする言語コードのフォルダーを作成](https://qiita-image-store.s3.amazonaws.com/0/78881/3c8a5d1c-8fa5-2db4-5111-1e1b7e7acae4.png)

Visual Studioで作成したコメントファイルのコピーを作成して、ローカライズする言語向けに編集します。

```xml:Sample.xml（既定の言語（英語））
<?xml version="1.0"?>
<doc>
    <assembly>
        <name>Sample</name>
    </assembly>
    <members>
        <member name="T:Sample.Tax">
            <summary>
            	Tax class.
            </summary>
        </member>
        <member name="P:Sample.Tax.TaxRate">
            <summary>
            	Gets and sets the consumption tax rate.
            </summary>
        </member>
        <member name="M:Sample.Tax.TaxIn(System.Int32)">
            <summary>
            	Calculates the tax inclusive price from the specified base price.
            </summary>
            <param name="price">Base price</param>
            <returns>Tax inclusive price</returns>
        </member>
    </members>
</doc>
```

```xml:ja/Sample.xml（日本語）
<?xml version="1.0"?>
<doc>
    <assembly>
        <name>Sample</name>
    </assembly>
    <members>
        <member name="T:Sample.Tax">
            <summary>
            	消費税のクラス。
            </summary>
        </member>
        <member name="P:Sample.Tax.TaxRate">
            <summary>
            	消費税率を取得・設定します。
            </summary>
        </member>
        <member name="M:Sample.Tax.TaxIn(System.Int32)">
            <summary>
            	指定した本体価格から税込価格を計算します。
            </summary>
            <param name="price">本体価格</param>
            <returns>税込価格</returns>
        </member>
    </members>
</doc>
```

```xml:de/Sample.xml（ドイツ語）
<?xml version="1.0"?>
<doc>
    <assembly>
        <name>Sample</name>
    </assembly>
    <members>
        <member name="T:Sample.Tax">
            <summary>
            	Steuer klasse.
            </summary>
        </member>
        <member name="P:Sample.Tax.TaxRate">
            <summary>
            	Ruft den Verbrauchsteuersatz ab und legt diesen fest.
            </summary>
        </member>
        <member name="M:Sample.Tax.TaxIn(System.Int32)">
            <summary>
            	Berechnet den steuerlichen Preis aus dem angegebenen Basispreis.
            </summary>
            <param name="price">Basispreis</param>
            <returns>Steuerpauschalpreis</returns>
        </member>
    </members>
</doc>
```

##2.2. nuspecファイルの書き方

コマンドプロンプトを起動し、プロジェクトファイルのあるディレクトリ[^1]に移動して、以下のコマンド[^2]を実行し、nuspecファイルを作成します。

[^1]: プロジェクトファイルのあるディレクトリ上で`nuget spec`を実行すると、作成するnuspecファイルの一部に、プロジェクトの設定値を使用するトークン（例 : `$id$`（アセンブリ名））が設定されます。
[^2]: 本記事で使用しているnuget.exeのバージョンは、3.4.4.1321です。

```bat:nuspecファイル作成
nuget spec
```

>※nuget.exeのあるディレクトリを環境変数「PATH」に追加しておくと、コマンド入力がスマートになり、便利です。
\>\> [Visual Studioの外部ツール機能を活用してみよう](http://qiita.com/nia_tn1012/items/d5510dcb7f1bf0d54381#ex2-nugetexe)

```xml:Sample.nuspec
<?xml version="1.0"?>
<package>
	<metadata>
		<id>$id$</id>
		<version>$version$</version>
		<title>$title$</title>
		<authors>$author$</authors>
		<owners>$author$</owners>
		<licenseUrl>http://LICENSE_URL_HERE_OR_DELETE_THIS_LINE</licenseUrl>
		<projectUrl>http://PROJECT_URL_HERE_OR_DELETE_THIS_LINE</projectUrl>
		<iconUrl>http://ICON_URL_HERE_OR_DELETE_THIS_LINE</iconUrl>
		<requireLicenseAcceptance>false</requireLicenseAcceptance>
		<description>$description$</description>
		<copyright>Copyright 2016</copyright>
		<tags>Tag1 Tag2</tags>
	</metadata>
</package>
```

作成したnuspecファイルを編集します。

```xml:Sample.nuspec
<?xml version="1.0"?>
<package>
	<metadata>
		<id>$id$</id>
		<version>$version$</version>
		<title>$title$</title>
		<authors>$author$</authors>
		<owners>$author$</owners>
		<requireLicenseAcceptance>false</requireLicenseAcceptance>
		<description>$description$</description>
		<copyright>Copyright 2016 Nia Tomonaka</copyright>
		<tags>C# sample</tags>
	</metadata>
	<files>
		<file src="bin/Release/ja/$id$.XML" target="lib/net452/ja/"/>
		<file src="bin/Release/de/$id$.XML" target="lib/net452/de/"/>
	</files>
</package>
```

>※今回はサンプルなので、ライセンスのURLやアイコンのURL、プロジェクトのURLは省略しています。

C#やVisual Basic.NETのプロジェクトファイルを指定してNuGetパッケージを作成する場合、それに含まれるのは**クラスライブラリと既定の言語のコメントファイルのみ**です。
また、作成したNuGetパッケージのインストールすると、「**packages/[パッケージ名]/lib/[ターゲットフレームワーク名]**」（本記事の場合、「**packages/Sample.1.0.0.0/lib/net452**」）フォルダー内に配置されます。

ローカライズした方のファイルも含めるには、**file**要素を追加し、**src**属性にローカライズしたコメントファイルのパスを、**target**属性にそのファイルの配置先のパスを設定します。

|属性|属性値|
|---|---|
|**src**|**nuspecファイルのあるディレクトリから見た相対パス**|
|**target**|**パッケージ名のディレクトリから見た相対パス**|

```xml:ローカライズした方のファイルを含める
<files>
	<file src="bin/Release/ja/$id$.XML" target="lib/net452/ja/"/>
	<file src="bin/Release/de/$id$.XML" target="lib/net452/de/"/>
</files>
```

##2.3. パッケージの作成

コマンドプロンプトで、以下のコマンド[^3]を実行し、NuGetパッケージを作成します。

```bat:nuspecファイル作成
nuget pack Sample.csproj -Prop Configuration=Release
```

[^3]: 「-Prop Configuration=Release」オプションは、Releaseビルド側のプロジェクト設定を指定します。

NuGetファイルは**ZIP形式**なので、拡張子を「zip」に変更すると、パッケージの中身を確認することができます。
「**[パッケージ名]/lib/[ターゲットフレームワーク名]**」（本記事の場合、「**Sample.1.0.0.0/lib/net452**」）のフォルダーの中に、クラスライブラリファイルと既定の言語のコメントファイルに加えて、**ローカライズする言語コードのフォルダー及びそのコメントファイル**が入っていれば、成功です。

![多言語対応したコメントファイルを配置できる、NuGetパッケージ](https://qiita-image-store.s3.amazonaws.com/0/78881/e0ba7c66-f8aa-d03f-6e33-dd2570394daf.png)

あとはNuGetパッケージを[NuGet Gallery](https://www.nuget.org/)やリポジトリに公開すればOK！

![IntelliSenseにコメント表示](https://qiita-image-store.s3.amazonaws.com/0/78881/323c718c-52b1-a38e-95d6-6d013501bb35.png)

![IntelliSenseにローカライズしたコメント表示](https://qiita-image-store.s3.amazonaws.com/0/78881/671ba413-0ea1-f0ae-0507-fac11aca2abc.png)

![IntelliSenseにローカライズしたドキュメントコメント表示](https://qiita-image-store.s3.amazonaws.com/0/78881/e699e7a0-01d5-90f9-10de-18d70ace2a9e.png)

>本記事で使用したファイルは、[Gistでも公開しています](https://gist.github.com/Nia-TN1012/a5629449efc05e911a1311cd26d68b87)。


#3. おわりに

今回は、多言語対応したXMLドキュメントコメントファイルをNuGetパッケージに含めて、インストール時に適切なディレクトリに配置し、Visual Studioの言語設定に合わせて、ローカライズされたコメントを切り替えられるようにする方法を紹介しました。

便利なクラスライブラリを作成したら、ぜひ、コメントを多言語対応にして、世界中の開発者にオススメしてみませんか？


それでは、See you next!


#参考サイト
* [Nuspec Reference - NuGet Docs](https://docs.nuget.org/ndocs/schema/nuspec)
* [XML Comment Localization - Surviveplus.net](http://www.surviveplus.net/ja/archives/166)

コメントのローカライズは、今のところ、手作業で編集するか外部ツールを使用して行う状況です。
リソースファイル（resxなど）のように、言語ごとのコメントファイルを簡単に作成できる仕組みがあればな・・・と、私は思います。