Header files are the wrong place to be less than helpful

by Michael S. Kaplan, published on 2010/11/08 07:01 -05:00, original URI: http://blogs.msdn.com/b/michkap/archive/2010/11/08/10087179.aspx


A lot the people who work on the absolute latest version of Windows should have a lot more respect and consideration for the other, previous versions -- including the latest shipping version.

Not all of them, mind you. There are those who care about those other versions a lot no matter what they are working on.

A lot of them ought to, though.

It was late last month when someone was asking:

I have a requirement to enumerate the list of all country/regions in localized form depending on the current OS locale, just like the way they are shown when the OS is installed. Can someone point me to the API?

Now when a question like this is asked by someone inside of Microsoft, there are levels upon levels of things to consider that aren't an issue with a question asked by someone outside of Microsoft.

Like do they literally want to do it the way setup does it (e.g. maybe code running at the same time? Think Easy Transfer Wizard type stuff!) or is equivalent functionality acceptable?

The lists setup builds have every item that will be in the new OS after it is installed, so it can't call the currently installed OS.

Is the person asking for possibly internal, undocumented functions? If so then are they on the Windows team?

We can't give out internal Windows stuff to folks not on Windows.

Or is it someone in Product Support or Consulting Services or some other customer facing org, asking on behalf of a customer -- or code being written for a customer?

I won't explain why this changes things, but it should be obvious.

No worries this time, they just wanted equivalent functionality, publicly documented. So it can be the same answer I would give everyone out in the world too. If I were answering the question.

I didn't answer the question then, someone else did:

EnumSystemLocalesEx and GetLocaleInfoEx with LOCALE_SLOCALIZEDCOUNTRYNAME.  LOCALE_SLOCALIZEDCOUNTRYNAME is only available on Windows 7 and later I’m afraid.  I’m hoping by “current OS locale” you mean the users UI language (and not User Locale or System Locale).

I am not going to answer it now, I'll let the answer given stand.

However, in true passive/aggressive form, I am going to criticize the answer a bit. :-)

i won't criticize the third sentence ("I’m hoping by “current OS locale” you mean the users UI language (and not User Locale or System Locale)."), that is good level-setting about using the right locale among the many choices -- in this case the UI language. I do that all the time!

But those first two sentences. Let's chat a bit.....

First of course the first sentence ("EnumSystemLocalesEx and GetLocaleInfoEx with LOCALE_SLOCALIZEDCOUNTRYNAME."):

I am a huge fan of EnumSystemLocalesEx over EnumSystemLocales, and GetLocaleInfoEx over GetLocaleInfo. After all, I have been the person fearlessly saying LCIDs Suck both internally and externally before we even had all of the functions that could be used instead of them. I was saying it back when people were arguing about whether new functions were needed, as part of the reason to do some of the work!

But I am aware of the fact that sometimes, in fact most times, developers everywhere in the world other than the Windows team have to consider the need to support versions of the operating system older than Vista.

So while I have no problem pushing one solutioon over the other, I like to provide a bit more context.

It isn't like the docs help. If you go to the list of Windows National Language Support functions, the table lays them out as:

EnumSystemLocales Enumerates the locales that are either installed on or supported by an operating system.
EnumSystemLocalesEx Enumerates the locales that are either installed on or supported by an operating system.

Boy, way to help point people in the right direction there! Like maybe the Ex function could mention "using standards conformant locale names" or whatever. Or maybe put all the old functions in a separate table after the first big list of the most up-to-date functions? Something....

As I pointed out in To Ex or not to Ex? THAT is the question., unless you are intimately familiar with the two functions then in most cases you won't know which one to use.

Okay, you get my point.

Now let's talk about that second sentence, "LOCALE_SLOCALIZEDCOUNTRYNAME is only available on Windows 7 and later I’m afraid.".

Hmmmm.

Let's take a look at WinNls.h, they've done some shuffling here:

//
// These are the various forms of the name of the locale:
//
#define LOCALE_SLOCALIZEDDISPLAYNAME  0x00000002   // localized name of locale, eg "German (Germany)" in UI language
#if (WINVER >= _WIN32_WINNT_WIN7)
#define LOCALE_SENGLISHDISPLAYNAME    0x00000072   // Display name (language + country/region usually) in English, eg "German (Germany)"
#define LOCALE_SNATIVEDISPLAYNAME     0x00000073   // Display name in native locale language, eg "Deutsch (Deutschland)
#endif //(WINVER >= _WIN32_WINNT_WIN7)

#if (WINVER >= _WIN32_WINNT_VISTA)
#define LOCALE_SLOCALIZEDLANGUAGENAME 0x0000006f   // Language Display Name for a language, eg "German" in UI language
#endif //(WINVER >= _WIN32_WINNT_VISTA)
#define LOCALE_SENGLISHLANGUAGENAME   0x00001001   // English name of language, eg "German"
#define LOCALE_SNATIVELANGUAGENAME    0x00000004   // native name of language, eg "Deutsch"

#define LOCALE_SLOCALIZEDCOUNTRYNAME  0x00000006   // localized name of country/region, eg "Germany" in UI language
#define LOCALE_SENGLISHCOUNTRYNAME    0x00001002   // English name of country/region, eg "Germany"
#define LOCALE_SNATIVECOUNTRYNAME     0x00000008   // native name of country/region, eg "Deutschland"

