Greetings from the future!
Actually you can, as I learnt from reading InitMUILanguage() vs MessageBox(), because I wanted to change the language as well. For me InitMUILanguage
does not work (and it uses the discouraged Language ID concept, see the 'rant' above LANG_NEUTRAL
in winnt.h
). But SetProcessPreferredUILanguages
and SetThreadPreferredUILanguages
both do.
Here's how to use it (adjusting the example you linked) :
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using static TaskDialog.NativeMethods;
namespace TaskDialog
{
internal static class Program
{
[STAThread]
static void Main()
{
//Remove the check if you know your parameters are in the correct format
CheckResult(SetProcessPreferredUILanguages(MUI_LANGUAGE_NAME, MakeMultiString("ab-CD", "zh-cn"), out _));
//Or SetThreadPreferredUILanguages(MUI_LANGUAGE_NAME, MakeMultiString("ab-CD", "zh-cn"), out _);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
internal static class NativeMethods
{
public static void CheckResult(bool success)
{
if (!success)
{
var ex = new Win32Exception();
Debug.WriteLine($"Error 0x{ex.NativeErrorCode:X}");
throw ex;
}
}
//Generates a double null-terminated multi-string buffer (PCZZWSTR)
public static string MakeMultiString(params string[] items) => string.Join("\0", items) + "\0";
//WinNls.h
public const uint MUI_LANGUAGE_NAME = 0x8; // Use ISO language (culture) name convention
//Omitting CharSet sets it to Ansi which is not what we want
// Even after typing this I changed this to Ansi to test it again and forgot to change it back;
// took me quite some time to figure out what was going on
//https://docs.microsoft.com/windows/desktop/api/winnls/nf-winnls-setprocesspreferreduilanguages
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool SetProcessPreferredUILanguages(
uint dwFlags,
string pwszLanguagesBuffer,
out uint pulNumLanguages
);
//https://docs.microsoft.com/windows/desktop/api/winnls/nf-winnls-setthreadpreferreduilanguages#c#-signature
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool SetThreadPreferredUILanguages(
uint dwFlags,
string pwszLanguagesBuffer,
out uint pulNumLanguages
);
}
}
pwszLanguagesBuffer
receives a list of locales consisting of a two-letter ISO 639-1 language name and a two-letter ISO 3166-1 alpha-2 region code separated by a hyphen, in order of decreasing preference. In this case ab-CD
is not an existing locale, so zh-CN
(a variant of chinese) is chosen. Only the first 5 valid languages will be considered.
Note that every item in the pwszLanguagesBuffer
list must end in a NULL character (\0
or \u0000
). The extra + '\0'
is because string.Join
only inserts the separator between the items. This list is then closed with an extra NULL terminator, inserted automatically by .NET (because it is a string parameter).
The result:
Related: How do I set the UI language for a multi-threaded .NET process, independent of the OS language?