Windows ランタイムアプリからはプロジェクト内に多言語化リソースを用意することで多言語対応がやりやすくなっています
上記の Strings フォルダ配下に配置した en-US/Resources.resw
と ja-JP/Resources.resw
がその多言語化リソースになります
フォルダ名の en-US や ja-JP は言語と地域を合わせたロケールを表すコードで、OS の地域と言語の設定により実際に参照されるリソースが自動的に切り替わる仕組みになっています
Resource.resw の中身は上記のような感じで記載した場合、画面に表示するには XAML を下記のように記述します
<TextBlock x:Uid="Description_EditLogo"/>
x:Uid
*1 にキー名を入力すると、Text プロパティには Resources.resw 内に登録されている {キー名}.Text が暗黙的に設定されるというわけです
通常はこのやり方で問題ないと思いますが、ViewModel のプロパティに多言語化リソースの値を指定したいというときには XAML ではなくC# コードベースで参照する必要があります・・・これをどうするか
今回は UWP で最新の Prism × Unity ライブラリを利用した場合限定ですが C# コードから多言語化対応リソースを取得する方法を試してみました
まずは App.xaml.cs の初期化処理を修正
/// <summary> /// Provides application-specific behavior to supplement the default Application class. /// </summary> sealed partial class App : PrismUnityApplication { ~ 中略 ~ /// <summary> /// Initialize application event handler /// </summary> /// <param name="args">Activated event arguments</param> /// <returns>Task</returns> protected override Task OnInitializeAsync(IActivatedEventArgs args) { // Registration service instance to Unity container this.Container.RegisterInstance<IResourceLoader>(new ResourceLoaderAdapter(new ResourceLoader()), new ContainerControlledLifetimeManager()); this.Container.RegisterInstance<INavigationService>(this.NavigationService, new ContainerControlledLifetimeManager()); // Customize title bar colors var titleBar = ApplicationView.GetForCurrentView().TitleBar; titleBar.BackgroundColor = Colors.Black; titleBar.ForegroundColor = Colors.White; titleBar.InactiveBackgroundColor = Colors.Black; titleBar.InactiveForegroundColor = Color.FromArgb(0xff, 0x66, 0x66, 0x66); titleBar.ButtonBackgroundColor = Colors.Black; titleBar.ButtonForegroundColor = Colors.White; titleBar.ButtonInactiveBackgroundColor = Colors.Black; titleBar.ButtonInactiveForegroundColor = Color.FromArgb(0xff, 0x66, 0x66, 0x66); titleBar.ButtonHoverBackgroundColor = Color.FromArgb(0xff, 0x17, 0x17, 0x17); titleBar.ButtonHoverForegroundColor = Colors.White; titleBar.ButtonPressedBackgroundColor = Color.FromArgb(0xff, 0x34, 0x34, 0x34); titleBar.ButtonPressedForegroundColor = Colors.White; return base.OnInitializeAsync(args); } ~ 中略 ~ }
変更したのは次の一行
this.Container.RegisterInstance<IResourceLoader>(new ResourceLoaderAdapter(new ResourceLoader()), new ContainerControlledLifetimeManager());
UnityContainer に IResourceLoader のインタフェースで ResourceLoaderAdapter のインスタンスを登録しました・・・Unity に頼りまくりの書き方です
その後 ViewModel 側を下記のように記述
/// <summary> /// TopPage ViewModel /// </summary> public partial class TopPageViewModel : ViewModel { /// <summary> /// Multi languages resource loader /// </summary> private IResourceLoader resource = null; /// <summary> /// Constructor /// </summary> public TopPageViewModel() { this.resource = ServiceLocator.Current.GetInstance<IResourceLoader>(); this.MenuItems = new ObservableCollection<MenuItemViewModel> { new MenuItemViewModel { DisplayName = this.resource.GetString("Menu_EditLogo/Text"), IconText = ((char)0xE71D).ToString(), Description = this.resource.GetString("Description_EditLogo/Text"), IsSelected = true, ContentType = typeof(EditViewModel), }, new MenuItemViewModel { DisplayName = this.resource.GetString("Menu_Settings/Text"), IconText = ((char)0xE713).ToString(), Description = this.resource.GetString("Description_Settings/Text"), IsSelected = false, ContentType = typeof(SettingsViewModel), }, }; this.SelectedMenuIndex = 0; } }
IResourceLoader のインスタンスを ServiceLocator 経由で取得し、下記のように GetString メソッドで引数のキー名に対応する多言語化リソースを取得するようにしました
DisplayName = this.resource.GetString("Menu_EditLogo/Text"),
Resources.resw 側で「.」の部分は「/」に置き換えて取得するのがポイントです
<prism:VisualStateAwarePage x:Class="XamarinLogoMaker.Views.TopPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:XamarinLogoMaker.Views" xmlns:vm="using:XamarinLogoMaker.ViewModels" xmlns:prism="using:Prism.Windows.Mvvm" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" DataContext="{x:Bind ViewModel}" RequestedTheme="Dark"> <Grid> <SplitView Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" DisplayMode="CompactInline" IsPaneOpen="{Binding Path=IsChecked, ElementName=humbergerButton, Mode=OneWay}"> <SplitView.Pane> <Grid> <Grid.RowDefinitions> <RowDefinition Height="48"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <ToggleButton x:Name="humbergerButton" Width="48" Height="48"> <FontIcon Glyph="" /> </ToggleButton> <ListBox Grid.Row="1" Padding="0" Background="Transparent" ItemsSource="{x:Bind ViewModel.MenuItems}" SelectedIndex="{x:Bind ViewModel.SelectedMenuIndex, Mode=TwoWay}"> <ListBox.ItemContainerStyle> <Style TargetType="ListBoxItem"> <Setter Property="Padding" Value="0"/> <Setter Property="Height" Value="48"/> <Setter Property="HorizontalContentAlignment" Value="Stretch"/> </Style> </ListBox.ItemContainerStyle> <ListBox.ItemTemplate> <DataTemplate x:DataType="vm:MenuItemViewModel"> <StackPanel Orientation="Horizontal" ToolTipService.ToolTip="{x:Bind Description}" Height="48"> <FontIcon Glyph="{x:Bind IconText}" Width="48"/> <TextBlock Text="{x:Bind DisplayName}" Margin="15,0,0,0" VerticalAlignment="Center" FontSize="20"/> </StackPanel> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </Grid> </SplitView.Pane> <StackPanel Orientation="Vertical"> <Border Background="#FF1F1F1F" Height="48"/> </StackPanel> </SplitView> </Grid> </prism:VisualStateAwarePage>
対象となっているのは下記の箇所です
<ListBox.ItemTemplate> <DataTemplate x:DataType="vm:MenuItemViewModel"> <StackPanel Orientation="Horizontal" ToolTipService.ToolTip="{x:Bind Description}" Height="48"> <FontIcon Glyph="{x:Bind IconText}" Width="48"/> <TextBlock Text="{x:Bind DisplayName}" Margin="15,0,0,0" VerticalAlignment="Center" FontSize="20"/> </StackPanel> </DataTemplate> </ListBox.ItemTemplate>
左側のメニューラベルとツールチップを多言語化対応してみました
試しに実行
ちゃんと文字リソースが表示できましたね
ちなみに言語設定を下記のように English 優先に設定して
この状態でアプリを再起動すると
Strings/en-US/Resources.resw が参照されるようになり英語の表示に切り替わります
*1:x:Uid の詳細は次のページに説明があります(ただし英語かも;)