【问题标题】:How do I set the UI language for a multi-threaded .NET process, independent of the OS language?如何独立于操作系统语言为多线程 .NET 进程设置 UI 语言?
【发布时间】:2011-11-29 14:23:39
【问题描述】:

问题:

我有一个在 Windows 7 上开发的 C# .NET 2.0 应用程序,它已经翻译了多种语言的资源(例如,zh-CHS 用于中文,es 用于西班牙语等.).

我有一个客户想要用英语运行他们的 Windows 7 操作系统,但用西班牙语 (es) 运行我的 .NET 应用程序。

我的应用程序是多线程的,所以仅仅改变主 GUI 线程的文化并不足以满足我的需求(相信我,我试过了)。这是因为通过 GUI 向用户显示的其他字符串是在其他线程上生成的。为了获得 100% 的完整覆盖率,我需要手动设置每个单独线程的文化,以确保资源文件中的所有文本都使用正确的语言。因为我的产品基本上是其他开发组编写的其他插件的框架,所以我无法控制在其他插件中创建的线程中执行的操作。因此,手动更改每个线程的区域性不是一个有效的选项。

我正在寻找一种方法来设置应用程序的整体语言,而无需更改任何操作系统用户设置。

在做一些研究时,我发现了以下为进程设置首选 UI 语言的方法:SetProcessPreferredUILanguages

在阅读完此内容后,看来此调用正是我所寻找的。但是,当我在 C# 应用程序的 Main 方法中实现此调用时,它似乎没有做任何事情。

以下代码的返回值为真,但我从未看到我的 GUI 应用程序以西班牙语显示文本。

    [DllImport("Kernel32.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
    public static extern Boolean SetProcessPreferredUILanguages(UInt32 dwFlags, String pwszLanguagesBuffer, ref UInt32 pulNumLanguages);

    public void SetLanguages()
    {
        uint numLangs = 0;
        string[] langs = new string[3];
        uint MUI_LANGUAGE_NAME = 0x8; // Use ISO language (culture) name convention

        langs[0] = "es\u0000";
                langs[1] = "zh-CHS\u0000";
        langs[2] = "en-US\u0000";

        numLangs = (uint)langs.Length;

        if (SetProcessPreferredUILanguages(MUI_LANGUAGE_NAME, String.Concat(langs), ref numLangs))
        {
            Console.WriteLine("Successfully changed UI language");
        }
    }

我是否还缺少其他东西才能成功运行加载了西班牙资源的 GUI 应用程序?

我正在尝试为 Building MUI Applications 实现 MSDN 页面底部表格的第二个选项,我在其中具有特定于应用程序的 UI 语言设置,并希望实现资源加载所需的结果:

应用程序调用 MUI API 来设置应用程序特定的 UI 语言或进程首选的 UI 语言,然后调用标准资源 加载函数。资源以设置的语言返回 应用程序或系统语言。

我已调用成功设置流程首选 UI 语言,但我的资源没有以我期望的语言加载。一位评论者提到此调用仅适用于非托管资源,我无法 100% 验证,但行为似乎表明情况确实如此。

我不是唯一一个尝试过以这种方式实现 .NET 应用程序的人。令人沮丧的是,没有更多关于如何执行此操作的信息。

提前致谢,

凯尔

【问题讨论】:

  • 您能否描述一下仅将 CurrentUICulture 设置为西班牙语时发现的问题?也就是说,为什么这还不够?
  • 更新了我的描述以解释为什么这还不够。
  • 您是否排除了 a) 不在后台线程中生成翻译字符串,而是生成 ui 可以翻译的资源键,或者 b) 将所需的文化从 ui 传递给工作线程,以便它可以返回所需文化的字符串?感觉就像使用线程“currentculture”在工作线程上进行翻译是错误的(尽管我无法真正说出为什么......)
  • 该函数只对非托管资源的资源加载器有影响,对 .NET 资源没有影响。没有好的方法可以做到这一点,你很早就遇到了问题,但线程池线程是典型的麻烦制造者。建议您的用户购买 Windows Ultimate 版本的许可证,以便她可以在语言之间动态切换。还确保 Windows 对话框具有正确的语言。
  • @HansPassant:我更新了我对这个主题所做的更多研究的问题描述。从我读过的所有内容(除非我遗漏了一些重要的细节),如果我设置进程首选的 UI 语言,我希望所有资源都使用指定的语言加载:msdn.microsoft.com/en-us/library/…

标签: multithreading internationalization c#-2.0 cultureinfo currentuiculture


【解决方案1】:

