티스토리 뷰

WPF .NET

Localization in WPF

kaki104 2021. 3. 22. 10:00
반응형

WPF에서 기본 로컬라이징 방식은 x:Uid를 이용 합니다. 자세한 내용은 여기를 참고합니다.

그런데, 이 방법은 몇가지 불편한 부분이 있습니다. 

예를 들어 "이름"을 로컬라이제이션 한다고 하면 이 "이름'이 Text 프로퍼티에 들어갈때와 Content 프로퍼티에 들어갈때 각각 정의를 해놓고 사용해야하는 부분과 ViewModel에서 직접꺼내 사용하기도 불편 합니다. 그래서 저는 DynamicResource라는 녀석을 만들어서 사용하고 있습니다. 그래서, 이번에 기존에 사용하던 클래스를 약간 수정을 하면서 실행되는 셈플 코드와 함께 공개하게 되었습니다.

 

여기에 Prism의 기능을 넣고, Telerik 컨트롤의 리소스까지 관리하기 위한 몇가지를 더 추가하면 완벽하게 사용하시는데 문제가 없을 것입니다.

DynamicResource.cs

using CoreWpfLocalization.Strings;
using System;
using System.Dynamic;
using System.Globalization;
using System.Linq;
using System.Resources;
using System.Threading;
using System.Windows;
using System.Windows.Markup;

namespace CoreWpfLocalization
{
    /// <summary>
    /// 다이나믹 리소스 - 모든 택스트 리소스는 여기를 통해서 출력됨
    /// </summary>
    public class DynamicResource : DynamicObject
    {
        public event EventHandler<string> LanguageChanged;
        /// <summary>
        /// 윈도우 리소스로더
        /// </summary>
        private readonly ResourceManager _resourceManager;
        private CultureInfo _clutureInfo;

        /// <summary>
        /// 생성자
        /// </summary>
        public DynamicResource()
        {
            _resourceManager = new ResourceManager(typeof(Resource));
        }

        #region 기본 기능

        /// <summary>
        /// 프로퍼티로 호출
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public string this[string id]
        {
            get
            {
                //1. 리소스에서 값 조회
                if (string.IsNullOrEmpty(id)) return null;
                string str = _resourceManager.GetString(id, _clutureInfo);
                if (string.IsNullOrEmpty(str))
                //2. 없으면 키 반환
                {
                    str = id;
                }
                return str;
            }
        }

        /// <summary>
        /// 이름으로 호출
        /// </summary>
        /// <param name="binder"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            string id = binder.Name;
            string str = _resourceManager.GetString(id, _clutureInfo);
            if (string.IsNullOrEmpty(str))
            {
                str = id;
            }
            result = str;
            return true;
        }

        #endregion

        /// <summary>
        /// 런타임 언어 변경
        /// </summary>
        /// <param name="languageCode"></param>
        public void ChangeLanguage(string languageCode)
        {
            _clutureInfo = new CultureInfo(languageCode);
            Thread.CurrentThread.CurrentCulture = _clutureInfo;
            Thread.CurrentThread.CurrentUICulture = _clutureInfo;
            //윈도우의 언어코드 변경
            foreach (Window window in Application.Current.Windows.Cast<Window>())
            {
                if (!window.AllowsTransparency)
                {
                    window.Language = XmlLanguage.GetLanguage(_clutureInfo.Name);
                }
            }

            if (LanguageChanged != null)
            {
                LanguageChanged.Invoke(this, _clutureInfo.Name);
            }
        }
    }
}

Resource.resx

Resource.resx 파일은 기본 리소스라고 보시면 됩니다. Resource.en-US.resx, Resource.ko-KR.resx는 각 언어별로 하나씩 존재하면 됩니다.

리소스 파일 내부에 속성이 있는데 Resource.resx 파일은 Public으로 설정하시고 나머지 언어별 파일은 설정하지 않습니다.

DynamicResource 사용

중요 사항

애플리케이션 생성할 때 StaticResource로 등록해서 사용하는 방법이 가장 좋은 것 같습니다. 다른 방법으로 사용해 볼려고 시도는 해보았는데, 실시간 언어 변경이 않되서 GG쳤습니다.

<Application x:Class="CoreWpfLocalization.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:CoreWpfLocalization"
             Startup="Application_Startup">
    <Application.Resources>
        <local:DynamicResource x:Key="DR"/>
    </Application.Resources>
</Application>

전체 애플리케이션 사용하는 DynamicResource는 StaticResource에 등록된 저녀석입니다.

XAML에서 기본 사용법

<TextBlock x:Name="textBlock" Text="{Binding [WRD_HelloWorld], Source={StaticResource DR}}"/>

리소스키는 반드시 []를 입력해 주셔야 합니다.

Code에서 사용법

Title = _dr["WRD_WindowTitle"];

이 클래스를 이용해서 실시간 언어 변경을 구현한 프로젝트에서 아직까지 문제가 발생한 적은 없습니다.

전 기본 언어가 영어로 되어있어서 처음에 영문으로 출력되고, ko-KR 버튼을 누르면 즉시, ko-KR로 변경되면서 리소스가 다시 출력됩니다.

물론 모든 컨트롤에 자동으로 변경되지는 않습니다.

XAML에서 바인딩해 놓은 부분은 거의 가능합니다. 그 외의 부분은 LanguageChanged 이벤트를 이용해서 코드에서 처리를 하면 됩니다.

 

Telerik의 경우 GridView 컬럼 Header 부분을 TextBlock으로 입력하고 사용하면 실시간으로 변경 가능합니다.

 

셈플

프로젝트는 CoreWpfLocalization입니다.

WpfTest/CoreWpfLocalization at master · kaki104/WpfTest (github.com)

 

kaki104/WpfTest

Contribute to kaki104/WpfTest development by creating an account on GitHub.

github.com

 

반응형
댓글