I thought I'd give a healthy sample of examples of how NOT to get the answer to the question "Is this a locale whose language uses bidirectional text?". All of these examples have actually been used at various points in Microsoft products, though they are [usually] being replaced as fast as they are found....
The names of the functions and identifying comments have been changed to protect the guilty (and the embarrassed).
Hopefully you have none of these in your own code -- or at the very least you hopefully will have none within a day or two of reading this post! :-)
BOOL IsBidi(LANGID langid) {
switch(langid) {
case 1025: // Arabic
case 1037: // Hebrew
case 1065: // Farsi
return true;
default:
return false;
}
}
BOOL IsBidi(LCID lcid) {
return (PRIMARYLANGID(lcid) == LANG_ARABIC ||
PRIMARYLANGID(lcid) == LANG_HEBREW)
}
BOOL IsBiDiLcid(LCID lcid)
{
if (lcid == 0x0859 || //Sindhi (Arabic script)
lcid == 0x0460) //Kashmiri (Arabic script)
return TRUE;
switch (PRIMARYLANGID(lcid))
{
case LANG_ARABIC:
case LANG_HEBREW:
case LANG_URDU:
case LANG_FARSI:
case LANG_PASHTO:
case LANG_UIGHUR:
case LANG_SYRIAC:
case LANG_DIVEHI:
return TRUE;
}
return FALSE;
}
BOOL GetBidiStatus(LANGID langid) {
WCHAR szDayName[80] = {0};
WORD ctype = 0;
GetLocaleInfo( langid,
LOCALE_SDAYNAME1,
szDayName,
sizeof(szDayName] / sizeof(WCHAR) );
GetStringType( CT_CTYPE2,
&szDayName[0],
1,
&ctype );
return( ctype & C2_RIGHTTOLEFT );
}
BOOL IsBidiLocale(LCID locale)
{
DWORD layout;
return(GetProcessDefaultLayout(&layout) &&
(layout & LAYOUT_RTL));
}
BOOL IsBidirectional(LCID lcid) {
LOCALESIGNATURE signature;
GetLocaleInfoW(lcid,
LOCALE_FONTSIGNATURE
(LPWSTR)&signature,
sizeof(signature));
if(signature.lsCsbSupported[0] & 0x60) {
return(TRUE);
else
return(FALSE);
}
}
BOOL FDetectBidi(LCID lcid) {
FONTSIGNATURE fontsignature;
GetLocaleInfoW(lcid,
LOCALE_FONTSIGNATURE
(LPWSTR)&fontsignature,
sizeof(fontsignature));
return(fontsignature.fsUsb[0] & 0x2800));
}
The number of problems with the above is HUGE. Buffer overruns, wrong parameters, not checking for return values, ignoring the LCID, making the wrong checks. It is enough to make you want to tear your hair out. Or, if you are a bit more clear-headed, enough to make you want to tear out the hair of some other developers whose name appeared on the SLM logs checking in the code....
Though I thought the use of a locale field like SDAYNAME1 (several other examples along this line with different values were also around) was somewhat clever (especially in downlevel cases where the preferred solution is not available).
Though I worry about the cases where the field chosen may not start with a strong right-to-left character....
Anyway, the best solution is to use the mystical bit #123 of the Unicode subset bitfields of the LOCALESIGNATURE which is defined as "Layout progress: horizontal from right to left" and which is around in Windows 2000 and later. Here is an example of that best approach:
WCHAR wchLCIDFontSig[16];
if (GetLocaleInfoW(lcid,
LOCALE_FONTSIGNATURE,
&wchLCIDFontSig[0],
(sizeof(wchLCIDFontSig)/sizeof(WCHAR))) &&
(wchLCIDFontSignature[7] & (WCHAR)0x0800))
or if you prefer to work with the actual locale signature rather than the bytes of a WCHAR array that are actually a locale signature underneath, it would be something more like the following (assuming my bit math is not faulty!):
LOCALESIGNATURE localesig;
if (GetLocaleInfoW(lcid,
LOCALE_FONTSIGNATURE,
(LPWSTR)&localesig,
(sizeof(localesig)/sizeof(WCHAR))) &&
(localesig.lsUsb[3] & 0x10000000))
What I find most impressive about the wide array of the different attempts people made is
- the inginuity of some people even when they are quite simply wrong;
- the number of random code mistakes that one can find in those examples;
- how clear it is that we need a LOCALE_IRIGHTTOLEFT LCType in the future for GetLocaleInfo!
Do you have a favorite? :-)
This post brought to you by "?" (U+05ea, a.k.a. HEBREW LETTER TAV)