What is my locale? Well, which locale do you mean?

by Michael S. Kaplan, published on 2005/02/01 09:35 -08:00, original URI: http://blogs.msdn.com/michkap/archive/2005/02/01/364707.aspx

A few years back (some time before Windows XP shipped) when we located in were in Building 9 and much smaller than we are now, someone else in the building was having a problem. Our kind of problem. An international problem. I don't remember what it was -- something to do with code pages, maybe?

Anyway, Wei Wu, one of our cool development leads, asked a few configuration questions, and at the end of the message, asked him "what is your default system locale?".

His response, which I cannot find a copy of now, was priceless. Assuming that Wei was going to stop by to look at the machine. this guy started to describe the location of his computer.... :-)

One of those jokes that most people won't quite get, and jokes are never funny if they have to be explained. Ah, the life of an international geek.

But it was pretty funny, I think. We all had a good laugh at the time.

There is a not-so-hidden truth in there -- our terminiology story is weak. It really ought to be better. So, once again, here is a quick glossary of the four most common types of locales:

DEFAULT USER LOCALE (Windows XP term: "Standards and Formats"):

This setting controls the way information is presented -- the sort order in list boxes, the format of date, time, number, and currency values, the calendar you prefer to use. The list of locales can be thought of as a big group of defaults that are grouped by many language/region pairings, which you can see in the first tab Regional and Language Options Control Panel applet. Several of the settings are customizable, particularly the various formats.

The setting is per-user and when you change the setting, it is effective immediately, and all top level windows in the user's windowstation will get a WM_SETTINGCHANGE message indicating that the change has happened so that they too can reflect the change immediately.

Developers will commonly use LOCALE_USER_DEFAULT as their LCID of choice, whether by doing so directly or by calling functions in SHELL, USER, or elsewhere that do so for them. Using this setting and behaving appropriately with the results thereby is a sure sign that the developer respects the user's settings.

DEFAULT SYSTEM LOCALE (Windows XP term: "Language for non-Unicode Programs"):

This setting has three major purposes:

  1. Specifies the default ANSI, OEM, MAC, and EBCDIC code pages to use for non-Unicode programs.
  2. Specifies some of the font linking preferences for CJK fonts and for legacy bitmap fonts.
  3. Specifies application behavior when developers incorrectly use this setting rather than the DEFAULT USER LOCALE.

This setting is found on the third tab of the Windows XP/Server 2003 Regional and Language Options dialog and in the "Default" button on the first tab of the Windows 2000 Regional Options dialog.

Changing the setting changes it for the entire machine and it requires a reboot to take effect. No notification mechanism is done, nor is one needed since no change happens until the reboot does. A small number of misbehaving applications which check the registry rather than using the APIs will get wrong results after the setting change but before the reboot.

Unfortunately, developers will sometimes use LOCALE_SYSTEM_DEFAULT for purposes other than #1 and #2, and by doing so they manage to simultaneously show their users disrespect and cause yet another compatibility weirdness with functionality tied to the default system locale. Given the fact that a reboot is required, you think people would avoid this, but dven developers are not always perfect.

The XP name should be a big hint, though sometimes it adds confusion.

DEFAULT USER INTERFACE LANGUAGE (Windows XP Term: "Language used in menus and dialogs"):

This setting controls the language in which the UI is presented. It is only present if you have the MUI version of Windows (which is to say that you have Windows with the multilanguage files installed).

This setting is found on the second tab of the Windows XP/Server 2003 Regional and Language Options dialog and in the middle of the first tab of the Windows 2000 Regional Options dialog.

The setting is per-user and changing it requires a logoff to take effect.

There is no constant for it but the GetUserDefaultUILanguage API will retrieve the setting quickly enough. Given the changes to the resource model that the changes to support MUI inspired, it is easy for applications to plug into the very same setting automatically. I'll talk more about this another time....

DEFAULT INPUT LOCALE (Windows XP Term: "Default Input Language"):

This setting controls the initial input language used for all newly created threads.

This setting is found on the second tab of the Windows XP/Server 2003 Regional and Language Options dialog (hit the "Details..." button) and on the last tab of the Windows 2000 Regional Options dialog.

The setting is per-user and it takes place immediately. But obviously will not change the input language on any existing threads; only new threads get the new default.

