]>
Commit | Line | Data |
---|---|---|
bd5b749c | 1 | /* |
8ca704e1 | 2 | * Copyright (c) 2011 Apple Inc. All rights reserved. |
bd5b749c A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * This file contains Original Code and/or Modifications of Original Code | |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. Please obtain a copy of the License at | |
10 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
11 | * file. | |
12 | * | |
13 | * The Original Code and all software distributed under the License are | |
14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
18 | * Please see the License for the specific language governing rights and | |
19 | * limitations under the License. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | */ | |
f64f9b69 | 23 | |
bd5b749c | 24 | /* CFLocale.c |
8ca704e1 A |
25 | Copyright (c) 2002-2011, Apple Inc. All rights reserved. |
26 | Responsibility: David Smith | |
bd5b749c A |
27 | */ |
28 | ||
29 | // Note the header file is in the OpenSource set (stripped to almost nothing), but not the .c file | |
30 | ||
31 | #include <CoreFoundation/CFLocale.h> | |
32 | #include <CoreFoundation/CFString.h> | |
33 | #include <CoreFoundation/CFArray.h> | |
34 | #include <CoreFoundation/CFDictionary.h> | |
35 | #include <CoreFoundation/CFPreferences.h> | |
36 | #include <CoreFoundation/CFCalendar.h> | |
37 | #include <CoreFoundation/CFNumber.h> | |
38 | #include "CFInternal.h" | |
cf7d2af9 | 39 | #include "CFLocaleInternal.h" |
bd5b749c A |
40 | #include <unicode/uloc.h> // ICU locales |
41 | #include <unicode/ulocdata.h> // ICU locale data | |
8ca704e1 | 42 | #include <unicode/ucal.h> |
bd5b749c A |
43 | #include <unicode/ucurr.h> // ICU currency functions |
44 | #include <unicode/uset.h> // ICU Unicode sets | |
45 | #include <unicode/putil.h> // ICU low-level utilities | |
46 | #include <unicode/umsg.h> // ICU message formatting | |
8ca704e1 | 47 | #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_LINUX |
bd5b749c A |
48 | #include <CoreFoundation/CFNumberFormatter.h> |
49 | #include <stdlib.h> | |
50 | #include <stdio.h> | |
51 | #include <unicode/ucol.h> | |
cf7d2af9 A |
52 | #elif DEPLOYMENT_TARGET_WINDOWS |
53 | #include <stdio.h> | |
bd5b749c | 54 | #endif |
8ca704e1 | 55 | #include <string.h> |
bd5b749c A |
56 | |
57 | CONST_STRING_DECL(kCFLocaleCurrentLocaleDidChangeNotification, "kCFLocaleCurrentLocaleDidChangeNotification") | |
58 | ||
59 | static const char *kCalendarKeyword = "calendar"; | |
60 | static const char *kCollationKeyword = "collation"; | |
61 | #define kMaxICUNameSize 1024 | |
62 | ||
63 | typedef struct __CFLocale *CFMutableLocaleRef; | |
64 | ||
cf7d2af9 A |
65 | PE_CONST_STRING_DECL(__kCFLocaleCollatorID, "locale:collator id") |
66 | ||
bd5b749c A |
67 | |
68 | enum { | |
cf7d2af9 | 69 | __kCFLocaleKeyTableCount = 21 |
bd5b749c A |
70 | }; |
71 | ||
72 | struct key_table { | |
73 | CFStringRef key; | |
74 | bool (*get)(CFLocaleRef, bool user, CFTypeRef *, CFStringRef context); // returns an immutable copy & reference | |
75 | bool (*set)(CFMutableLocaleRef, CFTypeRef, CFStringRef context); | |
76 | bool (*name)(const char *, const char *, CFStringRef *); | |
77 | CFStringRef context; | |
78 | }; | |
79 | ||
80 | ||
81 | // Must forward decl. these functions: | |
82 | static bool __CFLocaleCopyLocaleID(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context); | |
83 | static bool __CFLocaleSetNOP(CFMutableLocaleRef locale, CFTypeRef cf, CFStringRef context); | |
84 | static bool __CFLocaleFullName(const char *locale, const char *value, CFStringRef *out); | |
85 | static bool __CFLocaleCopyCodes(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context); | |
86 | static bool __CFLocaleCountryName(const char *locale, const char *value, CFStringRef *out); | |
87 | static bool __CFLocaleScriptName(const char *locale, const char *value, CFStringRef *out); | |
88 | static bool __CFLocaleLanguageName(const char *locale, const char *value, CFStringRef *out); | |
89 | static bool __CFLocaleCurrencyShortName(const char *locale, const char *value, CFStringRef *out); | |
90 | static bool __CFLocaleCopyExemplarCharSet(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context); | |
91 | static bool __CFLocaleVariantName(const char *locale, const char *value, CFStringRef *out); | |
92 | static bool __CFLocaleNoName(const char *locale, const char *value, CFStringRef *out); | |
93 | static bool __CFLocaleCopyCalendarID(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context); | |
94 | static bool __CFLocaleCalendarName(const char *locale, const char *value, CFStringRef *out); | |
95 | static bool __CFLocaleCollationName(const char *locale, const char *value, CFStringRef *out); | |
96 | static bool __CFLocaleCopyUsesMetric(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context); | |
97 | static bool __CFLocaleCopyCalendar(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context); | |
98 | static bool __CFLocaleCopyCollationID(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context); | |
99 | static bool __CFLocaleCopyMeasurementSystem(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context); | |
100 | static bool __CFLocaleCopyNumberFormat(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context); | |
101 | static bool __CFLocaleCopyNumberFormat2(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context); | |
102 | static bool __CFLocaleCurrencyFullName(const char *locale, const char *value, CFStringRef *out); | |
103 | static bool __CFLocaleCopyCollatorID(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context); | |
cf7d2af9 | 104 | static bool __CFLocaleCopyDelimiter(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context); |
bd5b749c A |
105 | |
106 | // Note string members start with an extra &, and are fixed up at init time | |
107 | static struct key_table __CFLocaleKeyTable[__kCFLocaleKeyTableCount] = { | |
cf7d2af9 A |
108 | {(CFStringRef)&kCFLocaleIdentifierKey, __CFLocaleCopyLocaleID, __CFLocaleSetNOP, __CFLocaleFullName, NULL}, |
109 | {(CFStringRef)&kCFLocaleLanguageCodeKey, __CFLocaleCopyCodes, __CFLocaleSetNOP, __CFLocaleLanguageName, (CFStringRef)&kCFLocaleLanguageCodeKey}, | |
110 | {(CFStringRef)&kCFLocaleCountryCodeKey, __CFLocaleCopyCodes, __CFLocaleSetNOP, __CFLocaleCountryName, (CFStringRef)&kCFLocaleCountryCodeKey}, | |
111 | {(CFStringRef)&kCFLocaleScriptCodeKey, __CFLocaleCopyCodes, __CFLocaleSetNOP, __CFLocaleScriptName, (CFStringRef)&kCFLocaleScriptCodeKey}, | |
112 | {(CFStringRef)&kCFLocaleVariantCodeKey, __CFLocaleCopyCodes, __CFLocaleSetNOP, __CFLocaleVariantName, (CFStringRef)&kCFLocaleVariantCodeKey}, | |
113 | {(CFStringRef)&kCFLocaleExemplarCharacterSetKey, __CFLocaleCopyExemplarCharSet, __CFLocaleSetNOP, __CFLocaleNoName, NULL}, | |
114 | {(CFStringRef)&kCFLocaleCalendarIdentifierKey, __CFLocaleCopyCalendarID, __CFLocaleSetNOP, __CFLocaleCalendarName, NULL}, | |
115 | {(CFStringRef)&kCFLocaleCalendarKey, __CFLocaleCopyCalendar, __CFLocaleSetNOP, __CFLocaleNoName, NULL}, | |
116 | {(CFStringRef)&kCFLocaleCollationIdentifierKey, __CFLocaleCopyCollationID, __CFLocaleSetNOP, __CFLocaleCollationName, NULL}, | |
117 | {(CFStringRef)&kCFLocaleUsesMetricSystemKey, __CFLocaleCopyUsesMetric, __CFLocaleSetNOP, __CFLocaleNoName, NULL}, | |
118 | {(CFStringRef)&kCFLocaleMeasurementSystemKey, __CFLocaleCopyMeasurementSystem, __CFLocaleSetNOP, __CFLocaleNoName, NULL}, | |
119 | {(CFStringRef)&kCFLocaleDecimalSeparatorKey, __CFLocaleCopyNumberFormat, __CFLocaleSetNOP, __CFLocaleNoName, (CFStringRef)&kCFNumberFormatterDecimalSeparatorKey}, | |
120 | {(CFStringRef)&kCFLocaleGroupingSeparatorKey, __CFLocaleCopyNumberFormat, __CFLocaleSetNOP, __CFLocaleNoName, (CFStringRef)&kCFNumberFormatterGroupingSeparatorKey}, | |
121 | {(CFStringRef)&kCFLocaleCurrencySymbolKey, __CFLocaleCopyNumberFormat2, __CFLocaleSetNOP, __CFLocaleCurrencyShortName, (CFStringRef)&kCFNumberFormatterCurrencySymbolKey}, | |
122 | {(CFStringRef)&kCFLocaleCurrencyCodeKey, __CFLocaleCopyNumberFormat2, __CFLocaleSetNOP, __CFLocaleCurrencyFullName, (CFStringRef)&kCFNumberFormatterCurrencyCodeKey}, | |
123 | {(CFStringRef)&kCFLocaleCollatorIdentifierKey, __CFLocaleCopyCollatorID, __CFLocaleSetNOP, __CFLocaleNoName, NULL}, | |
bd5b749c | 124 | {(CFStringRef)&__kCFLocaleCollatorID, __CFLocaleCopyCollatorID, __CFLocaleSetNOP, __CFLocaleNoName, NULL}, |
cf7d2af9 A |
125 | {(CFStringRef)&kCFLocaleQuotationBeginDelimiterKey, __CFLocaleCopyDelimiter, __CFLocaleSetNOP, __CFLocaleNoName, (CFStringRef)&kCFLocaleQuotationBeginDelimiterKey}, |
126 | {(CFStringRef)&kCFLocaleQuotationEndDelimiterKey, __CFLocaleCopyDelimiter, __CFLocaleSetNOP, __CFLocaleNoName, (CFStringRef)&kCFLocaleQuotationEndDelimiterKey}, | |
127 | {(CFStringRef)&kCFLocaleAlternateQuotationBeginDelimiterKey, __CFLocaleCopyDelimiter, __CFLocaleSetNOP, __CFLocaleNoName, (CFStringRef)&kCFLocaleAlternateQuotationBeginDelimiterKey}, | |
128 | {(CFStringRef)&kCFLocaleAlternateQuotationEndDelimiterKey, __CFLocaleCopyDelimiter, __CFLocaleSetNOP, __CFLocaleNoName, (CFStringRef)&kCFLocaleAlternateQuotationEndDelimiterKey}, | |
bd5b749c A |
129 | }; |
130 | ||
131 | ||
132 | static CFLocaleRef __CFLocaleSystem = NULL; | |
133 | static CFMutableDictionaryRef __CFLocaleCache = NULL; | |
134 | static CFSpinLock_t __CFLocaleGlobalLock = CFSpinLockInit; | |
135 | ||
136 | struct __CFLocale { | |
137 | CFRuntimeBase _base; | |
138 | CFStringRef _identifier; // canonical identifier, never NULL | |
139 | CFMutableDictionaryRef _cache; | |
140 | CFMutableDictionaryRef _overrides; | |
141 | CFDictionaryRef _prefs; | |
142 | CFSpinLock_t _lock; | |
cf7d2af9 | 143 | Boolean _nullLocale; |
bd5b749c | 144 | }; |
cf7d2af9 A |
145 | |
146 | __private_extern__ Boolean __CFLocaleGetNullLocale(struct __CFLocale *locale) { | |
147 | return locale->_nullLocale; | |
148 | } | |
149 | ||
150 | __private_extern__ void __CFLocaleSetNullLocale(struct __CFLocale *locale) { | |
151 | locale->_nullLocale = true; | |
152 | } | |
bd5b749c A |
153 | |
154 | /* Flag bits */ | |
155 | enum { /* Bits 0-1 */ | |
156 | __kCFLocaleOrdinary = 0, | |
157 | __kCFLocaleSystem = 1, | |
158 | __kCFLocaleUser = 2, | |
159 | __kCFLocaleCustom = 3 | |
160 | }; | |
161 | ||
162 | CF_INLINE CFIndex __CFLocaleGetType(CFLocaleRef locale) { | |
163 | return __CFBitfieldGetValue(((const CFRuntimeBase *)locale)->_cfinfo[CF_INFO_BITS], 1, 0); | |
164 | } | |
165 | ||
166 | CF_INLINE void __CFLocaleSetType(CFLocaleRef locale, CFIndex type) { | |
167 | __CFBitfieldSetValue(((CFRuntimeBase *)locale)->_cfinfo[CF_INFO_BITS], 1, 0, (uint8_t)type); | |
168 | } | |
169 | ||
170 | CF_INLINE void __CFLocaleLockGlobal(void) { | |
171 | __CFSpinLock(&__CFLocaleGlobalLock); | |
172 | } | |
173 | ||
174 | CF_INLINE void __CFLocaleUnlockGlobal(void) { | |
175 | __CFSpinUnlock(&__CFLocaleGlobalLock); | |
176 | } | |
177 | ||
178 | CF_INLINE void __CFLocaleLock(CFLocaleRef locale) { | |
cf7d2af9 | 179 | __CFSpinLock(&((struct __CFLocale *)locale)->_lock); |
bd5b749c A |
180 | } |
181 | ||
182 | CF_INLINE void __CFLocaleUnlock(CFLocaleRef locale) { | |
cf7d2af9 | 183 | __CFSpinUnlock(&((struct __CFLocale *)locale)->_lock); |
bd5b749c A |
184 | } |
185 | ||
186 | ||
187 | static Boolean __CFLocaleEqual(CFTypeRef cf1, CFTypeRef cf2) { | |
188 | CFLocaleRef locale1 = (CFLocaleRef)cf1; | |
189 | CFLocaleRef locale2 = (CFLocaleRef)cf2; | |
190 | // a user locale and a locale created with an ident are not the same even if their contents are | |
191 | if (__CFLocaleGetType(locale1) != __CFLocaleGetType(locale2)) return false; | |
192 | if (!CFEqual(locale1->_identifier, locale2->_identifier)) return false; | |
193 | if (NULL == locale1->_overrides && NULL != locale2->_overrides) return false; | |
194 | if (NULL != locale1->_overrides && NULL == locale2->_overrides) return false; | |
195 | if (NULL != locale1->_overrides && !CFEqual(locale1->_overrides, locale2->_overrides)) return false; | |
196 | if (__kCFLocaleUser == __CFLocaleGetType(locale1)) { | |
8ca704e1 | 197 | return CFEqual(locale1->_prefs, locale2->_prefs); |
bd5b749c A |
198 | } |
199 | return true; | |
200 | } | |
201 | ||
202 | static CFHashCode __CFLocaleHash(CFTypeRef cf) { | |
203 | CFLocaleRef locale = (CFLocaleRef)cf; | |
204 | return CFHash(locale->_identifier); | |
205 | } | |
206 | ||
207 | static CFStringRef __CFLocaleCopyDescription(CFTypeRef cf) { | |
208 | CFLocaleRef locale = (CFLocaleRef)cf; | |
209 | const char *type = NULL; | |
210 | switch (__CFLocaleGetType(locale)) { | |
211 | case __kCFLocaleOrdinary: type = "ordinary"; break; | |
212 | case __kCFLocaleSystem: type = "system"; break; | |
213 | case __kCFLocaleUser: type = "user"; break; | |
214 | case __kCFLocaleCustom: type = "custom"; break; | |
215 | } | |
216 | return CFStringCreateWithFormat(CFGetAllocator(locale), NULL, CFSTR("<CFLocale %p [%p]>{type = %s, identifier = '%@'}"), cf, CFGetAllocator(locale), type, locale->_identifier); | |
217 | } | |
218 | ||
219 | static void __CFLocaleDeallocate(CFTypeRef cf) { | |
220 | CFLocaleRef locale = (CFLocaleRef)cf; | |
221 | CFRelease(locale->_identifier); | |
222 | if (NULL != locale->_cache) CFRelease(locale->_cache); | |
223 | if (NULL != locale->_overrides) CFRelease(locale->_overrides); | |
224 | if (NULL != locale->_prefs) CFRelease(locale->_prefs); | |
225 | } | |
226 | ||
227 | static CFTypeID __kCFLocaleTypeID = _kCFRuntimeNotATypeID; | |
228 | ||
229 | static const CFRuntimeClass __CFLocaleClass = { | |
230 | 0, | |
231 | "CFLocale", | |
232 | NULL, // init | |
233 | NULL, // copy | |
234 | __CFLocaleDeallocate, | |
235 | __CFLocaleEqual, | |
236 | __CFLocaleHash, | |
237 | NULL, // | |
238 | __CFLocaleCopyDescription | |
239 | }; | |
240 | ||
241 | static void __CFLocaleInitialize(void) { | |
242 | CFIndex idx; | |
243 | __kCFLocaleTypeID = _CFRuntimeRegisterClass(&__CFLocaleClass); | |
244 | for (idx = 0; idx < __kCFLocaleKeyTableCount; idx++) { | |
245 | // table fixup to workaround compiler/language limitations | |
246 | __CFLocaleKeyTable[idx].key = *((CFStringRef *)__CFLocaleKeyTable[idx].key); | |
247 | if (NULL != __CFLocaleKeyTable[idx].context) { | |
248 | __CFLocaleKeyTable[idx].context = *((CFStringRef *)__CFLocaleKeyTable[idx].context); | |
249 | } | |
250 | } | |
251 | } | |
252 | ||
253 | CFTypeID CFLocaleGetTypeID(void) { | |
254 | if (_kCFRuntimeNotATypeID == __kCFLocaleTypeID) __CFLocaleInitialize(); | |
255 | return __kCFLocaleTypeID; | |
256 | } | |
257 | ||
258 | CFLocaleRef CFLocaleGetSystem(void) { | |
259 | CFLocaleRef locale; | |
260 | __CFLocaleLockGlobal(); | |
261 | if (NULL == __CFLocaleSystem) { | |
262 | __CFLocaleUnlockGlobal(); | |
263 | locale = CFLocaleCreate(kCFAllocatorSystemDefault, CFSTR("")); | |
264 | if (!locale) return NULL; | |
265 | __CFLocaleSetType(locale, __kCFLocaleSystem); | |
266 | __CFLocaleLockGlobal(); | |
267 | if (NULL == __CFLocaleSystem) { | |
268 | __CFLocaleSystem = locale; | |
269 | } else { | |
270 | if (locale) CFRelease(locale); | |
271 | } | |
272 | } | |
273 | locale = __CFLocaleSystem ? (CFLocaleRef)CFRetain(__CFLocaleSystem) : NULL; | |
274 | __CFLocaleUnlockGlobal(); | |
275 | return locale; | |
276 | } | |
277 | ||
cf7d2af9 A |
278 | extern CFDictionaryRef __CFXPreferencesCopyCurrentApplicationState(void); |
279 | ||
bd5b749c A |
280 | static CFLocaleRef __CFLocaleCurrent = NULL; |
281 | ||
cf7d2af9 | 282 | |
bd5b749c A |
283 | #if DEPLOYMENT_TARGET_MACOSX |
284 | #define FALLBACK_LOCALE_NAME CFSTR("") | |
cf7d2af9 A |
285 | #elif DEPLOYMENT_TARGET_EMBEDDED |
286 | #define FALLBACK_LOCALE_NAME CFSTR("en_US") | |
8ca704e1 | 287 | #elif DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX |
cf7d2af9 A |
288 | #define FALLBACK_LOCALE_NAME CFSTR("en_US") |
289 | #else | |
290 | #error Unknown or unspecified DEPLOYMENT_TARGET | |
bd5b749c A |
291 | #endif |
292 | ||
293 | CFLocaleRef CFLocaleCopyCurrent(void) { | |
294 | ||
295 | __CFLocaleLockGlobal(); | |
296 | if (__CFLocaleCurrent) { | |
297 | CFRetain(__CFLocaleCurrent); | |
298 | __CFLocaleUnlockGlobal(); | |
299 | return __CFLocaleCurrent; | |
300 | } | |
301 | __CFLocaleUnlockGlobal(); | |
302 | ||
303 | CFDictionaryRef prefs = NULL; | |
304 | CFStringRef identifier = NULL; | |
cf7d2af9 | 305 | |
bd5b749c A |
306 | struct __CFLocale *locale; |
307 | uint32_t size = sizeof(struct __CFLocale) - sizeof(CFRuntimeBase); | |
308 | locale = (struct __CFLocale *)_CFRuntimeCreateInstance(kCFAllocatorSystemDefault, CFLocaleGetTypeID(), size, NULL); | |
309 | if (NULL == locale) { | |
8ca704e1 A |
310 | if (prefs) CFRelease(prefs); |
311 | if (identifier) CFRelease(identifier); | |
bd5b749c A |
312 | return NULL; |
313 | } | |
314 | __CFLocaleSetType(locale, __kCFLocaleUser); | |
cf7d2af9 | 315 | if (NULL == identifier) identifier = (CFStringRef)CFRetain(FALLBACK_LOCALE_NAME); |
bd5b749c A |
316 | locale->_identifier = identifier; |
317 | locale->_cache = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks); | |
318 | locale->_overrides = NULL; | |
319 | locale->_prefs = prefs; | |
320 | locale->_lock = CFSpinLockInit; | |
cf7d2af9 | 321 | locale->_nullLocale = false; |
bd5b749c A |
322 | |
323 | __CFLocaleLockGlobal(); | |
324 | if (NULL == __CFLocaleCurrent) { | |
325 | __CFLocaleCurrent = locale; | |
326 | } else { | |
327 | CFRelease(locale); | |
328 | } | |
329 | locale = (struct __CFLocale *)CFRetain(__CFLocaleCurrent); | |
330 | __CFLocaleUnlockGlobal(); | |
331 | return locale; | |
332 | } | |
333 | ||
334 | __private_extern__ CFDictionaryRef __CFLocaleGetPrefs(CFLocaleRef locale) { | |
8ca704e1 | 335 | CF_OBJC_FUNCDISPATCH0(CFLocaleGetTypeID(), CFDictionaryRef, locale, "_prefs"); |
bd5b749c A |
336 | return locale->_prefs; |
337 | } | |
338 | ||
339 | CFLocaleRef CFLocaleCreate(CFAllocatorRef allocator, CFStringRef identifier) { | |
340 | if (allocator == NULL) allocator = __CFGetDefaultAllocator(); | |
341 | __CFGenericValidateType(allocator, CFAllocatorGetTypeID()); | |
342 | __CFGenericValidateType(identifier, CFStringGetTypeID()); | |
343 | CFStringRef localeIdentifier = NULL; | |
344 | if (identifier) { | |
345 | localeIdentifier = CFLocaleCreateCanonicalLocaleIdentifierFromString(allocator, identifier); | |
346 | } | |
347 | if (NULL == localeIdentifier) return NULL; | |
348 | CFStringRef old = localeIdentifier; | |
349 | localeIdentifier = (CFStringRef)CFStringCreateCopy(allocator, localeIdentifier); | |
350 | CFRelease(old); | |
351 | __CFLocaleLockGlobal(); | |
352 | // Look for cases where we can return a cached instance. | |
353 | // We only use cached objects if the allocator is the system | |
354 | // default allocator. | |
355 | if (!allocator) allocator = __CFGetDefaultAllocator(); | |
8ca704e1 | 356 | Boolean canCache = _CFAllocatorIsSystemDefault(allocator); |
bd5b749c A |
357 | if (canCache && __CFLocaleCache) { |
358 | CFLocaleRef locale = (CFLocaleRef)CFDictionaryGetValue(__CFLocaleCache, localeIdentifier); | |
359 | if (locale) { | |
360 | CFRetain(locale); | |
361 | __CFLocaleUnlockGlobal(); | |
362 | CFRelease(localeIdentifier); | |
363 | return locale; | |
364 | } | |
365 | } | |
366 | struct __CFLocale *locale = NULL; | |
367 | uint32_t size = sizeof(struct __CFLocale) - sizeof(CFRuntimeBase); | |
368 | locale = (struct __CFLocale *)_CFRuntimeCreateInstance(allocator, CFLocaleGetTypeID(), size, NULL); | |
369 | if (NULL == locale) { | |
370 | return NULL; | |
371 | } | |
372 | __CFLocaleSetType(locale, __kCFLocaleOrdinary); | |
373 | locale->_identifier = localeIdentifier; | |
374 | locale->_cache = CFDictionaryCreateMutable(allocator, 0, NULL, &kCFTypeDictionaryValueCallBacks); | |
375 | locale->_overrides = NULL; | |
376 | locale->_prefs = NULL; | |
377 | locale->_lock = CFSpinLockInit; | |
378 | if (canCache) { | |
379 | if (NULL == __CFLocaleCache) { | |
380 | __CFLocaleCache = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); | |
381 | } | |
382 | CFDictionarySetValue(__CFLocaleCache, localeIdentifier, locale); | |
383 | } | |
384 | __CFLocaleUnlockGlobal(); | |
385 | return (CFLocaleRef)locale; | |
386 | } | |
387 | ||
388 | CFLocaleRef CFLocaleCreateCopy(CFAllocatorRef allocator, CFLocaleRef locale) { | |
389 | return (CFLocaleRef)CFRetain(locale); | |
390 | } | |
391 | ||
392 | CFStringRef CFLocaleGetIdentifier(CFLocaleRef locale) { | |
393 | CF_OBJC_FUNCDISPATCH0(CFLocaleGetTypeID(), CFStringRef, locale, "localeIdentifier"); | |
394 | return locale->_identifier; | |
395 | } | |
396 | ||
397 | CFTypeRef CFLocaleGetValue(CFLocaleRef locale, CFStringRef key) { | |
8ca704e1 | 398 | #if DEPLOYMENT_TARGET_MACOSX |
cf7d2af9 A |
399 | if (!_CFExecutableLinkedOnOrAfter(CFSystemVersionSnowLeopard)) { |
400 | // Hack for Opera, which is using the hard-coded string value below instead of | |
401 | // the perfectly good public kCFLocaleCountryCode constant, for whatever reason. | |
402 | if (key && CFEqual(key, CFSTR("locale:country code"))) { | |
403 | key = kCFLocaleCountryCodeKey; | |
404 | } | |
405 | } | |
8ca704e1 | 406 | #endif |
bd5b749c A |
407 | CF_OBJC_FUNCDISPATCH1(CFLocaleGetTypeID(), CFTypeRef, locale, "objectForKey:", key); |
408 | CFIndex idx, slot = -1; | |
409 | for (idx = 0; idx < __kCFLocaleKeyTableCount; idx++) { | |
410 | if (__CFLocaleKeyTable[idx].key == key) { | |
411 | slot = idx; | |
412 | break; | |
413 | } | |
414 | } | |
415 | if (-1 == slot && NULL != key) { | |
416 | for (idx = 0; idx < __kCFLocaleKeyTableCount; idx++) { | |
417 | if (CFEqual(__CFLocaleKeyTable[idx].key, key)) { | |
418 | slot = idx; | |
419 | break; | |
420 | } | |
421 | } | |
422 | } | |
423 | if (-1 == slot) { | |
424 | return NULL; | |
425 | } | |
426 | CFTypeRef value; | |
427 | if (NULL != locale->_overrides && CFDictionaryGetValueIfPresent(locale->_overrides, __CFLocaleKeyTable[slot].key, &value)) { | |
428 | return value; | |
429 | } | |
430 | __CFLocaleLock(locale); | |
431 | if (CFDictionaryGetValueIfPresent(locale->_cache, __CFLocaleKeyTable[slot].key, &value)) { | |
432 | __CFLocaleUnlock(locale); | |
433 | return value; | |
434 | } | |
435 | if (__kCFLocaleUser == __CFLocaleGetType(locale) && __CFLocaleKeyTable[slot].get(locale, true, &value, __CFLocaleKeyTable[slot].context)) { | |
436 | if (value) CFDictionarySetValue(locale->_cache, __CFLocaleKeyTable[idx].key, value); | |
437 | if (value) CFRelease(value); | |
438 | __CFLocaleUnlock(locale); | |
439 | return value; | |
440 | } | |
441 | if (__CFLocaleKeyTable[slot].get(locale, false, &value, __CFLocaleKeyTable[slot].context)) { | |
442 | if (value) CFDictionarySetValue(locale->_cache, __CFLocaleKeyTable[idx].key, value); | |
443 | if (value) CFRelease(value); | |
444 | __CFLocaleUnlock(locale); | |
445 | return value; | |
446 | } | |
447 | __CFLocaleUnlock(locale); | |
448 | return NULL; | |
449 | } | |
450 | ||
451 | CFStringRef CFLocaleCopyDisplayNameForPropertyValue(CFLocaleRef displayLocale, CFStringRef key, CFStringRef value) { | |
452 | CF_OBJC_FUNCDISPATCH2(CFLocaleGetTypeID(), CFStringRef, displayLocale, "_copyDisplayNameForKey:value:", key, value); | |
453 | CFIndex idx, slot = -1; | |
454 | for (idx = 0; idx < __kCFLocaleKeyTableCount; idx++) { | |
455 | if (__CFLocaleKeyTable[idx].key == key) { | |
456 | slot = idx; | |
457 | break; | |
458 | } | |
459 | } | |
460 | if (-1 == slot && NULL != key) { | |
461 | for (idx = 0; idx < __kCFLocaleKeyTableCount; idx++) { | |
462 | if (CFEqual(__CFLocaleKeyTable[idx].key, key)) { | |
463 | slot = idx; | |
464 | break; | |
465 | } | |
466 | } | |
467 | } | |
468 | if (-1 == slot || !value) { | |
469 | return NULL; | |
470 | } | |
471 | // Get the locale ID as a C string | |
472 | char localeID[ULOC_FULLNAME_CAPACITY+ULOC_KEYWORD_AND_VALUES_CAPACITY]; | |
473 | char cValue[ULOC_FULLNAME_CAPACITY+ULOC_KEYWORD_AND_VALUES_CAPACITY]; | |
474 | if (CFStringGetCString(displayLocale->_identifier, localeID, sizeof(localeID)/sizeof(localeID[0]), kCFStringEncodingASCII) && CFStringGetCString(value, cValue, sizeof(cValue)/sizeof(char), kCFStringEncodingASCII)) { | |
475 | CFStringRef result; | |
476 | if ((NULL == displayLocale->_prefs) && __CFLocaleKeyTable[slot].name(localeID, cValue, &result)) { | |
477 | return result; | |
478 | } | |
479 | ||
480 | // We could not find a result using the requested language. Fall back through all preferred languages. | |
481 | CFArrayRef langPref; | |
482 | if (displayLocale->_prefs) { | |
483 | langPref = (CFArrayRef)CFDictionaryGetValue(displayLocale->_prefs, CFSTR("AppleLanguages")); | |
484 | if (langPref) CFRetain(langPref); | |
485 | } else { | |
486 | langPref = (CFArrayRef)CFPreferencesCopyAppValue(CFSTR("AppleLanguages"), kCFPreferencesCurrentApplication); | |
487 | } | |
488 | if (langPref != NULL) { | |
489 | CFIndex count = CFArrayGetCount(langPref); | |
490 | CFIndex i; | |
491 | bool success = false; | |
492 | for (i = 0; i < count && !success; ++i) { | |
493 | CFStringRef language = (CFStringRef)CFArrayGetValueAtIndex(langPref, i); | |
494 | CFStringRef cleanLanguage = CFLocaleCreateCanonicalLanguageIdentifierFromString(kCFAllocatorSystemDefault, language); | |
495 | if (CFStringGetCString(cleanLanguage, localeID, sizeof(localeID)/sizeof(localeID[0]), kCFStringEncodingASCII)) { | |
496 | success = __CFLocaleKeyTable[slot].name(localeID, cValue, &result); | |
497 | } | |
498 | CFRelease(cleanLanguage); | |
499 | } | |
500 | CFRelease(langPref); | |
501 | if (success) | |
502 | return result; | |
503 | } | |
504 | } | |
505 | return NULL; | |
506 | } | |
507 | ||
508 | CFArrayRef CFLocaleCopyAvailableLocaleIdentifiers(void) { | |
509 | int32_t locale, localeCount = uloc_countAvailable(); | |
510 | CFMutableSetRef working = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks); | |
511 | for (locale = 0; locale < localeCount; ++locale) { | |
512 | const char *localeID = uloc_getAvailable(locale); | |
513 | CFStringRef string1 = CFStringCreateWithCString(kCFAllocatorSystemDefault, localeID, kCFStringEncodingASCII); | |
bd5b749c | 514 | // do not include canonicalized version as IntlFormats cannot cope with that in its popup |
8ca704e1 | 515 | CFSetAddValue(working, string1); |
bd5b749c | 516 | CFRelease(string1); |
bd5b749c A |
517 | } |
518 | CFIndex cnt = CFSetGetCount(working); | |
519 | STACK_BUFFER_DECL(const void *, buffer, cnt); | |
520 | CFSetGetValues(working, buffer); | |
521 | CFArrayRef result = CFArrayCreate(kCFAllocatorSystemDefault, buffer, cnt, &kCFTypeArrayCallBacks); | |
522 | CFRelease(working); | |
523 | return result; | |
524 | } | |
525 | ||
526 | static CFArrayRef __CFLocaleCopyCStringsAsArray(const char* const* p) { | |
527 | CFMutableArrayRef working = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); | |
528 | for (; *p; ++p) { | |
529 | CFStringRef string = CFStringCreateWithCString(kCFAllocatorSystemDefault, *p, kCFStringEncodingASCII); | |
530 | CFArrayAppendValue(working, string); | |
531 | CFRelease(string); | |
532 | } | |
533 | CFArrayRef result = CFArrayCreateCopy(kCFAllocatorSystemDefault, working); | |
534 | CFRelease(working); | |
535 | return result; | |
536 | } | |
537 | ||
538 | static CFArrayRef __CFLocaleCopyUEnumerationAsArray(UEnumeration *enumer, UErrorCode *icuErr) { | |
539 | const UChar *next = NULL; | |
540 | int32_t len = 0; | |
541 | CFMutableArrayRef working = NULL; | |
542 | if (U_SUCCESS(*icuErr)) { | |
543 | working = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); | |
544 | } | |
545 | while ((next = uenum_unext(enumer, &len, icuErr)) && U_SUCCESS(*icuErr)) { | |
546 | CFStringRef string = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, (const UniChar *)next, (CFIndex) len); | |
547 | CFArrayAppendValue(working, string); | |
548 | CFRelease(string); | |
549 | } | |
550 | if (*icuErr == U_INDEX_OUTOFBOUNDS_ERROR) { | |
551 | *icuErr = U_ZERO_ERROR; // Temp: Work around bug (ICU 5220) in ucurr enumerator | |
552 | } | |
553 | CFArrayRef result = NULL; | |
554 | if (U_SUCCESS(*icuErr)) { | |
555 | result = CFArrayCreateCopy(kCFAllocatorSystemDefault, working); | |
556 | } | |
557 | if (working != NULL) { | |
558 | CFRelease(working); | |
559 | } | |
560 | return result; | |
561 | } | |
562 | ||
563 | CFArrayRef CFLocaleCopyISOLanguageCodes(void) { | |
564 | const char* const* p = uloc_getISOLanguages(); | |
565 | return __CFLocaleCopyCStringsAsArray(p); | |
566 | } | |
567 | ||
568 | CFArrayRef CFLocaleCopyISOCountryCodes(void) { | |
569 | const char* const* p = uloc_getISOCountries(); | |
570 | return __CFLocaleCopyCStringsAsArray(p); | |
571 | } | |
572 | ||
573 | CFArrayRef CFLocaleCopyISOCurrencyCodes(void) { | |
574 | UErrorCode icuStatus = U_ZERO_ERROR; | |
575 | UEnumeration *enumer = ucurr_openISOCurrencies(UCURR_ALL, &icuStatus); | |
576 | CFArrayRef result = __CFLocaleCopyUEnumerationAsArray(enumer, &icuStatus); | |
577 | uenum_close(enumer); | |
578 | return result; | |
579 | } | |
580 | ||
581 | CFArrayRef CFLocaleCopyCommonISOCurrencyCodes(void) { | |
582 | UErrorCode icuStatus = U_ZERO_ERROR; | |
583 | UEnumeration *enumer = ucurr_openISOCurrencies(UCURR_COMMON|UCURR_NON_DEPRECATED, &icuStatus); | |
584 | CFArrayRef result = __CFLocaleCopyUEnumerationAsArray(enumer, &icuStatus); | |
585 | uenum_close(enumer); | |
586 | return result; | |
587 | } | |
588 | ||
cf7d2af9 A |
589 | CFStringRef CFLocaleCreateLocaleIdentifierFromWindowsLocaleCode(CFAllocatorRef allocator, uint32_t lcid) { |
590 | char buffer[kMaxICUNameSize]; | |
591 | UErrorCode status = U_ZERO_ERROR; | |
592 | int32_t ret = uloc_getLocaleForLCID(lcid, buffer, kMaxICUNameSize, &status); | |
593 | if (U_FAILURE(status) || kMaxICUNameSize <= ret) return NULL; | |
594 | CFStringRef str = CFStringCreateWithCString(kCFAllocatorSystemDefault, buffer, kCFStringEncodingASCII); | |
595 | CFStringRef ident = CFLocaleCreateCanonicalLocaleIdentifierFromString(kCFAllocatorSystemDefault, str); | |
596 | CFRelease(str); | |
597 | return ident; | |
598 | } | |
599 | ||
600 | uint32_t CFLocaleGetWindowsLocaleCodeFromLocaleIdentifier(CFStringRef localeIdentifier) { | |
601 | CFStringRef ident = CFLocaleCreateCanonicalLocaleIdentifierFromString(kCFAllocatorSystemDefault, localeIdentifier); | |
602 | char localeID[ULOC_FULLNAME_CAPACITY+ULOC_KEYWORD_AND_VALUES_CAPACITY]; | |
603 | Boolean b = CFStringGetCString(ident, localeID, sizeof(localeID)/sizeof(char), kCFStringEncodingASCII); | |
604 | CFRelease(ident); | |
605 | return b ? uloc_getLCID(localeID) : 0; | |
606 | } | |
607 | ||
608 | CFLocaleLanguageDirection CFLocaleGetLanguageCharacterDirection(CFStringRef isoLangCode) { | |
609 | char localeID[ULOC_FULLNAME_CAPACITY+ULOC_KEYWORD_AND_VALUES_CAPACITY]; | |
610 | Boolean b = CFStringGetCString(isoLangCode, localeID, sizeof(localeID)/sizeof(char), kCFStringEncodingASCII); | |
611 | CFLocaleLanguageDirection dir; | |
612 | UErrorCode status = U_ZERO_ERROR; | |
613 | ULayoutType idir = b ? uloc_getCharacterOrientation(localeID, &status) : ULOC_LAYOUT_UNKNOWN; | |
614 | switch (idir) { | |
615 | case ULOC_LAYOUT_LTR: dir = kCFLocaleLanguageDirectionLeftToRight; break; | |
616 | case ULOC_LAYOUT_RTL: dir = kCFLocaleLanguageDirectionRightToLeft; break; | |
617 | case ULOC_LAYOUT_TTB: dir = kCFLocaleLanguageDirectionTopToBottom; break; | |
618 | case ULOC_LAYOUT_BTT: dir = kCFLocaleLanguageDirectionBottomToTop; break; | |
619 | default: dir = kCFLocaleLanguageDirectionUnknown; break; | |
620 | } | |
621 | return dir; | |
622 | } | |
623 | ||
624 | CFLocaleLanguageDirection CFLocaleGetLanguageLineDirection(CFStringRef isoLangCode) { | |
625 | char localeID[ULOC_FULLNAME_CAPACITY+ULOC_KEYWORD_AND_VALUES_CAPACITY]; | |
626 | Boolean b = CFStringGetCString(isoLangCode, localeID, sizeof(localeID)/sizeof(char), kCFStringEncodingASCII); | |
627 | CFLocaleLanguageDirection dir; | |
628 | UErrorCode status = U_ZERO_ERROR; | |
629 | ULayoutType idir = b ? uloc_getLineOrientation(localeID, &status) : ULOC_LAYOUT_UNKNOWN; | |
630 | switch (idir) { | |
631 | case ULOC_LAYOUT_LTR: dir = kCFLocaleLanguageDirectionLeftToRight; break; | |
632 | case ULOC_LAYOUT_RTL: dir = kCFLocaleLanguageDirectionRightToLeft; break; | |
633 | case ULOC_LAYOUT_TTB: dir = kCFLocaleLanguageDirectionTopToBottom; break; | |
634 | case ULOC_LAYOUT_BTT: dir = kCFLocaleLanguageDirectionBottomToTop; break; | |
635 | default: dir = kCFLocaleLanguageDirectionUnknown; break; | |
636 | } | |
637 | return dir; | |
638 | } | |
639 | ||
bd5b749c A |
640 | CFArrayRef CFLocaleCopyPreferredLanguages(void) { |
641 | CFMutableArrayRef newArray = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); | |
642 | CFArrayRef languagesArray = (CFArrayRef)CFPreferencesCopyAppValue(CFSTR("AppleLanguages"), kCFPreferencesCurrentApplication); | |
643 | if (languagesArray && (CFArrayGetTypeID() == CFGetTypeID(languagesArray))) { | |
644 | for (CFIndex idx = 0, cnt = CFArrayGetCount(languagesArray); idx < cnt; idx++) { | |
645 | CFStringRef str = (CFStringRef)CFArrayGetValueAtIndex(languagesArray, idx); | |
646 | if (str && (CFStringGetTypeID() == CFGetTypeID(str))) { | |
647 | CFStringRef ident = CFLocaleCreateCanonicalLanguageIdentifierFromString(kCFAllocatorSystemDefault, str); | |
648 | CFArrayAppendValue(newArray, ident); | |
649 | CFRelease(ident); | |
650 | } | |
651 | } | |
652 | } | |
653 | if (languagesArray) CFRelease(languagesArray); | |
654 | return newArray; | |
655 | } | |
656 | ||
657 | // -------- -------- -------- -------- -------- -------- | |
658 | ||
659 | // These functions return true or false depending on the success or failure of the function. | |
660 | // In the Copy case, this is failure to fill the *cf out parameter, and that out parameter is | |
661 | // returned by reference WITH a retain on it. | |
662 | static bool __CFLocaleSetNOP(CFMutableLocaleRef locale, CFTypeRef cf, CFStringRef context) { | |
663 | return false; | |
664 | } | |
665 | ||
666 | static bool __CFLocaleCopyLocaleID(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context) { | |
667 | *cf = CFRetain(locale->_identifier); | |
668 | return true; | |
669 | } | |
670 | ||
671 | ||
672 | static bool __CFLocaleCopyCodes(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context) { | |
673 | CFDictionaryRef codes = NULL; | |
674 | // this access of _cache is protected by the lock in CFLocaleGetValue() | |
675 | if (!CFDictionaryGetValueIfPresent(locale->_cache, CFSTR("__kCFLocaleCodes"), (const void **)&codes)) { | |
676 | codes = CFLocaleCreateComponentsFromLocaleIdentifier(kCFAllocatorSystemDefault, locale->_identifier); | |
677 | if (codes) CFDictionarySetValue(locale->_cache, CFSTR("__kCFLocaleCodes"), codes); | |
678 | if (codes) CFRelease(codes); | |
679 | } | |
680 | if (codes) { | |
681 | CFStringRef value = (CFStringRef)CFDictionaryGetValue(codes, context); // context is one of kCFLocale*Code constants | |
682 | if (value) CFRetain(value); | |
683 | *cf = value; | |
684 | return true; | |
685 | } | |
686 | return false; | |
687 | } | |
688 | ||
689 | CFCharacterSetRef _CFCreateCharacterSetFromUSet(USet *set) { | |
690 | UErrorCode icuErr = U_ZERO_ERROR; | |
691 | CFMutableCharacterSetRef working = CFCharacterSetCreateMutable(NULL); | |
692 | UChar buffer[2048]; // Suitable for most small sets | |
693 | int32_t stringLen; | |
694 | ||
695 | if (working == NULL) | |
696 | return NULL; | |
697 | ||
698 | int32_t itemCount = uset_getItemCount(set); | |
699 | int32_t i; | |
700 | for (i = 0; i < itemCount; ++i) | |
701 | { | |
702 | UChar32 start, end; | |
703 | UChar * string; | |
704 | ||
705 | string = buffer; | |
706 | stringLen = uset_getItem(set, i, &start, &end, buffer, sizeof(buffer)/sizeof(UChar), &icuErr); | |
707 | if (icuErr == U_BUFFER_OVERFLOW_ERROR) | |
708 | { | |
709 | string = (UChar *) malloc(sizeof(UChar)*(stringLen+1)); | |
710 | if (!string) | |
711 | { | |
712 | CFRelease(working); | |
713 | return NULL; | |
714 | } | |
715 | icuErr = U_ZERO_ERROR; | |
716 | (void) uset_getItem(set, i, &start, &end, string, stringLen+1, &icuErr); | |
717 | } | |
718 | if (U_FAILURE(icuErr)) | |
719 | { | |
720 | if (string != buffer) | |
721 | free(string); | |
722 | CFRelease(working); | |
723 | return NULL; | |
724 | } | |
725 | if (stringLen <= 0) | |
726 | CFCharacterSetAddCharactersInRange(working, CFRangeMake(start, end-start+1)); | |
727 | else | |
728 | { | |
729 | CFStringRef cfString = CFStringCreateWithCharactersNoCopy(kCFAllocatorSystemDefault, (UniChar *)string, stringLen, kCFAllocatorNull); | |
730 | CFCharacterSetAddCharactersInString(working, cfString); | |
731 | CFRelease(cfString); | |
732 | } | |
733 | if (string != buffer) | |
734 | free(string); | |
735 | } | |
736 | ||
737 | CFCharacterSetRef result = CFCharacterSetCreateCopy(kCFAllocatorSystemDefault, working); | |
738 | CFRelease(working); | |
739 | return result; | |
740 | } | |
741 | ||
742 | ||
743 | static bool __CFLocaleCopyExemplarCharSet(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context) { | |
744 | char localeID[ULOC_FULLNAME_CAPACITY+ULOC_KEYWORD_AND_VALUES_CAPACITY]; | |
745 | if (CFStringGetCString(locale->_identifier, localeID, sizeof(localeID)/sizeof(char), kCFStringEncodingASCII)) { | |
746 | UErrorCode icuStatus = U_ZERO_ERROR; | |
747 | ULocaleData* uld = ulocdata_open(localeID, &icuStatus); | |
748 | USet *set = ulocdata_getExemplarSet(uld, NULL, USET_ADD_CASE_MAPPINGS, ULOCDATA_ES_STANDARD, &icuStatus); | |
749 | ulocdata_close(uld); | |
750 | if (U_FAILURE(icuStatus)) | |
751 | return false; | |
752 | if (icuStatus == U_USING_DEFAULT_WARNING) // If default locale used, force to empty set | |
753 | uset_clear(set); | |
754 | *cf = (CFTypeRef) _CFCreateCharacterSetFromUSet(set); | |
755 | uset_close(set); | |
756 | return (*cf != NULL); | |
757 | } | |
758 | return false; | |
759 | } | |
760 | ||
761 | static bool __CFLocaleCopyICUKeyword(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context, const char *keyword) | |
762 | { | |
763 | char localeID[ULOC_FULLNAME_CAPACITY+ULOC_KEYWORD_AND_VALUES_CAPACITY]; | |
764 | if (CFStringGetCString(locale->_identifier, localeID, sizeof(localeID)/sizeof(char), kCFStringEncodingASCII)) | |
765 | { | |
766 | char value[ULOC_KEYWORD_AND_VALUES_CAPACITY]; | |
767 | UErrorCode icuStatus = U_ZERO_ERROR; | |
768 | if (uloc_getKeywordValue(localeID, keyword, value, sizeof(value)/sizeof(char), &icuStatus) > 0 && U_SUCCESS(icuStatus)) | |
769 | { | |
770 | *cf = (CFTypeRef) CFStringCreateWithCString(kCFAllocatorSystemDefault, value, kCFStringEncodingASCII); | |
771 | return true; | |
772 | } | |
773 | } | |
774 | *cf = NULL; | |
775 | return false; | |
776 | } | |
777 | ||
8ca704e1 A |
778 | static bool __CFLocaleCopyICUCalendarID(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context, const char *keyword) { |
779 | char localeID[ULOC_FULLNAME_CAPACITY+ULOC_KEYWORD_AND_VALUES_CAPACITY]; | |
780 | if (CFStringGetCString(locale->_identifier, localeID, sizeof(localeID)/sizeof(char), kCFStringEncodingASCII)) { | |
781 | UErrorCode icuStatus = U_ZERO_ERROR; | |
782 | UEnumeration *en = ucal_getKeywordValuesForLocale(keyword, localeID, TRUE, &icuStatus); | |
783 | int32_t len; | |
784 | const char *value = uenum_next(en, &len, &icuStatus); | |
785 | if (U_SUCCESS(icuStatus)) { | |
786 | *cf = (CFTypeRef) CFStringCreateWithCString(kCFAllocatorSystemDefault, value, kCFStringEncodingASCII); | |
787 | uenum_close(en); | |
788 | return true; | |
789 | } | |
790 | uenum_close(en); | |
791 | } | |
792 | *cf = NULL; | |
793 | return false; | |
794 | } | |
795 | ||
bd5b749c A |
796 | static bool __CFLocaleCopyCalendarID(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context) { |
797 | bool succeeded = __CFLocaleCopyICUKeyword(locale, user, cf, context, kCalendarKeyword); | |
8ca704e1 A |
798 | if (!succeeded) { |
799 | succeeded = __CFLocaleCopyICUCalendarID(locale, user, cf, context, kCalendarKeyword); | |
800 | } | |
bd5b749c | 801 | if (succeeded) { |
cf7d2af9 A |
802 | if (CFEqual(*cf, kCFCalendarIdentifierGregorian)) { |
803 | CFRelease(*cf); | |
804 | *cf = CFRetain(kCFCalendarIdentifierGregorian); | |
805 | } else if (CFEqual(*cf, kCFCalendarIdentifierBuddhist)) { | |
806 | CFRelease(*cf); | |
807 | *cf = CFRetain(kCFCalendarIdentifierBuddhist); | |
808 | } else if (CFEqual(*cf, kCFCalendarIdentifierJapanese)) { | |
809 | CFRelease(*cf); | |
810 | *cf = CFRetain(kCFCalendarIdentifierJapanese); | |
811 | } else if (CFEqual(*cf, kCFCalendarIdentifierIslamic)) { | |
812 | CFRelease(*cf); | |
813 | *cf = CFRetain(kCFCalendarIdentifierIslamic); | |
814 | } else if (CFEqual(*cf, kCFCalendarIdentifierIslamicCivil)) { | |
815 | CFRelease(*cf); | |
816 | *cf = CFRetain(kCFCalendarIdentifierIslamicCivil); | |
817 | } else if (CFEqual(*cf, kCFCalendarIdentifierHebrew)) { | |
818 | CFRelease(*cf); | |
819 | *cf = CFRetain(kCFCalendarIdentifierHebrew); | |
820 | } else if (CFEqual(*cf, kCFCalendarIdentifierChinese)) { | |
821 | CFRelease(*cf); | |
822 | *cf = CFRetain(kCFCalendarIdentifierChinese); | |
823 | } else if (CFEqual(*cf, kCFCalendarIdentifierRepublicOfChina)) { | |
824 | CFRelease(*cf); | |
825 | *cf = CFRetain(kCFCalendarIdentifierRepublicOfChina); | |
826 | } else if (CFEqual(*cf, kCFCalendarIdentifierPersian)) { | |
bd5b749c | 827 | CFRelease(*cf); |
cf7d2af9 A |
828 | *cf = CFRetain(kCFCalendarIdentifierPersian); |
829 | } else if (CFEqual(*cf, kCFCalendarIdentifierIndian)) { | |
bd5b749c | 830 | CFRelease(*cf); |
cf7d2af9 A |
831 | *cf = CFRetain(kCFCalendarIdentifierIndian); |
832 | } else if (CFEqual(*cf, kCFCalendarIdentifierISO8601)) { | |
bd5b749c | 833 | CFRelease(*cf); |
cf7d2af9 A |
834 | *cf = CFRetain(kCFCalendarIdentifierISO8601); |
835 | } else if (CFEqual(*cf, kCFCalendarIdentifierCoptic)) { | |
bd5b749c | 836 | CFRelease(*cf); |
cf7d2af9 A |
837 | *cf = CFRetain(kCFCalendarIdentifierCoptic); |
838 | } else if (CFEqual(*cf, kCFCalendarIdentifierEthiopicAmeteMihret)) { | |
bd5b749c | 839 | CFRelease(*cf); |
cf7d2af9 A |
840 | *cf = CFRetain(kCFCalendarIdentifierEthiopicAmeteMihret); |
841 | } else if (CFEqual(*cf, kCFCalendarIdentifierEthiopicAmeteAlem)) { | |
bd5b749c | 842 | CFRelease(*cf); |
cf7d2af9 A |
843 | *cf = CFRetain(kCFCalendarIdentifierEthiopicAmeteAlem); |
844 | } else { | |
bd5b749c | 845 | CFRelease(*cf); |
cf7d2af9 A |
846 | *cf = NULL; |
847 | return false; | |
bd5b749c A |
848 | } |
849 | } else { | |
cf7d2af9 | 850 | *cf = CFRetain(kCFCalendarIdentifierGregorian); |
bd5b749c A |
851 | } |
852 | return true; | |
853 | } | |
854 | ||
855 | static bool __CFLocaleCopyCalendar(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context) { | |
856 | if (__CFLocaleCopyCalendarID(locale, user, cf, context)) { | |
857 | CFCalendarRef calendar = CFCalendarCreateWithIdentifier(kCFAllocatorSystemDefault, (CFStringRef)*cf); | |
858 | CFCalendarSetLocale(calendar, locale); | |
cf7d2af9 A |
859 | CFDictionaryRef prefs = __CFLocaleGetPrefs(locale); |
860 | CFPropertyListRef metapref = prefs ? CFDictionaryGetValue(prefs, CFSTR("AppleFirstWeekday")) : NULL; | |
861 | if (NULL != metapref && CFGetTypeID(metapref) == CFDictionaryGetTypeID()) { | |
862 | metapref = (CFNumberRef)CFDictionaryGetValue((CFDictionaryRef)metapref, *cf); | |
863 | } | |
864 | if (NULL != metapref && CFGetTypeID(metapref) == CFNumberGetTypeID()) { | |
865 | CFIndex wkdy; | |
866 | if (CFNumberGetValue((CFNumberRef)metapref, kCFNumberCFIndexType, &wkdy)) { | |
867 | CFCalendarSetFirstWeekday(calendar, wkdy); | |
868 | } | |
869 | } | |
870 | metapref = prefs ? CFDictionaryGetValue(prefs, CFSTR("AppleMinDaysInFirstWeek")) : NULL; | |
871 | if (NULL != metapref && CFGetTypeID(metapref) == CFDictionaryGetTypeID()) { | |
872 | metapref = (CFNumberRef)CFDictionaryGetValue((CFDictionaryRef)metapref, *cf); | |
873 | } | |
874 | if (NULL != metapref && CFGetTypeID(metapref) == CFNumberGetTypeID()) { | |
875 | CFIndex mwd; | |
876 | if (CFNumberGetValue((CFNumberRef)metapref, kCFNumberCFIndexType, &mwd)) { | |
877 | CFCalendarSetMinimumDaysInFirstWeek(calendar, mwd); | |
878 | } | |
879 | } | |
bd5b749c A |
880 | CFRelease(*cf); |
881 | *cf = calendar; | |
882 | return true; | |
883 | } | |
884 | return false; | |
885 | } | |
886 | ||
cf7d2af9 A |
887 | static bool __CFLocaleCopyDelimiter(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context) { |
888 | ULocaleDataDelimiterType type = (ULocaleDataDelimiterType)0; | |
889 | if (context == kCFLocaleQuotationBeginDelimiterKey) { | |
890 | type = ULOCDATA_QUOTATION_START; | |
891 | } else if (context == kCFLocaleQuotationEndDelimiterKey) { | |
892 | type = ULOCDATA_QUOTATION_END; | |
893 | } else if (context == kCFLocaleAlternateQuotationBeginDelimiterKey) { | |
894 | type = ULOCDATA_ALT_QUOTATION_START; | |
895 | } else if (context == kCFLocaleAlternateQuotationEndDelimiterKey) { | |
896 | type = ULOCDATA_ALT_QUOTATION_END; | |
897 | } else { | |
898 | return false; | |
899 | } | |
900 | ||
901 | char localeID[ULOC_FULLNAME_CAPACITY+ULOC_KEYWORD_AND_VALUES_CAPACITY]; | |
902 | if (!CFStringGetCString(locale->_identifier, localeID, sizeof(localeID)/sizeof(char), kCFStringEncodingASCII)) { | |
903 | return false; | |
904 | } | |
905 | ||
906 | UChar buffer[130]; | |
907 | UErrorCode status = U_ZERO_ERROR; | |
908 | ULocaleData *uld = ulocdata_open(localeID, &status); | |
909 | int32_t len = ulocdata_getDelimiter(uld, type, buffer, sizeof(buffer) / sizeof(buffer[0]), &status); | |
910 | ulocdata_close(uld); | |
911 | if (U_FAILURE(status) || sizeof(buffer) / sizeof(buffer[0]) < len) { | |
912 | return false; | |
913 | } | |
914 | ||
915 | *cf = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, (UniChar *)buffer, len); | |
916 | return (*cf != NULL); | |
917 | } | |
918 | ||
bd5b749c A |
919 | static bool __CFLocaleCopyCollationID(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context) { |
920 | return __CFLocaleCopyICUKeyword(locale, user, cf, context, kCollationKeyword); | |
921 | } | |
922 | ||
923 | static bool __CFLocaleCopyCollatorID(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context) { | |
924 | CFStringRef canonLocaleCFStr = NULL; | |
cf7d2af9 A |
925 | if (user) { |
926 | CFStringRef pref = (CFStringRef)CFDictionaryGetValue(locale->_prefs, CFSTR("AppleCollationOrder")); | |
927 | if (pref) { | |
928 | // Canonicalize pref string in case it's not in the canonical format. | |
929 | canonLocaleCFStr = CFLocaleCreateCanonicalLanguageIdentifierFromString(kCFAllocatorSystemDefault, pref); | |
930 | } else { | |
931 | CFArrayRef languagesArray = (CFArrayRef)CFDictionaryGetValue(locale->_prefs, CFSTR("AppleLanguages")); | |
932 | if (languagesArray && (CFArrayGetTypeID() == CFGetTypeID(languagesArray))) { | |
933 | if (0 < CFArrayGetCount(languagesArray)) { | |
934 | CFStringRef str = (CFStringRef)CFArrayGetValueAtIndex(languagesArray, 0); | |
935 | if (str && (CFStringGetTypeID() == CFGetTypeID(str))) { | |
936 | canonLocaleCFStr = CFLocaleCreateCanonicalLanguageIdentifierFromString(kCFAllocatorSystemDefault, str); | |
937 | } | |
938 | } | |
939 | } | |
940 | } | |
941 | } | |
bd5b749c A |
942 | if (!canonLocaleCFStr) { |
943 | canonLocaleCFStr = CFLocaleGetIdentifier(locale); | |
944 | CFRetain(canonLocaleCFStr); | |
945 | } | |
946 | *cf = canonLocaleCFStr; | |
947 | return canonLocaleCFStr ? true : false; | |
948 | } | |
949 | ||
950 | static bool __CFLocaleCopyUsesMetric(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context) { | |
951 | bool us = false; // Default is Metric | |
952 | bool done = false; | |
cf7d2af9 A |
953 | #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED |
954 | if (user) { | |
955 | CFTypeRef pref = CFDictionaryGetValue(locale->_prefs, CFSTR("AppleMetricUnits")); | |
956 | if (pref) { | |
957 | us = (kCFBooleanFalse == pref); | |
958 | done = true; | |
959 | } else { | |
960 | pref = CFDictionaryGetValue(locale->_prefs, CFSTR("AppleMeasurementUnits")); | |
961 | if (pref) { | |
962 | us = CFEqual(pref, CFSTR("Inches")); | |
963 | done = true; | |
964 | } | |
965 | } | |
966 | } | |
cf7d2af9 | 967 | #endif |
bd5b749c A |
968 | if (!done) { |
969 | char localeID[ULOC_FULLNAME_CAPACITY+ULOC_KEYWORD_AND_VALUES_CAPACITY]; | |
970 | if (CFStringGetCString(locale->_identifier, localeID, sizeof(localeID)/sizeof(char), kCFStringEncodingASCII)) { | |
971 | UErrorCode icuStatus = U_ZERO_ERROR; | |
972 | UMeasurementSystem ms = UMS_SI; | |
973 | ms = ulocdata_getMeasurementSystem(localeID, &icuStatus); | |
974 | if (U_SUCCESS(icuStatus)) { | |
975 | us = (ms == UMS_US); | |
976 | done = true; | |
977 | } | |
978 | } | |
979 | } | |
980 | if (!done) | |
981 | us = false; | |
982 | *cf = us ? CFRetain(kCFBooleanFalse) : CFRetain(kCFBooleanTrue); | |
983 | return true; | |
984 | } | |
985 | ||
986 | static bool __CFLocaleCopyMeasurementSystem(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context) { | |
987 | if (__CFLocaleCopyUsesMetric(locale, user, cf, context)) { | |
988 | bool us = (*cf == kCFBooleanFalse); | |
989 | CFRelease(*cf); | |
990 | *cf = us ? CFRetain(CFSTR("U.S.")) : CFRetain(CFSTR("Metric")); | |
991 | return true; | |
992 | } | |
993 | return false; | |
994 | } | |
995 | ||
996 | static bool __CFLocaleCopyNumberFormat(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context) { | |
997 | CFStringRef str = NULL; | |
8ca704e1 | 998 | #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_LINUX |
bd5b749c A |
999 | CFNumberFormatterRef nf = CFNumberFormatterCreate(kCFAllocatorSystemDefault, locale, kCFNumberFormatterDecimalStyle); |
1000 | str = nf ? CFNumberFormatterCopyProperty(nf, context) : NULL; | |
1001 | if (nf) CFRelease(nf); | |
cf7d2af9 A |
1002 | #elif DEPLOYMENT_TARGET_WINDOWS |
1003 | #else | |
1004 | #error Unknown or unspecified DEPLOYMENT_TARGET | |
bd5b749c A |
1005 | #endif |
1006 | if (str) { | |
1007 | *cf = str; | |
1008 | return true; | |
1009 | } | |
1010 | return false; | |
1011 | } | |
1012 | ||
1013 | // ICU does not reliably set up currency info for other than Currency-type formatters, | |
1014 | // so we have to have another routine here which creates a Currency number formatter. | |
1015 | static bool __CFLocaleCopyNumberFormat2(CFLocaleRef locale, bool user, CFTypeRef *cf, CFStringRef context) { | |
1016 | CFStringRef str = NULL; | |
8ca704e1 | 1017 | #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_LINUX |
bd5b749c A |
1018 | CFNumberFormatterRef nf = CFNumberFormatterCreate(kCFAllocatorSystemDefault, locale, kCFNumberFormatterCurrencyStyle); |
1019 | str = nf ? CFNumberFormatterCopyProperty(nf, context) : NULL; | |
1020 | if (nf) CFRelease(nf); | |
cf7d2af9 A |
1021 | #elif DEPLOYMENT_TARGET_WINDOWS |
1022 | #else | |
1023 | #error Unknown or unspecified DEPLOYMENT_TARGET | |
bd5b749c A |
1024 | #endif |
1025 | if (str) { | |
1026 | *cf = str; | |
1027 | return true; | |
1028 | } | |
1029 | return false; | |
1030 | } | |
1031 | ||
1032 | typedef int32_t (*__CFICUFunction)(const char *, const char *, UChar *, int32_t, UErrorCode *); | |
1033 | ||
1034 | static bool __CFLocaleICUName(const char *locale, const char *valLocale, CFStringRef *out, __CFICUFunction icu) { | |
1035 | UErrorCode icuStatus = U_ZERO_ERROR; | |
1036 | int32_t size; | |
1037 | UChar name[kMaxICUNameSize]; | |
1038 | ||
1039 | size = (*icu)(valLocale, locale, name, kMaxICUNameSize, &icuStatus); | |
1040 | if (U_SUCCESS(icuStatus) && size > 0 && icuStatus != U_USING_DEFAULT_WARNING) { | |
1041 | *out = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, (UniChar *)name, size); | |
1042 | return (*out != NULL); | |
1043 | } | |
1044 | return false; | |
1045 | } | |
1046 | ||
1047 | static bool __CFLocaleICUKeywordValueName(const char *locale, const char *value, const char *keyword, CFStringRef *out) { | |
1048 | UErrorCode icuStatus = U_ZERO_ERROR; | |
1049 | int32_t size = 0; | |
1050 | UChar name[kMaxICUNameSize]; | |
1051 | // Need to make a fake locale ID | |
1052 | char lid[ULOC_FULLNAME_CAPACITY+ULOC_KEYWORD_AND_VALUES_CAPACITY]; | |
1053 | if (strlen(value) < ULOC_KEYWORD_AND_VALUES_CAPACITY) { | |
cf7d2af9 A |
1054 | strlcpy(lid, "en_US@", sizeof(lid)); |
1055 | strlcat(lid, keyword, sizeof(lid)); | |
1056 | strlcat(lid, "=", sizeof(lid)); | |
1057 | strlcat(lid, value, sizeof(lid)); | |
bd5b749c A |
1058 | size = uloc_getDisplayKeywordValue(lid, keyword, locale, name, kMaxICUNameSize, &icuStatus); |
1059 | if (U_SUCCESS(icuStatus) && size > 0 && icuStatus != U_USING_DEFAULT_WARNING) { | |
1060 | *out = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, (UniChar *)name, size); | |
1061 | return (*out != NULL); | |
1062 | } | |
1063 | } | |
1064 | return false; | |
1065 | } | |
1066 | ||
1067 | static bool __CFLocaleICUCurrencyName(const char *locale, const char *value, UCurrNameStyle style, CFStringRef *out) { | |
1068 | int valLen = strlen(value); | |
1069 | if (valLen != 3) // not a valid ISO code | |
1070 | return false; | |
1071 | UChar curr[4]; | |
1072 | UBool isChoice = FALSE; | |
1073 | int32_t size = 0; | |
1074 | UErrorCode icuStatus = U_ZERO_ERROR; | |
1075 | u_charsToUChars(value, curr, valLen); | |
1076 | curr[valLen] = '\0'; | |
1077 | const UChar *name; | |
1078 | name = ucurr_getName(curr, locale, style, &isChoice, &size, &icuStatus); | |
1079 | if (U_FAILURE(icuStatus) || icuStatus == U_USING_DEFAULT_WARNING) | |
1080 | return false; | |
1081 | UChar result[kMaxICUNameSize]; | |
1082 | if (isChoice) | |
1083 | { | |
1084 | UChar pattern[kMaxICUNameSize]; | |
1085 | CFStringRef patternRef = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("{0,choice,%S}"), name); | |
1086 | CFIndex pattlen = CFStringGetLength(patternRef); | |
1087 | CFStringGetCharacters(patternRef, CFRangeMake(0, pattlen), (UniChar *)pattern); | |
1088 | CFRelease(patternRef); | |
1089 | pattern[pattlen] = '\0'; // null terminate the pattern | |
1090 | // Format the message assuming a large amount of the currency | |
1091 | size = u_formatMessage("en_US", pattern, pattlen, result, kMaxICUNameSize, &icuStatus, 10.0); | |
1092 | if (U_FAILURE(icuStatus)) | |
1093 | return false; | |
1094 | name = result; | |
1095 | ||
1096 | } | |
1097 | *out = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, (UniChar *)name, size); | |
1098 | return (*out != NULL); | |
1099 | } | |
1100 | ||
1101 | static bool __CFLocaleFullName(const char *locale, const char *value, CFStringRef *out) { | |
1102 | UErrorCode icuStatus = U_ZERO_ERROR; | |
1103 | int32_t size; | |
1104 | UChar name[kMaxICUNameSize]; | |
1105 | ||
1106 | // First, try to get the full locale. | |
1107 | size = uloc_getDisplayName(value, locale, name, kMaxICUNameSize, &icuStatus); | |
1108 | if (U_FAILURE(icuStatus) || size <= 0) | |
1109 | return false; | |
1110 | ||
1111 | // Did we wind up using a default somewhere? | |
1112 | if (icuStatus == U_USING_DEFAULT_WARNING) { | |
1113 | // For some locale IDs, there may be no language which has a translation for every | |
1114 | // piece. Rather than return nothing, see if we can at least handle | |
1115 | // the language part of the locale. | |
1116 | UErrorCode localStatus = U_ZERO_ERROR; | |
1117 | int32_t localSize; | |
1118 | UChar localName[kMaxICUNameSize]; | |
1119 | localSize = uloc_getDisplayLanguage(value, locale, localName, kMaxICUNameSize, &localStatus); | |
1120 | if (U_FAILURE(localStatus) || size <= 0 || localStatus == U_USING_DEFAULT_WARNING) | |
1121 | return false; | |
1122 | } | |
1123 | ||
1124 | // This locale is OK, so use the result. | |
1125 | *out = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, (UniChar *)name, size); | |
1126 | return (*out != NULL); | |
1127 | } | |
1128 | ||
1129 | static bool __CFLocaleLanguageName(const char *locale, const char *value, CFStringRef *out) { | |
cf7d2af9 | 1130 | return __CFLocaleICUName(locale, value, out, uloc_getDisplayLanguage); |
bd5b749c A |
1131 | } |
1132 | ||
1133 | static bool __CFLocaleCountryName(const char *locale, const char *value, CFStringRef *out) { | |
1134 | // Need to make a fake locale ID | |
1135 | char lid[ULOC_FULLNAME_CAPACITY]; | |
cf7d2af9 A |
1136 | if (strlen(value) < sizeof(lid) - 3) { |
1137 | strlcpy(lid, "en_", sizeof(lid)); | |
1138 | strlcat(lid, value, sizeof(lid)); | |
bd5b749c A |
1139 | return __CFLocaleICUName(locale, lid, out, uloc_getDisplayCountry); |
1140 | } | |
1141 | return false; | |
1142 | } | |
1143 | ||
1144 | static bool __CFLocaleScriptName(const char *locale, const char *value, CFStringRef *out) { | |
1145 | // Need to make a fake locale ID | |
1146 | char lid[ULOC_FULLNAME_CAPACITY]; | |
1147 | if (strlen(value) == 4) { | |
cf7d2af9 A |
1148 | strlcpy(lid, "en_", sizeof(lid)); |
1149 | strlcat(lid, value, sizeof(lid)); | |
1150 | strlcat(lid, "_US", sizeof(lid)); | |
bd5b749c A |
1151 | return __CFLocaleICUName(locale, lid, out, uloc_getDisplayScript); |
1152 | } | |
1153 | return false; | |
1154 | } | |
1155 | ||
1156 | static bool __CFLocaleVariantName(const char *locale, const char *value, CFStringRef *out) { | |
1157 | // Need to make a fake locale ID | |
1158 | char lid[ULOC_FULLNAME_CAPACITY+ULOC_KEYWORD_AND_VALUES_CAPACITY]; | |
cf7d2af9 A |
1159 | if (strlen(value) < sizeof(lid) - 6) { |
1160 | strlcpy(lid, "en_US_", sizeof(lid)); | |
1161 | strlcat(lid, value, sizeof(lid)); | |
bd5b749c A |
1162 | return __CFLocaleICUName(locale, lid, out, uloc_getDisplayVariant); |
1163 | } | |
1164 | return false; | |
1165 | } | |
1166 | ||
1167 | static bool __CFLocaleCalendarName(const char *locale, const char *value, CFStringRef *out) { | |
1168 | return __CFLocaleICUKeywordValueName(locale, value, kCalendarKeyword, out); | |
1169 | } | |
1170 | ||
1171 | static bool __CFLocaleCollationName(const char *locale, const char *value, CFStringRef *out) { | |
1172 | return __CFLocaleICUKeywordValueName(locale, value, kCollationKeyword, out); | |
1173 | } | |
1174 | ||
1175 | static bool __CFLocaleCurrencyShortName(const char *locale, const char *value, CFStringRef *out) { | |
1176 | return __CFLocaleICUCurrencyName(locale, value, UCURR_SYMBOL_NAME, out); | |
1177 | } | |
1178 | ||
1179 | static bool __CFLocaleCurrencyFullName(const char *locale, const char *value, CFStringRef *out) { | |
1180 | return __CFLocaleICUCurrencyName(locale, value, UCURR_LONG_NAME, out); | |
1181 | } | |
1182 | ||
1183 | static bool __CFLocaleNoName(const char *locale, const char *value, CFStringRef *out) { | |
1184 | return false; | |
1185 | } | |
1186 | ||
bd5b749c A |
1187 | #undef kMaxICUNameSize |
1188 |