zoukankan      html  css  js  c++  java
  • WPF 本地多语言总结

    去年底今年初,由于WPF项目,需要支持日本地区,要求可以切换语言。本篇文章汇总了一下,当时的调研的结果,和项目中所采用的方式。

    业务背景

    当时,软件主要针对的是英语国家。后来因业务需要,增加了日本地区,需要一套可切换的日语操作界面。
    一方面,由于软件早期没有考虑过多语言,改造幅度比较大;另一方面,由于翻译问题,需要日本方面,可以即时修改翻译的内容。总结一下需求:

    1. 软件需全面修改,包括前端界面,后端文字,数据库配置项等;
    2. 易扩展,易修改;
    3. 调整翻译内容后,可及时展现在界面上;
    4. 希望使用 Excel 文档,来作为编辑工具;

    我的调研

    我在网上查找了不少资料,简单实现简单的demo,整体倾向使用,微软推荐的方法。具体查找的资料如下:

    我的实现方法

    参考了上面的所有方法,根据项目的需求,实现一个demo。实现起来其实蛮简单的:

    1. 创建一个多语言通用类库,这里面只包含多语言的resx文件,resx 可以包含:字符串、图像、音频、文件、其他;
      多语言通用类库

    2. 使用ResXResourceManager,来编写多语言内容,这个插件的功能很强大,有复制粘贴,排序,检索,导入导出excel等;还可以选中代码,快速添加;很多功能有待发现;
      resx资源管理插件
      resx资源管理插件2

    3. 自动生成的cs代码,具有强类型引用,不易出错;

    /// <summary>
    ///   硬件自动名称..
    /// </summary>
    public static string HW_Auto_Name {
        get {
            return ResourceManager.GetString("HW_Auto_Name", resourceCulture);
        }
    }
    
    // 使用方式
    string tip = ResourceText.HW_Auto_Name;
    
    
    1. 在WPF的 xaml 界面使用方式,示例;
    xmlns:resx="clr-namespace:LanguageResources;assembly=LanguageResources"
    
    Content="{x:Static resx:ResourceText.HW_Auto_Name}"
    
    1. 最后通过VS的编译,生成一个语言文件夹ja-JP里面包含一个资源dllLanguageResources.resources.dll;
    2. 如果对翻译内容进行改变,则还额外需要的操作步骤;
      使用Resgen.exe将每个文本XML资源文件编译为二进制.resources文件。输出是一组文件,这些文件的文件名与.resx或.txt文件相同,但扩展名为.resources。
    Visual Studio 2017,在文件路径中找到resgen.exe;  
    %PROGRAMFILES(X86)%Microsoft SDKsWindowsv10.0AinNETFX 4.6.2 Tools
    esgen.exe  
    
    //执行resgen命令,生成LanguageResources.ResourceText.ja-JP.resources  
    resgen LanguageResources.ResourceText.ja-JP.resx  
    
    //执行al命令,生成LanguageResources.resources.dll  
    al -target:lib -embed:LanguageResources.ResourceText.ja-JP.resources -culture:ja-JP -out:LanguageResources.resources.dll  
    

    至此,所有的操作完全完成。虽然,我的方法使用微软推荐的方法,同时也有相应的辅助工具,提高了开发的效率;但是她有一个致命的缺点,是在改变翻译内容后,还需要执行额外的操作。对翻译人员的操作不友好~~。放弃!!!

    项目中使用的方法

    在我的方法被否决以后,心里还是有不甘心。后来项目组,安排另一位同事,开发了个Excel版本的,时隔三个月再来看,他的方法确实比我的要简单,实用。下面我就简单介绍一下:

    1. Excel 文件的读取,使用 NPOI;
    2. DependencyProperty.RegisterAttached WPF的依赖属性,通过网上查找的 DependencyProperty 资料,简单了解到它是WPF的精华所在;
    3. 在具体使用中,使用两种方式:1、根据Key值来获取翻译内容;2、根据完整英文内容,获取对应的翻译内容;

    具体的代码,简化如下:

    public class LanguageManager
    {
        public bool IsLangOut { get; set; }
        public string Language { get; set; }
    
        private string langFile;
        private static LanguageManager _Instance;
        private Dictionary<string, LanguageObject> StrMap;
    
        public static LanguageManager Instance
        {
            get { return _Instance == null ? _Instance = new LanguageManager() : _Instance; }
        }
    
        private LanguageManager()
        {
            IsLangOut = false;
            Language = "en_US";
            langFile = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logs", "LanguageError.log");
        }
    
        public bool Load(Func<string, Dictionary<string, LanguageObject>> LoadFile)
        {
            StrMap = LoadFile?.Invoke(Language);
            if (StrMap == null || StrMap.Count == 0) return false;
            else return true;
        }
    
        public string GetTranslateStr(string key)
        {
            if (string.IsNullOrEmpty(key) || StrMap == null) return string.Empty;
            if (StrMap.ContainsKey(key))
            {
                return StrMap[key].Trans;
            }
            else
            {
                Write2File($"no key: [{key}]
    ");
                return key;
            }
        }
    
        public string TranslateStr(string text)
        {
            if (Language == "en-US" || StrMap == null) return text;
    
            var lang = StrMap.Values.FirstOrDefault(p => p.English.Trim() == text.Trim());
            if (lang == null)
            {
                Write2File($"can't find value: [{text}]
    ");
                return text;
            }
            else
                return lang.Trans;
        }
    
        public void Write2File(string context)
        {
            if (!IsLangOut) return;
            System.IO.File.AppendAllText(langFile, context, System.Text.Encoding.UTF8);
        }
    }
    
    public class LanguageBehavior
    {
        public static readonly DependencyProperty KeyProperty = 
            DependencyProperty.RegisterAttached("Key", 
                typeof(string), typeof(LanguageBehavior), 
                new FrameworkPropertyMetadata(string.Empty, OnKeyChanged));
        private static void OnKeyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            Update(sender, e.NewValue as string);
        }
        public static void SetKey(DependencyObject dp, string value)
        {
            dp.SetValue(KeyProperty, value);
        }
        public static string GetKey(DependencyObject dp)
        {
            return dp.GetValue(KeyProperty) as string;
        }
    
        public static readonly DependencyProperty ToolTipProperty = 
            DependencyProperty.RegisterAttached("ToolTip", 
                typeof(string), typeof(LanguageBehavior), 
                new FrameworkPropertyMetadata(string.Empty, OnToolTipChanged));
        private static void OnToolTipChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            UpdateToolTip(sender, e.NewValue as string);
        }
        public static void SetToolTip(DependencyObject dp, string value)
        {
            dp.SetValue(ToolTipProperty, value);
        }
        public static string GetToolTip(DependencyObject dp)
        {
            return dp.GetValue(ToolTipProperty) as string;
        }
    
        private static void Update(DependencyObject sender, string key)
        {
            if (sender is TabItem)
                (sender as TabItem).Header = LanguageManager.Instance.GetTranslateStr(key);
            else if (sender is Label)
                (sender as Label).Content = LanguageManager.Instance.GetTranslateStr(key);
            else if (sender is ContentControl)
                (sender as ContentControl).Content = LanguageManager.Instance.GetTranslateStr(key);
            else if (sender is TextBlock)
                (sender as TextBlock).Text = LanguageManager.Instance.GetTranslateStr(key);
            else if (sender is Run)
                (sender as Run).Text = LanguageManager.Instance.GetTranslateStr(key);
            else if (sender is GridViewColumn)
                (sender as GridViewColumn).Header = LanguageManager.Instance.GetTranslateStr(key);
        }
    
        private static void UpdateToolTip(DependencyObject sender, string key)
        {
            if (!(sender is FrameworkElement)) return;
            (sender as FrameworkElement).ToolTip = LanguageManager.Instance.GetTranslateStr(key);
        }
    }
    
    在 XAML 的使用示例:  
    <Button x:Name="btn1" Height="40" Lang:LanguageBehavior.Key="K00259" />  
    <Button x:Name="btn2" Width="32" Height="32" Lang:LanguageBehavior.ToolTip="K00189" />  
    
    在 cs 的使用示例:  
    this.textError.Text = LanguageManager.Instance.GetTranslateStr("K00204");  
    this.textError.Text = LanguageManager.Instance.TranslateStr("Can not connect server");  
    

    总结

    在项目运行至今,很明显第二种方法的好,测试人员,运维人员,在可以完全不接触代码的情况下。只操作Excel 来实现文件内容的翻译,并且“实时”的反应到软件上。
    而我固执己见的“微软官方推荐方法”,并不适用于项目;好比是纸上谈兵的赵括,真的是越来越为当时的“倔强”,感到羞愧。。。

  • 相关阅读:
    安装VMtools vim编辑器的使用 压缩包命令 Linux下的用户管理 (第三天)
    VM虚拟机安装 常用Linux命令 网卡配置 (第二天)
    数据库的交互模式 常用的dos命令 (第一天)
    Validate US Telephone Numbers FreeCodeCamp
    Arguments Optional FreeCodeCamp
    Everything Be True FreeCodeCamp
    Binary Agents FreeCodeCamp
    Steamroller FreeCodeCamp
    Drop it FreeCodeCamp
    Smallest Common Multiple FreeCodeCamp
  • 原文地址:https://www.cnblogs.com/BenAndWang/p/12698401.html
Copyright © 2011-2022 走看看