从 .NET 4.5 开始,CultureInfo.DefaultThreadCurrentCulture 属性允许您为当前应用程序域中的线程设置默认区域性。除此之外,我还没有找到优雅地做到这一点的方法。

以下代码将向您展示如何重现此行为:

using System;
using System.Globalization;
using System.Threading;
using System.Threading.Tasks;

namespace SimpleMultithreadedTestForCulture {

  class Program {

    static void Main() {
      const double value = 12345.78;
      Console.WriteLine("Value from local thread - default culture: {0}", value.ToString("C"));
      CultureInfo myCulture = new CultureInfo(Thread.CurrentThread.CurrentUICulture.Name);
      myCulture.NumberFormat.CurrencySymbol = "₤";
      Thread.CurrentThread.CurrentUICulture = myCulture;
      Thread.CurrentThread.CurrentCulture = myCulture;
      Console.WriteLine("Value from local thread - 'my' culture: {0}", value.ToString("C"));
      Task.Factory.StartNew(() => Console.WriteLine("Value from a different thread: {0}", value.ToString("C")));
      Console.ReadKey();
    }

  }

}

输出:

Value from local thread - default culture: $12,345.78
Value from local thread - 'my' culture: £12,345.78
Value from a different thread: $12,345.78

这是一个related question,它可能为 .NET 4.0 或更早版本提供解决方案,使用帮助程序类来创建您的线程。尽管他们在那里谈论 WPF,但它也适用于其他 .NET 多线程应用程序。

我知道您不想改变每个新线程的文化,但我想不出任何其他有效的替代方案。

【讨论】:

    【解决方案2】:

    根据此处的反馈和其他研究,我得出的结论是,使用 4.5 之前的 .NET 版本无法为多线程 .NET 应用程序设置独立于操作系统语言的文化。

    这真是太可惜了。

    我们最近升级了我们的软件以使用 .NET 4.0。

    【讨论】:

      【解决方案3】:

      您的代码存在一些问题。不幸的是,问题的修复并没有带来所要求的行为。

      代码应该是:

      [DllImport("Kernel32.dll", ExactSpelling = true, CharSet = CharSet.Unicode)]
      public static extern Boolean SetProcessPreferredUILanguages(UInt32 dwFlags,
        String pwszLanguagesBuffer, ref UInt32 pulNumLanguages);
      
      public void SetLanguages()
      {
        uint numLangs = 0;
        string[] langs = new string[4];
        uint MUI_LANGUAGE_NAME = 0x8; // Use ISO language (culture) name convention
      
        langs[0] = "es\0";
        langs[1] = "zh-CHS\0";
        langs[2] = "en-US\0";
        langs[3] = "\0"; //double null ending
      
        numLangs = (uint)langs.Length;
      
        if (SetProcessPreferredUILanguages(MUI_LANGUAGE_NAME, String.Concat(langs), ref numLangs))
        {
          if (numLangs == langs.Length - 1)
            Console.WriteLine("Successfully changed UI language");
          else if (numLangs < 1)
            Console.WriteLine("No language could be set");
          else
            Console.WriteLine("Not all languages were set");
        }
        else
          Console.WriteLine("No language could be set");
      }
      

      pwszLanguagesBuffer 定义为 PCZZWSTR,表示宽字符串列表(因此是 Charset.Unicode),字符串以零字符结尾(无需使用 unicode 转义序列),列表以双零字符结束(因此新的“空语言”;另一种方法是将双\0放在最后一种语言)。我还添加了新的调试描述,以测试当前设置的语言,但不幸的是我无法在此处(工作中)测试它,因为该功能需要 Win7。


      但是有一种完全不同的方法可以做到这一点。见AppLocale

      我不能保证它适用于你的情况。该应用程序需要Windows XP 和更新版本(在Windows 7 上安装有一个小问题;记得以管理员模式开始安装,否则无法运行)。该应用程序还有另一个更大的问题。从中欧转向西方是不可能的。但它适用于中欧到希腊,以及其他具有不同特征的国家。

      我使用此应用程序为某些国家/地区开发较旧的(非 unicode)应用程序。不幸的是,它在开始时给出了一个麻烦的警告......

      【讨论】:

      • 此外,语言环境名称 eszh-CHS 解释不正确。你应该分别使用es-ESzh-CN(小写也可以),正如我在stackoverflow.com/a/52103357/4454665中描述的那样。
      猜你喜欢
      • 1970-01-01
      • 2013-06-14
      • 2017-12-25
      • 2013-04-19
      • 2012-06-17
      • 1970-01-01
      • 1970-01-01
      • 2020-07-15
      • 2011-03-22
      相关资源
      最近更新 更多