ตัวเลือกการทำให้เป็นหลายภาษาสำหรับ WinUI 3

WinUI 3 รองรับการแปลภาษาได้อย่างสมบูรณ์ แม้กระนั้น วิธีที่เสถียรที่สุดในปัจจุบันคือการรีสตาร์ทแอปพลิเคชันหลังจากเปลี่ยนภาษา เพื่อให้แสดงผลได้อย่างสมบูรณ์ แม้ในทางเทคนิคจะสามารถรีเฟรชอินเทอร์เฟซภาษาแบบไดนามิกโดยไม่ต้องรีสตาร์ท แต่การพัฒนาจะซับซ้อนมากขึ้น ผมได้ใช้เวลามากในการแก้ไขปัญหานี้เอง และเคยพบปัญหาข้อความบนหน้าจอผิดปกติหรือแอปขัดข้องหลายครั้งหลังจากเปลี่ยนภาษา หลังจากทดลองและปรับแก้หลายครั้ง ผมได้จัดทำขั้นตอนการใช้งานระบบหลายภาษาสำหรับ WinUI 3 ที่มีเสถียรภาพ ซึ่งจะอธิบายรายละเอียดต่อไปนี้

ขั้นแรก ให้เพิ่มตรรกะการเริ่มต้นภาษาลงในโค้ดเบื้องหลังของ App.xaml.cs หากผู้ใช้ไม่ได้เลือกภาษาที่กำหนดเอง แอปจะใช้ภาษาเริ่มต้นของระบบปฏิบัติการ และหากภาษาของระบบไม่ได้อยู่ในรายการภาษาที่รองรับ แอปจะเปลี่ยนไปใช้ภาษาอังกฤษโดยอัตโนมัติ

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 ที่นี่ เนื่องจากต้องการเชื่อมโยงไปยังพร็อพเพอร์ตี้ Text ของคอนโทรล TextBlock โดยเฉพาะ

สำหรับข้อความแบบไดนามิกภายในโค้ด 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)

ใส่ความเห็น

อีเมลของคุณจะไม่แสดงให้คนอื่นเห็น ช่องข้อมูลจำเป็นถูกทำเครื่องหมาย *