张春 发表于 2024-8-6 22:10:29

WPF本地化/国际化,多语言切换

之前写过winformwinform使用本地化,中英文切换_winform 中英文切换_故里2130的博客-CSDN博客
根本的技术差不多,但是后来又发现了一个ResXManager工具,可以更好方便快捷的使用。
起首下载,网络欠好的话,去官网下载,然后安装,重启vs即可
https://i-blog.csdnimg.cn/blog_migrate/8a3a9c94db9f8fc4058bc84d5b58ada1.png
wpf做多语言切换
有许多种,可以使用自带的资源去做,就是使用xaml写key值,这种做法是最简单方便的,也是wpf独特使用的,假如有大量的翻译,那么需要人工去翻译,需要转折一次,此种方法就不说了。下面说2种使用.resx资源和ResXManager工具来做。唯一的利益,就是自带翻译功能,方便快捷。
第一种
1.此处使用.net6创建wpf项目,与.net framework不一样,.net framework要简单一些
创建如图项目
https://i-blog.csdnimg.cn/blog_migrate/821f294ad429e6948f26849a1b97e40e.png
2.打开ResXManager工具
https://i-blog.csdnimg.cn/blog_migrate/795d60df21eaa41dc21f1d456e7f322b.png
3.点击新增语言,增加几个语种
https://i-blog.csdnimg.cn/blog_migrate/2cb4de42539ebf4a06c5ae71f6e7e971.png

增加后,双击增加的语种,就会自动生成对应的.resx文件,想要多少种语言,就可以增加多少种
https://i-blog.csdnimg.cn/blog_migrate/47fa559035cb6b857632ba4bf7cc4d64.png
最后要修改一下后缀名,否则报错。
4. 点击翻译
此处,可以导出Excel,让别人翻译,然后再导入,也可以使用在线翻译的功能,点击翻译。
https://i-blog.csdnimg.cn/blog_migrate/6cfa91f596bb0386b6443db1f604cb33.png  
5.效果
https://i-blog.csdnimg.cn/blog_migrate/2fcb307339dffe389294ad8b597aedc4.png
6. 创建LanguageManager.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Resources;
using System.Text;

namespace WpfApp1.Resources
{
    public class LanguageManager : INotifyPropertyChanged
    {
      /// <summary>
      /// 资源
      /// </summary>
      private readonly ResourceManager _resourceManager;

      /// <summary>
      /// 懒加载
      /// </summary>
      private static readonly Lazy<LanguageManager> _lazy = new Lazy<LanguageManager>(() => new LanguageManager());   
      public static LanguageManager Instance => _lazy.Value;
      public event PropertyChangedEventHandler PropertyChanged;

      public LanguageManager()
      {
            //获取此命名空间下Resources的Lang的资源,Lang可以修改
            _resourceManager = new ResourceManager("WpfApp1.Resources.Lang", typeof(LanguageManager).Assembly);
      }

      /// <summary>
      /// 索引器的写法,传入字符串的下标
      /// </summary>
      /// <param name="name"></param>
      /// <returns></returns>
      /// <exception cref="ArgumentNullException"></exception>
      public string this
      {
            get
            {
                if (name == null)
                {
                  throw new ArgumentNullException(nameof(name));
                }
                return _resourceManager.GetString(name);
            }
      }

      public void ChangeLanguage(CultureInfo cultureInfo)
      {
            CultureInfo.CurrentCulture = cultureInfo;
            CultureInfo.CurrentUICulture = cultureInfo;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("item[]"));//字符串集合,对应资源的值
      }
    }
}
7.xaml界面
<Window x:Class="WpfApp1.MainWindow"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      xmlns:local="clr-namespace:WpfApp1.Resources"
      mc:Ignorable="d"
      xmlns:res="clr-namespace:System.Windows.Resources;assembly=PresentationFramework"
      
      Title="MainWindow" Height="450" Width="800">
    <StackPanel HorizontalAlignment="Center">
      <ComboBox x:Name="LanguageList" SelectedIndex="0" Margin="10" SelectionChanged="LanguageList_SelectionChanged"/>
      <!--这里绑定字符串的值,Source来源于语言的集合,Mode是响应的方式,有些需要,有些不需要-->
      <TextBlock FontSize="20" Margin="10" Text="{Binding , Source={x:Static local:LanguageManager.Instance}}"/>
      <TextBox FontSize="20" Margin="10"   Text="{Binding , Source={x:Static local:LanguageManager.Instance}, Mode=OneWay}"/>
      <Button FontSize="20" Margin="10" Content="{Binding , Source={x:Static local:LanguageManager.Instance}}"/>
    </StackPanel>
</Window>
8.配景
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using WpfApp1.Resources;

