If you find that GetLocaleInfo is driving you crazy, it may not be the right function to use

by Michael S. Kaplan, published on 2007/04/16 00:01 -04:00, original URI: http://blogs.msdn.com/b/michkap/archive/2007/04/15/2146890.aspx

Aaron asks (via the Contact link):

I apologize for this totally unsolicited email, but I'm starting to wonder if I'm crazy or not.  I'm using GetLocaleInfo to determine what language the user wants their UI strings displayed in.  I'm using LOCALE_SISO639LANGNAME and LOCALE_SISO3166CTRYNAME to get a string such as en-US. 

The part where I'm confused is where GetLocaleInfo is grabbing that information from.  In the XP regions and languages control panel, there's a strangely worded option in the "Advanced" tab called "Language for non-Unicode programs."  When I set that to something like "French (france)", I still get en-US instead of fr-FR.

The reason I'm doing this is because we have a custom localization scheme for our application, which runs on Windows 98 through Vista.  So we are not technically a Unicode application (we don't #define UNICODE), but we support Unicode in that we dynamically load the W version of every API we come across and prefer that to the A version (and our strings are encoded accordingly).  So does that option in the Advanced tab even apply to our application?  If it does, how would I get that information?

How far gone is my misunderstanding of things?  ;-) 

Thanks for your time!

GetLocaleInfo does not grab its information from any setting in Regional Options. It grabs its info from its own internal database of information, based on the locale you pass it.

The "Language for non-Unicode Programs" is also known as the "Default System Locale" and really if not a good setting upon which to base a localization strategy, for way too many reasons to enumerate fully (but the fact that the intent is to provide the locale to use for conversions between Unicode and "ANSI" ought to be reason enough on its own).

If you really wanted to get the information from this setting via GetLocaleInfo, you could just pass LOCALE_SYSTEM_DEFAULT as the LCID. But like I said, you do not want to use that setting (you claimed to want to support Windows 98, Aaron -- this setting is not changeable in Windows 98).

Now you could in theory use the "Standards and Formats" setting, also known a the default user locale (you would use LOCALE_USER_DEFAULT in that GetLocaleInfo call). It has the advantage of being settable on all platforms, if nothing else.

Clearly though, it is not intended to drive the UI language, and thus if you made it drive UI language in your application you would be providing confusing UI to the user.

But if you think about that locale list for a moment, the odds that it will match your list of UI languages for your application are probably pretty close to nil. So you do not miss much by not using that setting.

Now I could claim that you should use the results of the user interface language functions provided by MUI, but to be honest even though it has the advantage of being an accurate setting, it really isn't likely to be able to match your UI languages of your application either.

(Look on the right side of the page and expand the one that says Regional Options for more information on what each setting there is generally for.)

And also, not every version of Windows supports the two-letter ISO codes (and LOCALE_SISO639LANGNAME and LOCALE_SISO639CTRYNAME are often the three letter codes from which you cannot deterministically derive the two letter codes), not to mention the fact that from time to time some of them have been wrong. So using Win32 NLS API functions to call at runtime to get the language tags to use on any version of Windows from Win98 to Vista just seems like a bad idea.

If you are providing a localized copy of your application then you can default to the UI language of the operating system and then you should honestly provide your own user interface to let them change it, based on the list of localized versions of your application that you support. The various lists that Windows provides aren't actually good ways to choose your UI language (beyond that possible idea of the initial one you might choose via GetUserDefaultUILanguage when it is available -- that function is included in almost every version you need other than Win98; it even is there on WinME).

Thus my guess in the title of this post, Aaron -- the reason GetLocaleInfo is driving you crazy is that it is really not the function your application should be using here. It is driving you crazy for the same reason that a pair of pliers would drive you crazy for fixing a hangnail....


This post brought to you by(U+10ef, a.k.a. GEORGIAN LETTER JHAN)

# Aaron Ballman on 16 Apr 2007 10:56 AM:

Thank you very much for the excellent post!  You bring up some points which I hadn't thought of before (such as the three letter codes).

The trouble is -- this localization isn't just for my product (which is an IDE and programming language).  It's for my user's products (which they make with said IDE).  So the idea of using a list to pick from simply isn't viable.

Ideally, I'd like to be using string resources with the localized language ID directories so that calls to LoadString can figure out what string to grab for me.  But that solution is still a ways off, so I'm looking for a band-aid in the meantime.  It seems to me that the band-aid should be to use the LANGID as the filename on disk instead of what we're working on currently.

Thanks again!

# Mihai on 16 Apr 2007 4:48 PM:

<<use the LANGID as the filename on disk>>

I would recommend folder names, not file names. A folder can be used to store not only UI, but also samples, templates, help, etc.

Jacob Schäffer on 3 Aug 2011 10:48 AM:

Hello Michael,

I forgot to mention that the Moldov language LCID variants appear to be the only variants I can find that fail with the GetLocaleInfoW API.

If Windows cannot return the <language>-<Script>-<REGION> pattern for the Moldov variants via the GetLocaleInfoW API, how can one get access to the pattern for these variants?

All the best / Jacob (jas@skabelondesign.dk)

Michael S. Kaplan on 3 Aug 2011 11:17 AM:

This assumes there is support for a locale on Windows -- is there, in this case?

Jacob Schäffer on 3 Aug 2011 2:01 PM:

Hello Michael,

It seem that my first comment failed to reach you. Here it comes again:

I'm using the GetLocaleInfoW function to return the <language>-<Script>-<REGION> pattern for a specific LCID using the LOCALE_SNAME constant (in VB6 and Office VBA). While doing so I found that looking up LCID's 2072 (&H818) for Moldov Romanian and 2073 (&H819) for Moldov Russian fail, while any other LCID I try returns the expected pattern.

