WinUI 3 hỗ trợ đầy đủ tính năng quốc tế hóa. Tuy nhiên, phương pháp đáng tin cậy nhất hiện nay là khởi động lại ứng dụng sau khi đổi ngôn ngữ để đảm bảo giao diện hiển thị chính xác. Mặc dù về mặt kỹ thuật có thể cập nhật ngôn ngữ giao diện một cách động mà không cần khởi động lại, nhưng việc triển khai sẽ cực kỳ phức tạp. Tôi đã dành nhiều thời gian xử lý lỗi liên quan, thường xuyên gặp trường hợp nội dung chữ bị lỗi hoặc ứng dụng bị đóng đột ngột khi chuyển ngôn ngữ. Sau nhiều lần thử nghiệm và chỉnh sửa, tôi đã xây dựng quy trình quốc tế hóa ổn định cho WinUI 3, chi tiết sẽ được trình bày bên dưới.
Đầu tiên, thêm logic khởi tạo ngôn ngữ vào phần mã phụ của tệp App.xaml.cs. Nếu người dùng không chọn ngôn ngữ tùy chỉnh, ứng dụng sẽ sử dụng ngôn ngữ mặc định của hệ điều hành. Nếu ngôn ngữ hệ thống không nằm trong danh sách ngôn ngữ được hỗ trợ, ứng dụng sẽ chuyển sang sử dụng tiếng Anh.
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)
Cấu hình này vẫn có thể được tối ưu thêm. Để mã nguồn gọn gàng hơn, không nên viết cứng danh sách ngôn ngữ được hỗ trợ tại đây. Thay vào đó, hãy gom chúng vào lớp trợ giúp tĩnh, từ đó bạn có thể tái sử dụng cùng một danh sách ở mọi nơi cần quản lý cài đặt ngôn ngữ.
public class Languages
{
public static List<string> supportLangs = new() { "zh-CN", "en-US", "th-TH" };
}Code language: PHP (php)
Chức năng chính của khối mã trên là thiết lập ngôn ngữ khi ứng dụng khởi chạy: ưu tiên tải ngôn ngữ đã lưu bởi người dùng, sử dụng ngôn ngữ hệ thống nếu không có cài đặt tùy chỉnh và lấy tiếng Anh làm phương án dự phòng cuối cùng.
Tạo trang chuyên dụng để chuyển đổi ngôn ngữ
Chúng ta cần giao diện riêng để người dùng thay đổi ngôn ngữ hiển thị, nên tôi đã tạo một trang độc lập chỉ dùng cho việc cấu hình ngôn ngữ.
<?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)
Dưới đây là mã C# phía tương ứng:
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)
Mỗi khi người dùng lưu cài đặt ngôn ngữ mới, chúng tôi sẽ yêu cầu khởi động lại ứng dụng để toàn bộ nội dung chữ trên giao diện được cập nhật hoàn chỉnh.
Việc bản địa hóa trên WinUI 3 được chia thành hai phần riêng biệt: Thẻ đánh dấu trong tệp XAML và việc đọc nội dung chữ từ mã C# phía sau.
Đối với các thành phần XAML, hãy sử dụng thuộc tính x:Uid như ví dụ sau:
<TextBlock x:Uid="Txt_LangTip" FontSize="22"/>Code language: HTML, XML (xml)
Tiếp theo, thiết lập cấu trúc thư mục tài nguyên bản địa hóa: Tạo thư mục gốc tên Strings trong dự án, sau đó thêm các thư mục con theo mã vùng văn hóa, ví dụ en-US cho tiếng Anh Mỹ. Trong mỗi thư mục ngôn ngữ, tạo tệp Resources.resw chứa các cặp khóa-giá trị cho nội dung dịch thuật.
<data name="Btn_Save.Content" xml:space="preserve">
<value>Lưu</value>
</data>Code language: HTML, XML (xml)
Trường name đóng vai trò là khóa tra cứu. Phần hậu tố .Content chỉ đến thuộc tính tương ứng của điều khiển. Thành phần Txt_LangTip đã đề cập ở trên được định nghĩa như sau:
Chúng ta thêm .Text tại đây vì đang nhắm đến thuộc tính Text của điều khiển TextBlock.
Đối với nội dung chữ động trong mã C#, trước tiên cần khởi tạo đối tượng ResourceLoader:
private readonly ResourceLoader _resLoader;
public LanguageSettingPage()
{
InitializeComponent();
_resLoader = new ResourceLoader();Code language: PHP (php)
Sau khi khởi tạo xong, bạn có thể lấy chuỗi bản địa hóa ở mọi nơi bằng phương thức GetString:
Title = _resLoader.GetString("Dlg_LangChange_Title"),Code language: JavaScript (javascript)
