Как установить язык пользовательского интерфейса для многопоточного процесса.NET, независимо от языка ОС?
Проблема:
У меня есть приложение C# .NET 2.0, разработанное для Windows 7, которое переводит ресурсы на несколько языков (например, zh-CHS для китайского, es для испанского и т. Д.).
У меня есть клиент, который хочет запустить свою ОС Windows 7 на английском языке, но запустить приложение.NET на испанском (-ых).
Мое приложение является многопоточным, поэтому простого изменения культуры основного потока графического интерфейса недостаточно для моих потребностей (поверьте мне, я пытался). Это связано с тем, что другие строки, отображаемые пользователю через графический интерфейс, генерируются в других потоках. Чтобы получить 100% полное покрытие, мне нужно было бы вручную настроить культуру каждого отдельного потока, чтобы гарантировать, что весь текст из файлов ресурсов будет на правильном языке. Поскольку мой продукт по сути является основой для других плагинов, которые пишут другие группы разработчиков, я не могу контролировать действия, выполняемые в потоках, созданных в других плагинах. Из-за этого ручное изменение культуры для каждого потока недопустимо.
То, что я ищу, это способ установить общий язык для приложения, без необходимости изменять какие-либо пользовательские настройки ОС.
В ходе некоторых исследований я наткнулся на следующий метод установки предпочтительного языка пользовательского интерфейса для процесса: SetProcessPreferredUILanguages
После прочтения этого, кажется, что это то, что я ищу. Однако, когда я реализовал этот вызов в методе Main моего приложения на C#, он, похоже, ничего не делал.
Возвращаемое значение из следующего кода - true, но я никогда не вижу, чтобы мое приложение с графическим интерфейсом отображало текст на испанском языке.
[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");
}
}
Есть ли что-то еще, что мне не хватает для того, чтобы это успешно запустить мое приложение с графическим интерфейсом с загруженными испанскими ресурсами?
Я пытаюсь реализовать 2-й вариант таблицы в нижней части страницы MSDN для создания приложений MUI, где у меня есть настройки языка пользовательского интерфейса для конкретного приложения, и я хочу достичь желаемого результата для загрузки ресурсов:
Приложение вызывает MUI API для установки специфичных для приложения языков пользовательского интерфейса или предпочитаемых процессом языков пользовательского интерфейса, а затем вызывает стандартные функции загрузки ресурсов. Ресурсы возвращаются на языках, установленных приложением или системными языками.
Я позвонил, чтобы успешно установить предпочитаемые языком пользовательского интерфейса процесса, но мои ресурсы загружаются не на том языке, который я ожидал. Комментатор упомянул, что этот вызов будет работать только для неуправляемых ресурсов, которые я не могу проверить на 100%, но поведение, по-видимому, указывает на то, что это так.
Я не могу быть единственным человеком, который когда-либо пытался реализовать приложение.NET таким способом. Обидно, что нет больше информации о том, как это сделать.
Заранее спасибо,
рукав моря
3 ответа
Начиная с.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
Вот связанный вопрос с возможным решением для.NET 4.0 или более ранней версии с использованием вспомогательного класса для создания ваших потоков. Хотя там говорится о WPF, это относится и к другим многопоточным приложениям.NET.
Я знаю, что вы не хотели менять культуру для каждой новой темы, но я не могу придумать каких-либо других эффективных альтернатив для этого.
Основываясь на полученных здесь отзывах и других исследованиях, я пришел к выводу, что установка культуры для многопоточного приложения.NET, независимого от языка ОС, невозможна при использовании версий.NET более старых, чем 4.5.
Это действительно позор.
Недавно мы обновили наше программное обеспечение для использования.NET 4.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), конец строки с нулевым символом (не нужно использовать escape-последовательность Юникода ), список заканчивается двойным нулевым символом (следовательно, новый "пустой язык"; другое способ поставить двойной \0 на последний язык). Я также добавил новое описание отладки, чтобы протестировать установленные на данный момент языки, но, к сожалению, я не могу проверить его здесь (на работе), потому что функция нуждается в Win7.
Но есть совершенно другой способ, как это сделать. Смотрите AppLocale
Я не могу дать гарантию, что это сработает в вашем случае. Приложению требуется Windows XP и новее (при установке в Windows 7 существует небольшая проблема; не забудьте запустить установку в режиме администратора, иначе она не будет работать). Приложение имеет еще одну, более серьезную проблему. Невозможно переключиться с Центральной Европы на Западную. Но он работает из Центральной Европы в Грецию и другие страны с разными персонажами.
Я использую это приложение для разработки более старого (не Unicode) приложения для некоторых стран. К сожалению, это дает неприятное предупреждение на старте...