//
// Legacy labels for the locale name values
//
#define LOCALE_SLANGUAGE              0x00000002   // localized name of locale, eg "German (Germany)" in UI language
#if (WINVER >= _WIN32_WINNT_VISTA)
#define LOCALE_SLANGDISPLAYNAME       0x0000006f   // Language Display Name for a language, eg "German" in UI language
#endif //(WINVER >= _WIN32_WINNT_VISTA)
#define LOCALE_SENGLANGUAGE           0x00001001   // English name of language, eg "German"
#define LOCALE_SNATIVELANGNAME        0x00000004   // native name of language, eg "Deutsch"
#define LOCALE_SCOUNTRY               0x00000006   // localized name of country/region, eg "Germany" in UI language
#define LOCALE_SENGCOUNTRY            0x00001002   // English name of country/region, eg "Germany"
#define LOCALE_SNATIVECTRYNAME        0x00000008   // native name of country/region, eg "Deutschland"

// Additional LCTypes
#define LOCALE_ILANGUAGE              0x00000001   // language id, LOCALE_SNAME preferred

#define LOCALE_SABBREVLANGNAME        0x00000003   // arbitrary abbreviated language name, LOCALE_SISO639LANGNAME preferred

#define LOCALE_ICOUNTRY               0x00000005   // country/region code, eg 1, LOCALE_SISO3166CTRYNAME may be more useful.
#define LOCALE_SABBREVCTRYNAME        0x00000007   // arbitrary abbreviated country/region name, LOCALE_SISO3166CTRYNAME preferred

Okay, speaking literally the answer is incorrect; LOCALE_SLOCALIZEDCOUNTRYNAME is under #if (WINVER >= _WIN32_WINNT_VISTA) defines, not #if (WINVER >= _WIN32_WINNT_WIN7) defines.

But my problem is a bit more subtle than that.

Like the fact that LOCALE_SLOCALIZEDCOUNTRYNAME has the same value as LOCALE_SCOUNTRY and therefore has been around for a very very long time.

There isn't much good reason for version guarding here on the constants since this isn't about version-specific functionality; providing consistent naming for these constants was done to make them easier to use on all versions because knowing that LOCALE_SLANGUAGE and LOCALE_SCOUNTRY are the localized names is kind of obscure, and having the full names makes it easier to understand.

Personally, because pf the pointless version guarding my usual recommendation would be to use LOCALE_SCOUNTRY, and not the "easier" LOCALE_SLOCALIZEDCOUNTRYNAME constant that is actually harder due to this arbitrary baggage added in.

I wonder if removing the cruft is an appcompat risk now. It probably is, unfortunately.

Now there are other pieces there in the latest file I find troubling, like the way LOCALE_SABBREVLANGNAME is an "arbitrary abbreviated language name, LOCALE_SISO639LANGNAME preferred".

After all the time I have spent dealing with the myriad of issues around LOCALE_SABBREVLANGNAME and the blogs I have written about it like LOCALE_SABBREVLANGNAME is so not an ISO-639 code and LOCALE_SABBREVLANGNAME is more than just an ISO-639 code, I can say that these codes that uniquely identify locales and are the central method of recognizably identifying keyboards that have taken hundreds of hours of my professional life are not arbitrary. And I find myself almost personally offended that this push to use standards is willing to do at the expense of functionality and of history.

Some if it my own history!

I'm hardly Jesus, but I'd rather not be denied, all things being equal.

Regular long-time readers may remember Is it Hangul? or Hangeul? or Han'gŭl? or what?, where no effort is made to try and force a political issue, even when something is added for some interesting political reasons.

This is still true, by the way, of the Korean charset constant.

I guess if you aren't on the NLS team you don't over-think the issues so much. :-)

Look, I was once a developer who considered the header files to be the only documentation worth looking at, and I do not mind editing with an eye to an agenda as long as the fundamental goal of usefulness isn't compromised. Comments are always a welcome addition, they truly are. As are version defines that avoid adding unintended version dependencies.

Thinking of prior blogs like A way better model for features, part 2 that covers the flaws in the downlevel library for NLS functions, the fact that they are mesing up the down-level functionality in the header file at least has the benefit of consistency.

All in all, I can't say I'm a fan of the new header file; it is better to not include information or version defines than to make the file either harder to use or harder to learn from....


beepee on 10 Nov 2010 5:41 AM:

I hate locales. If you once make some result file using locale A, and switch to locale B, your results are near to impossible to compare.

But this is only a small thing compared to Java. Why must every computer have a file for every self-esteemed city in the world?

And in many cases mutiple times, as software X can not rely on software Y's international set. Why the hell is this city info - if ever used- not in one database file? Something for your next Blog? Ooh, it is not MS

Michael S. Kaplan on 10 Nov 2010 8:08 AM:

When you say you hate locales, you are saying you hate that people expect different things in different places. There are only three "solutions" here:

1) never develop software outside a small area

2) accept people as they are

3) take over the world and use your authority to force everyone to do things the same way

I choose #2, mostly. :-)

John Cowan on 11 Nov 2010 3:44 PM:

Beepee (who beeps you, by the way?):

Not every city, just every time zone that either (a) has been different since January 1, 1970; or (b) might be different in the future because under a different political authority.  That makes for about 400 time zones worldwide (and history matters because dates in the past and future matter, not just the present).

Michael S. Kaplan on 11 Nov 2010 5:40 PM:

On Windows we try not to conflate time zones with locales, as this causes each group to become more complicated than if they are handled as two separate things....


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