On Dec 11, 2006, at 1:46 PM, Pierre d'Herbemont wrote:
This patch allows Mac OS X Users that set in System Preferences.app a language (say English) and that don't use the default number and text formatting currently associated with this language (for instance if they use French number and text formatting), to have Wine displaying text in their choosen language. And thus it allows Wine to behave like 'regular' Mac OS X Applications.
The only problem is that we have to translate the language id to a locale id.
Pierre.
dlls/kernel32/locale.c | 54 +++++++++++++++++++++++++++++++++++++ ++++++++++- 1 files changed, 53 insertions(+), 1 deletions(-) diff --git a/dlls/kernel32/locale.c b/dlls/kernel32/locale.c index 8792623..345780f 100644 --- a/dlls/kernel32/locale.c +++ b/dlls/kernel32/locale.c @@ -2832,7 +2841,50 @@ void LOCALE_Init(void) unix_cp = CP_UTF8; /* default to utf-8 even if we don't get a valid locale */ setenv( "LANG", user_locale, 0 ); TRACE( "setting locale to '%s'\n", user_locale ); -#endif
- /* We still want to set the LC_MESSAGES env to what is the
prefered language in
System Preferences.app, because it can differ from
CFLocaleCopyCurrent().
However this prefered language is stored using the general
language denotation
that may not include the country code (en,fr,en-GB,...)
which is not accepted
by setlocale (setlocale would expect en_US,fr_FR,en_GB,...).
So we retrieve possible locales from /usr/share/locale and
intersect them
with the prefered languages using
CFBundleCopyLocalizationsForPreferences. */
- dir = opendir("/usr/share/locale");
- available_locales = CFArrayCreateMutable(kCFAllocatorDefault,
0, NULL);
- while ((file = readdir(dir)))
- {
/* This filters the 'right' locales (xx_xx.UTF-8) */
if (strstr(file->d_name, ".UTF-8"))
CFArrayAppendValue(available_locales, (void*)
CFStringCreateWithCString(kCFAllocatorDefault,
file-
d_name, kCFStringEncodingUTF8));
The above should use CFStringCreateWithFileSystemRepresentation instead of CFStringCreateWithCString. Also, you need to CFRelease the created string after adding it to the array. (The array manages its references, but that doesn't relieve you of the responsibility to manage your own.)
- }
- closedir(dir);
I think the above code to scan /usr/share/locale is unnecessary. Doesn't CFLocaleCopyAvailableLocaleIdentifiers provide the equivalent?
- CFPreferencesAppSynchronize(kCFPreferencesAnyApplication);
- prefered_languages = CFPreferencesCopyValue( CFSTR
("AppleLanguages"), kCFPreferencesAnyApplication,
kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
- if(prefered_languages)
- {
CFArrayRef intersected_locales =
CFBundleCopyLocalizationsForPreferences(available_locales, prefered_languages);
Do you need prefered_languages? The docs say that if you pass NULL for the second parameter of CFBundleCopyLocalizationsForPreferences, it uses the user's preferences. That would avoid you having to look them up, above.
Also, "preferred" is spelled with three R's.
CFStringRef user_language = NULL;
if(CFArrayGetCount(intersected_locales))
user_language = CFArrayGetValueAtIndex
(intersected_locales, 0);
if(user_language)
{
CFStringGetCString( user_language, user_locale, sizeof
(user_locale), kCFStringEncodingUTF8 );
CFRelease( user_language );
TRACE( "setting LC_MESSAGES to '%s'\n", user_locale );
setenv( "LC_MESSAGES", user_locale, 0 );
}
CFRelease(prefered_languages);
CFRelease(intersected_locales);
- }
- CFRelease(available_locales);
+#endif /* __APPLE__ */
setlocale( LC_ALL, "" );
unix_cp = setup_unix_locales();
On 11 déc. 06, at 22:42, Ken Thomases wrote:
On Dec 11, 2006, at 1:46 PM, Pierre d'Herbemont wrote:
/* This filters the 'right' locales (xx_xx.UTF-8) */
if (strstr(file->d_name, ".UTF-8"))
CFArrayAppendValue(available_locales, (void*)
CFStringCreateWithCString(kCFAllocatorDefault,
file-
d_name, kCFStringEncodingUTF8));
The above should use CFStringCreateWithFileSystemRepresentation instead of CFStringCreateWithCString. Also, you need to CFRelease the created string after adding it to the array. (The array manages its references, but that doesn't relieve you of the responsibility to manage your own.)
My bad.
- }
- closedir(dir);
I think the above code to scan /usr/share/locale is unnecessary. Doesn't CFLocaleCopyAvailableLocaleIdentifiers provide the equivalent?
You're quite right. I though setlocale didn't handle 'en' properly expecting full 'en_US' or 'en_GB', I can't understand why now. Probably didn't have enough sleep last night.
- prefered_languages = CFPreferencesCopyValue( CFSTR
("AppleLanguages"), kCFPreferencesAnyApplication,
kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
- if(prefered_languages)
- {
CFArrayRef intersected_locales =
CFBundleCopyLocalizationsForPreferences(available_locales, prefered_languages);
Do you need prefered_languages? The docs say that if you pass NULL for the second parameter of CFBundleCopyLocalizationsForPreferences, it uses the user's preferences. That would avoid you having to look them up, above.
My bad.
Also, "preferred" is spelled with three R's.
My bad too.
A lot of mistakes here... Thanks for pointing them so fast,
Pierre.
On 12 déc. 06, at 00:20, Pierre d'Herbemont wrote:
- }
- closedir(dir);
I think the above code to scan /usr/share/locale is unnecessary. Doesn't CFLocaleCopyAvailableLocaleIdentifiers provide the equivalent?
You're quite right. I though setlocale didn't handle 'en' properly expecting full 'en_US' or 'en_GB', I can't understand why now. Probably didn't have enough sleep last night.
I do remember now :) setlocale don't handle properly two letters language code like 'es' it expect a full locale name like 'es_ES' . And using the CFLocale API to obtain the full local from 'es' does not seem possible.
And even with CFLocaleCopyAvailableLocaleIdentifiers() the returned array will contain 'es' among 'es_ES' and 'en_GB' so, when intersecting with the preferred languages [1] threw CFBundleCopyLocalizationsForPreferences() 'es' will be returned. setlocale(LC_MESSAGES, NULL) will return "C", and thus fallback the standard english language (LC_CTYPE is not set on Mac OS X), whereas setlocale should have returned es.
The fix for this is to provide to setlocale a full locale name like 'es_ES', that's why I did it that way.
Do you see any better way to do this rather than probably filling a bug report to Apple?
[1] #defaults read -g AppleLanguages ( es, fr, en, de, ja, it, nl, sv, nb, da, fi, pt, "zh-Hans", "zh- Hant", ko)
Pierre.
PS: I'll resend the patch with your fixes.
On Dec 11, 2006, at 5:49 PM, Pierre d'Herbemont wrote:
On 12 déc. 06, at 00:20, Pierre d'Herbemont wrote:
- }
- closedir(dir);
I think the above code to scan /usr/share/locale is unnecessary. Doesn't CFLocaleCopyAvailableLocaleIdentifiers provide the equivalent?
You're quite right. I though setlocale didn't handle 'en' properly expecting full 'en_US' or 'en_GB', I can't understand why now. Probably didn't have enough sleep last night.
I do remember now :) setlocale don't handle properly two letters language code like 'es' it expect a full locale name like 'es_ES' . And using the CFLocale API to obtain the full local from 'es' does not seem possible.
And even with CFLocaleCopyAvailableLocaleIdentifiers() the returned array will contain 'es' among 'es_ES' and 'en_GB' so, when intersecting with the preferred languages [1] threw CFBundleCopyLocalizationsForPreferences() 'es' will be returned. setlocale(LC_MESSAGES, NULL) will return "C", and thus fallback the standard english language (LC_CTYPE is not set on Mac OS X), whereas setlocale should have returned es.
The fix for this is to provide to setlocale a full locale name like 'es_ES', that's why I did it that way.
Do you see any better way to do this rather than probably filling a bug report to Apple?
[1] #defaults read -g AppleLanguages ( es, fr, en, de, ja, it, nl, sv, nb, da, fi, pt, "zh-Hans", "zh- Hant", ko)
Hmm. Well, it probably results in more cumbersome code, but you could filter the array returned by CFLocaleCopyAvailableLocaleIdentifiers by testing which elements have a country code. For each, call CFLocaleCreateComponentsFromLocaleIdentifier and check if the resulting dictionary has an object for the kCFLocaleCountryCode key. You'd accumulate the locale IDs which pass the test in the mutable array.
However, this whole strategy (however it's implemented, by scanning / usr/share/locale or the above method) seems to produce bad results. When I test here, CFBundleCopyLocalizationsForPreferences() spits out "en_ZW" (English, Zimbabwe). Some testing reveals that it is *not* just picking the last entry in the locale array whose language matches. It's not entirely predictable. I suspect it's doing a binary search.
In System Preferences, I have my language preference set to English. If I edit the list and add U.S. English and put it at the top, then the above method does produce U.S. English. However, that's not how most people have things set up.
I suppose we could use a multi-step approach. If the first element of the AppleLanguages array has a country code, use it directly. Otherwise, combine the language code from that element with the country code from the current locale. If that combination is present in the array of available locales, use that. Otherwise, fall back to CFBundleCopyLocalizationsForPreferences.
-Ken
Pierre d'Herbemont pdherbemont@free.fr writes:
I do remember now :) setlocale don't handle properly two letters language code like 'es' it expect a full locale name like 'es_ES' . And using the CFLocale API to obtain the full local from 'es' does not seem possible.
parse_locale_name() can handle that format though, so you can use that directly without going through setlocale. And if you really want setlocale you can rebuild a Unix locale name from parse_locale_name() results.