2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
11 #include <afs/param.h>
15 #include <WINNT/TaLocale.h>
19 * DEFINITIONS ________________________________________________________________
25 WORD wPriority; // unused if MODULE_PRIORITY_REMOVED
26 HINSTANCE hInstance; // module handle
29 #define cREALLOC_MODULES 4
33 * VARIABLES __________________________________________________________________
37 static LANGID l_lang = LANG_USER_DEFAULT;
38 static CRITICAL_SECTION l_csModules;
39 static MODULE *l_aModules = NULL;
40 static size_t l_cModules = 0;
44 * PROTOTYPES _________________________________________________________________
48 BOOL IsValidStringTemplate (LPCSTRINGTEMPLATE pTable);
52 * ROUTINES ___________________________________________________________________
56 void TaLocale_Initialize (void)
58 static BOOL fInitialized = FALSE;
64 InitializeCriticalSection (&l_csModules);
65 TaLocale_SpecifyModule (GetModuleHandle(NULL));
67 LCID lcidUser = GetUserDefaultLCID();
68 TaLocale_SetLanguage (LANGIDFROMLCID (lcidUser));
71 if ((LangOverride = TaLocale_GetLanguageOverride()) != (LANGID)0)
72 TaLocale_SetLanguage (LangOverride);
77 BOOL TaLocaleReallocFunction (LPVOID *ppTarget, size_t cbElement, size_t *pcTarget, size_t cReq, size_t cInc)
82 if (cReq <= *pcTarget)
85 if ((cNew = cInc * ((cReq + cInc-1) / cInc)) <= 0)
88 if ((pNew = Allocate (cbElement * cNew)) == NULL)
90 memset (pNew, 0x00, cbElement * cNew);
94 memcpy (pNew, *ppTarget, cbElement * (*pcTarget));
105 *** TaLocale_GuessBestLangID
107 * This routine picks the most common language/sublanguage pair based on
108 * the current language and sublanguage. These values are based on the
109 * most likely targets for our localization teams--e.g., we usually put
110 * out a Simplified Chinese translation, so if we run under a Singapore
111 * locale it's a good bet that we should load the Simplified Chinese
112 * translation instead of just failing for lack of a Singapore binary.
116 LANGID TaLocale_GuessBestLangID (LANGID lang)
118 switch (PRIMARYLANGID(lang))
121 return MAKELANGID(LANG_KOREAN,SUBLANG_KOREAN);
124 return MAKELANGID(LANG_JAPANESE,SUBLANG_DEFAULT);
127 return MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US);
130 if (SUBLANGID(lang) != SUBLANG_CHINESE_TRADITIONAL)
131 return MAKELANGID(LANG_CHINESE,SUBLANG_CHINESE_SIMPLIFIED);
134 return MAKELANGID(LANG_GERMAN,SUBLANG_GERMAN);
137 return MAKELANGID(LANG_SPANISH,SUBLANG_SPANISH);
139 case LANG_PORTUGUESE:
140 return MAKELANGID(LANG_PORTUGUESE,SUBLANG_PORTUGUESE_BRAZILIAN);
148 *** TaLocale_SpecifyModule
150 * Adds the given module handle to TaLocale's known list of possible sources
151 * for localized resource data. By default, the list is initialized with
152 * the current executable's module handle; if resources are to be extracted
153 * from other .DLLs using the functions in the TaLocale library, this routine
154 * should be called first to notify TaLocale about each .DLL.
158 void TaLocale_SpecifyModule (HINSTANCE hInstance, WORD wSearchPriority)
160 TaLocale_Initialize();
161 EnterCriticalSection (&l_csModules);
163 // First see if the specified module handle already exists; if
164 // so, remove it (its priority may be changing, or we may actually
165 // have been asked to remove it).
167 for (size_t iModule = 0; iModule < l_cModules; ++iModule)
169 if (l_aModules[ iModule ].wPriority == 0) // end of list?
171 if (l_aModules[ iModule ].hInstance == hInstance) // found module?
174 if (iModule < l_cModules)
176 for ( ; iModule < l_cModules-1; ++iModule)
177 memcpy (&l_aModules[ iModule ], &l_aModules[ iModule+1 ], sizeof(MODULE));
178 memset (&l_aModules[ iModule ], 0x00, sizeof(MODULE));
181 // Next, if we have been asked to add this module, find a good place for it.
182 // We want to leave {iModule} pointing to the slot where this module
183 // should be inserted--anything currently at or after {iModule} will have
184 // to be moved outward.
186 if (wSearchPriority != MODULE_PRIORITY_REMOVE)
188 for (iModule = 0; iModule < l_cModules; ++iModule)
190 if (l_aModules[ iModule ].wPriority == 0) // end of list?
192 if (l_aModules[ iModule ].wPriority > wSearchPriority) // good place?
196 for (size_t iTarget = iModule; iTarget < l_cModules; ++iTarget)
198 if (l_aModules[ iTarget ].wPriority == 0) // end of list?
202 if (REALLOC (l_aModules, l_cModules, 1+iTarget, cREALLOC_MODULES))
204 for (size_t iSource = iTarget -1; (LONG)iSource >= (LONG)iModule; --iSource, --iTarget)
205 memcpy (&l_aModules[ iTarget ], &l_aModules[ iSource ], sizeof(MODULE));
207 l_aModules[ iModule ].wPriority = wSearchPriority;
208 l_aModules[ iModule ].hInstance = hInstance;
212 LeaveCriticalSection (&l_csModules);
217 *** FindAfsCommonPath
219 * Because our localized files are often placed in a single Common directory
220 * for the AFS product, we need to know how to find that directory. This
221 * routine, and its helper FindAfsCommonPathByComponent(), do that search.
225 BOOL FindAfsCommonPathByComponent (LPTSTR pszCommonPath, LPTSTR pszComponent)
229 TCHAR szRegPath[ MAX_PATH ];
230 wsprintf (szRegPath, TEXT("Software\\TransarcCorporation\\%s\\CurrentVersion"), pszComponent);
233 if (RegOpenKey (HKEY_LOCAL_MACHINE, szRegPath, &hk) == 0)
235 DWORD dwType = REG_SZ;
236 DWORD dwSize = MAX_PATH;
238 if (RegQueryValueEx (hk, TEXT("PathName"), NULL, &dwType, (PBYTE)pszCommonPath, &dwSize) == 0)
240 *(LPTSTR)FindBaseFileName (pszCommonPath) = TEXT('\0');
242 if (pszCommonPath[0] && (pszCommonPath[ lstrlen(pszCommonPath)-1 ] == TEXT('\\')))
243 pszCommonPath[ lstrlen(pszCommonPath)-1 ] = TEXT('\0');
245 if (pszCommonPath[0])
246 lstrcat (pszCommonPath, TEXT("\\Common"));
252 return !!*pszCommonPath;
256 BOOL FindAfsCommonPath (LPTSTR pszCommonPath)
258 if (FindAfsCommonPathByComponent (pszCommonPath, TEXT("AFS Client")))
260 if (FindAfsCommonPathByComponent (pszCommonPath, TEXT("AFS Control Center")))
262 if (FindAfsCommonPathByComponent (pszCommonPath, TEXT("AFS Server")))
264 if (FindAfsCommonPathByComponent (pszCommonPath, TEXT("AFS Supplemental Documentation")))
271 *** TaLocale_LoadCorrespondingModule
273 * This routine looks for a .DLL named after the specified module, but
274 * with a suffix reflecting the current locale--it loads that library
275 * and calls TaLocale_SpecifyModule for the library.
279 HINSTANCE TaLocale_LoadCorrespondingModule (HINSTANCE hInstance, WORD wSearchPriority)
281 // If the caller was sloppy and didn't supply an instance handle,
282 // assume we should find the module corresponding with the current .EXE.
284 if (hInstance == NULL)
285 hInstance = GetModuleHandle(NULL);
287 TCHAR szFilename[ MAX_PATH ] = TEXT("");
288 GetModuleFileName (hInstance, szFilename, MAX_PATH);
290 return TaLocale_LoadCorrespondingModuleByName (hInstance, szFilename, wSearchPriority);
293 HINSTANCE TaLocale_LoadCorrespondingModuleByName (HINSTANCE hInstance, LPTSTR pszFilename, WORD wSearchPriority)
295 HINSTANCE hDLL = NULL;
297 TCHAR szFilename[ MAX_PATH ];
298 if (lstrchr (pszFilename, TEXT('\\')) != NULL)
299 lstrcpy (szFilename, pszFilename);
302 GetModuleFileName (hInstance, szFilename, MAX_PATH);
303 lstrcpy ((LPTSTR)FindBaseFileName (szFilename), pszFilename);
307 // If the caller was sloppy and didn't supply an instance handle,
308 // assume we should find the module corresponding with the current .EXE.
310 if (hInstance == NULL)
311 hInstance = GetModuleHandle(NULL);
314 if ((pchExtension = (LPTSTR)FindExtension (szFilename)) != NULL)
317 // Find the filename associated with the specified module, remove its
318 // extension, and append "_409.dll" (where the 409 is, naturally, the
319 // current LANGID). Then try to load that library.
321 wsprintf (pchExtension, TEXT("_%lu.dll"), TaLocale_GetLanguage());
322 if ((hDLL = LoadLibrary (szFilename)) == NULL)
323 hDLL = LoadLibrary (FindBaseFileName (szFilename));
325 // If we couldn't find the thing under that name, it's possible we
326 // have a .DLL made for the proper language but not for the user's
327 // specific sublanguage (say, a US English .DLL but we're running
328 // in a Canadian English locale). Make an intelligent guess about
329 // what the valid ID would be.
333 wsprintf (pchExtension, TEXT("_%lu.dll"), TaLocale_GuessBestLangID (TaLocale_GetLanguage()));
334 if ((hDLL = LoadLibrary (szFilename)) == NULL)
335 hDLL = LoadLibrary (FindBaseFileName (szFilename));
338 // If we STILL couldn't find a corresponding resource library,
339 // we'll take anything we can find; this should cover the case
340 // where a Setup program asked the user what language to use,
341 // and just installed that matching library. Look in the
342 // appropriate directory for any .DLL that fits the naming convention.
346 wsprintf (pchExtension, TEXT("_*.dll"));
349 memset (&wfd, 0x00, sizeof(wfd));
352 if ((hFind = FindFirstFile (szFilename, &wfd)) != NULL)
354 if (wfd.cFileName[0])
356 wsprintf ((LPTSTR)FindBaseFileName (szFilename), wfd.cFileName);
357 hDLL = LoadLibrary (szFilename);
363 // If we EVEN NOW couldn't find a corresponding resource library,
364 // we may have done our wildcard search in the wrong directory.
365 // Try to find the Common subdirectory of our AFS installation,
366 // and look for any matching DLL there.
370 wsprintf (pchExtension, TEXT("_*.dll"));
372 TCHAR szCommonPath[ MAX_PATH ];
373 if (FindAfsCommonPath (szCommonPath))
375 lstrcat (szCommonPath, TEXT("\\"));
376 lstrcat (szCommonPath, FindBaseFileName (szFilename));
379 memset (&wfd, 0x00, sizeof(wfd));
382 if ((hFind = FindFirstFile (szCommonPath, &wfd)) != NULL)
384 if (wfd.cFileName[0])
386 wsprintf ((LPTSTR)FindBaseFileName (szCommonPath), wfd.cFileName);
387 hDLL = LoadLibrary (szCommonPath);
394 // If all else fails, we'll try to find the English library
395 // somewhere on our path.
399 wsprintf (pchExtension, TEXT("_%lu.dll"), MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US));
400 if ((hDLL = LoadLibrary (szFilename)) == NULL)
401 hDLL = LoadLibrary (FindBaseFileName (szFilename));
404 // If we were successful in loading the resource library, add it
405 // to our chain of modules-to-search
409 TaLocale_SpecifyModule (hDLL, wSearchPriority);
418 *** TaLocale_EnumModule
420 * Enables enumeration of each of the product modules which will be seached
421 * by TaLocale for resources. Use TaLocale_SpecifyModule to modify this list.
422 * Modules will be returned in priority order, with the highest-priority
423 * modules being searched first.
427 BOOL TaLocale_EnumModule (size_t iModule, HINSTANCE *phInstance, WORD *pwSearchPriority)
430 TaLocale_Initialize();
431 EnterCriticalSection (&l_csModules);
433 if ( (iModule < l_cModules) && (l_aModules[ iModule ].wPriority != 0) )
437 *phInstance = l_aModules[ iModule ].hInstance;
438 if (pwSearchPriority)
439 *pwSearchPriority = l_aModules[ iModule ].wPriority;
442 LeaveCriticalSection (&l_csModules);
448 *** TaLocale_GetLanguage
449 *** TaLocale_SetLanguage
451 * Allows specification of a default language for resources extracted by
452 * the functions exported by tal_string.h and tal_dialog.h. When a particular
453 * string or dialog resource is required, the resource which matches this
454 * specified language will be retrieved--if no localized resource is available,
455 * the default resource will instead be used.
459 LANGID TaLocale_GetLanguage (void)
461 TaLocale_Initialize();
466 void TaLocale_SetLanguage (LANGID lang)
468 TaLocale_Initialize();
474 *** TaLocale_GetLanguageOverride
475 *** TaLocale_SetLanguageOverride
477 * Allows specification of a persistent default language for resources
478 * extracted by the functions exported by tal_string.h and tal_dialog.h.
479 * If a language override (which is really just a registry entry) exists,
480 * all TaLocale-based applications will default to that locale when first
485 static HKEY REGSTR_BASE_OVERRIDE = HKEY_LOCAL_MACHINE;
486 static TCHAR REGSTR_PATH_OVERRIDE[] = TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Nls");
487 static TCHAR REGSTR_VAL_OVERRIDE[] = TEXT("Default Language");
489 LANGID TaLocale_GetLanguageOverride (void)
494 if (RegOpenKey (REGSTR_BASE_OVERRIDE, REGSTR_PATH_OVERRIDE, &hk) == 0)
496 DWORD dwType = REG_DWORD;
497 DWORD dwSize = sizeof(DWORD);
498 if (RegQueryValueEx (hk, REGSTR_VAL_OVERRIDE, 0, &dwType, (PBYTE)&dwLang, &dwSize) != 0)
503 return (LANGID)dwLang;
507 void TaLocale_SetLanguageOverride (LANGID lang)
510 if (RegCreateKey (REGSTR_BASE_OVERRIDE, REGSTR_PATH_OVERRIDE, &hk) == 0)
512 DWORD dwLang = (DWORD)lang;
513 RegSetValueEx (hk, REGSTR_VAL_OVERRIDE, 0, REG_DWORD, (PBYTE)&dwLang, sizeof(DWORD));
519 void TaLocale_RemoveLanguageOverride (void)
522 if (RegOpenKey (REGSTR_BASE_OVERRIDE, REGSTR_PATH_OVERRIDE, &hk) == 0)
524 RegDeleteValue (hk, REGSTR_VAL_OVERRIDE);
531 *** TaLocale_GetResource
533 * Returns a pointer an in-memory image of a language-specific resource.
534 * The resource is found by searching all specified module handles
535 * (as determined automatically, or as specified by previous calls to
536 * TaLocale_SpecifyModule()) for an equivalent resource identifier matching
537 * the requested resource type and localized into the requested language.
538 * In the event that a matching localized resource cannot be found, the
539 * search is repeated using LANG_USER_DEFAULT.
541 * The pointer returned should be treated as read-only, and should not be freed.
545 LPCVOID TaLocale_GetResourceEx (LPCTSTR pszType, LPCTSTR pszRes, LANGID lang, HINSTANCE *phInstFound, BOOL fSearchDefaultLanguageToo)
550 for (size_t iModule = 0; TaLocale_EnumModule (iModule, &hInstance); ++iModule)
553 if ((hr = FindResourceEx (hInstance, pszType, pszRes, lang)) == NULL)
555 // Our translation teams don't usually change the language
556 // constants within .RC files, so we should look for English
557 // language translations too.
559 if ((hr = FindResourceEx (hInstance, pszType, pszRes, MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US))) == NULL)
561 // If we still can't find it, we'll take anything...
563 if ((hr = FindResource (hInstance, pszType, pszRes)) == NULL)
568 // Once we get here, we'll only fail for weird out-of-memory problems.
571 if ((hg = LoadResource (hInstance, hr)) != NULL)
573 if ((pr = (PVOID)LockResource (hg)) != NULL)
576 *phInstFound = hInstance;
586 LPCVOID TaLocale_GetResource (LPCTSTR pszType, LPCTSTR pszRes, LANGID lang, HINSTANCE *phInstFound)
588 return TaLocale_GetResourceEx (pszType, pszRes, lang, phInstFound, TRUE);
594 *** TaLocale_GetStringResource
595 *** TaLocale_GetDialogResource
597 * Convenience wrappers around TaLocale_GetResource for obtaining resources
598 * of a particular type by numeric identifier; these routines specify the
599 * default language (from TaLocale_SetLanguage()) when calling
600 * TaLocale_GetResource().
604 LPCDLGTEMPLATE TaLocale_GetDialogResource (int idd, HINSTANCE *phInstFound)
606 return (LPCDLGTEMPLATE)TaLocale_GetResource (RT_DIALOG, MAKEINTRESOURCE( idd ), TaLocale_GetLanguage(), phInstFound);
610 LPCSTRINGTEMPLATE TaLocale_GetStringResource (int ids, HINSTANCE *phInstFound)
612 // Strings are organized into heaps of String Tables, each table
613 // holding 16 strings (regardless of their length). The first table's
614 // first string index is for string #1. When searching for a string,
615 // the string's table is the index given to FindResource.
617 LPCSTRINGTEMPLATE pst = NULL;
618 LANGID lang = TaLocale_GetLanguage();
620 int iTable = (ids / 16) + 1; // 1 = first string table
621 int iIndex = ids - ((iTable-1) * 16); // 0 = first string in the table
624 for (size_t iModule = 0; !pst && TaLocale_EnumModule (iModule, &hInstance); ++iModule)
627 if ((hr = FindResourceEx (hInstance, RT_STRING, MAKEINTRESOURCE( iTable ), lang)) == NULL)
629 // Our translation teams don't usually change the language
630 // constants within .RC files, so we should look for English
631 // language translations too.
633 if ((hr = FindResourceEx (hInstance, RT_STRING, MAKEINTRESOURCE( iTable ), MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US))) == NULL)
635 // If we still can't find it, we'll take anything...
637 if ((hr = FindResource (hInstance, RT_STRING, MAKEINTRESOURCE( iTable ))) == NULL)
643 if ((hg = LoadResource (hInstance, hr)) != NULL)
646 if ((pTable = (WORD*)LockResource (hg)) != NULL)
649 // Skip words in the string table until we reach the string
650 // index we're looking for.
652 for (int iIndexWalk = iIndex; iIndexWalk; --iIndexWalk)
653 pTable += 1 + ((LPCSTRINGTEMPLATE)pTable)->cchString;
655 if (IsValidStringTemplate ((LPCSTRINGTEMPLATE)pTable))
657 pst = (LPCSTRINGTEMPLATE)pTable;
659 *phInstFound = hInstance;
664 // If we walked off the end of the table, then the
665 // string we want just wasn't there.
675 BOOL IsValidStringTemplate (LPCSTRINGTEMPLATE pTable)
677 if (!pTable->cchString)
680 for (size_t ii = 0; ii < pTable->cchString; ++ii)
682 if (!pTable->achString[ii])
691 *** TaLocale_LoadMenu
692 *** TaLocale_LoadImage
694 * Replacements for Win32 functions. By using these functions instead, the
695 * caller can load the appropriate resources regardless of the module in
696 * which they reside, or the language which is required.
700 HMENU TaLocale_LoadMenu (int idm)
702 const MENUTEMPLATE *pTemplate;
703 if ((pTemplate = (const MENUTEMPLATE *)TaLocale_GetResource (RT_MENU, MAKEINTRESOURCE( idm ), TaLocale_GetLanguage())) == NULL)
705 return LoadMenuIndirect (pTemplate);
709 HANDLE TaLocale_LoadImage (int idi, UINT imageType, int cx, int cy, UINT imageFlags)
712 for (size_t iModule = 0; TaLocale_EnumModule (iModule, &hInstance); ++iModule)
715 if (imageType == IMAGE_ICON)
716 hImage = (HANDLE)LoadIcon (hInstance, MAKEINTRESOURCE(idi));
718 hImage = LoadImage (hInstance, MAKEINTRESOURCE(idi), imageType, cx, cy, imageFlags);
728 HICON TaLocale_LoadIcon (int idi)
730 return (HICON)TaLocale_LoadImage (idi, IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR);
734 HACCEL TaLocale_LoadAccelerators (int ida)
737 for (size_t iModule = 0; TaLocale_EnumModule (iModule, &hInstance); ++iModule)
740 if ((hAccel = LoadAccelerators (hInstance, MAKEINTRESOURCE(ida))) != NULL)