views:

981

answers:

3

Is there some way in the Win32 API to convert a three letter language code, as returned by GetLocaleInfo() with LOCALE_SABBREVLANGNAME specified, to a corresponding LANGID or LCID? That is, going in "reverse" to what GetLocaleInfo() normally does?

What I'm trying to do is to parse what kind of language a resource DLL is using, and so far, without touching anything about the DLL, going by the dll name with a format nameLNG.dll, where LNG is a three letter language code, seems to be the easiest method, assuming such a function exists.

If this isn't easy to do, I guess Plan B is to give our language DLL's a version info resource, specify their respective cultures there, and later on in the application, read which cultures they use.

+1  A: 

You can enumerate the installed locales using EnumSystemLocales() and build the map yourself. I do this during application initialization in a service that I wrote a while ago and it has worked out well thus far.

I would recommend using Plan B in this case. I usually steer clear of encoding stuff into the file name. If for no other reason, using the 3-character ISO-639 variant isn't perfect unless you strictly specify which variant you are using - ISO-639-2/B, ISO-639-2/T, or ISO-639-3.

If you need to provide locale-specific variants, then you should take a close look at RFC3066. Basically, you need to specify the language and country code and, in some cases, the region code as well. In any case, the LCID encapsulates all of this goodness.

The one thing that I am not completely certain of is whether the langID in the resource information is a full LCID or not. The codes listed in the VERSIONINFO reference are LCIDs so I would try using an LCID in the VERSIONINFO header. If not, you can always include the information as a string in the string block.

D.Shawley
EnumSystemLocales only give me a list of three letter acronyms, right? I'm unsure how to build a map by using only this information, and lacking the LCID's or LANGID's to translate from? This solution would be perfect if the callback function for EnumSystemLocales would have returned a *pair* of LCID's and "ISO" strings.
Jonas
The argument to the `EnumLocalesProc` is actually the LCID formatted as a hexadecimal number into a string. The documentation is less than clear about this - _"Pointer to a buffer containing a null-terminated locale identifier string"_. What they fail to mention is that you have to call something like `_tcstoul(lpLocaleString, NULL, 16)` on it to get the locale identifier into a usable form. Once you have the LCID, you can get what you need from `GetLocaleInfo()` using `LOCALE_SISO639LANGNAME` and `LOCALE_SISO3166CTRYNAME`.
D.Shawley
+1  A: 

Unfortunately, there's no direct Win32 API that gives you a LANGID given a 3-letters abbreviation.

It looks like CLanguageSupport is your friend today :-) It already implements your plan B to lookup the LANGID based on the contents of the version info resource.

The piece of code you're looking for is int the function

LANGID CLanguageSupport::GetLangIdFromFile(LPCTSTR pszFilename)

Of course, the drawback is that you may have a mismatch between the version info and the DLL name. But you'd very quickly catch it during tests. And if you let a tool such as appTranslator create the DLLs for you, you're sure to be on the safe side.

Serge - appTranslator
Yes, I think I'll end up using this class then, since you have apparently already side-stepped not only one, but two of the issues we're now facing. We may even re-implement the language submenu that I just days ago took away, thinking our switch to the MFC mechanism would be final. Oh well...
Jonas
A: 

You can obtain the LangID by calling GetLocaleInfo() with LOCAL_RETURN_NUMBER|LOCALE_ILANGUAGE for the LCType parameter, just as you passed LOCALE_SABBREVLANGNAME to obtain the three-letter ISO code. By passing the same lcid to this function, you can store the corresponding LangID with the ISO code.

Note that MSDN says LOCALE_ILANGUAGE should not be used in favor LOCALE_SLANG on Vista and above, but I believe the comment does not apply for use of LOCALE_ILANGUAGE with LOCALE_RETURN_NUMBER.

As your project evolves, you might generate several localized files for each language. For this reason, I would suggest you store your localized files in subdirectories named after the language. Microsoft has used directories named after the LangID's, for example "1033" as the English resources directory. I think it would be more friendly to use the three-letter code, such as "ENU\name.dll". In any case, subdirectories are a simple solution and hopefully won't complicate your build process as much as changing the target file name.

Heath Hunnicutt
I unfortunately don't have access to the LCID, but want a LANGID from the language code three letter acronym (the faux-ISO code).
Jonas
FWIW, MS wants us to move away from LOCALE_ILANGUAGE because they want to shift to string-only "lang ids", a la .NET cultures ids. iow they try to convince us that LANGID *may* die some day, at least for newly supported languages in Win8+
Serge - appTranslator