Opciones de internacionalización para WinUI 3

WinUI 3 es totalmente compatible con la internacionalización. Dicho esto, el método más fiable actualmente requiere reiniciar la aplicación después de cambiar el idioma para garantizar un funcionamiento correcto. Aunque técnicamente es posible actualizar dinámicamente el idioma de la interfaz sin reinicios, su implementación resulta demasiado compleja. He dedicado mucho tiempo a solucionar errores en este aspecto, enfrentándome en repetidas ocasiones a textos rotos o cierres inesperados al cambiar de idioma. Tras numerosos ensayos y correcciones, he definido un flujo de trabajo estable para la internacionalización en WinUI 3, que explicaré a continuación.

En primer lugar, añade la lógica de inicialización de idioma en el código asociado a App.xaml.cs. Si el usuario no ha seleccionado un idioma personalizado, la aplicación utilizará el idioma predeterminado del sistema operativo; si el idioma del sistema no está incluido en la lista de idiomas admitidos, se recurrirá al inglés.

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
    };
}Lenguaje del código: PHP (php)

Esta configuración se puede optimizar. Para mantener un código más limpio, no insertes la lista de idiomas admitidos de forma fija aquí. En su lugar, agrúpalos en una clase auxiliar estática, de modo que puedas reutilizar la misma lista en cualquier parte donde gestiones la configuración de idiomas.

    public class Languages
    {
        public static List<string> supportLangs = new() { "zh-CN", "en-US", "th-TH" };
    }Lenguaje del código: PHP (php)

La función principal del bloque de código anterior es configurar el idioma de la aplicación al iniciarse: carga primero la preferencia guardada por el usuario, usa el idioma del sistema si no hay ajustes personalizados y establece el inglés como última opción de respaldo.

Crear una página exclusiva para cambiar de idioma

Necesitamos una interfaz dedicada para que los usuarios modifiquen el idioma de visualización, por lo que he creado una página independiente destinada exclusivamente a la configuración de idiomas.

<?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>
Lenguaje del código: HTML, XML (xml)

A continuación se muestra el código C# correspondiente del backend:

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();
        }


    }
}
Lenguaje del código: HTML, XML (xml)

Cada vez que el usuario guarda una nueva preferencia de idioma, se le solicita reiniciar la aplicación para que todo el texto de la interfaz se actualice por completo.

La implementación de la localización en WinUI 3 se divide en dos partes independientes: el marcado dentro de los archivos XAML y la lectura de textos desde el código C# del backend.

Para los elementos XAML, utiliza el atributo x:Uid tal como se muestra en el ejemplo:

<TextBlock x:Uid="Txt_LangTip" FontSize="22"/>Lenguaje del código: HTML, XML (xml)

A continuación, configura la estructura de carpetas de recursos de localización: crea una carpeta raíz llamada Strings en tu proyecto y añade subcarpetas con los códigos de cultura correspondientes, por ejemplo en-US para el inglés estadounidense. Dentro de cada carpeta de idioma, crea un archivo Resources.resw con pares clave-valor para las traducciones.

  <data name="Btn_Save.Content" xml:space="preserve">
      <value>Guardar</value>
  </data>Lenguaje del código: HTML, XML (xml)

El campo name actúa como clave de búsqueda. El sufijo .Content indica la propiedad del control objetivo. El elemento Txt_LangTip mencionado anteriormente se define de la siguiente manera:

<data name="Txt_LangTip.Text" xml:space="preserve"><value>Seleccionar idioma de la aplicación</value></data>Lenguaje del código: HTML, XML (xml)

Se añade .Text aquí porque nos referimos específicamente a la propiedad Text del control TextBlock.

Para los textos dinámicos en el código C#, primero debes crear una instancia de ResourceLoader:

 private readonly ResourceLoader _resLoader;

 public LanguageSettingPage()
 {
     InitializeComponent();
     _resLoader = new ResourceLoader();Lenguaje del código: PHP (php)

Tras la inicialización, puedes obtener las cadenas localizadas en cualquier parte mediante el método GetString:

Title = _resLoader.GetString("Dlg_LangChange_Title"),Lenguaje del código: JavaScript (javascript)

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *