如何为多线程.NET进程设置UI语言,而不依赖于OS语言?

时间:2011-11-29 14:23:39

标签: multithreading internationalization c#-2.0 cultureinfo currentuiculture

问题:

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

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

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

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

在进行一些研究时,我遇到了以下方法来设置流程的首选UI语言:SetProcessPreferredUILanguages

在阅读完之后,看来这个电话就是我要找的。但是,当我在C#应用程序的Main方法中实现此调用时,它似乎没有做任何事情。

以下代码的返回值为true,但我从未看到我的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应用程序的人。令人沮丧的是,没有更多关于如何做到这一点的信息。

提前致谢,

凯尔

3 个答案:

答案 0 :(得分:0)

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

这真是太遗憾了。

我们最近将软件升级为使用.NET 4.0。

答案 1 :(得分:0)

从.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 :(得分:0)

您的代码存在一些问题。不幸的是,问题的解决不会带来请求的行为。

代码应为:

[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)应用程序。不幸的是,它在开始时给出了麻烦的警告......