WinUI 3 は多言語化に完全対応しています。しかし現時点で最も安定した運用方法は、言語切り替え後にアプリを再起動させ、正常な表示を実現する方式です。技術的には再起動なしでUIの言語を動的に更新することも可能ですが、実装の複雑度が大幅に上がります。私自身も長時間トラブルシューティングを行い、言語切り替え後にUI文字が崩れたりアプリがクラッシュしたりする問題に何度も遭遇しました。試行錯誤を重ねた結果、安定した WinUI 3 多言語化の実装フローを確立しました。以下で詳しく解説していきます。
まず App.xaml.cs のコードビハインドに言語初期化ロジックを追加します。ユーザーが任意の言語を設定していない場合、アプリはOSの既定言語を読み込みます。システム言語がサポートリストに存在しない場合は、英語にフォールバックします。
protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
{
const string LangSettingKey = "AppLanguageCode";
// All supported language tags of project
List<string> supportLangs = new() { "zh-CN", "en-US", "th-TH" };
string finalLang = "en-US"; // Default language
try
{
var localSetting = ApplicationData.Current.LocalSettings;
// ① Use saved language if local configuration exists
if (localSetting.Values.TryGetValue(LangSettingKey, out var savedVal))
{
finalLang = NormalizeLang(savedVal?.ToString() ?? "");
}
else
{
// ② No saved config: read system primary preferred language
string sysLang = ApplicationLanguages.Languages.FirstOrDefault() ?? "";
sysLang = NormalizeLang(sysLang);
// ③ Use system language if supported, otherwise fall back to English
finalLang = supportLangs.Contains(sysLang) ? sysLang : "en-US";
}
}
catch
{
// Ignore exception when running unpackaged, ApplicationData unavailable
}
ApplicationLanguages.PrimaryLanguageOverride = finalLang;
_window = new MainWindow();
_window.Activate();
}
/// <summary>
/// Standardize culture code: map system returned zh-Hans-CN to zh-CN
/// </summary>
private static string NormalizeLang(string lang)
{
if (string.IsNullOrEmpty(lang)) return lang;
return lang switch
{
"zh-Hans-CN" => "zh-CN",
"zh-Hant-TW" => "zh-TW", // For future Traditional Chinese support
_ => lang
};
}Code language: PHP (php)
このコードはさらに最適化できます。コードを簡潔にするため、ここでサポート言語リストをハードコーディングせず、静的ヘルパークラスにまとめてください。これにより、言語設定を変更する必要がある箇所すべてで同一のリストを参照できます。
public class Languages
{
public static List<string> supportLangs = new() { "zh-CN", "en-US", "th-TH" };
}Code language: PHP (php)
上記のコードブロックの主な役割は、アプリ起動時の言語設定を処理することです。優先的にユーザーが保存した言語設定を読み込み、独自設定が存在しない場合はシステム既定言語を使用し、最終的なフォールバック先として英語を設定しています。
言語切り替え用の専用ページを作成する
ユーザーが表示言語を変更するための専用インターフェースが必要なため、言語設定を管理する独立したページを作成します。
<?xml version="1.0" encoding="utf-8"?>
<Page
x:Class="ThaiTong.Pages.LanguageSettingPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ThaiTong.Pages"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
mc:Ignorable="d">
<StackPanel Padding="20" Spacing="24">
<TextBlock x:Uid="Txt_LangTip" FontSize="22"/>
<ComboBox x:Name="cbLangSelect" Width="320" Height="40" CornerRadius="6">
<ComboBoxItem Tag="zh-CN" Content="简体中文"/>
<ComboBoxItem Tag="en-US" Content="English"/>
<ComboBoxItem Tag="th-TH" Content="ภาษาไทย"/>
</ComboBox>
<StackPanel Orientation="Horizontal" Spacing="12">
<Button x:Name="btnSave" x:Uid="Btn_Save" Content="Save" Click="BtnSave_Click" Width="120" Height="38" CornerRadius="5"/>
</StackPanel>
<TextBlock Foreground="Gray" Text="Note: After saving, page will refresh to apply language."/>
</StackPanel>
</Page>
Code language: HTML, XML (xml)
対応するC#コードビハインドは以下の通りです。
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
using Microsoft.Windows.ApplicationModel.Resources;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using ThaiTong.Common;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.Globalization;
using Windows.Storage;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
namespace ThaiTong.Pages
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class LanguageSettingPage : Page
{
private const string LangSettingKey = "AppLanguageCode";
private string _currentLang;
private readonly ResourceLoader _resLoader;
public LanguageSettingPage()
{
InitializeComponent();
_resLoader = new ResourceLoader();
var setting = ApplicationData.Current.LocalSettings;
if (setting.Values.TryGetValue(LangSettingKey, out var saveLang))
{
_currentLang = saveLang.ToString();
}
else
{
string sysLang = ApplicationLanguages.Languages.FirstOrDefault() ?? "";
_currentLang = Languages.supportLangs.Contains(sysLang) ? sysLang : "en-US";
}
// Pre-select matched language in dropdown list
foreach (var item in cbLangSelect.Items)
{
if (item is ComboBoxItem cbi && cbi.Tag.ToString() == _currentLang)
{
cbLangSelect.SelectedItem = item;
break;
}
}
}
// Save selected language setting
private async void BtnSave_Click(object sender, RoutedEventArgs e)
{
if (cbLangSelect.SelectedItem is not ComboBoxItem selectItem) return;
string newLang = selectItem.Tag.ToString();
if (_currentLang == newLang) return;
ApplicationData.Current.LocalSettings.Values[LangSettingKey] = newLang;
_currentLang = newLang;
ApplicationLanguages.PrimaryLanguageOverride = newLang;
// Load localized text from resw resource file
ContentDialog tipDialog = new ContentDialog()
{
XamlRoot = this.XamlRoot,
Title = _resLoader.GetString("Dlg_LangChange_Title"),
Content = _resLoader.GetString("Dlg_LangChange_Content"),
CloseButtonText = _resLoader.GetString("Dlg_LangChange_BtnOk")
};
await tipDialog.ShowAsync();
}
}
}
Code language: HTML, XML (xml)
ユーザーが新しい言語設定を保存するたび、アプリの再起動を促すメッセージを表示し、すべての画面文字が完全に更新されるようにします。
WinUI 3 のローカライズ実装は二つの部分に分かれます。XAMLファイル内のマークアップと、C#コードビハインドでの文字参照処理です。
XAML要素には、サンプルのように x:Uid 属性を使用してください。
<TextBlock x:Uid="Txt_LangTip" FontSize="22"/>Code language: HTML, XML (xml)
次にローカライズ用リソースフォルダを構成します。プロジェクトのルートに Strings フォルダを作成し、各カルチャコード名のサブフォルダを追加します。例えば米国英語は en-US となります。各言語フォルダ内に Resources.resw ファイルを作成し、キーと値のペアで翻訳文字列を記述します。
<data name="Btn_Save.Content" xml:space="preserve">
<value>保存</value>
</data>Code language: HTML, XML (xml)
nameフィールドが検索用のキーとなり、後ろに付加した .Content は対象のコントロールプロパティを示します。先ほどの Txt_LangTip は以下のように定義します。
<data name="Txt_LangTip.Text" xml:space="preserve"><value>アプリの言語を選択</value></data>Code language: HTML, XML (xml)
ここで .Text を指定しているのは、TextBlock コントロールの Text プロパティを対象としているためです。
C#コード内の動的な文字列を扱う場合は、まず ResourceLoader インスタンスを初期化してください。
private readonly ResourceLoader _resLoader;
public LanguageSettingPage()
{
InitializeComponent();
_resLoader = new ResourceLoader();Code language: PHP (php)
初期化完了後、GetString メソッドを使って任意の場所でローカライズ文字列を取得できます。
Title = _resLoader.GetString("Dlg_LangChange_Title"),Code language: JavaScript (javascript)
