Xamarin開発記04 - 日本語を既定の言語にして多言語対応する

# 1. はじめに
[前回の記事](https://qiita.com/kojimadev/items/f1f5a5c84ad1db524249)では、Xamarin.Formsで線形と放射状のグラデーションを使い分けて矩形と円を描画するコントロールを作成しました。
本記事では、Xamarin.Formsで、日本語を既定の言語にして多言語対応する方法を紹介します。

# 2. 多言語対応とは
日本語以外の言語でアプリを表示できるようにすることです。ほとんどの場合、対象ユーザーを日本人だけにするより、英語でもリリースした方がアプリのダウンロード数は多くなります。対象となるユーザーの数が日本人だけより何十倍も増えるためです。
また、多言語対応は、後からやろうとすると手間がかかるため、最初から仕組みだけは作っておいた方が良いと思います。

Xamarin.Formsで既定の言語を英語とする前提での多言語対応の記事は多くあります。本稿では、日本語を既定の言語にする方法を紹介します。
既定の言語を日本語にすると、何が嬉しいかというと、下図のようにリソース文字列のインテリセンスで日本語の文字列が表示される点です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403606/dd8f68d1-68c0-7768-e7fd-6783cc0f91a5.png)
また、日本語と英語のリソース文字列を同時に定義するのが面倒な場合、とりあえず既定の言語である日本語のリソース文字列だけを定義して、英語のリソース文字列は未定義のままにしておけます(英語のリソース文字列が未定義で英語で表示しようとした場合、既定の言語である日本語のリソース文字列が表示されます)。

