Why I think the thread locale really stinks

by Michael S. Kaplan, published on 2005/08/22 03:03 -04:00, original URI: http://blogs.msdn.com/b/michkap/archive/2005/08/22/454360.aspx

I do not like the thread locale.

Yes, GetThreadLocale and SetThreadLocale are two of the many NLS functions that the GIFT team supports.

And yes, if we are to look at the functions we own as if they are our children then we are supposed to love them all.

But in that case, I guess I am a lousy parent (if you will recall, I think that SetLocaleInfo really stinks, too).

First of all, there are the weird dependencies in USER32 and SHELL32 that probably ought to be using the user locale but instead use the thread locale and fall back to the system locale if something goes wrong.

Second of all, there is the poor story in GetThreadLocale:

Return Values

The function returns the system's default user locale.


When a thread is created, it uses the system default user locale. The system reads the system default user locale from the registry when the system boots. This system default can be modified for future process and thread creation using Control Panel's International application.

Since it always returns the thread locale, which starts its life as the user locale (set in Regional Options) but can be changed by a call to SetThreadLocale, you can make a fair case that both parts of the text are losuy.

Third of all, there is the worse story in SetThreadLocale:

Return Values

If the function succeeds, the return value is a nonzero value.

If the function fails, the return value is zero. To get extended error information, call GetLastError.


When a thread is created, it uses the system default thread locale. The system reads the system default thread locale from the registry when the system boots. This system default can be modified for future process and thread creation using Control Panel's International application.

The SetThreadLocale function affects the selection of resources that are defined with a LANGUAGE statement. This affects such functions as CreateDialog, DialogBox, LoadMenu, LoadString, and FindResource, and sets the code page implied by CP_THREAD_ACP, but does not affect FindResourceEx.

Windows 2000/XP: Do not use SetThreadLocale to select a UI language. To select the proper resource that is defined with a LANGUAGE statement, use FindResourceEx.

Where do I start? The function should return an LCID on success -- the previous thread locale! -- not a BOOL. Just like functions like SetWindowLong does. Oh well, I guess that is not the end of the world.

There is that same type of silly text about the 'system default thread locale' which is a beast not found in nature. The notion that it is read from the registry on boot is also crap. it is based on user locale. Always.

Then there is the text about how resource loading is affected -- and a warning sans explanation to not use that functionality on Win2000 and later. Since it is not supported on Win9x, what it is really saying is "good for only NT 3.1, 3.5x, and 4.0". In other words a warning that the function is not useful on modern platforms unless you want to affect the Shell and User subsystem in strange ways. Although it does not bother to say so.

Fourth of all, the fact is that resource loading is incredibly complicated, in part because of this questionable functionality. It is not based on the user locale, and it is not in essence based on the thread locale unless you change it. In which case it suddenly is based on the thread locale. Unless it is based on the UI language -- which it should always be. I swear you need a spirit dancer and a Ouija board to know what resources load if you start mucking with the various locale settings, and that is mostly because of this weird setting that hovers between UI language and user locale -- the thread locale.

On older platforms it is worse -- the system locale is the one that is used except when you set the thread locale (even though the thread locale is initially the user locale). I guess that is about the same as Win2000 and later, just substitute system locale for UI language.

This is by the way (now that I think about it) the first reasonable explanation for the strange Visual Basic <= 6.0 behavior where in the IDE the user locale is the language used for resource loading, even though in the compiled application that would not work -- VB was setting the the thread locale to the user locale and confusing millions of VB developers with this never-before-now-fully-explained behavior. Geez.

Anyway, we document that developers should use LOCALE_USER_DEFAULT and not GetThreadLocale when they are trying to respect the user's preferences.

If you ask me, we should treat every case where the thread locale is currently used as a bug to be fixed and not as a legacy behavior to be coddled. We have been slowly breaking the behavior anyway with each version without explaining why since it was already broken, so why not just cut the cord and stop using this dastardly functionality?

The thread locale really stinks, after all!


This post brought to you by "ײ" (U+05f2, HEBREW LIGATURE YIDDISH DOUBLE YOD)

# Lonnie McCullough on 22 Aug 2005 12:14 PM:

I totally agree with everything you said here. The whole concept of thread locales makes the system seem more complicated than it has to be. Its just another concept that is introduced throughout the docs, that make internationalization more impenetrable, even though a properly written app should never have to worry about it.

