WinUI 3 국제화 옵션

WinUI 3는 국제화 기능을 완벽하게 지원합니다. 그렇지만 현재 가장 안정적인 방식은 언어를 전환한 후 애플리케이션을 재시작해야 모든 화면이 정상적으로 표시됩니다. 기술적으로 재시작 없이 UI 언어를 동적으로 갱신하는 것도 가능하지만 구현 난이도가 매우 높습니다. 저는 오랜 시간 문제 해결 작업을 진행했으며, 언어 전환 후 UI 텍스트가 깨지거나 앱이 비정상 종료되는 문제를 여러 차례 겪었습니다. 수많은 시행착오 끝에 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를 추가한 이유는 TextBlock 컨트롤의 Text 속성을 지정하기 때문입니다.

C# 코드 내 동적 텍스트를 사용하려면 먼저 Resource

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다