如何为多线程 .NET 进程设置独立于操作系统语言的 UI 语言?
问题:
我有一个在 Windows 7 上开发的 C# .NET 2.0 应用程序,该应用程序已翻译多种语言的资源(例如,中文的 zh-CHS,es em> 西班牙语等)。
我有一位客户希望以英语运行 Windows 7 操作系统,但以西班牙语 (es) 运行我的 .NET 应用程序。
我的应用程序是多线程的,因此仅更改主 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 应用程序,我是否还缺少其他内容?
我正在尝试实现 MSDN 页面底部表格的第二个选项 构建 MUI 应用程序,其中我具有应用程序特定的 UI 语言设置,并希望实现资源加载所需的结果:
应用程序调用 MUI API 来设置应用程序特定的 UI 语言或进程首选的 UI 语言,然后调用标准资源 加载函数。资源以设置的语言返回 应用程序或系统语言。
我已进行调用以成功设置流程首选 UI 语言,但我的资源未以我期望的语言加载。一位评论者提到此调用仅适用于非托管资源,我无法 100% 验证,但该行为似乎表明情况确实如此。
我不可能是唯一一个尝试以这种方式实现 .NET 应用程序的人。令人沮丧的是,没有更多关于如何执行此操作的信息。
Problem:
I have a C# .NET 2.0 application developed on Windows 7 that has translated resources for multiple languages (ex. zh-CHS for Chinese, es for Spanish, etc.).
I have a customer who wants to run their Windows 7 OS in English, but run my .NET application in Spanish (es).
My application is multi-threaded, so just changing the culture of the main GUI thread is not sufficient for my needs (trust me, I tried). This is because other strings displayed to the user through the GUI are generated on other threads. In order to get 100% complete coverage, I would need to set the culture of each individual thread manually to ensure all text from resource files is in the correct language. Because my product is basically a framework for other plugins that other development groups write, I don't have control over actions performed in threads created in other plugins. Because of this, manually changing the culture for each thread is not a valid option.
What I am looking for is a way to set the overall language for the application, without having to change any of the OS user settings.
In doing some research, I came across the following method for setting the preferred UI language for a process: SetProcessPreferredUILanguages
After reading up on this, it appears that this call is what I am looking for. However, when I implemented this call in the Main method of my C# application, it doesn't appear to do anything.
The return value from the following code is true, but I never see my GUI application displaying the text in Spanish.
[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");
}
}
Is there something else I am missing in order for this to successfully run my GUI application with the Spanish resources loaded?
I am trying to implement the 2nd option of the table at the bottom of the MSDN page for Building MUI Applications, where I have Application-specific UI language settings and want to achieve the desired result for resource loading:
Application calls MUI API to set application-specific UI languages or process-preferred UI languages and then calls standard resource
loading functions. Resources are returned in the languages set by the
application or system languages.
I have made the call to successfully set the process preferred UI languages, but my resources are not being loaded in the language I would expect. A commenter mentioned this call will only work for un-managed resources, which I could not verify 100%, but the behavior seems in indicate this is the case.
I can't be the only person who has ever tried to implement a .NET application in this manner. It's frustrating that there isn't more information about how to do this.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
从 .NET 4.5 开始, CultureInfo.DefaultThreadCurrentCulture 属性允许您设置当前应用程序域中线程的默认区域性。除此之外我还没有找到一种方法来优雅地做到这一点。
下面的代码将向您展示如何重现此行为:
输出:
这是一个 相关问题,其中包含 . NET 4.0 或更早版本,使用辅助类来创建线程。尽管他们在那里谈论 WPF,但它也适用于其他 .NET 多线程应用程序。
我知道您不想改变每个新线程的文化,但我想不出任何其他有效的替代方案。
Starting with .NET 4.5 the CultureInfo.DefaultThreadCurrentCulture property allows you to set the default culture for threads in the current application domain. Other than that I have not found a way to do this elegantly.
Here is the code that will show you how to reproduce this behavior:
Output:
Here is a related question with a possible solution for .NET 4.0 or before, using a helper class to create your threads. Even though they talk about WPF in there, it applies to other .NET multi-threaded applications too.
I know you did not want to change the culture for each of the new threads, but I cannot think of any other efficient alternatives for it.
根据此处的反馈和其他研究,我得出的结论是,使用 4.5 之前的 .NET 版本不可能为多线程 .NET 应用程序设置独立于操作系统语言的区域性。
这实在是一种耻辱。
我们最近升级了我们的软件以使用.NET 4.0。
Based on feedback here, and other research, I have come to the conclusion that setting the culture for a multi-threaded .NET application independent of the OS language is not possible using .NET versions older than 4.5.
This really is a shame.
We recently upgraded our software to use .NET 4.0.
您的代码有一些问题。不幸的是,问题的解决并没有带来所要求的行为。
代码应该是:
pwszLanguagesBuffer 被定义为 PCZZWSTR ,这意味着宽字符串列表(因此 Charset.Unicode),字符串以零字符结尾(不需要使用 unicode 转义序列)< /em>,列表以双零字符结尾(因此新的“空语言”;其他方法是将双\0放在最后一个语言)。我还添加了新的调试描述,以测试当前设置的语言,但不幸的是我无法在这里(在工作中)测试它,因为该功能需要Win7。
但有一种完全不同的方法可以做到这一点。请参阅 AppLocale
我不能保证它适合你的情况。该应用程序需要Windows XP及更高版本(在Windows 7上安装有一个小问题;记住以管理员模式开始安装,否则将无法工作)。该应用程序还有另一个更大的问题。从中欧转向西欧是不可能的。但从中欧到希腊,以及其他具有不同特征的国家,它都适用。
我使用此应用程序为某些国家/地区开发较旧的(非 unicode)应用程序。不幸的是,它在启动时给出了一个麻烦的警告......
Your code has a few problems. Unfortunately the fix of problems do not bring the requested behaviour.
The code should be:
The pwszLanguagesBuffer is defined as PCZZWSTR and that means list of wide string (therefore Charset.Unicode), string end by zero character (no need to use unicode escape sequence), list ends with double zero character (therefore new "empty language"; other way is to put double \0 to last language). I also added new debug description, to test currently set languages, but unfortunately I can not test it here (at work) because the function needs Win7.
But there is a fully different way how to do this. See AppLocale
I can not give a guarantee, that it will work in your case. The application needs Windows XP and newer (there is a small problem in installing on Windows 7; remember to start the installation in administrator mode, otherwise it would not work). The application has also another, bigger problem. It is not possible to switch from Central Europe to Western. But it works from Central Europe to Greece, and other countries with different characters.
I use this application to develop an older (non unicode) application for some countries. Unfortunately it gives a troublesome warning on start...