Developers can find out what the current setting is by calling the SystemParametersInfo API with the SPI_GETDEFAULTINPUTLANG parameter. You can even set it with the SPI_SETDEFAULTINPUTLANG parameter but this almost always something that a developer should not be doing -- it is a user preference. Since proper application behavior is mostly about respect, this really is a constant that you should avoid. :-)

For more on this topic, Dr. International has bigger lists here and here, and there is more information here.

But when you are a developer, respect is the key here -- respect of the user's preferences and settings.

When you are a user, consider which applications respect your settings and which do not. Because while doing so may not be convenient for an application, it is certainly possible....


This post sponsored by "ð" (U+00f0, a.k.a. LATIN SMALL LETTER ETH)

# Ben Bryant on Tuesday, February 01, 2005 3:59 PM:

Confusion between default user and system locales is huge. I call them the setlocale and GetACP settings respectively. Delphi 7 has this problem in its core SysUtils functions because it uses the code page associated with the default user locale in some of its Ansi String functions (which on Windows is absolutely wrong). Very few programmers seem to be aware of this issue, I guess because often they are the same code pages. The MSDN documentation is plain wrong in many places, such as setlocale where it says the "system-default ANSI code page obtained from the operating system".

# Ben Bryant on Tuesday, February 01, 2005 4:24 PM:

I was looking through the Dr. International pages you listed, and saw the following clarification: "Although available user locales are often listed as a language (sometimes in combination with a country), a user locale is NOT a language setting, and has nothing to do with input languages, keyboard layouts, codepages or user interface languages." There is a code page associated with the user locale because you need a code page that can support the characters found in the weekdays and so forth. In fact, as a programmer I suppose you might need to explicitly deal with this user locale code page if you are handling strings from date time functions in a non-UNICODE program.

# Michael Kaplan on Tuesday, February 01, 2005 4:46 PM:

Hi Ben,

The lines blur -- because although user locale has nothing to do with input language, both XP and Server 2003 will add the default keyboard of any locale you change to when you change to it.

The best thing to do is be willing to accept any user format and then always use Unicode!

# Michael Kaplan on Tuesday, February 01, 2005 8:55 PM:

Here is the mail -- names of others removed to protect the innocent (and the guilty!). Thanks to a coworker for digging it up!....

-----Original Message-----
From: Xxxxxxxx Xxxxxxxxxxx
Sent: Tuesday, April 17, 2001 11:46 AM
To: Wei Wu
Subject: RE: AutoDesk is in the CD lab and has a couple of shell issues Shell issue

This machine is sitting in the CD lab Bld 27 cafeteria 2 and 3 next to the cafeteria not in the cafeteria.

-----Original Message-----
From: Wei Wu
Sent: Tuesday, April 17, 2001 11:43 AM
To: Xxxxxxxx Xxxxxxxxxxx
Subject: FW: AutoDesk is in the CD lab and has a couple of shell issues Shell issue

Xxxxxxxx, do you know the system locale of your repro machine?

-----Original Message-----
From: Wei Wu
Sent: Tuesday, April 17, 2001 11:25 AM
To: Xxxxx Xxxxxxx; Xxxxxxx Xxxx; Xxxxxxxx Xxxxxxxxxxx; Xxxxx Xxxxxxxx; Xxxxx Xxx
Cc: Xxxxx Xxxxxx
Subject: RE: AutoDesk is in the CD lab and has a couple of shell issues Shell issue

I'll investigate this.


-----Original Message-----
From: Xxxxx Xxxxxxx
Sent: Tuesday, April 17, 2001 11:21 AM
To: Xxxxxxx Xxxx; Xxxxxxxx Xxxxxxxxxx; Xxxxx Xxxxxxxx; Xxxxx Xxx; Wei Wu
Cc: Xxxxx Xxxxxx
Subject: RE: AutoDesk is in the CD lab and has a couple of shell issues Shell issue

Wei - Can you please take a look at this? This sounds familiar to the problem you were working on recently with the code for the DLL encodings. I believe this is fixed in the latest build?


# Jonathan Payne on Wednesday, February 02, 2005 3:42 AM:

It would be nice if all these locales were presented a bit better in the Windows installer. At the moment, each time I install Windows XP, I have to dig through numerous dialogs and change lots of different settings from "English US" to "English UK" and then change the time zone to GMT and put up with that annoying error message when I want to delete the US keyboard so only a UK keyboard is installed. I know that some people might want to have unusual combinations of locales and time zones and keyboards but it would be nice if the installer offered sensible defaults for the common (non-US) cases. It would also be nice if it asked all these questions at the beginning or end of the install so I could just leave the installer to get on with it rather than having to feed it more information every five minutes.

# Michael Kaplan on Wednesday, February 02, 2005 6:15 AM:

Funny, when I was first reading I was confused as I though you meant .MSI files when you said "Windows Installer", rather than thinking literlly the "Installer program for Windows."

That is a worthwhile "rant" -- we are intuitive within a SKU but often they do not customize SKUs for english languages since "English is already supported". I believe MS does the same thing with Spanish as well....

So it is easy to get that "local" feel, *if* you live in right location and buy a copy of Windows meant for that location. Which is great for that subset. But I think the rest of the world deserves that, too -- up to and including the right "flavour" and "localisation" of English or whatever the language is!

# Mike Dimmick on Wednesday, February 02, 2005 6:31 AM:

Another defect with the setup procedure: Windows Setup prompts first for the locale, then for the current date and time. Unfortunately it asks for the current date and time using the US formats, not the formats you've just specified!

# Jonathan Payne on Wednesday, February 02, 2005 7:41 AM:


Thanks for your reply - sorry I wasn't clear about the "Windows Installer" / "The Installer That Installs Windows" point :-).

I think the solution you are talking about would mean having Windows CDs for each area so instead of looking for the CD that says "English" I would find a CD that says "English UK" and install that. I am not so keen on that as it would be more cases to test – if I can’t recreate a bug, is it because I am using a different version of English or is it something else. Also, having a CD for each common locale would probably use up the entire global supply of what ever resource CDs need to produce the hundreds of extra MSDN Universal CDs that would be needed.

I think it would be better to make the first screen of regional settings dialog (the one with lots of text and two customize buttons) offer a combo with the common combinations of machine locale / default non-Unicode language / time zone / keyboard / etc. That way, most users could just pick their something appropriate to their location, eg "English, UK", from the list and everything else would drop into place with the option of changing the settings in the current way if they wanted something slightly different.

Another alternative would be to move all locale settings to a list of combos on one dialog so it would be easy to set them all in one go (at the moment, it is a bit frustrating seeing each of the places the first dialog says “English US” in the two long text strings and trying to find UI to change them - perhaps these strings could be hyperlinks).

If you consider this issue in terms of (number of times English Windows XP is installed outside of the US) x (amount of time it takes to dig through all those dialogs) it multiplies up to a large amount of time.

Also, the current installer makes it very easy for non-US users to install multiple keyboards when they really meant to just install the one non-US keyboard they wanted (it is not obvious that you need to delete the US keyboard and when you do, you get a scary error message as the installer is currently using the layout). This in turn leads to the problem a few weeks later when the user accidentally presses the shortcut that changes the current keyboard layout and then complains that their keyboard is broken as some of the symbols have been swapped (I think the double quote key is in different places on US and UK keyboards for example).

# Michael Kaplan on Wednesday, February 02, 2005 7:47 AM:

All true, and I don't know that I was necessarily suggesting seperate media, I was more pointing put that the current mechanism is great in some cases, not so great in others. And that it would be better if it could be great in more situations, though I do not think the current "solution" is scalable to al of the places that Windows goes.

Getting it there is an interesting challenge, and there are actually people looking at that in Longhorn, and beyond Longhorn.

# Ivo on Wednesday, February 02, 2005 12:42 PM:

Speaking of locales... How do I print an integer number according to the locale settings? For example "12,345,678".

I looked at the function GetNumberFormat. The way I see it I can use it in 2 ways - to specify a locale, or to specify a format. The thing is, if I use the locale I get "12,345,678.00" - seems like the function is designed for currency values mostly.

If I use the format I can get the number in any format, but I don't want any format, I want the one according to the locale settings (except the non-integer part). Is there a way to get a NUMBERFMT for the selected locale? Then I can modify it by setting NumDigits to 0 and lpDecimalSep to "".

# Michael Kaplan on Wednesday, February 02, 2005 12:46 PM:

Every prop of number formatting can be found with GetLocaleInfo calls to get the various bits of info (some can be set right in the NUMBERFMT).

# Ivo on Wednesday, February 02, 2005 4:05 PM:

I was hoping there is an easier way. Especially converting the grouping from the text "3;2;0" to the number 32 seems overly complicted. Is that what GetNumberFormat is doing every time?

# Michael Kaplan on Wednesday, February 02, 2005 4:26 PM:

More or less -- it isw doing that annoying parsing work for you....

# Richard on Thursday, February 03, 2005 1:09 AM:

Another request... on the XP/2003 regional settings dialogue's advanced tabs. For the code page converstion tables a "select all" button.

It is a real pain to go through the long list ensuring all are selected... by default some are and some arenit, in combination with some that can be deselected usually means two passes to check I've not missed any.

# Michael Kaplan on Thursday, February 03, 2005 1:45 AM:

Well, its too early to say what will happen exactly, but I am pretty sure I can promise you that *something* good will happen here to resolve that for you in Longhorn.

This statement is true for most values of good.

# Mike Williams on Thursday, February 03, 2005 3:18 AM:

It would be nice to have only a non-US keyboard language but since SP2, no matter what I do, the US keyboard is reinstalled as an additional kl within 2 reboots, without fail. It just doesn't want me to have non-US English solo. I've seen this on three other machines with SP2. Sadly the issue, though bugged, got no traction during the beta. Non-US English locales are really second-class citizens in terms of testing and product design at Microsoft. Can you think of a market of comparable size that doesn't have a localized version of Windows or Office?

# Richard on Monday, February 07, 2005 2:14 AM:

> I am pretty sure I can promise you that
> *something* good will happen here to resolve
> that for you in Longhorn.

Good news.

> This statement is true for most values of good.


# Ovate on Friday, April 15, 2005 10:21 PM:

Dovark for right hand and Chinese locales

Hi there, I type with Dvorak for the right hand. However the Chinese locale input method MS-Pinyin 98 that I need to use to type Chinese relies on the Qwerty layout. I need to be able to change this to the right hand Dvorak layout within the Chinese locale (I actually need to be able to do the same for Japanese)

I have downloaded a copy of MKLC (Microsoft Keyboard layout creator). However when I go to File\ load existing keyboard, it only displays Chinese (PRC) US keyboard option. This is not even a Chinese input method. I already have all the Chinese locals installed on my computer. Would anyone have any idea why the other Chinese input methods do not display or how I can locate them so that I can alter the keys to a Dvorak right hand layout. If I'm going about this totally the wrong way, please excuse the ignorance :-)

# Michael S. Kaplan on Saturday, April 16, 2005 11:57 AM:

I will talk about this point in an upcoming post....

# Michael Holtstrom on Friday, January 09, 2009 11:46 AM:

I have a db that contains latin1 data and is accessed by several apps. One app is on the dos console. It used to be built with vs6, now vs9. We call setlocale(LC_CTYPE,""); near startup.

Default winxp is oemcp 437. That means that not all latin1 chars can be displayed in dos. Fine, we detect these chars display simple-ascii escape sequences instead.

Now the suffering begins. Default winxp is LC_CTYPE English_United States.1252. In vs6 that didn't matter, but in vs9, printfs are overriden and a magic last-minute best-fit mapping occurs. Very painful for me.

Here is a little app that your readers might find useful.

#include <iostream>

#include <windows.h>

// Note: In order to ditch the UNICODE define you have to do the following:

// Configuration Properties >> C/C++ >> Preprocessor >> Preprocessor Definitions >> [uncheck] Inherit from parent or project defaults

// If your product is 14 years old, switching to UNICODE just isn't an option.