The same thing happens when I use the LOCALE_SISO639LANGNAME and LOCALE_SISO3166CTRYNAME instead of LOCALE_SNAME.

I cannot find the LCID's 2072 (&H818) and 2073 (&H819) in the "Language Identifier Constants and Strings" table at msdn.microsoft.com/.../dd318693(v=VS.85).aspx, so I suppose Windows (tested on Windows 7) cannot properly deal with these Moldov language variants. Can this really be true?

Surprisingly, MS Office 2010 declare the constants wdRomanianMoldova (2072) and wdRussianMoldova (2073) in the WdLanguageID enum, and similarly in the MsoLanguageID enum.

Is it possible that MS Office supports languages for both document editing and the Office GUI that doesn't exist in Windows, or do I need to use another function to get access to the complete locale info database (I need support back to Windows XP)?

I'm using the <language>-<Script>-<REGION> pattern to identify which language file (not standard ressource files, but external UTF-16LE encoded XML documents) to load strings from. Although I might never run into the need for a Moldov Romanian setup I'd prefer to have a generic and reliable function that returns meaningful <language>-<Script>-<REGION> patterns for all MS Office declared LCID's.

Finally, I also need the reverse functionality. I've been looking for an API function or a constant for GetLocaleInfoW that returns the LCID based on the input string pattern <language>-<Script>-<REGION>. I may have become blind and overlooked something during my searches, but I didn't find any API method for this. Does such a beast exist or do I have to build my own table (which is a highly undesirable thing to do)?

All the best / Jacob (jas@skabelondesign.dk)

Michael S. Kaplan on 3 Aug 2011 3:33 PM:

This locale is not supported by Windows; you should discuss the issue with Office and whatever functions they might provide if you want the solution for this....

Jacob Schäffer on 3 Aug 2011 5:43 PM:

Thanks. If these locales aren't supported by Windows there is nothing generally wrong with the function I use. I'll just add these LCID's to an exception list so I can deal with this situation separately.

The reverse function, however, which takes the <language>-<Script>-<REGION> pattern for input (supported locales only, of course), might be built upon the GetLocaleInfoEx() API and then calculate the LCID from some return values, I believe. However this function isn't available on Windows XP, which is essential for me to support. Is there any way to build a <language>-<Script>-<REGION> pattern -> LCID conversion function on XP via API functions?

All the best /Jacob

Michael S. Kaplan on 4 Aug 2011 12:31 AM:

The actual function does not construct the LOCALE_SNAME values from pieces; it has the full names and LCIDs in the locale data....

Jacob Schäffer on 4 Aug 2011 8:09 AM:

Ahhh ... In that case it's definitely most interesting to build lookup functions that use the Windows locale database - in both directions. As far as I can see this can easily be done on Vista and later Windows versions, but not on Windows XP.

Is there ANY way on Windows XP to provide eg. "da-DK" for input to an API function and have "1030" (or the hex variant) returned? I can easily do it with GetLocaleInfoEx() with the LOCALE_ILANGUAGE flag on Vista+, but since GetLocaleInfoEx() is not available on XP I need another route on XP.

Is there any other way on XP than building a private lookup table, that maps eg. "da-DK" to "1030"?

Thanks again /Jacob

Michael S. Kaplan on 4 Aug 2011 8:53 AM:

The Downlevel NLS Support library will help with some of these tasks. Have you tried it out?

Jacob Schäffer on 8 Aug 2011 12:34 AM:

Excellent tip. The DownlevelLCIDToLocaleName() and DownlevelLocaleNameToLCID() does what I want.

But "Nobody likes change", you know (blogs.msdn.com/.../667622.aspx), so I have to have backward-looking customers accept to install a static, additional library that back-ports the functionality to XP from Vista+. This could be a problem at certain customers running Citrix on Server 2003 :-)

Would it make any sense to resolve anything from the keys in HKEY_CLASSES_ROOT\MIME\Database\Rfc1766 as an alternative in those cases?

Thanks again / Jacob

Jacob Schäffer on 8 Aug 2011 1:16 AM:

Perhaps the best way on XP would be to build and cache an internal lookup table with EnumSystemLocales() using the LCID_SUPPORTED flag, and for each returned locale identifier fetch the locale name via GetLocaleInfo() with LOCALE_SNAME.

This would eliminate any need for adding the Downlevel NLS functions for XP, and should work as far back as Windows 2000 (if I read MSDN correctly).

Would this method be too nonsensical?

All the best /Jacob

Jacob Schäffer on 8 Aug 2011 4:35 AM:

Hmmm ... while enumerating with EnumSystemLocales() and fetching the Locale Names with GetLocaleInfo() I can see that Microsoft doesn't seem to follow the two-letter ISO naming convention for languages (which I thought MS actually did).

In fact, neither the <language> part nor the <REGION> part of the Locale Names is guaranteed to follow the two-letter convention. For example, the Locale Name for Caribbean English is returned as "en-029" and Russian Yakut is returned as "sah-RU", which both break the convention.

Interesting, but scrutiny unveil that it's documented and very simple to deal with as long as one don't conjecture about abbreviations.

GetLocaleInfo() certainly WAS driving me crazy, but in cooperation with EnumSystemLocales() it definitely IS the right function to use. In this case, at least :-)

All the best /Jacob

Michael S. Kaplan on 8 Aug 2011 9:06 PM:

See blogs.msdn.com/.../742245.aspx for the size mismatch issue, described....

Please consider a donation to keep this archive running, maintained and free of advertising.
Donate €20 or more to receive an offline copy of the whole archive including all images.

referenced by

2011/08/05 Windows isn't Office (and vice versa)

go to newer or older post, or back to index or month or day