分类: C/C++
2008-08-23 22:26:48
I have read a lot of good articles on CodeProject. Now it's time for me to provide one. Please leave any comment or suggestion you may have. All are welcome and greatly appreciated. I do want to get your feedback on this implementation of multiple language support. Following requirements have been made for this implementation of multiple language support for MFC applications with MFC extension DLL:
InitInstance()
/ExitInstance()
and DllMain()
and that should be enough. This document is for Visual Studio 6, may be version 5, but not for version 7, 7.1, 8.0. MFC version 7 has some built-in support for multiple languages. This article includes the following:
I have read some articles here at CodeProject and Microsoft:
LoadString()
etc. but the comment area of this article is pretty good.
From VC7 and up, you shouldn't worry that much on multiple language support.
I wrapped some language related functions to a class. I made some changes for VC6, and put a demo project using it. You need to exit and launch your application again for a new language selected in the Control Panel.
AFX_EXT_CLASS
in the class definition header file. In Main, add a new menu item to run this test dialog. Sure, you need to include main.h or resource.h in TestDlg.cpp, and include TestDlg.h in MainFrame.cpp.
_MBCS
to _UNICODE
at C/C++ tab in project settings dialog, change Entry-point symbol at output category of Link tab to wWinMainCRTStartup
. You need do this for Main and SubDLL, Release and Debug.
Visual C++ allows you to choose different languages when you create an MFC AppWizard program. The East Asian language support DLLs (Japanese, Korean, and simplified Chinese) for the MFC AppWizard, which require double-byte enabled operating systems, are not installed by default. Thus you will not see them in the language drop-down list on the first page of the MFC AppWizard. However, you can find them in the Microsoft Visual Studio\Common\msdev98\bin\ide directory on the Visual C++ CD as follows:
Language | AppWizard DLL |
Japanese | APPWZJPN.DLL |
Korean | APPWZKOR.DLL |
Chinese (simplified) | APPWZCHS.DLL |
To take advantage of East Asian language support:
If your application is dynamically linked to MFC, you must have the corresponding localized version of the MFC resource DLL, MFC##LOC.DLL, in your Windows system directory. To do this, copy the corresponding DLL in the MFC\include\L.XXX\MFC##XXX.DLL on the Visual C++ CD into the Windows system directory, and rename it to MFC##LOC.DLL. For more information on using the localized resources that Visual C++ provides, see Tech Note 56 and Tech Note 57.
– or–
If your application is statically linked to MFC, you must have the appropriate localized MFC resource files in your MFC\[src|include]\L.XXX\*.rc directory. You can find these files in the corresponding directories of Visual Studio CD1. For more information on using the localized resources that Visual C++ provides, see Tech Note 56 and Tech Note 57.
Static linking to MFC is supported only in Visual C++ Professional and Enterprise Editions. For more information, see Visual C++ Editions.
Now build English resource only DLL files for Main.exe and subdll.dll. A resource-only DLL is a DLL that contains nothing but resources, such as icons, bitmaps, strings, and dialog boxes. It is also a good way to provide an application with resources localized for multiple languages. See “Creating a Resource-Only DLL” (1.) in MSDN. Here are the steps to build a resource-only language DLL. It's named after its EXE or DLL, appending the three-letter language code. E.g. Main.exe has its English resource DLL as MainENU.DLL. Following are the steps to build English (ENU) resource-only language DLL from the previous German (DEU) code.
AFX_TARG_DEU
" to "AFX_TARG_ENU
".
LANG_GERMAN, SUBLANG_GERMAN
" to "LANG_ENGLISH, SUBLANG_ENGLISH_US
".
LANG_ENGLISH
is 9, LANG_GERMAN
is 7; both sublanguages are 1.)
l.deu\\
" or "l.deu\
" for English. For language other than English, use “l.xxx” subfolder to locate the MFC resource for that language. xxx
is the three-letter language code. See tables below.
res\
" to "resENU\
".
VERSIONINFO
block from "040704B0" to "040904B0" - "7" for German and "9" for English.
For MUI (Multiple User Interface) OS, a user can update his OS user language in Control Panel, Region and language options, Language. Following code is partially copied from VC7.1 appcore.cpp. It detects UI language, and stores a language list for loading later.
LANGID CMultiLanguage::DetectUILanguage() { LANGID langid = 0; int nPrimaryLang = 0; int nSubLang = 0; LCID lcid = 0; PFNGETUSERDEFAULTUILANGUAGE pfnGetUserDefaultUILanguage; PFNGETSYSTEMDEFAULTUILANGUAGE pfnGetSystemDefaultUILanguage; HINSTANCE hKernel32; hKernel32 = ::GetModuleHandle(_T("kernel32.dll")); ASSERT(hKernel32 != NULL); pfnGetUserDefaultUILanguage = (PFNGETUSERDEFAULTUILANGUAGE)::GetProcAddress(hKernel32, "GetUserDefaultUILanguage"); if(pfnGetUserDefaultUILanguage != NULL) { // First, try the user's UI language langid = pfnGetUserDefaultUILanguage(); AddLangId( langid ); TRACE(_T("CMultiLanguage::DetectUILanguage()" _T" 1st/2nd = %04X\n"), langid ); // Then, try the system's default UI language pfnGetSystemDefaultUILanguage = (PFNGETSYSTEMDEFAULTUILANGUAGE)::GetProcAddress(hKernel32, "GetSystemDefaultUILanguage"); ASSERT( pfnGetSystemDefaultUILanguage != NULL ); langid = pfnGetSystemDefaultUILanguage(); AddLangId( langid ); TRACE(_T("CMultiLanguage::DetectUILanguage()" _T" 3rd/4th = %04X\n"), langid ); } else { // We're not on an MUI-capable system. if (::GetVersion()&0x80000000) { // We're on Windows 9x, so look // in the registry for the UI language HKEY hKey = NULL; LONG nResult = ::RegOpenKeyEx(HKEY_CURRENT_USER, _T( "Control Panel\\Desktop\\ResourceLocale" ), 0, KEY_READ, &hKey); if (nResult == ERROR_SUCCESS) { DWORD dwType; TCHAR szValue[16]; ULONG nBytes = sizeof( szValue ); nResult = ::RegQueryValueEx(hKey, NULL, NULL, &dwType, LPBYTE( szValue ), &nBytes ); if ((nResult == ERROR_SUCCESS) && (dwType == REG_SZ)) { DWORD dwLangID; int nFields = _stscanf( szValue, _T( "%x" ), &dwLangID ); if( nFields == 1 ) { langid = LANGID( dwLangID ); AddLangId( langid ); TRACE(_T("CMultiLanguage::DetectUILanguage()" _T" 9X1st/2nd = %04X\n"), langid ); } } ::RegCloseKey(hKey); } } else { // We're on NT 4. The UI language // is the same as the language of the // version resource in ntdll.dll HMODULE hNTDLL = ::GetModuleHandle( _T( "ntdll.dll" ) ); if (hNTDLL != NULL) { langid = 0; ::EnumResourceLanguages( hNTDLL, RT_VERSION, MAKEINTRESOURCE( 1 ), _AfxEnumResLangProc, reinterpret_cast< LONG_PTR >( &langid ) ); if (langid != 0) { AddLangId( langid ); TRACE(_T("CMultiLanguage::DetectUILanguage()" _T" NT1st/2nd = %04X\n"), langid ); } } } } if ( m_nLocales < MAX_NUM_LCID ) { m_alcidSearch[m_nLocales] = LOCALE_SYSTEM_DEFAULT; m_nLocales++; } else { m_alcidSearch[MAX_NUM_LCID-1] = LOCALE_SYSTEM_DEFAULT; m_nLocales = MAX_NUM_LCID; } return LANGIDFROMLCID(m_alcidSearch[0]); }
While the above code works for MUI OS, a user may change the default locale, not the UI language. The following code detects the user default language, not the UI language.
LANGID CMultiLanguage::DetectLangID() { LANGID langid = 0; int nPrimaryLang = 0; int nSubLang = 0; LCID lcid = 0; int nLocales = 0; langid = GetUserDefaultLangID(); // WinNT3.1/95 and later AddLangId( langid ); TRACE(_T("CMultiLanguage::GetUserDefaultLangID() 1st/2nd = %0X\n"), langid ); LANGID langSysid = GetSystemDefaultLangID();// WinNT3.1/95 and later AddLangId( langSysid ); TRACE(_T("CMultiLanguage::GetSystemDefaultLangID() 3rd/4th = %0X\n"), langid ); return langid; }
We can get user selected languages in the OS now. I tested these only on Win200 Pro and WinXP Pro. If you can test this on 9X or NT, let me know if this failed or succeeded.
Like that in MFC version 7, we attempt to load the resource DLL for each of the following languages in order, stopping when it finds one:
GetUserDefaultLangID()
Win32 API.
GetSystemDefaultLangID()
Win32 API.
GetUserDefaultUILanguage()
Win32 API.
GetSystemDefaultUILanguage()
API. On other platforms, this is the language of the OS itself.
To detect the user’s default language and system default language, we should make a call to DetectLangID()
. For user and system’s UI language, calling DetectUILanguage()
will be OK. After these two calls, a list of languages requested is stored in the CMultiLanguage::m_alcidSearch[]
array. To load it, use the following:
HINSTANCE CMultiLanguage::LoadLangResourceDLL(LPCTSTR szModuleName, LANGID langUpdateId) { TCHAR szResDLLName[_MAX_PATH+14]; HINSTANCE hLangDLL = NULL; LCID alcid[MAX_NUM_LCID+1]; TCHAR szLangCode[4]; //LPTSTR pszExtension; int nNoExtension; LCID lcid; int nLocales = 0; //pszExtension = ::PathFindExtension(szModuleName); //nNoExtension = pszExtension - szModuleName; temp. for ".exe" nNoExtension = lstrlen(szModuleName) - 3 ; // Quick and kind of dirty way to take ".exe"/".dll" away. if ( langUpdateId != MAKELANGID(LANG_NEUTRAL,SUBLANG_NEUTRAL) ) { alcid[nLocales] = MAKELCID(langUpdateId, SORT_DEFAULT); nLocales++; } for ( int iLocale = 0; iLocale < m_nLocales; iLocale++ ) { if ( m_alcidSearch[iLocale] != 0 ) { alcid[nLocales] = m_alcidSearch[iLocale]; nLocales++; } } for ( iLocale = 0; iLocale < nLocales; iLocale++ ) { lcid = alcid[iLocale]; if (lcid == LOCALE_SYSTEM_DEFAULT) lstrcpy(szLangCode, _T("LOC")); else { int nResult = ::GetLocaleInfo(lcid, LOCALE_SABBREVLANGNAME, szLangCode, 4); ASSERT( nResult == 4 ); if ( nResult == 0 ) return NULL; } if ( nNoExtension + 3 + 4 + 1 < _MAX_PATH+14 ) { // append "ENU.DLL" to moduleName lstrcpyn(szResDLLName, szModuleName, nNoExtension); lstrcat(szResDLLName, szLangCode); lstrcat(szResDLLName, _T(".DLL")); } else { ASSERT(FALSE); // No enough space to hold language resource dll name path. return NULL; } hLangDLL = ::LoadLibrary(szResDLLName); if(hLangDLL != NULL) return hLangDLL;// Successful return } return hLangDLL; }
We can call LoadLangResourceDLL(…)
to load the first available language DLL in the list. You can change the language ID detected from above. For example, you only include the resource DLL for Chinese PRC, and you want to use it for Chinese Taiwan and Chinese Singapore too. You can do this hard-coding here. If you don't want to hard code here, you can make a duplicated copy of the resource-only DLL file with another three-letter language name. (Copy XXXXCHS.DLL to XXXXCHT.DLL for this case.)
If your EXE or DLL files don't include resource, you need to have one system default language "LOC" in your resource-only language DLL file list. This is the last chance the system will try to find the language resource.
For Main.exe to load a language related resource-only DLL, add the following code to main.cpp in the very beginning of CMain::InitInstance()
:
// Begin of Multiple Language support if ( CMultiLanguage::m_nLocales <= 0 ) // Not detected yet { CMultiLanguage::DetectLangID(); // Detect language as user locale CMultiLanguage::DetectUILanguage(); // Detect language in MUI OS } TCHAR szModuleFileName[MAX_PATH]; // Get Module File Name and path int ret = ::GetModuleFileName(theApp.m_hInstance, szModuleFileName, MAX_PATH); if ( ret == 0 || ret == MAX_PATH ) ASSERT(FALSE); // Load resource-only language DLL. It will use the languages // detected above, take first available language, // or you can specify another language as second parameter to // LoadLangResourceDLL. And try that first. ghLangInst = CMultiLanguage::LoadLangResourceDLL( szModuleFileName ); if (ghLangInst) AfxSetResourceHandle( ghLangInst ); // End of Multiple Language support
You may want to free the library in ExitInstance()
.
For the MFC extension DLL SubDLL.DLL to load language related resource-only DLL, add the following code to SubDLL.cpp in DllMain(...)
just before calling CDynLinkLibrary(...)
:
// Begin of Multiple Language support if ( CMultiLanguage::m_nLocales <= 0 ) // Not detected yet { CMultiLanguage::DetectLangID(); // Detect language as user locale CMultiLanguage::DetectUILanguage(); // Detect language in MUI OS } TCHAR szModuleFileName[MAX_PATH]; // Get Module File Name and path int ret = ::GetModuleFileName(hInstance, szModuleFileName, MAX_PATH); if ( ret == 0 || ret == MAX_PATH ) ASSERT(FALSE); // Load resource-only language DLL. It will use the languages // detected above, take first available language, // or you can specify another language as second parameter to // LoadLangResourceDLL. And try that first. shLangInst = CMultiLanguage::LoadLangResourceDLL( szModuleFileName ); if (shLangInst) SubDLLDLL.hResource = shLangInst; // End of Multiple Language support
You may want to free the library when system detaches the process.
DetectUILanguage()
function in DLLMain(...)
will be called earlier than that in InitInstance()
.
Column one is LANGID
; the lower 10 bits are for the language, the higher 6 bits are for the sub-language. Column two is the three-letter language code. Column three is the three-letter language code without sub-language. It looks for this language if the language in column two does not exist. Column one and column four are got from “Language Identifier”(3.); column two and column three are got from the GetLocaleInfo()
function on my XP Pro PC. See CMultiLanguage::PrintThreeLetterLanguageCodeList()
function in MultiLanguage.cpp for details.
Identifier | Column 2 | Column 3 | Description and notes |
0x0436 | AFK | AFK | Afrikaans |
0x041c | SQI | SQI | Albanian |
0x0401 | ARA | ARA | Arabic (Saudi Arabia) |
0x0801 | ARI | ARA | Arabic (Iraq) |
0x0c01 | ARE | ARA | Arabic (Egypt) |
0x1001 | ARL | ARA | Arabic (Libya) |
0x1401 | ARG | ARA | Arabic (Algeria) |
0x1801 | ARM | ARA | Arabic (Morocco) |
0x1c01 | ART | ARA | Arabic (Tunisia) |
0x2001 | ARO | ARA | Arabic (Oman) |
0x2401 | ARY | ARA | Arabic (Yemen) |
0x2801 | ARS | ARA | Arabic (Syria) |
0x2c01 | ARJ | ARA | Arabic (Jordan) |
0x3001 | ARB | ARA | Arabic (Lebanon) |
0x3401 | ARK | ARA | Arabic (Kuwait) |
0x3801 | ARU | ARA | Arabic (U.A.E.) |
0x3c01 | ARH | ARA | Arabic (Bahrain) |
0x4001 | ARQ | ARA | Arabic (Qatar) |
0x042b | HYE | HYE | Windows 2000/XP: Armenian. This is Unicode only. |
0x042c | AZE | AZE | Azeri (Latin) |
0x082c | AZE | AZE | Azeri (Cyrillic) |
0x042d | EUQ | EUQ | Basque |
0x0423 | BEL | BEL | Belarusian |
0x0445 | BNG | BNG | Bengali (India) |
0x141a | BSB | HRV | Bosnian (Bosnia and Herzego vina) |
0x0402 | BGR | BGR | Bulgarian |
0x0455 | === | === | Burmese |
0x0403 | CAT | CAT | Catalan |
0x0404 | CHT | CHT | Chinese (Taiwan) |
0x0804 | CHS | CHT | Chinese (PRC) |
0x0c04 | ZHH | CHT | Chinese (Hong Kong SAR, PRC ) |
0x1004 | ZHI | CHT | Chinese (Singapore) |
0x1404 | ZHM | CHT | Windows 98/ME, Windows 2000 /XP: Chinese (Macao SAR) |
0x041a | HRV | HRV | Croatian |
0x101a | HRB | HRV | Croatian (Bosnia and Herzeg ovina) |
0x0405 | CSY | CSY | Czech |
0x0406 | DAN | DAN | Danish |
0x0465 | DIV | DIV | Windows XP: Divehi. This is Unicode only. |
0x0413 | NLD | NLD | Dutch (Netherlands) |
0x0813 | NLB | NLD | Dutch (Belgium) |
0x0409 | ENU | ENU | English (United States) |
0x0809 | ENG | ENU | English (United Kingdom) |
0x0c09 | ENA | ENU | English (Australian) |
0x1009 | ENC | ENU | English (Canadian) |
0x1409 | ENZ | ENU | English (New Zealand) |
0x1809 | ENI | ENU | English (Ireland) |
0x1c09 | ENS | ENU | English (South Africa) |
0x2009 | ENJ | ENU | English (Jamaica) |
0x2409 | ENB | ENU | English (Caribbean) |
0x2809 | ENL | ENU | English (Belize) |
0x2c09 | ENT | ENU | English (Trinidad) |
0x3009 | ENW | ENU | Windows 98/ME, Windows 2000 /XP: English (Zimbabwe) |
0x3409 | ENP | ENU | Windows 98/ME, Windows 2000 /XP: English (Philippines) |
0x0425 | ETI | ETI | Estonian |
0x0438 | FOS | FOS | Faeroese |
0x0429 | FAR | FAR | Farsi |
0x040b | FIN | FIN | Finnish |
0x040c | FRA | FRA | French (Standard) |
0x080c | FRB | FRA | French (Belgian) |
0x0c0c | FRC | FRA | French (Canadian) |
0x100c | FRS | FRA | French (Switzerland) |
0x140c | FRL | FRA | French (Luxembourg) |
0x180c | FRM | FRA | Windows 98/ME, Windows 2000 /XP: French (Monaco) |
0x0456 | GLC | GLC | Windows XP: Galician |
0x0437 | KAT | KAT | Windows 2000/XP: Georgian. This is Unicode only. |
0x0407 | DEU | DEU | German (Standard) |
0x0807 | DES | DEU | German (Switzerland) |
0x0c07 | DEA | DEU | German (Austria) |
0x1007 | DEL | DEU | German (Luxembourg) |
0x1407 | DEC | DEU | German (Liechtenstein) |
0x0408 | ELL | ELL | Greek |
0x0447 | GUJ | GUJ | Windows XP: Gujarati. This is Unicode only. |
0x040d | HEB | HEB | Hebrew |
0x0439 | HIN | HIN | Windows 2000/XP: Hindi. This is Unicode only. |
0x040e | HUN | HUN | Hungarian |
0x040f | ISL | ISL | Icelandic |
0x0421 | IND | IND | Indonesian |
0x0434 | XHO | XHO | isiXhosa/Xhosa (South Africa) |
0x0435 | ZUL | ZUL | isiZulu/Zulu (South Africa) |
0x0410 | ITA | ITA | Italian (Standard) |
0x0810 | ITS | ITA | Italian (Switzerland) |
0x0411 | JPN | JPN | Japanese |
0x044b | KAN | KAN | Windows XP: Kannada. This is Unicode only. |
0x0457 | KNK | KNK | Windows 2000/XP: Konkani. This is Unicode only. |
0x0412 | KOR | KOR | Korean |
0x0812 | === | KOR | Windows 95, Windows NT 4.0 only: Korean (Johab) |
0x0440 | KYR | KYR | Windows XP: Kyrgyz. |
0x0426 | LVI | LVI | Latvian |
0x0427 | LTH | LTH | Lithuanian |
0x0827 | === | LTH | Windows 98 only: Lithuanian (Classic) |
0x042f | MKI | MKI | Macedonian (FYROM) |
0x043e | MSL | MSL | Malay (Malaysian) |
0x083e | MSB | MSL | Malay (Brunei Darussalam) |
0x044c | MYM | MYM | Malayalam (India) |
0x0481 | MRI | MRI | Maori (New Zealand) |
0x043a | MLT | MLT | Maltese (Malta) |
0x044e | MAR | MAR | Windows 2000/XP: Marathi. This is Unicode only. |
0x0450 | MON | MON | Windows XP: Mongolian |
0x0414 | NOR | NOR | Norwegian (Bokmal) |
0x0814 | NON | NOR | Norwegian (Nynorsk) |
0x0415 | PLK | PLK | Polish |
0x0416 | PTB | PTB | Portuguese (Brazil) |
0x0816 | PTG | PTB | Portuguese (Portugal) |
0x0446 | PAN | PAN | Windows XP: Punjabi. This is Unicode only. |
0x046b | QUB | QUB | Quechua (Bolivia) |
0x086b | QUE | QUB | Quechua (Ecuador) |
0x0c6b | QUP | QUB | Quechua (Peru) |
0x0418 | ROM | ROM | Romanian |
0x0419 | RUS | RUS | Russian |
0x044f | SAN | SAN | Windows 2000/XP: Sanskrit. This is Unicode only. |
0x043b | SME | SME | Sami, Northern (Norway) |
0x083b | SMF | SME | Sami, Northern (Sweden) |
0x0c3b | SMG | SME | Sami, Northern (Finland) |
0x103b | SMJ | SME | Sami, Lule (Norway) |
0x143b | SMK | SME | Sami, Lule (Sweden) |
0x183b | SMA | SME | Sami, Southern (Norway) |
0x1c3b | SMB | SME | Sami, Southern (Sweden) |
0x203b | SMS | SME | Sami, Skolt (Finland) |
0x243b | SMN | SME | Sami, Inari (Finland) |
0x0c1a | SRB | HRV | Serbian (Cyrillic) |
0x1c1a | SRN | HRV | Serbian (Cyrillic, Bosnia, and Herzegovina) |
0x081a | SRL | HRV | Serbian (Latin) |
0x181a | SRS | HRV | Serbian (Latin, Bosnia, and Herzegovina) |
0x046c | NSO | NSO | Sesotho sa Leboa/Northern Sotho (South Africa) |
0x0432 | TSN | TSN | Setswana/Tswana (South Africa) |
0x041b | SKY | SKY | Slovak |
0x0424 | SLV | SLV | Slovenian |
0x040a | ESP | ESP | Spanish (Spain, Traditional Sort) |
0x080a | ESM | ESP | Spanish (Mexican) |
0x0c0a | ESN | ESP | Spanish (Spain, Modern Sort ) |
0x100a | ESG | ESP | Spanish (Guatemala) |
0x140a | ESC | ESP | Spanish (Costa Rica) |
0x180a | ESA | ESP | Spanish (Panama) |
0x1c0a | ESD | ESP | Spanish (Dominican Republic) |
0x200a | ESV | ESP | Spanish (Venezuela) |
0x240a | ESO | ESP | Spanish (Colombia) |
0x280a | ESR | ESP | Spanish (Peru) |
0x2c0a | ESS | ESP | Spanish (Argentina) |
0x300a | ESF | ESP | Spanish (Ecuador) |
0x340a | ESL | ESP | Spanish (Chile) |
0x380a | ESY | ESP | Spanish (Uruguay) |
0x3c0a | ESZ | ESP | Spanish (Paraguay) |
0x400a | ESB | ESP | Spanish (Bolivia) |
0x440a | ESE | ESP | Spanish (El Salvador) |
0x480a | ESH | ESP | Spanish (Honduras) |
0x4c0a | ESI | ESP | Spanish (Nicaragua) |
0x500a | ESU | ESP | Spanish (Puerto Rico) |
0x0430 | === | === | Sutu |
0x0441 | SWK | SWK | Swahili (Kenya) |
0x041d | SVE | SVE | Swedish |
0x081d | SVF | SVE | Swedish (Finland) |
0x045a | SYR | SYR | Windows XP: Syriac. This is Unicode only. |
0x0449 | TAM | TAM | Windows 2000/XP: Tamil. This is Unicode only. |
0x0444 | TTT | TTT | Tatar (Tatarstan) |
0x044a | TEL | TEL | Windows XP: Telugu. This is Unicode only. |
0x041e | THA | THA | Thai |
0x041f | TRK | TRK | Turkish |
0x0422 | UKR | UKR | Ukrainian |
0x0420 | URD | URD | Windows 98/ME, Windows 2000 /XP: Urdu (Pakistan) |
0x0820 | === | URD | Urdu (India) |
0x0443 | UZB | UZB | Uzbek (Latin) |
0x0843 | UZB | UZB | Uzbek (Cyrillic) |
0x042a | VIT | VIT | Windows 98/ME, Windows NT 4 .0 and later: Vietnamese |
0x0452 | CYM | CYM | Welsh (United Kingdom) |