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>
17 #include <WINNT/TaLocale.h>
21 * DEFINITIONS ________________________________________________________________
27 WORD wPriority; // unused if MODULE_PRIORITY_REMOVED
28 HINSTANCE hInstance; // module handle
31 #define cREALLOC_MODULES 4
35 * VARIABLES __________________________________________________________________
39 static LANGID l_lang = LANG_USER_DEFAULT;
40 static CRITICAL_SECTION l_csModules;
41 static MODULE *l_aModules = NULL;
42 static size_t l_cModules = 0;
46 * PROTOTYPES _________________________________________________________________
50 BOOL IsValidStringTemplate (LPCSTRINGTEMPLATE pTable);
54 * ROUTINES ___________________________________________________________________
58 void TaLocale_Initialize (void)
60 static BOOL fInitialized = FALSE;
64 wsprintf(mutexName, TEXT("TaLocale_Initialize pid=%d"), getpid());
65 HANDLE hMutex = CreateMutex(NULL, TRUE, mutexName);
66 if ( GetLastError() == ERROR_ALREADY_EXISTS ) {
67 if ( WaitForSingleObject( hMutex, INFINITE ) != WAIT_OBJECT_0 ) {
75 InitializeCriticalSection (&l_csModules);
82 TaLocale_SpecifyModule (GetModuleHandle(NULL));
84 LCID lcidUser = GetUserDefaultLCID();
85 TaLocale_SetLanguage (LANGIDFROMLCID (lcidUser));
88 if ((LangOverride = TaLocale_GetLanguageOverride()) != (LANGID)0)
89 TaLocale_SetLanguage (LangOverride);
95 BOOL TaLocaleReallocFunction (LPVOID *ppTarget, size_t cbElement, size_t *pcTarget, size_t cReq, size_t cInc)
100 if (cReq <= *pcTarget)
103 if ((cNew = cInc * ((cReq + cInc-1) / cInc)) <= 0)
106 if ((pNew = Allocate (cbElement * cNew)) == NULL)
108 memset (pNew, 0x00, cbElement * cNew);
112 memcpy (pNew, *ppTarget, cbElement * (*pcTarget));
123 *** TaLocale_GuessBestLangID
125 * This routine picks the most common language/sublanguage pair based on
126 * the current language and sublanguage. These values are based on the
127 * most likely targets for our localization teams--e.g., we usually put
128 * out a Simplified Chinese translation, so if we run under a Singapore
129 * locale it's a good bet that we should load the Simplified Chinese
130 * translation instead of just failing for lack of a Singapore binary.
134 LANGID TaLocale_GuessBestLangID (LANGID lang)
136 switch (PRIMARYLANGID(lang))
139 return MAKELANGID(LANG_KOREAN,SUBLANG_KOREAN);
142 return MAKELANGID(LANG_JAPANESE,SUBLANG_DEFAULT);
145 return MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US);
148 if (SUBLANGID(lang) != SUBLANG_CHINESE_TRADITIONAL)
149 return MAKELANGID(LANG_CHINESE,SUBLANG_CHINESE_SIMPLIFIED);
152 return MAKELANGID(LANG_GERMAN,SUBLANG_GERMAN);
155 return MAKELANGID(LANG_SPANISH,SUBLANG_SPANISH);
157 case LANG_PORTUGUESE:
158 return MAKELANGID(LANG_PORTUGUESE,SUBLANG_PORTUGUESE_BRAZILIAN);
166 *** TaLocale_SpecifyModule
168 * Adds the given module handle to TaLocale's known list of possible sources
169 * for localized resource data. By default, the list is initialized with
170 * the current executable's module handle; if resources are to be extracted
171 * from other .DLLs using the functions in the TaLocale library, this routine
172 * should be called first to notify TaLocale about each .DLL.
176 void TaLocale_SpecifyModule (HINSTANCE hInstance, WORD wSearchPriority)
178 TaLocale_Initialize();
179 EnterCriticalSection (&l_csModules);
181 // First see if the specified module handle already exists; if
182 // so, remove it (its priority may be changing, or we may actually
183 // have been asked to remove it).
186 for (iModule = 0; iModule < l_cModules; ++iModule)
188 if (l_aModules[ iModule ].wPriority == 0) // end of list?
190 if (l_aModules[ iModule ].hInstance == hInstance) // found module?
193 if (iModule < l_cModules)
195 for ( ; iModule < l_cModules-1; ++iModule)
196 memcpy (&l_aModules[ iModule ], &l_aModules[ iModule+1 ], sizeof(MODULE));
197 memset (&l_aModules[ iModule ], 0x00, sizeof(MODULE));
200 // Next, if we have been asked to add this module, find a good place for it.
201 // We want to leave {iModule} pointing to the slot where this module
202 // should be inserted--anything currently at or after {iModule} will have
203 // to be moved outward.
205 if (wSearchPriority != MODULE_PRIORITY_REMOVE)
207 for (iModule = 0; iModule < l_cModules; ++iModule)
209 if (l_aModules[ iModule ].wPriority == 0) // end of list?
211 if (l_aModules[ iModule ].wPriority > wSearchPriority) // good place?
216 for (iTarget = iModule; iTarget < l_cModules; ++iTarget)
218 if (l_aModules[ iTarget ].wPriority == 0) // end of list?
222 if (REALLOC (l_aModules, l_cModules, 1+iTarget, cREALLOC_MODULES))
224 for (size_t iSource = iTarget -1; (LONG)iSource >= (LONG)iModule; --iSource, --iTarget)
225 memcpy (&l_aModules[ iTarget ], &l_aModules[ iSource ], sizeof(MODULE));
227 l_aModules[ iModule ].wPriority = wSearchPriority;
228 l_aModules[ iModule ].hInstance = hInstance;
232 LeaveCriticalSection (&l_csModules);
237 *** FindAfsCommonPath
239 * Because our localized files are often placed in a single Common directory
240 * for the AFS product, we need to know how to find that directory. This
241 * routine, and its helper FindAfsCommonPathByComponent(), do that search.
245 BOOL FindAfsCommonPathByComponent (LPTSTR pszCommonPath, LPTSTR pszComponent)
249 TCHAR szRegPath[ MAX_PATH ];
250 wsprintf (szRegPath, TEXT("Software\\TransarcCorporation\\%s\\CurrentVersion"), pszComponent);
253 if (RegOpenKey (HKEY_LOCAL_MACHINE, szRegPath, &hk) == 0)
255 DWORD dwType = REG_SZ;
256 DWORD dwSize = MAX_PATH;
258 if (RegQueryValueEx (hk, TEXT("PathName"), NULL, &dwType, (PBYTE)pszCommonPath, &dwSize) == 0)
260 *(LPTSTR)FindBaseFileName (pszCommonPath) = TEXT('\0');
262 if (pszCommonPath[0] && (pszCommonPath[ lstrlen(pszCommonPath)-1 ] == TEXT('\\')))
263 pszCommonPath[ lstrlen(pszCommonPath)-1 ] = TEXT('\0');
265 if (pszCommonPath[0])
266 lstrcat (pszCommonPath, TEXT("\\Common"));
272 return !!*pszCommonPath;
276 BOOL FindAfsCommonPath (LPTSTR pszCommonPath)
278 if (FindAfsCommonPathByComponent (pszCommonPath, TEXT("AFS Client")))
280 if (FindAfsCommonPathByComponent (pszCommonPath, TEXT("AFS Control Center")))
282 if (FindAfsCommonPathByComponent (pszCommonPath, TEXT("AFS Server")))
284 if (FindAfsCommonPathByComponent (pszCommonPath, TEXT("AFS Supplemental Documentation")))
291 *** TaLocale_LoadCorrespondingModule
293 * This routine looks for a .DLL named after the specified module, but
294 * with a suffix reflecting the current locale--it loads that library
295 * and calls TaLocale_SpecifyModule for the library.
299 HINSTANCE TaLocale_LoadCorrespondingModule (HINSTANCE hInstance, WORD wSearchPriority)
301 // If the caller was sloppy and didn't supply an instance handle,
302 // assume we should find the module corresponding with the current .EXE.
304 if (hInstance == NULL)
305 hInstance = GetModuleHandle(NULL);
307 TCHAR szFilename[ MAX_PATH ] = TEXT("");
308 GetModuleFileName (hInstance, szFilename, MAX_PATH);
310 return TaLocale_LoadCorrespondingModuleByName (hInstance, szFilename, wSearchPriority);
313 HINSTANCE TaLocale_LoadCorrespondingModuleByName (HINSTANCE hInstance, LPTSTR pszFilename, WORD wSearchPriority)
315 HINSTANCE hDLL = NULL;
317 TCHAR szFilename[ MAX_PATH ];
318 if (lstrchr (pszFilename, TEXT('\\')) != NULL)
319 lstrcpy (szFilename, pszFilename);
322 GetModuleFileName (hInstance, szFilename, MAX_PATH);
323 lstrcpy ((LPTSTR)FindBaseFileName (szFilename), pszFilename);
327 // If the caller was sloppy and didn't supply an instance handle,
328 // assume we should find the module corresponding with the current .EXE.
330 if (hInstance == NULL)
331 hInstance = GetModuleHandle(NULL);
334 if ((pchExtension = (LPTSTR)FindExtension (szFilename)) != NULL)
337 // Find the filename associated with the specified module, remove its
338 // extension, and append "_409.dll" (where the 409 is, naturally, the
339 // current LANGID). Then try to load that library.
341 wsprintf (pchExtension, TEXT("_%lu.dll"), TaLocale_GetLanguage());
342 if ((hDLL = LoadLibrary (szFilename)) == NULL)
343 hDLL = LoadLibrary (FindBaseFileName (szFilename));
345 // If we couldn't find the thing under that name, it's possible we
346 // have a .DLL made for the proper language but not for the user's
347 // specific sublanguage (say, a US English .DLL but we're running
348 // in a Canadian English locale). Make an intelligent guess about
349 // what the valid ID would be.
353 wsprintf (pchExtension, TEXT("_%lu.dll"), TaLocale_GuessBestLangID (TaLocale_GetLanguage()));
354 if ((hDLL = LoadLibrary (szFilename)) == NULL)
355 hDLL = LoadLibrary (FindBaseFileName (szFilename));
358 // If we STILL couldn't find a corresponding resource library,
359 // we'll take anything we can find; this should cover the case
360 // where a Setup program asked the user what language to use,
361 // and just installed that matching library. Look in the
362 // appropriate directory for any .DLL that fits the naming convention.
366 wsprintf (pchExtension, TEXT("_*.dll"));
369 memset (&wfd, 0x00, sizeof(wfd));
372 if ((hFind = FindFirstFile (szFilename, &wfd)) != NULL)
374 if (wfd.cFileName[0])
376 wsprintf ((LPTSTR)FindBaseFileName (szFilename), wfd.cFileName);
377 hDLL = LoadLibrary (szFilename);
383 // If we EVEN NOW couldn't find a corresponding resource library,
384 // we may have done our wildcard search in the wrong directory.
385 // Try to find the Common subdirectory of our AFS installation,
386 // and look for any matching DLL there.
390 wsprintf (pchExtension, TEXT("_*.dll"));
392 TCHAR szCommonPath[ MAX_PATH ];
393 if (FindAfsCommonPath (szCommonPath))
395 lstrcat (szCommonPath, TEXT("\\"));
396 lstrcat (szCommonPath, FindBaseFileName (szFilename));
399 memset (&wfd, 0x00, sizeof(wfd));
402 if ((hFind = FindFirstFile (szCommonPath, &wfd)) != NULL)
404 if (wfd.cFileName[0])
406 wsprintf ((LPTSTR)FindBaseFileName (szCommonPath), wfd.cFileName);
407 hDLL = LoadLibrary (szCommonPath);
414 // If all else fails, we'll try to find the English library
415 // somewhere on our path.
419 wsprintf (pchExtension, TEXT("_%lu.dll"), MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US));
420 if ((hDLL = LoadLibrary (szFilename)) == NULL)
421 hDLL = LoadLibrary (FindBaseFileName (szFilename));
424 // If we were successful in loading the resource library, add it
425 // to our chain of modules-to-search
429 TaLocale_SpecifyModule (hDLL, wSearchPriority);
438 *** TaLocale_EnumModule
440 * Enables enumeration of each of the product modules which will be seached
441 * by TaLocale for resources. Use TaLocale_SpecifyModule to modify this list.
442 * Modules will be returned in priority order, with the highest-priority
443 * modules being searched first.
447 BOOL TaLocale_EnumModule (size_t iModule, HINSTANCE *phInstance, WORD *pwSearchPriority)
450 TaLocale_Initialize();
451 EnterCriticalSection (&l_csModules);
453 if ( (iModule < l_cModules) && (l_aModules[ iModule ].wPriority != 0) )
457 *phInstance = l_aModules[ iModule ].hInstance;
458 if (pwSearchPriority)
459 *pwSearchPriority = l_aModules[ iModule ].wPriority;
462 LeaveCriticalSection (&l_csModules);
468 *** TaLocale_GetLanguage
469 *** TaLocale_SetLanguage
471 * Allows specification of a default language for resources extracted by
472 * the functions exported by tal_string.h and tal_dialog.h. When a particular
473 * string or dialog resource is required, the resource which matches this
474 * specified language will be retrieved--if no localized resource is available,
475 * the default resource will instead be used.
479 LANGID TaLocale_GetLanguage (void)
481 TaLocale_Initialize();
486 void TaLocale_SetLanguage (LANGID lang)
488 TaLocale_Initialize();
494 *** TaLocale_GetLanguageOverride
495 *** TaLocale_SetLanguageOverride
497 * Allows specification of a persistent default language for resources
498 * extracted by the functions exported by tal_string.h and tal_dialog.h.
499 * If a language override (which is really just a registry entry) exists,
500 * all TaLocale-based applications will default to that locale when first
505 static HKEY REGSTR_BASE_OVERRIDE = HKEY_LOCAL_MACHINE;
506 static TCHAR REGSTR_PATH_OVERRIDE[] = TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Nls");
507 static TCHAR REGSTR_VAL_OVERRIDE[] = TEXT("Default Language");
509 LANGID TaLocale_GetLanguageOverride (void)
514 if (RegOpenKey (REGSTR_BASE_OVERRIDE, REGSTR_PATH_OVERRIDE, &hk) == 0)
516 DWORD dwType = REG_DWORD;
517 DWORD dwSize = sizeof(DWORD);
518 if (RegQueryValueEx (hk, REGSTR_VAL_OVERRIDE, 0, &dwType, (PBYTE)&dwLang, &dwSize) != 0)
523 return (LANGID)dwLang;
527 void TaLocale_SetLanguageOverride (LANGID lang)
530 if (RegCreateKey (REGSTR_BASE_OVERRIDE, REGSTR_PATH_OVERRIDE, &hk) == 0)
532 DWORD dwLang = (DWORD)lang;
533 RegSetValueEx (hk, REGSTR_VAL_OVERRIDE, 0, REG_DWORD, (PBYTE)&dwLang, sizeof(DWORD));
539 void TaLocale_RemoveLanguageOverride (void)
542 if (RegOpenKey (REGSTR_BASE_OVERRIDE, REGSTR_PATH_OVERRIDE, &hk) == 0)
544 RegDeleteValue (hk, REGSTR_VAL_OVERRIDE);
551 *** TaLocale_GetResource
553 * Returns a pointer an in-memory image of a language-specific resource.
554 * The resource is found by searching all specified module handles
555 * (as determined automatically, or as specified by previous calls to
556 * TaLocale_SpecifyModule()) for an equivalent resource identifier matching
557 * the requested resource type and localized into the requested language.
558 * In the event that a matching localized resource cannot be found, the
559 * search is repeated using LANG_USER_DEFAULT.
561 * The pointer returned should be treated as read-only, and should not be freed.
565 LPCVOID TaLocale_GetResourceEx (LPCTSTR pszType, LPCTSTR pszRes, LANGID lang, HINSTANCE *phInstFound, BOOL fSearchDefaultLanguageToo)
570 for (size_t iModule = 0; TaLocale_EnumModule (iModule, &hInstance); ++iModule)
573 if ((hr = FindResourceEx (hInstance, pszType, pszRes, lang)) == NULL)
575 // Our translation teams don't usually change the language
576 // constants within .RC files, so we should look for English
577 // language translations too.
579 if ((hr = FindResourceEx (hInstance, pszType, pszRes, MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US))) == NULL)
581 // If we still can't find it, we'll take anything...
583 if ((hr = FindResource (hInstance, pszType, pszRes)) == NULL)
588 // Once we get here, we'll only fail for weird out-of-memory problems.
591 if ((hg = LoadResource (hInstance, hr)) != NULL)
593 if ((pr = (PVOID)LockResource (hg)) != NULL)
596 *phInstFound = hInstance;
606 LPCVOID TaLocale_GetResource (LPCTSTR pszType, LPCTSTR pszRes, LANGID lang, HINSTANCE *phInstFound)
608 return TaLocale_GetResourceEx (pszType, pszRes, lang, phInstFound, TRUE);
614 *** TaLocale_GetStringResource
615 *** TaLocale_GetDialogResource
617 * Convenience wrappers around TaLocale_GetResource for obtaining resources
618 * of a particular type by numeric identifier; these routines specify the
619 * default language (from TaLocale_SetLanguage()) when calling
620 * TaLocale_GetResource().
624 LPCDLGTEMPLATE TaLocale_GetDialogResource (int idd, HINSTANCE *phInstFound)
626 return (LPCDLGTEMPLATE)TaLocale_GetResource (RT_DIALOG, MAKEINTRESOURCE( idd ), TaLocale_GetLanguage(), phInstFound);
630 LPCSTRINGTEMPLATE TaLocale_GetStringResource (int ids, HINSTANCE *phInstFound)
632 // Strings are organized into heaps of String Tables, each table
633 // holding 16 strings (regardless of their length). The first table's
634 // first string index is for string #1. When searching for a string,
635 // the string's table is the index given to FindResource.
637 LPCSTRINGTEMPLATE pst = NULL;
638 LANGID lang = TaLocale_GetLanguage();
640 int iTable = (ids / 16) + 1; // 1 = first string table
641 int iIndex = ids - ((iTable-1) * 16); // 0 = first string in the table
643 HINSTANCE hInstance = NULL;
645 for (; !pst && TaLocale_EnumModule (iModule, &hInstance); ++iModule)
648 if ((hr = FindResourceEx (hInstance, RT_STRING, MAKEINTRESOURCE( iTable ), lang)) == NULL)
650 // Our translation teams don't usually change the language
651 // constants within .RC files, so we should look for English
652 // language translations too.
654 if ((hr = FindResourceEx (hInstance, RT_STRING, MAKEINTRESOURCE( iTable ), MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US))) == NULL)
656 // If we still can't find it, we'll take anything...
658 if ((hr = FindResource (hInstance, RT_STRING, MAKEINTRESOURCE( iTable ))) == NULL)
664 if ((hg = LoadResource (hInstance, hr)) != NULL)
667 if ((pTable = (WORD*)LockResource (hg)) != NULL)
670 // Skip words in the string table until we reach the string
671 // index we're looking for.
673 for (int iIndexWalk = iIndex; iIndexWalk && ((LPCSTRINGTEMPLATE)pTable)->cchString; --iIndexWalk) {
674 pTable += 1 + ((LPCSTRINGTEMPLATE)pTable)->cchString;
677 if (IsValidStringTemplate ((LPCSTRINGTEMPLATE)pTable))
679 pst = (LPCSTRINGTEMPLATE)pTable;
681 *phInstFound = hInstance;
683 UnlockResource(pTable);
689 UnlockResource(pTable);
691 // If we walked off the end of the table, then the
692 // string we want just wasn't there.
702 BOOL IsValidStringTemplate (LPCSTRINGTEMPLATE pTable)
704 if (!pTable->cchString)
707 for (size_t ii = 0; ii < pTable->cchString; ++ii)
709 if (!pTable->achString[ii])
718 *** TaLocale_LoadMenu
719 *** TaLocale_LoadImage
721 * Replacements for Win32 functions. By using these functions instead, the
722 * caller can load the appropriate resources regardless of the module in
723 * which they reside, or the language which is required.
727 HMENU TaLocale_LoadMenu (int idm)
729 const MENUTEMPLATE *pTemplate;
730 if ((pTemplate = (const MENUTEMPLATE *)TaLocale_GetResource (RT_MENU, MAKEINTRESOURCE( idm ), TaLocale_GetLanguage())) == NULL)
732 return LoadMenuIndirect (pTemplate);
736 HANDLE TaLocale_LoadImage (int idi, UINT imageType, int cx, int cy, UINT imageFlags)
739 for (size_t iModule = 0; TaLocale_EnumModule (iModule, &hInstance); ++iModule)
742 if (imageType == IMAGE_ICON)
743 hImage = (HANDLE)LoadIcon (hInstance, MAKEINTRESOURCE(idi));
745 hImage = LoadImage (hInstance, MAKEINTRESOURCE(idi), imageType, cx, cy, imageFlags);
755 HICON TaLocale_LoadIcon (int idi)
757 return (HICON)TaLocale_LoadImage (idi, IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR);
761 HACCEL TaLocale_LoadAccelerators (int ida)
764 for (size_t iModule = 0; TaLocale_EnumModule (iModule, &hInstance); ++iModule)
767 if ((hAccel = LoadAccelerators (hInstance, MAKEINTRESOURCE(ida))) != NULL)