TaskDialog change button language

2019-07-22 06:05发布

问题:

I use the Vista TaskDialog Wrapper and Emulator for WindowsForms.

It works fine, but how can I change the language of the buttons?

回答1:

I have reasons to think change language of common buttons is NOT possible. (Common buttons are treated in special way and also return special results, please see TASKDIALOGCONFIG structure. There is no option provided for language change.)

So if you are speaking about change of language of common buttons Yes, No, OK, Cancel, Retry, Close, then text on their labels is taken from resources of active Windows UI language. This is the same case as with buttons of MsgBox() dialog which was there from beginning of Windows. (Buttons are Yes, No, OK, Cancel, Abort, Retry, Ignore, Help.) I believe wording on common buttons was not made changeable to keep degree of uniformity in all basic dialog boxes on the same machine.

Your application is not alone with this issue and most users who already installed applications in different language simply accept this behavior and don't view it as a bug. You can always explain this is standard behavior for dialog boxes made using the template provided by Windows. You know very well that change of labels is not the only, but one of many constraints of TaskDialog.

Workaround is to create custom buttons although along with this you are losing ability to create links. If you are writing large application, consider writing own foundation for this type of dialog boxes as many applications already implement, too.



回答2:

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?