# 3. 基本的な多言語対応の実施手順
基本的な多言語対応として、コードビハインドとxamlでリソース文字列を取得する方法を紹介します。この章の内容は、英語を既定の言語にする場合と同じです。従って、下記の記事に書いてあることと、だいたい同じです。
[ローカリゼーション - Xamarin | Microsoft Docs](https://docs.microsoft.com/ja-jp/xamarin/xamarin-forms/app-fundamentals/localization/text?tabs=windows)
[Xamarin.FormsアプリのUIを日本語以外でも表示する - GrapeCity.devlog](https://devlog.grapecity.co.jp/entry/2016/12/14/xamarin_l10n)


## 3.1 既定のリソースファイルを作成
共通プロジェクトにResourcesフォルダを作成し、[新しい項目] - [リソースファイル]を選択して、AppResources.resx というファイルを作成します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403606/f8020683-7fea-e8fb-db45-b44359912e97.png" width=50%>

## 3.2 リソース文字列を定義
作成した AppResources.resx に、任意のリソース文字列を設定します。ここでは例として「LabelText」という名前で「日本語テキスト」というリソース文字列を設定しています。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403606/ee4d381f-43a3-ad00-729b-0c25f5e96cd9.png" width=50%>

## 3.3 コードビハインドでリソース文字列を利用
コードビハインドでリソース文字列を利用する場合は、AppResourceクラスのプロパティでリソース文字列を取得できます。以下は、_Label という Lable のテキストにリソース文字列を設定する例です。

```c#
public partial class MainPage : ContentPage
{
	public MainPage()
	{
		InitializeComponent();

		// コードビハインドでリソース文字列を設定
		_Label.Text = AppResources.LabelText;
	}
}
```

## 3.4 xamlでリソース文字列を利用
xamlでリソース文字列を利用する場合は、まずxamlのマークアップ拡張を定義するクラスとしてTranslateExtensionクラスを作成します。

```c#
/// <summary>
/// TranslateのXAMLマークアップ拡張により、リソース文字列を取得する
/// </summary>
[ContentProperty("Key")]  // Keyを規定のプロパティに指定 
public class TranslateExtension : IMarkupExtension
{
	/// <summary>
	/// リソース文字列のプロパティ名として利用する文字列
	/// </summary>
	public string Key { get; set; }

	/// <summary>
	/// IMarkupExtension インタフェースとしてのマークアップ拡張処理
	/// </summary>
	/// <param name="serviceProvider"></param>
	/// <returns>リソース文字列</returns>
	public object ProvideValue(IServiceProvider serviceProvider)
	{
		if (string.IsNullOrEmpty(Key))
		{
			return "Key is Null or Empty";
		}

		// Keyプロパティに指定されたキーをもとにリソースから文字列を取得
		return AppResources.ResourceManager.GetString(Key, AppResources.Culture) ?? Key;
	}
}
```
その上で、上記のマークアップ拡張を用いて、xamlで AppResource.resxに定義したリソース文字列を取得します。

```xml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:d="http://xamarin.com/schemas/2014/forms/design"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:local="clr-namespace:XamarinGlobalizeSample;assembly=XamarinGlobalizeSample"
             mc:Ignorable="d"
             x:Class="XamarinGlobalizeSample.MainPage">
	<StackLayout>
		<!-- マークアップ拡張を用いて「LabelText」のリソース文字列をTextに設定 -->
		<Label Text="{local:Translate LabelText}" />
	</StackLayout>
</ContentPage>
```

# 4. 日本語を既定の言語にして多言語対応する手順
この章で、日本語を既定の言語にする方法を説明します。

## 4.1 英語(既定ではない)のリソースファイルを作成
Resourcesフォルダで、[新しい項目] - [リソースファイル]を選択して、AppResources.en.resx というファイルを作成します(.en の部分が英語を意味します)。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403606/a754f891-7bab-36d4-bf5c-e83940c8b568.png" width=50%>

以下のように、同じキー名([名前]列の値)で、英語用に別のリソース文字列を定義します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403606/d3b96a8b-80ef-aaa8-f1aa-dec95bb3ca08.png" width=50%>

## 4.2 既定の言語を変更する
デフォルトでは既定の言語は英語になっているため、以下の方法で既定の言語を日本語に変更します。

### 4.2.1 共通プロジェクトの既定の言語を変更
共通プロジェクトの[プロパティ]-[パッケージ]で既定の言語を「日本語(日本)」に変更します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403606/fb0d2c36-bb9a-08ed-b8b3-9f44590508ea.png" width=50%>

### 4.2.2 UWPとAndroidとiOSのプロジェクトで既定の言語を変更
UWPとAndroidとiOSのAssemblyInfoに既定の言語の設定を追加します。具体的には、以下の行を末尾に追加します。

```csharp
[assembly: System.Resources.NeutralResourcesLanguage("ja-JP")]
```
また、UWPプロジェクトのPackage.appxmanifestの既定の言語を ja-JP に変更します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403606/66e90394-ac6d-9a0e-471f-2302032760e0.png" width=50%>


## 4.3 アプリの起動中に使用する言語を指定する
ここまでの手順で、英語と日本語のリソース文字列が定義済みであれば、以下のようにカルチャを用いて言語を指定することで、その言語のリソース文字列を表示できます。Appクラスのコンストラクタなど、アプリの起動時に設定可能です。

```csharp
// 日本語を指定する場合
AppResources.Culture =  new CultureInfo("ja-JP");
// 英語を指定する場合
AppResources.Culture =  new CultureInfo("en-US");
```
アプリを起動したプラットフォームの言語を元に、自動的にカルチャを設定する方法は、以下の記事を参照ください。
[適切な言語の表示 - Xamarin | Microsoft Docs](https://docs.microsoft.com/ja-jp/xamarin/xamarin-forms/app-fundamentals/localization/text?tabs=windows#displaying-the-correct-language)

Androidの場合の例を以下に記載します。
以下のコードを実行すると、プラットフォームの言語を元に自動的に適したカルチャを設定します。

```csharp
// 共通プロジェクトのAppクラスでAppResoucesのカルチャを設定するメソッドを追加
public partial class App : Application
{
	public static void SetCulture(CultureInfo culture)
	{
		AppResources.Culture = culture;
	}
	...
}

// AndroidプロジェクトのMainActivityクラスでプラットフォームの言語を元にカルチャを設定
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
	protected override void OnCreate(Bundle savedInstanceState)
	{
		// Androidプラットフォームでの言語を取得
		var androidLocale = Java.Util.Locale.Default;
		// ja_JP を ja-JP に変換
		string netLanguage = androidLocale.ToString().Replace("_", "-");
		// プラットフォームが日本語ならばカルチャを日本語、それ以外ならカルチャを英語とする
		var culture = (netLanguage == "ja" || netLanguage == "ja-JP")
			? new System.Globalization.CultureInfo("ja-JP")
			: new System.Globalization.CultureInfo("en-US");
		// カルチャを設定する
		App.SetCulture(culture);
		...
	}
	...
}
```

# 5. まとめ
日本語を既定の言語にして、多言語対応する方法を紹介しました。
本稿で説明したソースコードは以下から取得できます。
[GitHubのソースコード](https://github.com/kojimadev/XamarinGlobalizeSample)