namespace WpfApp1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
      public MainWindow()
      {
            InitializeComponent();
            LanguageList.ItemsSource = new List<string>
            {
                "en-Us",
                "zh-CN",
                "ar"
            };
//this.DataContext = new Lazy<LanguageManager>(() => new LanguageManager()).Value;
      }

      /// <summary>
      /// 下拉框赋值语言的类型
      /// </summary>
      /// <param name="sender"></param>
      /// <param name="e"></param>
      private void LanguageList_SelectionChanged(object sender, SelectionChangedEventArgs e)
      {
            LanguageManager.Instance.ChangeLanguage(new CultureInfo((sender as ComboBox).SelectedItem.ToString()));
      }
    }
}
此处使用mvvm的话,可以直接绑定,1对1绑定语言 
9.效果
https://i-blog.csdnimg.cn/blog_migrate/86646614ec72a0c15bdefaf359a1ebc4.png
https://i-blog.csdnimg.cn/blog_migrate/c2edb051cc431b7f3f757bab64ee9cbf.png
https://i-blog.csdnimg.cn/blog_migrate/a0404f21f71f7dfd4bd5c25f3ed5aa44.png
第二种
1.起首创建一个程序
https://i-blog.csdnimg.cn/blog_migrate/156c714773996a09c256e12373c1e2e9.png
2.在Resources文件夹中,创建文件
https://i-blog.csdnimg.cn/blog_migrate/9934dc39f372abf473c018523ddf9bc6.png
3.GlobalClass.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;

namespace WpfApp2.Resources
{
    /// <summary>
    /// 全局类
    /// </summary>
    public static class GlobalClass
    {
      static bool? inDesignMode = null;
      /// <summary>
      /// 判断是设计器还是程序运行
      /// </summary>
      public static bool InDesignMode
      {
            get
            {
                if (inDesignMode == null)
                {
#if SILVERLIGHT
                  inDesignMode = DesignerProperties.IsInDesignTool;
#else
                  var prop = DesignerProperties.IsInDesignModeProperty;
                  inDesignMode = (bool)DependencyPropertyDescriptor.FromProperty(prop, typeof(FrameworkElement)).Metadata.DefaultValue;

                  if (!inDesignMode.GetValueOrDefault(false) && Process.GetCurrentProcess().ProcessName.StartsWith("devenv", StringComparison.Ordinal))
                        inDesignMode = true;
#endif
                }

                return inDesignMode.GetValueOrDefault(false);
            }
      }
      /// <summary>
      /// 语言改变通知事件
      /// </summary>
      public static EventHandler<EventArgs> LanguageChangeEvent;
      static Resource StringResource;
      static GlobalClass()
      {
            StringResource = new Resource();
      }
      /// <summary>
      /// 获取资源内容
      /// </summary>
      /// <param name="key"></param>
      /// <returns></returns>
      public static string GetString(string key)
      {
            return StringResource.GetString(key);
      }
      /// <summary>
      /// 改变语言
      /// </summary>
      /// <param name="language">CultureInfo列表(http://www.csharpwin.com/csharpspace/8948r7277.shtml)</param>
      public static void ChangeLanguage(string language)
      {
            CultureInfo culture = new CultureInfo(language);
            Thread.CurrentThread.CurrentCulture = culture;
            Thread.CurrentThread.CurrentUICulture = culture;

            StringResource.CurrentCulture = culture;

            if (LanguageChangeEvent != null)
            {
                LanguageChangeEvent(null, null);
            }
      }
    }
}
4.Resource.cs
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Resources;

namespace WpfApp2.Resources
{
    public interface IResource
    {
      string GetString(string name);
      CultureInfo CurrentCulture { set; }
    }
    public class Resource : IResource
    {
      private ResourceManager stringResource;
      //我们这样设置的时候ResourceManager会去查找MultilanguageTest.StringResource.en-us.resx,如果没有会查找MultilanguageTest.StringResource.resx
      private CultureInfo culture = new CultureInfo("zh-cn");   //默认值
      public Resource()
      {
            //MultilanguageTest.StringResource是根名称,该实例使用指定的System.Reflection.Assmbly查找从指定的跟名称导出的文件中包含的资源
            //此处注意WpfApp2.Resources.Lang,Lang是资源文件的名字
            stringResource = new ResourceManager("WpfApp2.Resources.Lang", typeof(Resource).Assembly);
      }
      /// <summary>
      /// 通过资源名称获取资源内容
      /// </summary>
      /// <param name="name"></param>
      /// <returns></returns>
      public string GetString(string name)
      {
            return stringResource.GetString(name, culture);
      }
      /// <summary>
      /// 改变当前的区域信息,ResourceManager可以通过当前区域信息去查找.resx文件
      /// </summary>
      public CultureInfo CurrentCulture
      {
            set
            {
                culture = value;
            }
      }
    }
}
5.StringResourceExtension.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Data;
using System.Windows.Markup;

namespace WpfApp2.Resources
{
   
