1

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:

  • Cloned my existing Release build
  • Opened up my .rc file in Notepad++ and cloned the "default" language sections
  • Changed the language macro in the cloned sections ( LANGUAGE LANG_VIETNAMESE, SUBLANG_DEFAULT)
  • Translated everything from Menu, Dialogs, etc.
  • Reloaded in visual studio, and I can see 2x set of all resources. ( menu, dialogs, everything in 2 sets - 1 english and 1 vietnamese )
  • Opened up project propierties -> Resources and changed Culture to Vietnamese
  • Built the project successfully

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.

Mecanik
  • 1,539
  • 1
  • 20
  • 50
  • You need to look at where you load resources from the RC file and call `FindResourceExA` with the appropriate language ID https://learn.microsoft.com/en-gb/windows/win32/api/winbase/nf-winbase-findresourceexa – Richard Critten Jul 24 '19 at 12:54
  • @RichardCritten Interesting, I'm looking at this. – Mecanik Jul 24 '19 at 12:57
  • @RichardCritten This does seem to be what I need, however I got no clue how to use this properly... – Mecanik Jul 24 '19 at 13:05
  • @NorbertBoros [This](https://www.codeproject.com/Articles/18/Multilingual-Application-Change-Application-Langua) is an old case. I'm not sure if it will help you. – Strive Sun Jul 25 '19 at 03:29

1 Answers1

1

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!

Mecanik
  • 1,539
  • 1
  • 20
  • 50