mirror of
https://github.com/reactos/wine.git
synced 2025-01-31 17:23:53 +00:00
kernel32: Overhaul the locale determination on the Mac.
The C library locale is inadequate for conveying the Mac user settings between LOCALE_Init() and setup_unix_locales(). The set of locales supported by the C library can't express the combinations allowed in user settings. Setting LANG to a user-settings-derived locale when there's no corresponding C library locale is actually worse than leaving it unset. It will prevent the C library from honoring the LC_* variables (other than LC_ALL). That's why Terminal.app won't set LANG in that case, it just sets LC_CTYPE=UTF-8. This commit makes Wine follow similar logic in not setting LANG if the C library doesn't support the Mac user settings. Rather, it uses a wrapper around setlocale() to query the locale. That wrapper returns a value representing the Mac user settings if the C library comes up empty. It also has logic to handle Terminal's setting LC_CTYPE=UTF-8, since parse_locale_name() can't handle that properly. Signed-off-by: Ken Thomases <ken@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
bf4a2ba264
commit
d90f29d817
@ -911,6 +911,139 @@ void LOCALE_InitRegistry(void)
|
||||
}
|
||||
|
||||
|
||||
#ifdef __APPLE__
|
||||
/***********************************************************************
|
||||
* get_mac_locale
|
||||
*
|
||||
* Return a locale identifer string reflecting the Mac locale, in a form
|
||||
* that parse_locale_name() will understand. So, strip out unusual
|
||||
* things like script, variant, etc. Or, rather, just construct it as
|
||||
* <lang>[_<country>].UTF-8.
|
||||
*/
|
||||
static const char* get_mac_locale(void)
|
||||
{
|
||||
static char mac_locale[50];
|
||||
|
||||
if (!mac_locale[0])
|
||||
{
|
||||
CFLocaleRef locale = CFLocaleCopyCurrent();
|
||||
CFStringRef lang = CFLocaleGetValue( locale, kCFLocaleLanguageCode );
|
||||
CFStringRef country = CFLocaleGetValue( locale, kCFLocaleCountryCode );
|
||||
CFStringRef locale_string;
|
||||
|
||||
if (country)
|
||||
locale_string = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@_%@"), lang, country);
|
||||
else
|
||||
locale_string = CFStringCreateCopy(NULL, lang);
|
||||
|
||||
CFStringGetCString(locale_string, mac_locale, sizeof(mac_locale), kCFStringEncodingUTF8);
|
||||
strcat(mac_locale, ".UTF-8");
|
||||
|
||||
CFRelease(locale);
|
||||
CFRelease(locale_string);
|
||||
}
|
||||
|
||||
return mac_locale;
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* has_env
|
||||
*/
|
||||
static BOOL has_env(const char* name)
|
||||
{
|
||||
const char* value = getenv( name );
|
||||
return value && value[0];
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* get_locale
|
||||
*
|
||||
* Get the locale identifier for a given category. On most platforms,
|
||||
* this is just a thin wrapper around setlocale(). On OS X, though, it
|
||||
* is common for the Mac locale settings to not be supported by the C
|
||||
* library. So, we sometimes override the result with the Mac locale.
|
||||
*/
|
||||
static const char* get_locale(int category, const char* category_name)
|
||||
{
|
||||
const char* ret = setlocale(category, NULL);
|
||||
|
||||
#ifdef __APPLE__
|
||||
/* If LC_ALL is set, respect it as a user override.
|
||||
If LC_* is set, respect it as a user override, except if it's LC_CTYPE
|
||||
and equal to UTF-8. That's because, when the Mac locale isn't supported
|
||||
by the C library, Terminal.app sets LC_CTYPE=UTF-8 and doesn't set LANG.
|
||||
parse_locale_name() doesn't handle that properly, so we override that
|
||||
with the Mac locale (which uses UTF-8 for the charset, anyway).
|
||||
Otherwise:
|
||||
For LC_MESSAGES, we override the C library because the user language
|
||||
setting is separate from the locale setting on which LANG was based.
|
||||
If the C library didn't get anything better from LANG than C or POSIX,
|
||||
override that. That probably means the Mac locale isn't supported by
|
||||
the C library. */
|
||||
if (!has_env( "LC_ALL" ) &&
|
||||
((category == LC_CTYPE && !strcmp( ret, "UTF-8" )) ||
|
||||
(!has_env( category_name ) &&
|
||||
(category == LC_MESSAGES || !strcmp( ret, "C" ) || !strcmp( ret, "POSIX" )))))
|
||||
{
|
||||
const char* override = get_mac_locale();
|
||||
|
||||
if (category == LC_MESSAGES)
|
||||
{
|
||||
/* Retrieve the preferred language as chosen in System Preferences. */
|
||||
static char messages_locale[50];
|
||||
|
||||
if (!messages_locale[0])
|
||||
{
|
||||
CFArrayRef preferred_langs = CFLocaleCopyPreferredLanguages();
|
||||
if (preferred_langs && CFArrayGetCount( preferred_langs ))
|
||||
{
|
||||
CFStringRef preferred_lang = CFArrayGetValueAtIndex( preferred_langs, 0 );
|
||||
CFDictionaryRef components = CFLocaleCreateComponentsFromLocaleIdentifier( NULL, preferred_lang );
|
||||
if (components)
|
||||
{
|
||||
CFStringRef lang = CFDictionaryGetValue( components, kCFLocaleLanguageCode );
|
||||
CFStringRef country = CFDictionaryGetValue( components, kCFLocaleCountryCode );
|
||||
CFLocaleRef locale = NULL;
|
||||
CFStringRef locale_string;
|
||||
|
||||
if (!country)
|
||||
{
|
||||
locale = CFLocaleCopyCurrent();
|
||||
country = CFLocaleGetValue( locale, kCFLocaleCountryCode );
|
||||
}
|
||||
|
||||
if (country)
|
||||
locale_string = CFStringCreateWithFormat( NULL, NULL, CFSTR("%@_%@"), lang, country );
|
||||
else
|
||||
locale_string = CFStringCreateCopy( NULL, lang );
|
||||
CFStringGetCString( locale_string, messages_locale, sizeof(messages_locale), kCFStringEncodingUTF8 );
|
||||
strcat( messages_locale, ".UTF-8" );
|
||||
|
||||
CFRelease( locale_string );
|
||||
if (locale) CFRelease( locale );
|
||||
CFRelease( components );
|
||||
}
|
||||
}
|
||||
if (preferred_langs)
|
||||
CFRelease( preferred_langs );
|
||||
}
|
||||
|
||||
if (messages_locale[0])
|
||||
override = messages_locale;
|
||||
}
|
||||
|
||||
TRACE( "%s is %s; overriding with %s\n", category_name, debugstr_a(ret), debugstr_a(override) );
|
||||
ret = override;
|
||||
}
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* setup_unix_locales
|
||||
*/
|
||||
@ -918,10 +1051,10 @@ static UINT setup_unix_locales(void)
|
||||
{
|
||||
struct locale_name locale_name;
|
||||
WCHAR buffer[128], ctype_buff[128];
|
||||
char *locale;
|
||||
const char *locale;
|
||||
UINT unix_cp = 0;
|
||||
|
||||
if ((locale = setlocale( LC_CTYPE, NULL )))
|
||||
if ((locale = get_locale( LC_CTYPE, "LC_CTYPE" )))
|
||||
{
|
||||
strcpynAtoW( ctype_buff, locale, sizeof(ctype_buff)/sizeof(WCHAR) );
|
||||
parse_locale_name( ctype_buff, &locale_name );
|
||||
@ -935,7 +1068,7 @@ static UINT setup_unix_locales(void)
|
||||
locale_name.lcid, locale_name.matches, debugstr_a(locale) );
|
||||
|
||||
#define GET_UNIX_LOCALE(cat) do \
|
||||
if ((locale = setlocale( cat, NULL ))) \
|
||||
if ((locale = get_locale( cat, #cat ))) \
|
||||
{ \
|
||||
strcpynAtoW( buffer, locale, sizeof(buffer)/sizeof(WCHAR) ); \
|
||||
if (!strcmpW( buffer, ctype_buff )) lcid_##cat = lcid_LC_CTYPE; \
|
||||
@ -3612,71 +3745,32 @@ void LOCALE_Init(void)
|
||||
|
||||
UINT ansi_cp = 1252, oem_cp = 437, mac_cp = 10000, unix_cp;
|
||||
|
||||
setlocale( LC_ALL, "" );
|
||||
|
||||
#ifdef __APPLE__
|
||||
/* MacOS doesn't set the locale environment variables so we have to do it ourselves */
|
||||
char user_locale[50];
|
||||
|
||||
CFLocaleRef user_locale_ref = CFLocaleCopyCurrent();
|
||||
CFStringRef user_locale_lang_ref = CFLocaleGetValue( user_locale_ref, kCFLocaleLanguageCode );
|
||||
CFStringRef user_locale_country_ref = CFLocaleGetValue( user_locale_ref, kCFLocaleCountryCode );
|
||||
CFStringRef user_locale_string_ref;
|
||||
|
||||
if (user_locale_country_ref)
|
||||
if (!has_env("LANG"))
|
||||
{
|
||||
user_locale_string_ref = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@_%@"),
|
||||
user_locale_lang_ref, user_locale_country_ref);
|
||||
}
|
||||
else
|
||||
{
|
||||
user_locale_string_ref = CFStringCreateCopy(NULL, user_locale_lang_ref);
|
||||
}
|
||||
const char* mac_locale = get_mac_locale();
|
||||
|
||||
CFStringGetCString( user_locale_string_ref, user_locale, sizeof(user_locale), kCFStringEncodingUTF8 );
|
||||
strcat(user_locale, ".UTF-8");
|
||||
|
||||
setenv( "LANG", user_locale, 0 );
|
||||
TRACE( "setting locale to '%s'\n", user_locale );
|
||||
setenv( "LANG", mac_locale, 1 );
|
||||
if (setlocale( LC_ALL, "" ))
|
||||
TRACE( "setting LANG to '%s'\n", mac_locale );
|
||||
else
|
||||
{
|
||||
/* no C library locale matching Mac locale; don't pass garbage to children */
|
||||
unsetenv("LANG");
|
||||
TRACE( "Mac locale %s is not supported by the C library\n", debugstr_a(mac_locale) );
|
||||
}
|
||||
}
|
||||
#endif /* __APPLE__ */
|
||||
|
||||
setlocale( LC_ALL, "" );
|
||||
|
||||
unix_cp = setup_unix_locales();
|
||||
if (!lcid_LC_MESSAGES) lcid_LC_MESSAGES = lcid_LC_CTYPE;
|
||||
|
||||
#ifdef __APPLE__
|
||||
if (!unix_cp)
|
||||
unix_cp = CP_UTF8; /* default to utf-8 even if we don't get a valid locale */
|
||||
|
||||
/* Override lcid_LC_MESSAGES with user's preferred language if LC_MESSAGES is set to default */
|
||||
if (!getenv("LC_ALL") && !getenv("LC_MESSAGES"))
|
||||
{
|
||||
/* Retrieve the preferred language as chosen in System Preferences. */
|
||||
/* If language is a less specific variant of locale (e.g. 'en' vs. 'en_US'),
|
||||
leave things be. */
|
||||
CFArrayRef preferred_langs = CFLocaleCopyPreferredLanguages();
|
||||
CFStringRef canonical_lang_string_ref = CFLocaleCreateCanonicalLanguageIdentifierFromString(NULL, user_locale_string_ref);
|
||||
CFStringRef user_language_string_ref;
|
||||
if (preferred_langs && canonical_lang_string_ref && CFArrayGetCount( preferred_langs ) &&
|
||||
(user_language_string_ref = CFArrayGetValueAtIndex( preferred_langs, 0 )) &&
|
||||
!CFEqual(user_language_string_ref, user_locale_lang_ref) &&
|
||||
!CFEqual(user_language_string_ref, canonical_lang_string_ref))
|
||||
{
|
||||
struct locale_name locale_name;
|
||||
WCHAR buffer[128];
|
||||
CFStringGetCString( user_language_string_ref, user_locale, sizeof(user_locale), kCFStringEncodingUTF8 );
|
||||
strcpynAtoW( buffer, user_locale, sizeof(buffer)/sizeof(WCHAR) );
|
||||
parse_locale_name( buffer, &locale_name );
|
||||
lcid_LC_MESSAGES = locale_name.lcid;
|
||||
TRACE( "setting lcid_LC_MESSAGES to '%s' %04x\n", user_locale, lcid_LC_MESSAGES );
|
||||
}
|
||||
if (preferred_langs)
|
||||
CFRelease( preferred_langs );
|
||||
if (canonical_lang_string_ref)
|
||||
CFRelease( canonical_lang_string_ref );
|
||||
}
|
||||
|
||||
CFRelease( user_locale_ref );
|
||||
CFRelease( user_locale_string_ref );
|
||||
#endif
|
||||
|
||||
NtSetDefaultUILanguage( LANGIDFROMLCID(lcid_LC_MESSAGES) );
|
||||
|
Loading…
x
Reference in New Issue
Block a user