I18N-Portable
Xamarinと.Netに対応していて、UWPアプリでも利用できます。
i18nの初期化
アプリリソース内からローカリゼーションデータのテキストリソースを巡回するため、初期化にはAppクラスのAssemblyが必要です。
I18NPortable.I18N.Current
.Init(GetType().Assembly);
Localesフォルダに翻訳データを配置
デフォルトではKeyValuePairの形式で翻訳テキストを記述します。
- 翻訳データファイルはファイルのプロパティからビルドアクションを「埋め込みリソース」に指定する
- ファイル名は言語コード(ja-JPやen-USなど)である必要があります。
# key = value (the key will be the same across locales)
one = uno
two = dos
three = tres
four = cuatro
five = cinco
# Enums are supported
Animals.Dog = Perro
Animals.Cat = Gato
Animals.Rat = Rata
Animals.Tiger = Tigre
Animals.Monkey = Mono
# Support for string.Format()
stars.count = Tienes {0} estrellas
TextWithLineBreakCharacters = Line One\nLine Two\r\nLine Three
Multiline = Line One
Line Two
Line Three
Jsonで扱いたい場合は別途Readerの追加指定が必要
https://github.com/xleon/I18N-Portable#supported-formats
使用例:コードからローカライズ
YourName = 君の名は
var localizedText = "YourName".Translate();
使用例:Xamlでローカライズ
MarkupExtensionを利用する場合
using System;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Markup;
namespace I18NPortable.Xaml.Extensions
{
[MarkupExtensionReturnType(ReturnType = typeof(string))]
public class LocalizeExtension : MarkupExtension
{
public object Key { get; set; }
public object[] Parameters { get; set; }
protected override object ProvideValue()
{
if (Key is Enum enumValue)
{
return enumValue.Translate();
}
else if (Key is string keyStr)
{
if (Parameters is null)
{
return I18N.Current.Translate(keyStr);
}
else
{
return I18N.Current.Translate(keyStr, Parameters);
}
}
else
{
throw new NotSupportedException("LocalizeExtension not supported Type: " + Key?.GetType().Name);
}
}
}
}
<Page xmlns:i18nExt="using:I18NPortable.Xaml.Extensions">
<Text Text="{i18nExt:Localize Key=YourName}" />
</Page>
ValueConverterを利用する場合
using System;
using Windows.UI.Xaml.Data;
using I18NPortable;
namespace MyApp.Views.Conveters
{
public sealed class LocalizeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value is Enum enumValue)
{
return enumValue.Translate();
}
else if (value is string stringValue)
{
if (parameter is string str)
{
return stringValue.Translate(str);
}
else if (parameter is object[] p)
{
return stringValue.Translate(p);
}
else
{
return stringValue.Translate();
}
}
else
{
throw new ArgumentException("Failed localize : " + value?.ToString());
}
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
}
<Page xmlns:localization="using:MyApp.Views.Conveters">
<prism:PrismApplication.Resources>
<localization:LocalizeConverter x:Key="LocalizeConverter" />
</prism:PrismApplication.Resources>
<Grid>
<TextBlock Text="{Binding Source=YourName, Converter={StaticResource LocalizeConverter}}" />
</Grid>
</prism:PrismApplication>
Resourcesにi18n.Currentを配置する場合
Resources["Strings"] = I18NPortable.I18N.Current;
Text="{Binding Source={StaticResource Strings}, Path=[YourName]}"
(UWPでチェックしただけなのでWPFで動作するかはわかりません)
MarkupExtensionとValueConverterの使い分けについて
Bindingが絡む動的な値に対応したいところではConverterを使い、それ以外の固定テキストのローカライズにはMarkupExtension(i18nExt:Localize)や Resourcesにi18n.Currentを配置する方の方法を使用してます。
本当はText="i18nExt:Localize Key={Binding SomeValue}"
みたいな書き方がしたいんですが、MarkupExtensionクラスがDependencyObjectを継承していないため無理な模様。(WPF/UWPの両方で無理)
MarkupExtension with binding parameters - stack overflow
https://stackoverflow.com/questions/10328802/markupextension-with-binding-parameters/10328974
また、MarkupExtensionの方について、WPFの場合は引数付きコンストラクタを定義するとKey=
を省いて記述出来ます。UWPではおそらく非対応。
注意点
本家で紹介されているViewModelからi18N.Currentをプロパティとして公開して{Binding Strings[key]}
とする方法でなければ言語変更に動的に対応することが出来ません。
ここで紹介したConveterを使った方法でアプリ内から言語を変更する場合には、ページのキャッシュを破棄してロードし直すか、言語変更後にアプリ再起動を促すUIを追加し再起動して貰う、などする必要があります。