# Michael Dunn_ on 22 Aug 2005 7:08 PM:

So what's the _right_ way to answer the question "what language should I show resources in?" I'm confused by all the various get-locale and get-language functions and, as you've noted, the SDK wording isn't the best. (My situation is I need to send a language id [any kind of id, doesn't have to be a LCID] to a web page so it knows what language it should show stuff in.)
Just going by API names, GetUserDefaultUILanguage looks good but I need something that works on 98 as well.

# Michael S. Kaplan on 22 Aug 2005 9:31 PM:

Hi Mike -- this sounds like a great topic for a blog post! :-)

# Stephen Whipp on 9 Sep 2008 11:51 AM:

What you say is all well and good, but it doesn;t alter the fact that sometimes it is actually necessary to use ThreadLocale, rather than user locale as we have been forced to do. There are several reasons. If every app hotswapped correctly in response to changes in locale it would be fine, but most do not.

1) We have users who aren't very computer literate and don;t know where to find such settings in control panel etc. Furthermore they need to be able to hot swap them easilly as they need to be able to use the app in more than 1 language. Both for input language and locale settings.

2) Our users may be unable to access Control Panel owing to network restrictions. Certainly be unable to edit them.

3) Programmatically altering the user_locale settings would likely cause problems with other applications if it were not kept strictly local.

Its an odd fact, but there are cases where hotswapping locales is necessary. Input Language can be selected from the language bar... if such is installed. However it doesn;t function correctly in our case. Try intercepting the localechange message from a window which can't take focus sometime. We have to manage such internally, implying the need for thread  based locale settings.

# Michael S. Kaplan on 9 Sep 2008 12:00 PM:

But the wrong results are returned if you use it, due to the problems.

What locale are you hot-swapping to in this case? If you show UI then why not have one global in your app that stores the value, rather than trying to use malfunctioning thread locale?

I can understand wanting better behavior, but the thread locale cqn't deliver that....

# serans1 on 22 Nov 2008 4:35 AM:

Greate article.

I have searched on th web for a way to get a thread locale - any thread local (by thread ID/process ID) and had no success.

does anyone has any idea of how this can be accomplished ?

# Michael S. Kaplan on 22 Nov 2008 12:38 PM:

If you think it is such a great article, then why do you want to get the information on a setting that the article goes to great lengths to explain you shouldn't ever use? :-)

To answer your question, that information is not available by any public function in the Win32 API. You must be in a thread to query its thread locale. Good thing you never really need it!

Farproc on 8 Aug 2010 9:50 AM:

So, what are the shell and user effects of this function? Because, readin the docs, the only visible purpose this function has - would be to influence the language selected when calling the resource functions. And then, that exact use, is singled out as the use the function should not be used for. Is there an alternate ((more) legitimate) use of this function?

Being told to use FindResourceEx is a bit disingenuous as SetThreadLocale would allow one to change the language of resources where code could not be re-factored (for example, when invoking a common dialog out a library dll).

There is of course, SetThreadUILanguage but how does that not suffer the same problems as SetThreadLocale (whatever those problems may be).

Michael S. Kaplan on 8 Aug 2010 10:15 AM:

Shell is guilty of using it to influence many of its functions that would use a locale -- as if it were the user locale (for formatting and sorting). And USER does the same in many cases. It thus has a lot of unintended effects on program behavior and the program stops respecting user preferences....

That is if the fact that it is broken for resource loading in hard to predict ways doesn't convince you!

Doug Edey on 27 Sep 2010 6:57 AM:


Just out of interest, if we never use SetThreadLocale() would you still recommend against using GetThreadLocale()?

Also, the documentation doesn't state DON'T use GetThreadLocale().

Michael S. Kaplan on 27 Sep 2010 9:46 AM:

Well, the real question is "what would you use it for?", of course....

referenced by

2010/03/19 Strange how unsympathetic I was, until I was suddenly quite sympathetic

2007/05/28 Nothing stinks worse than the thread locale, other than the thread code page

2006/04/28 DEFAULT_GUI_FONT really stinks

2005/12/10 CurrentRegion is not based on the GEOID

2005/10/12 Some of GetGeoInfo is sorta broken

2005/08/31 Sometimes it *does* pay to be neutral

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