Mecanik
Mecanik

Reputation: 1051

Building application with different language specified in resource.rc

I never done something like this, so this is my first attempt. From reading online different information and tutorials, I can confidently say that I got very confused so I went on my own with this.

What I am trying to achieve is have different build configurations with different languages for the GUI.

My application is Win32 built using Visual Studio 2019.

Steps I tried:

However the result is nothing. The menu and dialogs are still in English.

Is there something else I must do to specify which set of resources to use to obtain a different language build ?

Or am I over complicating this and going all wrong...

Any advice/examples are much appreciated.

Upvotes: 1

Views: 840

Answers (1)

Mecanik
Mecanik

Reputation: 1051

It seems I will answer my own question because I believe others have hit this problem as well.

The correct and official way to change the language of a Windows GUI applications is:

SetThreadUILanguage

Using this function, and the exact thing I did in my question; the function will apply the resources in that language at runtime. ( menu, dialogs, everything )

I my case it was as simple as:

SetThreadUILanguage(MAKELANGID(LANG_VIETNAMESE, SUBLANG_VIETNAMESE_VIETNAM));

However in my case I am creating builds per language, so if you want to allow the user to change the language at runtime please see the notes and this article.

The un-official way of doing this is a bit more work, however quite stable and not error prone.

  • You must translate and rebuild your menu in that language
  • You must translate and rename each dialog ID per language, and show that dialog ID per language

For the community and other developers, I will share the code to rebuild the menu:

// Find our Menu resource based on desired language
HRSRC hRes = FindResourceExW(hInstance, RT_MENU, MAKEINTRESOURCE(IDC_APPLICATION), MAKELANGID(LANG_VIETNAMESE, SUBLANG_VIETNAMESE_VIETNAM));

if (!hRes) {
    wchar_t buf[MAX_PATH] = { 0 };
    FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, (sizeof(buf) / sizeof(wchar_t)), NULL);
    MessageBoxW(0, buf, _TEXT(L"FindResourceExW Error"), MB_OK | MB_ICONERROR);
    return FALSE;
}

// Load our Menu resource based on desired language
HGLOBAL hGlo = LoadResource(hInstance, hRes);

if (!hGlo) {
    wchar_t buf[MAX_PATH] = { 0 };
    FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, (sizeof(buf) / sizeof(wchar_t)), NULL);
    MessageBoxW(0, buf, _TEXT(L"LoadResource Error"), MB_OK | MB_ICONERROR);
    return FALSE;
}

// Lock the resource
LPVOID pData = LockResource(hGlo);

if (pData == NULL) {
    wchar_t buf[MAX_PATH] = { 0 };
    FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, (sizeof(buf) / sizeof(wchar_t)), NULL);
    MessageBoxW(0, buf, _TEXT(L"LockResource Error"), MB_OK | MB_ICONERROR);
    return FALSE;
}

// Load the new Menu into memory
HMENU hMenu = LoadMenuIndirectW((MENUTEMPLATE*)pData);

if (!hMenu) {
    wchar_t buf[MAX_PATH] = { 0 };
    FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, (sizeof(buf) / sizeof(wchar_t)), NULL);
    MessageBoxW(0, buf, _TEXT(L"LoadMenuIndirectW Error"), MB_OK | MB_ICONERROR);
    return FALSE;
}

// Get our default Menu
HMENU hMenu_old = GetMenu(g_Hwnd);

// Set no Menu
SetMenu(g_Hwnd, NULL);

// Erase default Menu
DestroyMenu(hMenu_old);

// Set our new Menu
SetMenu(g_Hwnd, hMenu);

// Draw our new Menu
DrawMenuBar(g_Hwnd);

NOTE: If you want to use FindResourceEx to search for strings, it's more complicated because of RT_STRING so please see this before you waste your time.

Enjoy!

Upvotes: 1

Related Questions