Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

LoadString works only if I don't have an English string table

I want to be able to modify the application's language programatically, or at least use the language specified in Control Panel -> Regional and Language Options -> Formats.

If I add an english string table, make a french and a german copy of it, and delete the english one, I can programatically switch between loading the french and the german strings. If I keep the english copy, the english strings get loaded, no matter what, when I try to load the german or the french ones.

I think that this is a resource loader bug, and that the resource loader ignores SetThreadLocale, if it finds a string table in the same language as the windows ui language (the language of the windows explorer menus for example).

I tried to change Control Panel -> Regional and Language Options -> Formats to French, but that has no effect. The resource editor shows the french string table without the language appended, but my program still always loads the english strings. Copying this change to the system account has no effect either.

Here is the code I tried this with:

#include "stdafx.h"
#include <iostream>
#include "windows.h" // this should go to stdafx.h
#include "resource.h" // this should not go to stdafx.h
using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
    // 1036 = french, 1031 = german
    SetThreadLocale(MAKELCID(1036, SORT_DEFAULT));
    const int maxSize = 100;
    wchar_t c[maxSize];
    LoadString(GetModuleHandle(NULL), IDS_STRING101, c, maxSize);
    std::cout << c;
    return 0;
}

Here is a half wrong, incomplete explanation (in the second half of Method 2). The second workaround proposed there, using only country-neutral string tables is useless, because I have separate portuguese-Portugal and portuguese-Brazil string tables.

The first workaround proposed there does not work. With the code below, I get error 1814.

HRSRC r = FindResource(
    GetModuleHandle(NULL),
    MAKEINTRESOURCE(IDS_STRING101),
    RT_STRING);
DWORD e = GetLastError();

So, what should I do ? What's the explanation of this strange "bug" ?

LATER EDIT:

After some more tests I found out that:

  1. GetThreadLocale() returns what is set in Control Panel -> Regional and Language Options -> Formats.
  2. The resource loader bug is essentially that if my program has US English resources too, those resources will be loaded, no matter what is set at Formats. If it does not have US English resources, the language set at Formats will be used.
  3. If I have a French (Neutral) and a German (Neutral) string table, and I set Formats to French (France), the german strings are loaded. If I add an English (Neutral) string table, the english strings are loaded. So, the neutral culture fallback does not work for what is set at Formats.
  4. If I add a Neutral string table, that one will be used, even if I have another English (Neutral) or an English (United States) string table.
like image 728
Jeno Csupor Avatar asked Jul 31 '09 16:07

Jeno Csupor


2 Answers

Detailed description of how localized resources are selected (including the search order of FindResource) straight from MSDN: Multiple-Language Resources

Edit: However, from my experience (at least on Windows XP) the search order detailed for FindResource on that page does not describe the actual behaviour. The actual behaviour seems to be:

  1. LANG_NEUTRAL resource
  2. resource where Lang and SubLang match UI language Lang and SubLang
  3. resource where Lang matches UI language Lang and resource Sublang is neutral
  4. resource where Lang and SubLang match locale language Lang and SubLang
  5. resource where Lang matches locale language Lang and resource Sublang is neutral
  6. resource with lowest numerical LANGID

Note: I don't have any sources to validate that list, so if anybody can update or correct anything, please do so.

Edit: To understand this behaviour, it is important to recognize the difference between the 'locale' and 'UIlanguage' as explained here: NLS Terminology. The FindResource function language selection is based primarily off of the UI language which is NOT the 'Regional Options' setting in the 'Regional and Language Options' (that is the 'locale' setting, which is the same as calling SetThreadLocale()).

As far as I can tell, the reason the locale setting or 'SetThreadLocale()' affects FindResource() is because of the exception case described by @Kirill V. Lyadvinsky in one of the answers here, explained in more detail on Michael Kaplan's blog.

Setting the language for FindResource in code cleanly and deterministically was only made possible with the new function 'SetThreadUILanguage' in Vista onwards. Every place you see that instead uses SetThreadLocale will have hacks to make it work and/or have issues when the UI language changes (ie: Foreign language windows install).

like image 165
Sogger Avatar answered Sep 18 '22 07:09

Sogger


Are you running Vista or Windows 7? If so then SetThreadLocale doesn't work (even though it returns TRUE, sigh) and you have to use SetThreadUILanguage.

I have just completed a WTL app that has been translated into 7 different languages and the user can switch languages without the problems you are describing. I am using SetThreadLocale on XP and SetThreadUILanguage on Vista/7.

More info:

http://social.msdn.microsoft.com/forums/en-US/windowscompatibility/thread/d3a44b1c-900c-4c64-bdf8-fe94e46722e2/

http://www.curlybrace.com/words/2008/06/10/setthreadlocale-and-setthreaduilanguage-for-localization-on-windows-xp-and-vista/

like image 38
Rob Avatar answered Sep 22 '22 07:09

Rob