void info()


  // GetCPInfoEx vars

  BOOL res;


  // Registry vars

  #define CHAR_CMD_REG_DATA_SIZE 1024

  DWORD ret;

  HKEY hKey = 0;



  bool regIsOpen = false;

  // GetLocaleInfo vars

  int status;

  #define LOCALE_DATA_SIZE 1024

  char localeData[LOCALE_DATA_SIZE];

  int localeDataSize = LOCALE_DATA_SIZE;

  // Open the registry

  ret = RegOpenKeyEx( HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\Nls\\CodePage", 0, KEY_EXECUTE, &hKey );

  if (ret) { printf("\n RegOpenKeyEx Failed"); }

  else { regIsOpen = true; }

  // -----------------------------------------------------

  // http://blogs.msdn.com/michkap/archive/2005/02/01/364707.aspx


  // LOCALE_USER_DEFAULT   : Control Panel >>  Regional and Language Options >> Regional Options [tab] >> Standards and Formats

  // - is per user and does not require reboot


  // LOCALE_SYSTEM_DEFAULT : Control Panel >>  Regional and Language Options >> Advanced [tab] >> Language for non-Unicode Programs

  // - is system wide and requires reboot

  // - specifies the default ANSI and OEM code pages, and some of the font linking preferences

  // -----------------------------------------------------

  printf("\n GetConsoleCP()                            %d  -- Note: effected by the chcp command.\n",GetConsoleCP());

  printf("\n GetOEMCP()                                %d",GetOEMCP());

  res = GetCPInfoEx( CP_OEMCP, 0, &CPInfoEx );

  if (res==0) { printf("\n GetCPInfoEx Failed"); }

  else { printf("\n GetCPInfoEx(CP_OEMCP)                     %d",CPInfoEx.CodePage); }

  status = GetLocaleInfo( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTCODEPAGE, localeData, localeDataSize );

  if (status==0) { printf("\n GetLocaleInfo Failed"); }

  else { printf("\n GetLocaleInfo(LOCALE_USER_DEFAULT,OEM)    %s",localeData); }

  status = GetLocaleInfo( LOCALE_SYSTEM_DEFAULT, LOCALE_IDEFAULTCODEPAGE, localeData, localeDataSize );

  if (status==0) { printf("\n GetLocaleInfo Failed"); }

  else { printf("\n GetLocaleInfo(LOCALE_SYSTEM_DEFAULT,OEM)  %s",localeData); }

  if (regIsOpen)


     dataSize = CHAR_CMD_REG_DATA_SIZE;

     ret = RegQueryValueEx( hKey, "OEMCP", NULL, NULL, data, &dataSize );

     if (ret) { printf("\n RegQueryValueEx Failed"); }

     else { printf("\n Registry...\\CodePage\\OEMCP                %s\n",(char*)data); }


  // -----------------------------------------------------

  printf("\n GetACP()                                  %d",GetACP());

  res = GetCPInfoEx( CP_ACP, 0, &CPInfoEx );

  if (res==0) { printf("\n GetCPInfoEx Failed"); }

  else { printf("\n GetCPInfoEx(CP_ACP)                       %d",CPInfoEx.CodePage); }

  status = GetLocaleInfo( LOCALE_USER_DEFAULT, LOCALE_IDEFAULTANSICODEPAGE, localeData, localeDataSize );

  if (status==0) { printf("\n GetLocaleInfo Failed"); }

  else { printf("\n GetLocaleInfo(LOCALE_USER_DEFAULT,ANSI)   %s",localeData); }

  status = GetLocaleInfo( LOCALE_SYSTEM_DEFAULT, LOCALE_IDEFAULTANSICODEPAGE, localeData, localeDataSize );

  if (status==0) { printf("\n GetLocaleInfo Failed"); }

  else { printf("\n GetLocaleInfo(LOCALE_SYSTEM_DEFAULT,ANSI) %s",localeData); }

  if (regIsOpen)


     dataSize = CHAR_CMD_REG_DATA_SIZE;

     ret = RegQueryValueEx( hKey, "ACP", NULL, NULL, data, &dataSize );

     if (ret) { printf("\n RegQueryValueEx Failed"); }

     else { printf("\n Registry...\\CodePage\\ACP                  %s\n",(char*)data); }


  // -----------------------------------------------------

  char * str;

  str = setlocale(LC_ALL     , NULL); if (str == NULL) { printf("\n LC_ALL      = unknown"); } else { printf("\n LC_ALL      = %s",str); }

  str = setlocale(LC_COLLATE , NULL); if (str == NULL) { printf("\n LC_COLLATE  = unknown"); } else { printf("\n LC_COLLATE  = %s",str); }

  str = setlocale(LC_CTYPE   , NULL); if (str == NULL) { printf("\n LC_CTYPE    = unknown"); } else { printf("\n LC_CTYPE    = %s",str); }

  str = setlocale(LC_MONETARY, NULL); if (str == NULL) { printf("\n LC_MONETARY = unknown"); } else { printf("\n LC_MONETARY = %s",str); }

  str = setlocale(LC_NUMERIC , NULL); if (str == NULL) { printf("\n LC_NUMERIC  = unknown"); } else { printf("\n LC_NUMERIC  = %s",str); }

  str = setlocale(LC_TIME    , NULL); if (str == NULL) { printf("\n LC_TIME     = unknown"); } else { printf("\n LC_TIME     = %s",str); }


  "    0 1 2 3 4 5 6 7 8 9 A B C D E F\n\n"

  " 2  \x20 \x21 \x22 \x23 \x24 \x25 \x26 \x27 \x28 \x29 \x2a \x2b \x2c \x2d \x2e \x2f\n"

  " 3  \x30 \x31 \x32 \x33 \x34 \x35 \x36 \x37 \x38 \x39 \x3a \x3b \x3c \x3d \x3e \x3f\n"

  " 4  \x40 \x41 \x42 \x43 \x44 \x45 \x46 \x47 \x48 \x49 \x4a \x4b \x4c \x4d \x4e \x4f\n"

  " 5  \x50 \x51 \x52 \x53 \x54 \x55 \x56 \x57 \x58 \x59 \x5a \x5b \x5c \x5d \x5e \x5f\n"

  " 6  \x60 \x61 \x62 \x63 \x64 \x65 \x66 \x67 \x68 \x69 \x6a \x6b \x6c \x6d \x6e \x6f\n"

  " 7  \x70 \x71 \x72 \x73 \x74 \x75 \x76 \x77 \x78 \x79 \x7a \x7b \x7c \x7d \x7e \x7f\n"

  " 8  \x80 \x81 \x82 \x83 \x84 \x85 \x86 \x87 \x88 \x89 \x8a \x8b \x8c \x8d \x8e \x8f\n"

  " 9  \x90 \x91 \x92 \x93 \x94 \x95 \x96 \x97 \x98 \x99 \x9a \x9b \x9c \x9d \x9e \x9f\n"

  " a  \xa0 \xa1 \xa2 \xa3 \xa4 \xa5 \xa6 \xa7 \xa8 \xa9 \xaa \xab \xac \xad \xae \xaf\n"

  " b  \xb0 \xb1 \xb2 \xb3 \xb4 \xb5 \xb6 \xb7 \xb8 \xb9 \xba \xbb \xbc \xbd \xbe \xbf\n"

  " c  \xc0 \xc1 \xc2 \xc3 \xc4 \xc5 \xc6 \xc7 \xc8 \xc9 \xca \xcb \xcc \xcd \xce \xcf\n"

  " d  \xd0 \xd1 \xd2 \xd3 \xd4 \xd5 \xd6 \xd7 \xd8 \xd9 \xda \xdb \xdc \xdd \xde \xdf\n"

  " e  \xe0 \xe1 \xe2 \xe3 \xe4 \xe5 \xe6 \xe7 \xe8 \xe9 \xea \xeb \xec \xed \xee \xef\n"

  " f  \xf0 \xf1 \xf2 \xf3 \xf4 \xf5 \xf6 \xf7 \xf8 \xf9 \xfa \xfb \xfc \xfd \xfe \xff\n"



int main(int argc, char** argv)





return 0;


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

2012/01/17 The evolving Story of Locale Support, part 15: Fixing our listings up in Windows 8!

2010/09/15 How to format? What locale?

2009/07/13 Anything still wrong is probably wrong for good....

2008/06/01 Seeing through the non-English "Mesh"

2006/11/19 Another satisfied customer!

2006/09/22 Inaccurate localization can make you bust out laughing

2005/04/16 Not all keyboards are included in MSKLC's lists

2005/04/14 On approaching international programming....

2005/02/21 Give me a [word-]break!

2005/02/04 GEOID -- The LCIDs maligned little brother....

2005/02/03 English only! (or how to misuse NLS APIs)

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