    public class StringResourceExtension : MarkupExtension, INotifyPropertyChanged
    {
      /// <summary>
      /// 资源的名称,与资源文件StringResource.resx对应
      /// </summary>
      
      public string Key
      {
            get;
            set;
      }
      string _DefaultValue;
      /// <summary>
      /// 默认值,为了使在设计器的情况时把默认值绑到设计器
      /// </summary>
      public string DefaultValue
      {
            get
            {
                return _DefaultValue;
            }
            set
            {
                _DefaultValue = value;
            }
      }
      string _Value;
      /// <summary>
      /// 资源的具体内容,通过资源名称也就是上面的Key找到对应内容
      /// </summary>
      public string Value
      {
            get
            {
                ///如果为设计器模式,本地的资源没有实例化,我们不能从资源文件得到内容,所以从KEY或默认值绑定到设计器去
                if (GlobalClass.InDesignMode)
                {
                  if (Key != null && DefaultValue != null)
                        return DefaultValue;
                  if (Key == null && DefaultValue != null)
                        return DefaultValue;
                  if (Key != null && DefaultValue == null)
                        return Key;
                  if (Key == null && DefaultValue == null)
                        return "NULL";
                }
                else
                {
                  if (Key != null)
                  {
                        string strResault = null;
                        try
                        {
                            strResault = GlobalClass.GetString(Key);
                        }
                        catch
                        {
                        }
                        if (strResault == null)
                        {
                            strResault = _DefaultValue;
                        }
                        return strResault;
                  }
                }
                return _Value;
            }
            set
            {
                _Value = value;
            }
      }
      public StringResourceExtension(string key)
            : this()
      {
            Key = key;
            GlobalClass.LanguageChangeEvent += new EventHandler<EventArgs>(Language_Event);
      }
      public StringResourceExtension(string key, string DefaultValue)
            : this()
      {
            Key = key;
            _DefaultValue = DefaultValue;
            GlobalClass.LanguageChangeEvent += new EventHandler<EventArgs>(Language_Event);

      }
      public StringResourceExtension()
      {
      }
      /// <summary>
      /// 每一标记扩展实现的 ProvideValue 方法能在可提供上下文的运行时使用 IServiceProvider。然后会查询此 IServiceProvider 以获取传递信息的特定服务
      ///当 XAML 处理器在处理一个类型节点和成员值,且该成员值是标记扩展时,它将调用该标记扩展的 ProvideValue 方法并将结果写入到对象关系图或序列化流,XAML 对象编写器将服务环境通过 serviceProvider 参数传递到每个此类实现。
      /// </summary>
      /// <param name="serviceProvider"></param>
      /// <returns></returns>
      public override object ProvideValue(IServiceProvider serviceProvider)
      {
            IProvideValueTarget target = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;

            Setter setter = target.TargetObject as Setter;
            if (setter != null)
            {
                return new Binding("Value") { Source = this, Mode = BindingMode.OneWay };
            }
            else
            {
                Binding binding = new Binding("Value") { Source = this, Mode = BindingMode.OneWay };
                return binding.ProvideValue(serviceProvider);
            }
      }
      public event PropertyChangedEventHandler PropertyChanged;

      static readonly System.ComponentModel.PropertyChangedEventArgs
            valueChangedEventArgs = new System.ComponentModel.PropertyChangedEventArgs("Value");

      protected void NotifyValueChanged()
      {
            if (PropertyChanged != null)
                PropertyChanged(this, valueChangedEventArgs);
      }
      /// <summary>
      /// 语言改变通知事件,当程序初始化的时候会绑定到全局的GlobalClass.LanguageChangeEvent事件
      /// </summary>
      /// <param name="sender"></param>
      /// <param name="e"></param>
      private void Language_Event(object sender, EventArgs e)
      {
            //通知Value值已经改变,需重新获取
            NotifyValueChanged();
      }
    }
}
6.创建3个语言的文件
https://i-blog.csdnimg.cn/blog_migrate/7b0c467dab8eff6b72ccfd496ff80863.png
使用ResXManager工具进行翻译,参考上面的细节
https://i-blog.csdnimg.cn/blog_migrate/2d7950f2eb308173b58de55a536382ef.png
7.效果
https://i-blog.csdnimg.cn/blog_migrate/63f3e49d7139f6acd665899292dda768.png
https://i-blog.csdnimg.cn/blog_migrate/64434db60b257f9ff909b121582a20fc.png
https://i-blog.csdnimg.cn/blog_migrate/5bceeb39c18b0e299050c2d6370cf6c7.png
2种方式都可以,第一种要简单一点。 
源码:
https://download.csdn.net/download/u012563853/87944124


泉源:WPF本地化/国际化,多语言切换_wpf 国际化_故里2130的博客-CSDN博客

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: WPF本地化/国际化,多语言切换