I have some existing Visual C++ code where I need to add the conversion of wide character strings to upper or lower case.
I know there are pitfalls to this (such as the Turkish "I"), but most of these can be ironed-out if you know the language. Fortunately in this area of code I know the LCID value (locale ID) which I guess is the same as knowing the language.
As LCID is a Windows type, is there a Windows function that will convert wide strings to upper or lower case?
The C runtime function _towupper_l() sounds like it would be ideal but it takes a _locale_t parameter instead of LCID, so I guess it's unsuitable unless there is a completely reliable way of converting an LCID to a _locale_t.
The function you're searching for is called
LCMapStringand it is part of the Windows NLS APIs. TheLCMAP_UPPERCASEflag maps characters to uppercase, while theLCMAP_LOWERCASEmaps characters to lowercase.For applications targeting Windows Vista and later, there is an
Exvariant that works on locale names instead of identifiers, which are what Microsoft now says you should prefer to use.In fact, in the CRT implementation provided with VS 2010 (and presumably other versions as well), functions such as
_towupper_lultimately end up callingLCMapStringafter they extract the locale ID (LCID) from the specified_locale_t.If you're like me, and less familiar with the i8n APIs than you should be, you probably already know about the
CharUpper,CharLower,CharUpperBuff, andCharLowerBufffamily of functions. These have been the old standbys from the early days of Windows for altering the case of chars/strings, but as their documentation warns:What it neglects to mention is filled in by a couple of posts on Michael Kaplan's wonderful blog on internationalization issues: What does "linguistic casing" mean?, How best to alter case. The executive summary is that you achieve the same results as the
CharXxxfamily of functions by callingLCMapStringand not specifying theLCMAP_LINGUISTIC_CASINGflag, whereas you can be linguistically sensitive by ensuring that you do specify theLCMAP_LINGUISTIC_CASINGflag.Sample code: