#include <CoreFoundation/CFLocale.h>
#include <CoreFoundation/CFString.h>
#include <CoreFoundation/CFArray.h>
#include <CoreFoundation/CFDictionary.h>
#include <CoreFoundation/CFPreferences.h>
#include <CoreFoundation/CFCalendar.h>
#include <CoreFoundation/CFNumber.h>
#include "CFInternal.h"
#include <unicode/uloc.h> // ICU locales
#include <unicode/ulocdata.h> // ICU locale data
#include <unicode/ucurr.h> // ICU currency functions
#include <unicode/uset.h> // ICU Unicode sets
#include <unicode/putil.h> // ICU low-level utilities
#include <unicode/umsg.h> // ICU message formatting
#if DEPLOYMENT_TARGET_MACOSX
#include <CoreFoundation/CFNumberFormatter.h>
#include <stdlib.h>
#include <stdio.h>
#include <unicode/ucol.h>
#endif
CONST_STRING_DECL(kCFLocaleCurrentLocaleDidChangeNotification, "kCFLocaleCurrentLocaleDidChangeNotification")
static const char *kCalendarKeyword = "calendar";
static const char *kCollationKeyword = "collation";
#define kMaxICUNameSize 1024
typedef struct __CFLocale *CFMutableLocaleRef;
__private_extern__ CONST_STRING_DECL(__kCFLocaleCollatorID, "locale:collator id")
enum {
__kCFLocaleKeyTableCount = 16
};
struct key_table {
CFStringRef key;
bool (*get)(CFLocaleRef, bool user, CFTypeRef *, CFStringRef context); bool (*set)(CFMutableLocaleRef, CFTypeRef, CFStringRef context);
bool (*name)(const char *, const char *, CFStringRef *);
CFStringRef context;
};
static bool __CFLocaleCopyLocaleID(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context);
static bool __CFLocaleSetNOP(CFMutableLocaleRef locale, CFTypeRef cf, CFStringRef context);
static bool __CFLocaleFullName(const char *locale, const char *value, CFStringRef *out);
static bool __CFLocaleCopyCodes(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context);
static bool __CFLocaleCountryName(const char *locale, const char *value, CFStringRef *out);
static bool __CFLocaleScriptName(const char *locale, const char *value, CFStringRef *out);
static bool __CFLocaleLanguageName(const char *locale, const char *value, CFStringRef *out);
static bool __CFLocaleCurrencyShortName(const char *locale, const char *value, CFStringRef *out);
static bool __CFLocaleCopyExemplarCharSet(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context);
static bool __CFLocaleVariantName(const char *locale, const char *value, CFStringRef *out);
static bool __CFLocaleNoName(const char *locale, const char *value, CFStringRef *out);
static bool __CFLocaleCopyCalendarID(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context);
static bool __CFLocaleCalendarName(const char *locale, const char *value, CFStringRef *out);
static bool __CFLocaleCollationName(const char *locale, const char *value, CFStringRef *out);
static bool __CFLocaleCopyUsesMetric(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context);
static bool __CFLocaleCopyCalendar(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context);
static bool __CFLocaleCopyCollationID(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context);
static bool __CFLocaleCopyMeasurementSystem(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context);
static bool __CFLocaleCopyNumberFormat(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context);
static bool __CFLocaleCopyNumberFormat2(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context);
static bool __CFLocaleCurrencyFullName(const char *locale, const char *value, CFStringRef *out);
static bool __CFLocaleCopyCollatorID(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context);
static struct key_table __CFLocaleKeyTable[__kCFLocaleKeyTableCount] = {
{(CFStringRef)&kCFLocaleIdentifier, __CFLocaleCopyLocaleID, __CFLocaleSetNOP, __CFLocaleFullName, NULL},
{(CFStringRef)&kCFLocaleLanguageCode, __CFLocaleCopyCodes, __CFLocaleSetNOP, __CFLocaleLanguageName, (CFStringRef)&kCFLocaleLanguageCode},
{(CFStringRef)&kCFLocaleCountryCode, __CFLocaleCopyCodes, __CFLocaleSetNOP, __CFLocaleCountryName, (CFStringRef)&kCFLocaleCountryCode},
{(CFStringRef)&kCFLocaleScriptCode, __CFLocaleCopyCodes, __CFLocaleSetNOP, __CFLocaleScriptName, (CFStringRef)&kCFLocaleScriptCode},
{(CFStringRef)&kCFLocaleVariantCode, __CFLocaleCopyCodes, __CFLocaleSetNOP, __CFLocaleVariantName, (CFStringRef)&kCFLocaleVariantCode},
{(CFStringRef)&kCFLocaleExemplarCharacterSet, __CFLocaleCopyExemplarCharSet, __CFLocaleSetNOP, __CFLocaleNoName, NULL},
{(CFStringRef)&kCFLocaleCalendarIdentifier, __CFLocaleCopyCalendarID, __CFLocaleSetNOP, __CFLocaleCalendarName, NULL},
{(CFStringRef)&kCFLocaleCalendar, __CFLocaleCopyCalendar, __CFLocaleSetNOP, __CFLocaleNoName, NULL},
{(CFStringRef)&kCFLocaleCollationIdentifier, __CFLocaleCopyCollationID, __CFLocaleSetNOP, __CFLocaleCollationName, NULL},
{(CFStringRef)&kCFLocaleUsesMetricSystem, __CFLocaleCopyUsesMetric, __CFLocaleSetNOP, __CFLocaleNoName, NULL},
{(CFStringRef)&kCFLocaleMeasurementSystem, __CFLocaleCopyMeasurementSystem, __CFLocaleSetNOP, __CFLocaleNoName, NULL},
{(CFStringRef)&kCFLocaleDecimalSeparator, __CFLocaleCopyNumberFormat, __CFLocaleSetNOP, __CFLocaleNoName, (CFStringRef)&kCFNumberFormatterDecimalSeparator},
{(CFStringRef)&kCFLocaleGroupingSeparator, __CFLocaleCopyNumberFormat, __CFLocaleSetNOP, __CFLocaleNoName, (CFStringRef)&kCFNumberFormatterGroupingSeparator},
{(CFStringRef)&kCFLocaleCurrencySymbol, __CFLocaleCopyNumberFormat2, __CFLocaleSetNOP, __CFLocaleCurrencyShortName, (CFStringRef)&kCFNumberFormatterCurrencySymbol},
{(CFStringRef)&kCFLocaleCurrencyCode, __CFLocaleCopyNumberFormat2, __CFLocaleSetNOP, __CFLocaleCurrencyFullName, (CFStringRef)&kCFNumberFormatterCurrencyCode},
{(CFStringRef)&__kCFLocaleCollatorID, __CFLocaleCopyCollatorID, __CFLocaleSetNOP, __CFLocaleNoName, NULL},
};
static CFLocaleRef __CFLocaleSystem = NULL;
static CFMutableDictionaryRef __CFLocaleCache = NULL;
static CFSpinLock_t __CFLocaleGlobalLock = CFSpinLockInit;
struct __CFLocale {
CFRuntimeBase _base;
CFStringRef _identifier; CFMutableDictionaryRef _cache;
CFMutableDictionaryRef _overrides;
CFDictionaryRef _prefs;
CFSpinLock_t _lock;
};
enum {
__kCFLocaleOrdinary = 0,
__kCFLocaleSystem = 1,
__kCFLocaleUser = 2,
__kCFLocaleCustom = 3
};
CF_INLINE CFIndex __CFLocaleGetType(CFLocaleRef locale) {
return __CFBitfieldGetValue(((const CFRuntimeBase *)locale)->_cfinfo[CF_INFO_BITS], 1, 0);
}
CF_INLINE void __CFLocaleSetType(CFLocaleRef locale, CFIndex type) {
__CFBitfieldSetValue(((CFRuntimeBase *)locale)->_cfinfo[CF_INFO_BITS], 1, 0, (uint8_t)type);
}
CF_INLINE void __CFLocaleLockGlobal(void) {
__CFSpinLock(&__CFLocaleGlobalLock);
}
CF_INLINE void __CFLocaleUnlockGlobal(void) {
__CFSpinUnlock(&__CFLocaleGlobalLock);
}
CF_INLINE void __CFLocaleLock(CFLocaleRef locale) {
__CFSpinLock(&locale->_lock);
}
CF_INLINE void __CFLocaleUnlock(CFLocaleRef locale) {
__CFSpinUnlock(&locale->_lock);
}
static Boolean __CFLocaleEqual(CFTypeRef cf1, CFTypeRef cf2) {
CFLocaleRef locale1 = (CFLocaleRef)cf1;
CFLocaleRef locale2 = (CFLocaleRef)cf2;
if (__CFLocaleGetType(locale1) != __CFLocaleGetType(locale2)) return false;
if (!CFEqual(locale1->_identifier, locale2->_identifier)) return false;
if (NULL == locale1->_overrides && NULL != locale2->_overrides) return false;
if (NULL != locale1->_overrides && NULL == locale2->_overrides) return false;
if (NULL != locale1->_overrides && !CFEqual(locale1->_overrides, locale2->_overrides)) return false;
if (__kCFLocaleUser == __CFLocaleGetType(locale1)) {
return CFEqual(locale1->_prefs, locale2->_prefs);
}
return true;
}
static CFHashCode __CFLocaleHash(CFTypeRef cf) {
CFLocaleRef locale = (CFLocaleRef)cf;
return CFHash(locale->_identifier);
}
static CFStringRef __CFLocaleCopyDescription(CFTypeRef cf) {
CFLocaleRef locale = (CFLocaleRef)cf;
const char *type = NULL;
switch (__CFLocaleGetType(locale)) {
case __kCFLocaleOrdinary: type = "ordinary"; break;
case __kCFLocaleSystem: type = "system"; break;
case __kCFLocaleUser: type = "user"; break;
case __kCFLocaleCustom: type = "custom"; break;
}
return CFStringCreateWithFormat(CFGetAllocator(locale), NULL, CFSTR("<CFLocale %p [%p]>{type = %s, identifier = '%@'}"), cf, CFGetAllocator(locale), type, locale->_identifier);
}
static void __CFLocaleDeallocate(CFTypeRef cf) {
CFLocaleRef locale = (CFLocaleRef)cf;
CFRelease(locale->_identifier);
if (NULL != locale->_cache) CFRelease(locale->_cache);
if (NULL != locale->_overrides) CFRelease(locale->_overrides);
if (NULL != locale->_prefs) CFRelease(locale->_prefs);
}
static CFTypeID __kCFLocaleTypeID = _kCFRuntimeNotATypeID;
static const CFRuntimeClass __CFLocaleClass = {
0,
"CFLocale",
NULL, NULL, __CFLocaleDeallocate,
__CFLocaleEqual,
__CFLocaleHash,
NULL, __CFLocaleCopyDescription
};
static void __CFLocaleInitialize(void) {
CFIndex idx;
__kCFLocaleTypeID = _CFRuntimeRegisterClass(&__CFLocaleClass);
for (idx = 0; idx < __kCFLocaleKeyTableCount; idx++) {
__CFLocaleKeyTable[idx].key = *((CFStringRef *)__CFLocaleKeyTable[idx].key);
if (NULL != __CFLocaleKeyTable[idx].context) {
__CFLocaleKeyTable[idx].context = *((CFStringRef *)__CFLocaleKeyTable[idx].context);
}
}
}
CFTypeID CFLocaleGetTypeID(void) {
if (_kCFRuntimeNotATypeID == __kCFLocaleTypeID) __CFLocaleInitialize();
return __kCFLocaleTypeID;
}
CFLocaleRef CFLocaleGetSystem(void) {
CFLocaleRef locale;
__CFLocaleLockGlobal();
if (NULL == __CFLocaleSystem) {
__CFLocaleUnlockGlobal();
locale = CFLocaleCreate(kCFAllocatorSystemDefault, CFSTR(""));
if (!locale) return NULL;
__CFLocaleSetType(locale, __kCFLocaleSystem);
__CFLocaleLockGlobal();
if (NULL == __CFLocaleSystem) {
__CFLocaleSystem = locale;
} else {
if (locale) CFRelease(locale);
}
}
locale = __CFLocaleSystem ? (CFLocaleRef)CFRetain(__CFLocaleSystem) : NULL;
__CFLocaleUnlockGlobal();
return locale;
}
static CFLocaleRef __CFLocaleCurrent = NULL;
#if DEPLOYMENT_TARGET_MACOSX
#define FALLBACK_LOCALE_NAME CFSTR("")
#endif
CFLocaleRef CFLocaleCopyCurrent(void) {
__CFLocaleLockGlobal();
if (__CFLocaleCurrent) {
CFRetain(__CFLocaleCurrent);
__CFLocaleUnlockGlobal();
return __CFLocaleCurrent;
}
__CFLocaleUnlockGlobal();
CFDictionaryRef prefs = NULL;
CFStringRef identifier = NULL;
struct __CFLocale *locale;
uint32_t size = sizeof(struct __CFLocale) - sizeof(CFRuntimeBase);
locale = (struct __CFLocale *)_CFRuntimeCreateInstance(kCFAllocatorSystemDefault, CFLocaleGetTypeID(), size, NULL);
if (NULL == locale) {
return NULL;
}
__CFLocaleSetType(locale, __kCFLocaleUser);
if (NULL == identifier) identifier = CFRetain(FALLBACK_LOCALE_NAME);
locale->_identifier = identifier;
locale->_cache = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
locale->_overrides = NULL;
locale->_prefs = prefs;
locale->_lock = CFSpinLockInit;
__CFLocaleLockGlobal();
if (NULL == __CFLocaleCurrent) {
__CFLocaleCurrent = locale;
} else {
CFRelease(locale);
}
locale = (struct __CFLocale *)CFRetain(__CFLocaleCurrent);
__CFLocaleUnlockGlobal();
return locale;
}
__private_extern__ CFDictionaryRef __CFLocaleGetPrefs(CFLocaleRef locale) {
return locale->_prefs;
}
CFLocaleRef CFLocaleCreate(CFAllocatorRef allocator, CFStringRef identifier) {
if (allocator == NULL) allocator = __CFGetDefaultAllocator();
__CFGenericValidateType(allocator, CFAllocatorGetTypeID());
__CFGenericValidateType(identifier, CFStringGetTypeID());
CFStringRef localeIdentifier = NULL;
if (identifier) {
localeIdentifier = CFLocaleCreateCanonicalLocaleIdentifierFromString(allocator, identifier);
}
if (NULL == localeIdentifier) return NULL;
CFStringRef old = localeIdentifier;
localeIdentifier = (CFStringRef)CFStringCreateCopy(allocator, localeIdentifier);
CFRelease(old);
__CFLocaleLockGlobal();
if (!allocator) allocator = __CFGetDefaultAllocator();
Boolean canCache = (kCFAllocatorSystemDefault == allocator);
if (canCache && __CFLocaleCache) {
CFLocaleRef locale = (CFLocaleRef)CFDictionaryGetValue(__CFLocaleCache, localeIdentifier);
if (locale) {
CFRetain(locale);
__CFLocaleUnlockGlobal();
CFRelease(localeIdentifier);
return locale;
}
}
struct __CFLocale *locale = NULL;
uint32_t size = sizeof(struct __CFLocale) - sizeof(CFRuntimeBase);
locale = (struct __CFLocale *)_CFRuntimeCreateInstance(allocator, CFLocaleGetTypeID(), size, NULL);
if (NULL == locale) {
return NULL;
}
__CFLocaleSetType(locale, __kCFLocaleOrdinary);
locale->_identifier = localeIdentifier;
locale->_cache = CFDictionaryCreateMutable(allocator, 0, NULL, &kCFTypeDictionaryValueCallBacks);
locale->_overrides = NULL;
locale->_prefs = NULL;
locale->_lock = CFSpinLockInit;
if (canCache) {
if (NULL == __CFLocaleCache) {
__CFLocaleCache = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
}
CFDictionarySetValue(__CFLocaleCache, localeIdentifier, locale);
}
__CFLocaleUnlockGlobal();
return (CFLocaleRef)locale;
}
CFLocaleRef CFLocaleCreateCopy(CFAllocatorRef allocator, CFLocaleRef locale) {
return (CFLocaleRef)CFRetain(locale);
}
CFStringRef CFLocaleGetIdentifier(CFLocaleRef locale) {
CF_OBJC_FUNCDISPATCH0(CFLocaleGetTypeID(), CFStringRef, locale, "localeIdentifier");
return locale->_identifier;
}
CFTypeRef CFLocaleGetValue(CFLocaleRef locale, CFStringRef key) {
CF_OBJC_FUNCDISPATCH1(CFLocaleGetTypeID(), CFTypeRef, locale, "objectForKey:", key);
CFIndex idx, slot = -1;
for (idx = 0; idx < __kCFLocaleKeyTableCount; idx++) {
if (__CFLocaleKeyTable[idx].key == key) {
slot = idx;
break;
}
}
if (-1 == slot && NULL != key) {
for (idx = 0; idx < __kCFLocaleKeyTableCount; idx++) {
if (CFEqual(__CFLocaleKeyTable[idx].key, key)) {
slot = idx;
break;
}
}
}
if (-1 == slot) {
return NULL;
}
CFTypeRef value;
if (NULL != locale->_overrides && CFDictionaryGetValueIfPresent(locale->_overrides, __CFLocaleKeyTable[slot].key, &value)) {
return value;
}
__CFLocaleLock(locale);
if (CFDictionaryGetValueIfPresent(locale->_cache, __CFLocaleKeyTable[slot].key, &value)) {
__CFLocaleUnlock(locale);
return value;
}
if (__kCFLocaleUser == __CFLocaleGetType(locale) && __CFLocaleKeyTable[slot].get(locale, true, &value, __CFLocaleKeyTable[slot].context)) {
if (value) CFDictionarySetValue(locale->_cache, __CFLocaleKeyTable[idx].key, value);
if (value) CFRelease(value);
__CFLocaleUnlock(locale);
return value;
}
if (__CFLocaleKeyTable[slot].get(locale, false, &value, __CFLocaleKeyTable[slot].context)) {
if (value) CFDictionarySetValue(locale->_cache, __CFLocaleKeyTable[idx].key, value);
if (value) CFRelease(value);
__CFLocaleUnlock(locale);
return value;
}
__CFLocaleUnlock(locale);
return NULL;
}
CFStringRef CFLocaleCopyDisplayNameForPropertyValue(CFLocaleRef displayLocale, CFStringRef key, CFStringRef value) {
CF_OBJC_FUNCDISPATCH2(CFLocaleGetTypeID(), CFStringRef, displayLocale, "_copyDisplayNameForKey:value:", key, value);
CFIndex idx, slot = -1;
for (idx = 0; idx < __kCFLocaleKeyTableCount; idx++) {
if (__CFLocaleKeyTable[idx].key == key) {
slot = idx;
break;
}
}
if (-1 == slot && NULL != key) {
for (idx = 0; idx < __kCFLocaleKeyTableCount; idx++) {
if (CFEqual(__CFLocaleKeyTable[idx].key, key)) {
slot = idx;
break;
}
}
}
if (-1 == slot || !value) {
return NULL;
}
char localeID[ULOC_FULLNAME_CAPACITY+ULOC_KEYWORD_AND_VALUES_CAPACITY];
char cValue[ULOC_FULLNAME_CAPACITY+ULOC_KEYWORD_AND_VALUES_CAPACITY];
if (CFStringGetCString(displayLocale->_identifier, localeID, sizeof(localeID)/sizeof(localeID[0]), kCFStringEncodingASCII) && CFStringGetCString(value, cValue, sizeof(cValue)/sizeof(char), kCFStringEncodingASCII)) {
CFStringRef result;
if ((NULL == displayLocale->_prefs) && __CFLocaleKeyTable[slot].name(localeID, cValue, &result)) {
return result;
}
CFArrayRef langPref;
if (displayLocale->_prefs) {
langPref = (CFArrayRef)CFDictionaryGetValue(displayLocale->_prefs, CFSTR("AppleLanguages"));
if (langPref) CFRetain(langPref);
} else {
langPref = (CFArrayRef)CFPreferencesCopyAppValue(CFSTR("AppleLanguages"), kCFPreferencesCurrentApplication);
}
if (langPref != NULL) {
CFIndex count = CFArrayGetCount(langPref);
CFIndex i;
bool success = false;
for (i = 0; i < count && !success; ++i) {
CFStringRef language = (CFStringRef)CFArrayGetValueAtIndex(langPref, i);
CFStringRef cleanLanguage = CFLocaleCreateCanonicalLanguageIdentifierFromString(kCFAllocatorSystemDefault, language);
if (CFStringGetCString(cleanLanguage, localeID, sizeof(localeID)/sizeof(localeID[0]), kCFStringEncodingASCII)) {
success = __CFLocaleKeyTable[slot].name(localeID, cValue, &result);
}
CFRelease(cleanLanguage);
}
CFRelease(langPref);
if (success)
return result;
}
}
return NULL;
}
CFArrayRef CFLocaleCopyAvailableLocaleIdentifiers(void) {
int32_t locale, localeCount = uloc_countAvailable();
CFMutableSetRef working = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
for (locale = 0; locale < localeCount; ++locale) {
const char *localeID = uloc_getAvailable(locale);
CFStringRef string1 = CFStringCreateWithCString(kCFAllocatorSystemDefault, localeID, kCFStringEncodingASCII);
CFStringRef string2 = CFLocaleCreateCanonicalLocaleIdentifierFromString(kCFAllocatorSystemDefault, string1);
CFSetAddValue(working, string1);
CFRelease(string1);
CFRelease(string2);
}
CFIndex cnt = CFSetGetCount(working);
STACK_BUFFER_DECL(const void *, buffer, cnt);
CFSetGetValues(working, buffer);
CFArrayRef result = CFArrayCreate(kCFAllocatorSystemDefault, buffer, cnt, &kCFTypeArrayCallBacks);
CFRelease(working);
return result;
}
static CFArrayRef __CFLocaleCopyCStringsAsArray(const char* const* p) {
CFMutableArrayRef working = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks);
for (; *p; ++p) {
CFStringRef string = CFStringCreateWithCString(kCFAllocatorSystemDefault, *p, kCFStringEncodingASCII);
CFArrayAppendValue(working, string);
CFRelease(string);
}
CFArrayRef result = CFArrayCreateCopy(kCFAllocatorSystemDefault, working);
CFRelease(working);
return result;
}
static CFArrayRef __CFLocaleCopyUEnumerationAsArray(UEnumeration *enumer, UErrorCode *icuErr) {
const UChar *next = NULL;
int32_t len = 0;
CFMutableArrayRef working = NULL;
if (U_SUCCESS(*icuErr)) {
working = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks);
}
while ((next = uenum_unext(enumer, &len, icuErr)) && U_SUCCESS(*icuErr)) {
CFStringRef string = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, (const UniChar *)next, (CFIndex) len);
CFArrayAppendValue(working, string);
CFRelease(string);
}
if (*icuErr == U_INDEX_OUTOFBOUNDS_ERROR) {
*icuErr = U_ZERO_ERROR; }
CFArrayRef result = NULL;
if (U_SUCCESS(*icuErr)) {
result = CFArrayCreateCopy(kCFAllocatorSystemDefault, working);
}
if (working != NULL) {
CFRelease(working);
}
return result;
}
CFArrayRef CFLocaleCopyISOLanguageCodes(void) {
const char* const* p = uloc_getISOLanguages();
return __CFLocaleCopyCStringsAsArray(p);
}
CFArrayRef CFLocaleCopyISOCountryCodes(void) {
const char* const* p = uloc_getISOCountries();
return __CFLocaleCopyCStringsAsArray(p);
}
CFArrayRef CFLocaleCopyISOCurrencyCodes(void) {
UErrorCode icuStatus = U_ZERO_ERROR;
UEnumeration *enumer = ucurr_openISOCurrencies(UCURR_ALL, &icuStatus);
CFArrayRef result = __CFLocaleCopyUEnumerationAsArray(enumer, &icuStatus);
uenum_close(enumer);
return result;
}
CFArrayRef CFLocaleCopyCommonISOCurrencyCodes(void) {
UErrorCode icuStatus = U_ZERO_ERROR;
UEnumeration *enumer = ucurr_openISOCurrencies(UCURR_COMMON|UCURR_NON_DEPRECATED, &icuStatus);
CFArrayRef result = __CFLocaleCopyUEnumerationAsArray(enumer, &icuStatus);
uenum_close(enumer);
return result;
}
CFArrayRef CFLocaleCopyPreferredLanguages(void) {
CFMutableArrayRef newArray = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks);
CFArrayRef languagesArray = (CFArrayRef)CFPreferencesCopyAppValue(CFSTR("AppleLanguages"), kCFPreferencesCurrentApplication);
if (languagesArray && (CFArrayGetTypeID() == CFGetTypeID(languagesArray))) {
for (CFIndex idx = 0, cnt = CFArrayGetCount(languagesArray); idx < cnt; idx++) {
CFStringRef str = (CFStringRef)CFArrayGetValueAtIndex(languagesArray, idx);
if (str && (CFStringGetTypeID() == CFGetTypeID(str))) {
CFStringRef ident = CFLocaleCreateCanonicalLanguageIdentifierFromString(kCFAllocatorSystemDefault, str);
CFArrayAppendValue(newArray, ident);
CFRelease(ident);
}
}
}
if (languagesArray) CFRelease(languagesArray);
return newArray;
}
static bool __CFLocaleSetNOP(CFMutableLocaleRef locale, CFTypeRef cf, CFStringRef context) {
return false;
}
static bool __CFLocaleCopyLocaleID(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context) {
*cf = CFRetain(locale->_identifier);
return true;
}
static bool __CFLocaleCopyCodes(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context) {
CFDictionaryRef codes = NULL;
if (!CFDictionaryGetValueIfPresent(locale->_cache, CFSTR("__kCFLocaleCodes"), (const void **)&codes)) {
codes = CFLocaleCreateComponentsFromLocaleIdentifier(kCFAllocatorSystemDefault, locale->_identifier);
if (codes) CFDictionarySetValue(locale->_cache, CFSTR("__kCFLocaleCodes"), codes);
if (codes) CFRelease(codes);
}
if (codes) {
CFStringRef value = (CFStringRef)CFDictionaryGetValue(codes, context); if (value) CFRetain(value);
*cf = value;
return true;
}
return false;
}
CFCharacterSetRef _CFCreateCharacterSetFromUSet(USet *set) {
UErrorCode icuErr = U_ZERO_ERROR;
CFMutableCharacterSetRef working = CFCharacterSetCreateMutable(NULL);
UChar buffer[2048]; int32_t stringLen;
if (working == NULL)
return NULL;
int32_t itemCount = uset_getItemCount(set);
int32_t i;
for (i = 0; i < itemCount; ++i)
{
UChar32 start, end;
UChar * string;
string = buffer;
stringLen = uset_getItem(set, i, &start, &end, buffer, sizeof(buffer)/sizeof(UChar), &icuErr);
if (icuErr == U_BUFFER_OVERFLOW_ERROR)
{
string = (UChar *) malloc(sizeof(UChar)*(stringLen+1));
if (!string)
{
CFRelease(working);
return NULL;
}
icuErr = U_ZERO_ERROR;
(void) uset_getItem(set, i, &start, &end, string, stringLen+1, &icuErr);
}
if (U_FAILURE(icuErr))
{
if (string != buffer)
free(string);
CFRelease(working);
return NULL;
}
if (stringLen <= 0)
CFCharacterSetAddCharactersInRange(working, CFRangeMake(start, end-start+1));
else
{
CFStringRef cfString = CFStringCreateWithCharactersNoCopy(kCFAllocatorSystemDefault, (UniChar *)string, stringLen, kCFAllocatorNull);
CFCharacterSetAddCharactersInString(working, cfString);
CFRelease(cfString);
}
if (string != buffer)
free(string);
}
CFCharacterSetRef result = CFCharacterSetCreateCopy(kCFAllocatorSystemDefault, working);
CFRelease(working);
return result;
}
static bool __CFLocaleCopyExemplarCharSet(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context) {
char localeID[ULOC_FULLNAME_CAPACITY+ULOC_KEYWORD_AND_VALUES_CAPACITY];
if (CFStringGetCString(locale->_identifier, localeID, sizeof(localeID)/sizeof(char), kCFStringEncodingASCII)) {
UErrorCode icuStatus = U_ZERO_ERROR;
ULocaleData* uld = ulocdata_open(localeID, &icuStatus);
USet *set = ulocdata_getExemplarSet(uld, NULL, USET_ADD_CASE_MAPPINGS, ULOCDATA_ES_STANDARD, &icuStatus);
ulocdata_close(uld);
if (U_FAILURE(icuStatus))
return false;
if (icuStatus == U_USING_DEFAULT_WARNING) uset_clear(set);
*cf = (CFTypeRef) _CFCreateCharacterSetFromUSet(set);
uset_close(set);
return (*cf != NULL);
}
return false;
}
static bool __CFLocaleCopyICUKeyword(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context, const char *keyword)
{
char localeID[ULOC_FULLNAME_CAPACITY+ULOC_KEYWORD_AND_VALUES_CAPACITY];
if (CFStringGetCString(locale->_identifier, localeID, sizeof(localeID)/sizeof(char), kCFStringEncodingASCII))
{
char value[ULOC_KEYWORD_AND_VALUES_CAPACITY];
UErrorCode icuStatus = U_ZERO_ERROR;
if (uloc_getKeywordValue(localeID, keyword, value, sizeof(value)/sizeof(char), &icuStatus) > 0 && U_SUCCESS(icuStatus))
{
*cf = (CFTypeRef) CFStringCreateWithCString(kCFAllocatorSystemDefault, value, kCFStringEncodingASCII);
return true;
}
}
*cf = NULL;
return false;
}
static bool __CFLocaleCopyCalendarID(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context) {
bool succeeded = __CFLocaleCopyICUKeyword(locale, user, cf, context, kCalendarKeyword);
if (succeeded) {
if (CFEqual(*cf, kCFGregorianCalendar)) {
CFRelease(*cf);
*cf = CFRetain(kCFGregorianCalendar);
} else if (CFEqual(*cf, kCFBuddhistCalendar)) {
CFRelease(*cf);
*cf = CFRetain(kCFBuddhistCalendar);
} else if (CFEqual(*cf, kCFJapaneseCalendar)) {
CFRelease(*cf);
*cf = CFRetain(kCFJapaneseCalendar);
} else if (CFEqual(*cf, kCFIslamicCalendar)) {
CFRelease(*cf);
*cf = CFRetain(kCFIslamicCalendar);
} else if (CFEqual(*cf, kCFIslamicCivilCalendar)) {
CFRelease(*cf);
*cf = CFRetain(kCFIslamicCivilCalendar);
} else if (CFEqual(*cf, kCFHebrewCalendar)) {
CFRelease(*cf);
*cf = CFRetain(kCFHebrewCalendar);
} else if (CFEqual(*cf, kCFChineseCalendar)) {
CFRelease(*cf);
*cf = CFRetain(kCFChineseCalendar);
}
} else {
*cf = CFRetain(kCFGregorianCalendar);
}
return true;
}
static bool __CFLocaleCopyCalendar(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context) {
if (__CFLocaleCopyCalendarID(locale, user, cf, context)) {
CFCalendarRef calendar = CFCalendarCreateWithIdentifier(kCFAllocatorSystemDefault, (CFStringRef)*cf);
CFCalendarSetLocale(calendar, locale);
CFRelease(*cf);
*cf = calendar;
return true;
}
return false;
}
static bool __CFLocaleCopyCollationID(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context) {
return __CFLocaleCopyICUKeyword(locale, user, cf, context, kCollationKeyword);
}
static bool __CFLocaleCopyCollatorID(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context) {
CFStringRef canonLocaleCFStr = NULL;
if (!canonLocaleCFStr) {
canonLocaleCFStr = CFLocaleGetIdentifier(locale);
CFRetain(canonLocaleCFStr);
}
*cf = canonLocaleCFStr;
return canonLocaleCFStr ? true : false;
}
static bool __CFLocaleCopyUsesMetric(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context) {
bool us = false; bool done = false;
if (!done) {
char localeID[ULOC_FULLNAME_CAPACITY+ULOC_KEYWORD_AND_VALUES_CAPACITY];
if (CFStringGetCString(locale->_identifier, localeID, sizeof(localeID)/sizeof(char), kCFStringEncodingASCII)) {
UErrorCode icuStatus = U_ZERO_ERROR;
UMeasurementSystem ms = UMS_SI;
ms = ulocdata_getMeasurementSystem(localeID, &icuStatus);
if (U_SUCCESS(icuStatus)) {
us = (ms == UMS_US);
done = true;
}
}
}
if (!done)
us = false;
*cf = us ? CFRetain(kCFBooleanFalse) : CFRetain(kCFBooleanTrue);
return true;
}
static bool __CFLocaleCopyMeasurementSystem(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context) {
if (__CFLocaleCopyUsesMetric(locale, user, cf, context)) {
bool us = (*cf == kCFBooleanFalse);
CFRelease(*cf);
*cf = us ? CFRetain(CFSTR("U.S.")) : CFRetain(CFSTR("Metric"));
return true;
}
return false;
}
static bool __CFLocaleCopyNumberFormat(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context) {
CFStringRef str = NULL;
#if DEPLOYMENT_TARGET_MACOSX
CFNumberFormatterRef nf = CFNumberFormatterCreate(kCFAllocatorSystemDefault, locale, kCFNumberFormatterDecimalStyle);
str = nf ? CFNumberFormatterCopyProperty(nf, context) : NULL;
if (nf) CFRelease(nf);
#endif
if (str) {
*cf = str;
return true;
}
return false;
}
static bool __CFLocaleCopyNumberFormat2(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context) {
CFStringRef str = NULL;
#if DEPLOYMENT_TARGET_MACOSX
CFNumberFormatterRef nf = CFNumberFormatterCreate(kCFAllocatorSystemDefault, locale, kCFNumberFormatterCurrencyStyle);
str = nf ? CFNumberFormatterCopyProperty(nf, context) : NULL;
if (nf) CFRelease(nf);
#endif
if (str) {
*cf = str;
return true;
}
return false;
}
typedef int32_t (*__CFICUFunction)(const char *, const char *, UChar *, int32_t, UErrorCode *);
static bool __CFLocaleICUName(const char *locale, const char *valLocale, CFStringRef *out, __CFICUFunction icu) {
UErrorCode icuStatus = U_ZERO_ERROR;
int32_t size;
UChar name[kMaxICUNameSize];
size = (*icu)(valLocale, locale, name, kMaxICUNameSize, &icuStatus);
if (U_SUCCESS(icuStatus) && size > 0 && icuStatus != U_USING_DEFAULT_WARNING) {
*out = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, (UniChar *)name, size);
return (*out != NULL);
}
return false;
}
static bool __CFLocaleICUKeywordValueName(const char *locale, const char *value, const char *keyword, CFStringRef *out) {
UErrorCode icuStatus = U_ZERO_ERROR;
int32_t size = 0;
UChar name[kMaxICUNameSize];
char lid[ULOC_FULLNAME_CAPACITY+ULOC_KEYWORD_AND_VALUES_CAPACITY];
if (strlen(value) < ULOC_KEYWORD_AND_VALUES_CAPACITY) {
snprintf(lid, sizeof(lid), "en_US@%s=%s", keyword, value);
size = uloc_getDisplayKeywordValue(lid, keyword, locale, name, kMaxICUNameSize, &icuStatus);
if (U_SUCCESS(icuStatus) && size > 0 && icuStatus != U_USING_DEFAULT_WARNING) {
*out = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, (UniChar *)name, size);
return (*out != NULL);
}
}
return false;
}
static bool __CFLocaleICUCurrencyName(const char *locale, const char *value, UCurrNameStyle style, CFStringRef *out) {
int valLen = strlen(value);
if (valLen != 3) return false;
UChar curr[4];
UBool isChoice = FALSE;
int32_t size = 0;
UErrorCode icuStatus = U_ZERO_ERROR;
u_charsToUChars(value, curr, valLen);
curr[valLen] = '\0';
const UChar *name;
name = ucurr_getName(curr, locale, style, &isChoice, &size, &icuStatus);
if (U_FAILURE(icuStatus) || icuStatus == U_USING_DEFAULT_WARNING)
return false;
UChar result[kMaxICUNameSize];
if (isChoice)
{
UChar pattern[kMaxICUNameSize];
CFStringRef patternRef = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("{0,choice,%S}"), name);
CFIndex pattlen = CFStringGetLength(patternRef);
CFStringGetCharacters(patternRef, CFRangeMake(0, pattlen), (UniChar *)pattern);
CFRelease(patternRef);
pattern[pattlen] = '\0'; size = u_formatMessage("en_US", pattern, pattlen, result, kMaxICUNameSize, &icuStatus, 10.0);
if (U_FAILURE(icuStatus))
return false;
name = result;
}
*out = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, (UniChar *)name, size);
return (*out != NULL);
}
static bool __CFLocaleFullName(const char *locale, const char *value, CFStringRef *out) {
UErrorCode icuStatus = U_ZERO_ERROR;
int32_t size;
UChar name[kMaxICUNameSize];
size = uloc_getDisplayName(value, locale, name, kMaxICUNameSize, &icuStatus);
if (U_FAILURE(icuStatus) || size <= 0)
return false;
if (icuStatus == U_USING_DEFAULT_WARNING) {
UErrorCode localStatus = U_ZERO_ERROR;
int32_t localSize;
UChar localName[kMaxICUNameSize];
localSize = uloc_getDisplayLanguage(value, locale, localName, kMaxICUNameSize, &localStatus);
if (U_FAILURE(localStatus) || size <= 0 || localStatus == U_USING_DEFAULT_WARNING)
return false;
}
*out = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, (UniChar *)name, size);
return (*out != NULL);
}
static bool __CFLocaleLanguageName(const char *locale, const char *value, CFStringRef *out) {
int len = strlen(value);
if (len >= 2 && len <= 3)
return __CFLocaleICUName(locale, value, out, uloc_getDisplayLanguage);
return false;
}
static bool __CFLocaleCountryName(const char *locale, const char *value, CFStringRef *out) {
char lid[ULOC_FULLNAME_CAPACITY];
if (strlen(value) == 2) {
snprintf(lid, sizeof(lid), "en_%s", value);
return __CFLocaleICUName(locale, lid, out, uloc_getDisplayCountry);
}
return false;
}
static bool __CFLocaleScriptName(const char *locale, const char *value, CFStringRef *out) {
char lid[ULOC_FULLNAME_CAPACITY];
if (strlen(value) == 4) {
snprintf(lid, sizeof(lid), "en_%s_US", value);
return __CFLocaleICUName(locale, lid, out, uloc_getDisplayScript);
}
return false;
}
static bool __CFLocaleVariantName(const char *locale, const char *value, CFStringRef *out) {
char lid[ULOC_FULLNAME_CAPACITY+ULOC_KEYWORD_AND_VALUES_CAPACITY];
if (strlen(value) < ULOC_FULLNAME_CAPACITY) {
snprintf(lid, sizeof(lid), "en_US_%s", value);
return __CFLocaleICUName(locale, lid, out, uloc_getDisplayVariant);
}
return false;
}
static bool __CFLocaleCalendarName(const char *locale, const char *value, CFStringRef *out) {
return __CFLocaleICUKeywordValueName(locale, value, kCalendarKeyword, out);
}
static bool __CFLocaleCollationName(const char *locale, const char *value, CFStringRef *out) {
return __CFLocaleICUKeywordValueName(locale, value, kCollationKeyword, out);
}
static bool __CFLocaleCurrencyShortName(const char *locale, const char *value, CFStringRef *out) {
return __CFLocaleICUCurrencyName(locale, value, UCURR_SYMBOL_NAME, out);
}
static bool __CFLocaleCurrencyFullName(const char *locale, const char *value, CFStringRef *out) {
return __CFLocaleICUCurrencyName(locale, value, UCURR_LONG_NAME, out);
}
static bool __CFLocaleNoName(const char *locale, const char *value, CFStringRef *out) {
return false;
}
CONST_STRING_DECL(kCFLocaleIdentifier, "locale:id")
CONST_STRING_DECL(kCFLocaleLanguageCode, "locale:language code")
CONST_STRING_DECL(kCFLocaleCountryCode, "locale:country code")
CONST_STRING_DECL(kCFLocaleScriptCode, "locale:script code")
CONST_STRING_DECL(kCFLocaleVariantCode, "locale:variant code")
CONST_STRING_DECL(kCFLocaleExemplarCharacterSet, "locale:exemplar characters")
CONST_STRING_DECL(kCFLocaleCalendarIdentifier, "calendar")
CONST_STRING_DECL(kCFLocaleCalendar, "locale:calendarref")
CONST_STRING_DECL(kCFLocaleCollationIdentifier, "collation")
CONST_STRING_DECL(kCFLocaleUsesMetricSystem, "locale:uses metric")
CONST_STRING_DECL(kCFLocaleMeasurementSystem, "locale:measurement system")
CONST_STRING_DECL(kCFLocaleDecimalSeparator, "locale:decimal separator")
CONST_STRING_DECL(kCFLocaleGroupingSeparator, "locale:grouping separator")
CONST_STRING_DECL(kCFLocaleCurrencySymbol, "locale:currency symbol")
CONST_STRING_DECL(kCFLocaleCurrencyCode, "currency")
CONST_STRING_DECL(kCFGregorianCalendar, "gregorian")
CONST_STRING_DECL(kCFBuddhistCalendar, "buddhist")
CONST_STRING_DECL(kCFJapaneseCalendar, "japanese")
CONST_STRING_DECL(kCFIslamicCalendar, "islamic")
CONST_STRING_DECL(kCFIslamicCivilCalendar, "islamic-civil")
CONST_STRING_DECL(kCFHebrewCalendar, "hebrew")
CONST_STRING_DECL(kCFChineseCalendar, "chinese")
#undef kMaxICUNameSize