/* NSLocale.m Copyright (C) 2010 Free Software Foundation, Inc. Written by: Stefan Bidigaray Date: June, 2010 This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; see the file COPYING.LIB. If not, see or write to the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #define EXPOSE_NSLocale_IVARS 1 #import "common.h" #import "Foundation/NSLocale.h" #import "Foundation/NSArray.h" #import "Foundation/NSCalendar.h" #import "Foundation/NSCoder.h" #import "Foundation/NSCharacterSet.h" #import "Foundation/NSDictionary.h" #import "Foundation/NSLock.h" #import "Foundation/NSValue.h" #import "Foundation/NSNotification.h" #import "Foundation/NSNumberFormatter.h" #import "Foundation/NSUserDefaults.h" #import "Foundation/NSString.h" #import "GNUstepBase/NSMutableString+GNUstepBase.h" #if defined(HAVE_UNICODE_ULOC_H) # include #endif #if defined(HAVE_UNICODE_ULOCDATA_H) # include #endif #if defined(HAVE_UNICODE_UCURR_H) # include #endif #if defined(HAVE_ICU_H) # include #endif @interface NSLocale (PrivateMethods) + (void) _updateCanonicalLocales; - (NSString *) _getMeasurementSystem; - (NSCharacterSet *) _getExemplarCharacterSet; - (NSString *) _getDelimiterWithType: (NSInteger) delimiterType; - (NSCalendar *) _getCalendar; - (NSString *) _getDecimalSeparator; - (NSString *) _getGroupingSeparator; - (NSString *) _getCurrencySymbol; - (NSString *) _getCurrencyCode; @end #if GS_USE_ICU == 1 // // ICU Component Keywords // static const char * ICUCalendarKeyword = "calendar"; static const char * ICUCollationKeyword = "collation"; static NSLocaleLanguageDirection ICUToNSLocaleOrientation (ULayoutType layout) { switch (layout) { case ULOC_LAYOUT_LTR: return NSLocaleLanguageDirectionLeftToRight; case ULOC_LAYOUT_RTL: return NSLocaleLanguageDirectionRightToLeft; case ULOC_LAYOUT_TTB: return NSLocaleLanguageDirectionTopToBottom; case ULOC_LAYOUT_BTT: return NSLocaleLanguageDirectionBottomToTop; default: return NSLocaleLanguageDirectionUnknown; } } static NSArray *_currencyCodesWithType (uint32_t currType) { NSArray *result; NSMutableArray *currencies; UErrorCode err = U_ZERO_ERROR; const char *currCode; UEnumeration *codes; codes = ucurr_openISOCurrencies (currType, &err); if (U_FAILURE(err)) return nil; currencies = [[NSMutableArray alloc] initWithCapacity: 10]; do { int strLength; err = U_ZERO_ERROR; currCode = uenum_next (codes, &strLength, &err); if (U_FAILURE(err)) { uenum_close (codes); [currencies release]; return nil; } if (currCode == NULL) break; [currencies addObject: [NSString stringWithUTF8String: currCode]]; } while (NULL != currCode); uenum_close (codes); result = [NSArray arrayWithArray: currencies]; [currencies release]; return result; } #endif @implementation NSLocale static NSLocale *autoupdatingLocale = nil; static NSLocale *currentLocale = nil; static NSLocale *systemLocale = nil; static NSMutableDictionary *allLocales = nil; static NSDictionary *canonicalLocales = nil; static NSRecursiveLock *classLock = nil; + (void) initialize { if (self == [NSLocale class]) { classLock = [NSRecursiveLock new]; [[NSObject leakAt: &classLock] release]; allLocales = [[NSMutableDictionary alloc] initWithCapacity: 0]; [[NSObject leakAt: &allLocales] release]; } } + (void) defaultsDidChange: (NSNotification*)n { NSUserDefaults *defs; NSString *name; defs = [NSUserDefaults standardUserDefaults]; name = [defs stringForKey: @"Locale"]; if ([name isEqual: autoupdatingLocale->_localeId] == NO) { [classLock lock]; RELEASE(autoupdatingLocale->_localeId); RELEASE(autoupdatingLocale->_components); autoupdatingLocale->_localeId = RETAIN(name); autoupdatingLocale->_components = nil; RELEASE(currentLocale); currentLocale = nil; [classLock unlock]; [[NSNotificationCenter defaultCenter] postNotificationName: NSCurrentLocaleDidChangeNotification object: nil]; } } + (id) autoupdatingCurrentLocale { NSLocale *result; [classLock lock]; if (nil == autoupdatingLocale) { autoupdatingLocale = [[self currentLocale] copy]; [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(defaultsDidChange:) name: NSUserDefaultsDidChangeNotification object: nil]; } result = RETAIN(autoupdatingLocale); [classLock unlock]; return AUTORELEASE(result); } + (NSArray *) availableLocaleIdentifiers { static NSArray *available = nil; #if GS_USE_ICU == 1 if (nil == available) { [classLock lock]; if (nil == available) { NSMutableArray *array; int32_t i; int32_t count = uloc_countAvailable (); array = [[NSMutableArray alloc] initWithCapacity: count]; for (i = 0; i < count; ++i) { const char *localeID = uloc_getAvailable (i); [array addObject: [NSString stringWithUTF8String: localeID]]; } available = [[NSArray alloc] initWithArray: array]; [array release]; } [classLock unlock]; } #endif return [[available copy] autorelease]; } + (NSString *) canonicalLanguageIdentifierFromString: (NSString *) string { NSString *result; NSString *localeId; NSArray *localeComps; /* Can't use the ICU functions here because, according to Apple locale docs, the language has a format like "zh-Hant". ICU, however, uses an underscore to separate Scripts "zh_Hant". */ if (canonicalLocales == nil) [self _updateCanonicalLocales]; localeId = [canonicalLocales objectForKey: string]; if (nil == localeId) { result = string; } else { localeComps = [localeId componentsSeparatedByString: @"_"]; result = [localeComps objectAtIndex: 0]; } return result; } + (NSString *) canonicalLocaleIdentifierFromString: (NSString *) string { /* The way this works, according to Apple docs, is a mess. It seems that both BCP 47's "-" and ICU's "_" separators are used. According to "Language and Locale Designations" (Apple docs) Taiwan, for example, has zh-Hant_TW as it's locale identifier (was zh_TW on 10.3.9 and below). Since ICU doesn't use "-" as a separator it will modify that identifier to zh_Hant_TW. */ NSString *result; NSRange range; if (string == nil) return nil; if (canonicalLocales == nil) [self _updateCanonicalLocales]; result = [canonicalLocales objectForKey: string]; if (result == nil) result = string; /* Strip script info (if present) from hyphenated form. * eg. try to cope with zh-Hant_TW */ range = [result rangeOfString: @"-"]; if (range.length > 0) { NSUInteger start = range.location; NSUInteger length = [result length]; range = [result rangeOfString: @"_" options: 0 range: NSMakeRange(start, length - start)]; if (range.length > 0) { NSMutableString *mStr; /* Found -..._ sequence, so delete the script part. */ mStr = [NSMutableString stringWithString: result]; length = range.location - start; [mStr deleteCharactersInRange: NSMakeRange(start, length)]; result = [NSString stringWithString: mStr]; } } return result; } + (NSLocaleLanguageDirection) characterDirectionForLanguage: (NSString *)isoLangCode { #if GS_USE_ICU == 1 ULayoutType result; UErrorCode status = U_ZERO_ERROR; result = uloc_getCharacterOrientation ([isoLangCode UTF8String], &status); if (U_FAILURE(status) || ULOC_LAYOUT_UNKNOWN == result) return NSLocaleLanguageDirectionUnknown; return ICUToNSLocaleOrientation (result); #else return NSLocaleLanguageDirectionLeftToRight; // FIXME #endif } + (NSDictionary *) componentsFromLocaleIdentifier: (NSString *) string { #if GS_USE_ICU == 1 char buffer[ULOC_KEYWORD_AND_VALUES_CAPACITY]; const char *cLocaleId = [string UTF8String]; int32_t strLength; UEnumeration *enumerator; UErrorCode error = U_ZERO_ERROR; NSDictionary *result; NSMutableDictionary *tmpDict = [[NSMutableDictionary alloc] initWithCapacity: 5]; strLength = uloc_getLanguage (cLocaleId, buffer, ULOC_KEYWORD_AND_VALUES_CAPACITY, &error); if (U_SUCCESS(error) && strLength) { [tmpDict setValue: [NSString stringWithUTF8String: buffer] forKey: NSLocaleLanguageCode]; } error = U_ZERO_ERROR; strLength = uloc_getCountry (cLocaleId, buffer, ULOC_KEYWORD_AND_VALUES_CAPACITY, &error); if (U_SUCCESS(error) && strLength) { [tmpDict setValue: [NSString stringWithUTF8String: buffer] forKey: NSLocaleCountryCode]; } error = U_ZERO_ERROR; strLength = uloc_getScript (cLocaleId, buffer, ULOC_KEYWORD_AND_VALUES_CAPACITY, &error); if (U_SUCCESS(error) && strLength) { [tmpDict setValue: [NSString stringWithUTF8String: buffer] forKey: NSLocaleScriptCode]; } error = U_ZERO_ERROR; strLength = uloc_getVariant (cLocaleId, buffer, ULOC_KEYWORD_AND_VALUES_CAPACITY, &error); if (U_SUCCESS(error) && strLength) { [tmpDict setValue: [NSString stringWithUTF8String: buffer] forKey: NSLocaleVariantCode]; } error = U_ZERO_ERROR; enumerator = uloc_openKeywords (cLocaleId, &error); if (U_SUCCESS(error)) { const char *keyword; error = U_ZERO_ERROR; keyword = uenum_next(enumerator, NULL, &error); while (keyword && U_SUCCESS(error)) { error = U_ZERO_ERROR; strLength = uloc_getKeywordValue (cLocaleId, keyword, buffer, ULOC_KEYWORD_AND_VALUES_CAPACITY, &error); if (strLength && U_SUCCESS(error)) { // This is OK because NSLocaleCalendarIdentifier = "calendar" // and NSLocaleCollationIdentifier = "collation". [tmpDict setValue: [NSString stringWithUTF8String: buffer] forKey: [NSString stringWithUTF8String: keyword]]; error = U_ZERO_ERROR; keyword = uenum_next (enumerator, NULL, &error); } } } uenum_close (enumerator); result = [NSDictionary dictionaryWithDictionary: tmpDict]; RELEASE(tmpDict); return result; #else return nil; // FIXME #endif } + (id) currentLocale { NSLocale *result; [classLock lock]; if (nil == currentLocale) { NSString *localeId; [classLock unlock]; localeId = [[NSUserDefaults standardUserDefaults] objectForKey: @"Locale"]; [classLock lock]; if (currentLocale == nil) currentLocale = [[NSLocale alloc] initWithLocaleIdentifier: localeId]; } result = RETAIN(currentLocale); [classLock unlock]; return AUTORELEASE(result); } + (NSArray *) commonISOCurrencyCodes { #if GS_USE_ICU == 1 return _currencyCodesWithType (UCURR_COMMON | UCURR_NON_DEPRECATED); #else return nil; // FIXME #endif } + (NSArray *) ISOCurrencyCodes { #if GS_USE_ICU == 1 return _currencyCodesWithType (UCURR_ALL); #else return nil; // FIXME #endif } + (NSArray *) ISOCountryCodes { static NSArray *countries = nil; if (nil == countries) { #if GS_USE_ICU == 1 [classLock lock]; if (nil == countries) { NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity: 10]; const char *const *codes = uloc_getISOCountries (); while (*codes != NULL) { [array addObject: [NSString stringWithUTF8String: *codes]]; ++codes; } countries = [[NSArray alloc] initWithArray: array]; [array release]; } [classLock unlock]; #endif } return [[countries copy] autorelease]; } + (NSArray *) ISOLanguageCodes { static NSArray *languages = nil; if (nil == languages) { #if GS_USE_ICU == 1 [classLock lock]; if (nil == languages) { NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity: 10]; const char *const *codes = uloc_getISOLanguages (); while (*codes != NULL) { [array addObject: [NSString stringWithUTF8String: *codes]]; ++codes; } languages = [[NSArray alloc] initWithArray: array]; [array release]; } [classLock unlock]; #endif } return [[languages copy] autorelease]; } + (NSLocaleLanguageDirection) lineDirectionForLanguage: (NSString *) isoLangCode { #if GS_USE_ICU == 1 ULayoutType result; UErrorCode status = U_ZERO_ERROR; result = uloc_getLineOrientation ([isoLangCode UTF8String], &status); if (U_FAILURE(status) || ULOC_LAYOUT_UNKNOWN == result) return NSLocaleLanguageDirectionUnknown; return ICUToNSLocaleOrientation (result); #else return NSLocaleLanguageDirectionTopToBottom; // FIXME #endif } + (NSArray *) preferredLanguages { NSArray *result; NSMutableArray *mArray; NSUInteger cnt; NSUInteger idx = 0; NSArray *languages; languages = [[NSUserDefaults standardUserDefaults] stringArrayForKey: @"NSLanguages"]; if (languages == nil) return [NSArray arrayWithObject: @"en"]; mArray = [NSMutableArray array]; cnt = [languages count]; while (idx < cnt) { NSString *lang = [self canonicalLanguageIdentifierFromString: [languages objectAtIndex: idx]]; if (![mArray containsObject: lang]) [mArray addObject: lang]; ++idx; } result = [NSArray arrayWithArray: mArray]; return result; } + (id) systemLocale { NSLocale *result; [classLock lock]; if (nil == systemLocale) { #if GS_USE_ICU == 1 #if U_ICU_VERSION_MAJOR_NUM >= 64 /* Since ICU 64, the locale handling has changed such that passing an empty string to uloc_canonicalize no longer returns the en_US_POSIX locale as it did in previous versions. See https://icu.unicode.org/download/64#h.plg55ia6o3du */ systemLocale = [[NSLocale alloc] initWithLocaleIdentifier: @"en_US_POSIX"]; #else systemLocale = [[NSLocale alloc] initWithLocaleIdentifier: @""]; #endif #endif } result = RETAIN(systemLocale); [classLock unlock]; return AUTORELEASE(result); } + (id) localeWithLocaleIdentifier:(NSString *)string { return AUTORELEASE([[NSLocale alloc] initWithLocaleIdentifier: string]); } + (NSString *) localeIdentifierFromComponents: (NSDictionary *) dict { NSString *result; NSMutableString *string; const char *language = [[dict objectForKey: NSLocaleLanguageCode] UTF8String]; const char *script = [[dict objectForKey: NSLocaleScriptCode] UTF8String]; const char *country = [[dict objectForKey: NSLocaleCountryCode] UTF8String]; const char *variant = [[dict objectForKey: NSLocaleVariantCode] UTF8String]; const char *calendar = [[[dict objectForKey: NSLocaleCalendar] calendarIdentifier] UTF8String]; const char *collation = [[dict objectForKey: NSLocaleCollationIdentifier] UTF8String]; const char *currency = [[dict objectForKey: NSLocaleCurrencyCode] UTF8String]; if (!calendar) { calendar = [[dict objectForKey: NSLocaleCalendarIdentifier] UTF8String]; } // A locale cannot be constructed without a language. if (language == NULL) return nil; #define TEST_CODE(x) (x ? "_" : ""), (x ? x : "") string = [[NSMutableString alloc] initWithFormat: @"%s%s%s%s%s%s%s", language, TEST_CODE(script), TEST_CODE(country), TEST_CODE(variant)]; #undef TEST_CODE // I'm not using uloc_setKeywordValue() here because the format is easy // enough to reproduce and has the added advatange that we doesn't need ICU. if (calendar || collation || currency) [string appendString: @"@"]; if (calendar) [string appendFormat: @"calendar=%s", calendar]; if (collation) { if (calendar) [string appendString: @";"]; [string appendFormat: @"collation=%s", collation]; } if (currency) { if (calendar || currency) [string appendString: @";"]; [string appendFormat: @"currency=%s", currency]; } result = [NSString stringWithString: string]; RELEASE(string); return result; } + (NSString *) localeIdentifierFromWindowsLocaleCode: (uint32_t) lcid { #if GS_USE_ICU == 1 char buffer[ULOC_FULLNAME_CAPACITY]; UErrorCode status = U_ZERO_ERROR; uloc_getLocaleForLCID (lcid, buffer, ULOC_FULLNAME_CAPACITY, &status); if (U_FAILURE(status)) return nil; return [NSString stringWithUTF8String: buffer]; #else return nil; // FIXME Check // msdn.microsoft.com/en-us/library/0h88fahh%28v=vs.85%29.aspx #endif } + (uint32_t) windowsLocaleCodeFromLocaleIdentifier: (NSString *)localeIdentifier { #if GS_USE_ICU == 1 return uloc_getLCID ([localeIdentifier UTF8String]); #else return 0; // FIXME: Check // msdn.microsoft.com/en-us/library/0h88fahh%28v=vs.85%29.aspx #endif } - (NSString *) displayNameForKey: (NSString *) key value: (id) value { #if GS_USE_ICU == 1 int32_t length = 0; unichar buffer[ULOC_FULLNAME_CAPACITY]; UErrorCode status = 0; const char *keyword = NULL; const char *locale = [_localeId UTF8String]; if ([key isEqualToString: NSLocaleIdentifier]) { length = uloc_getDisplayName([value UTF8String], locale, (UChar *)buffer, sizeof(buffer)/sizeof(unichar), &status); } else if ([key isEqualToString: NSLocaleLanguageCode]) { length = uloc_getDisplayLanguage([value UTF8String], locale, (UChar *)buffer, sizeof(buffer)/sizeof(unichar), &status); } else if ([key isEqualToString: NSLocaleCountryCode]) { length = uloc_getDisplayCountry([value UTF8String], locale, (UChar *)buffer, sizeof(buffer)/sizeof(unichar), &status); } else if ([key isEqualToString: NSLocaleScriptCode]) { length = uloc_getDisplayCountry([value UTF8String], locale, (UChar *)buffer, sizeof(buffer)/sizeof(unichar), &status); } else if ([key isEqualToString: NSLocaleVariantCode]) { length = uloc_getDisplayVariant([value UTF8String], locale, (UChar *)buffer, sizeof(buffer)/sizeof(unichar), &status); } else if ([key isEqualToString: NSLocaleCalendar]) { keyword = ICUCalendarKeyword; } else if ([key isEqualToString: NSLocaleCollationIdentifier]) { keyword = ICUCollationKeyword; } else { return nil; } /* * TODO: Implement handling of the other locale component constants. */ if (NULL != keyword) { length = uloc_getDisplayKeywordValue ([value UTF8String], keyword, locale, (UChar *)buffer, sizeof(buffer)/sizeof(unichar), &status); } if (U_FAILURE(status)) return nil; return [NSString stringWithCharacters: buffer length: (NSUInteger)length]; #else return nil; // FIXME #endif } - (id) initWithLocaleIdentifier: (NSString*)string { NSLocale *newLocale; NSString *localeId; #if GS_USE_ICU == 1 char cLocaleId[ULOC_FULLNAME_CAPACITY]; UErrorCode error = U_ZERO_ERROR; localeId = [NSLocale canonicalLocaleIdentifierFromString: string]; // Normalize locale ID uloc_canonicalize ([localeId UTF8String], cLocaleId, ULOC_FULLNAME_CAPACITY, &error); if (U_FAILURE(error)) { [self release]; return nil; } localeId = [NSString stringWithUTF8String: cLocaleId]; #else localeId = [NSLocale canonicalLocaleIdentifierFromString: string]; #endif if (nil == localeId) { [self release]; return nil; } [classLock lock]; newLocale = [allLocales objectForKey: localeId]; if (nil == newLocale) { _localeId = [localeId copy]; _components = [[NSMutableDictionary alloc] initWithCapacity: 0]; [allLocales setObject: self forKey: localeId]; } else { [self release]; self = [newLocale retain]; } [classLock unlock]; return self; } - (NSString *) localeIdentifier { return _localeId; } - (id) objectForKey: (id) key { id result = nil; #if GS_USE_ICU == 1 if (key == NSLocaleIdentifier || key == NSLocaleCollatorIdentifier) return _localeId; if ((result = [_components objectForKey: key])) return result; if ([_components count] == 0) { [_components addEntriesFromDictionary: [NSLocale componentsFromLocaleIdentifier: _localeId]]; if ((result = [_components objectForKey: key])) return result; } if ([key isEqualToString: NSLocaleUsesMetricSystem]) { NSString *mSys = [_components objectForKey: key]; mSys = (mSys == nil) ? [self _getMeasurementSystem] : mSys; if (mSys != nil) { [_components setValue: mSys forKey: NSLocaleMeasurementSystem]; if ([mSys isEqualToString: @"Metric"]) result = [NSNumber numberWithBool: YES]; else result = [NSNumber numberWithBool: NO]; } } else if ([key isEqualToString: NSLocaleMeasurementSystem]) result = [self _getMeasurementSystem]; else if ([key isEqualToString: NSLocaleExemplarCharacterSet]) result = [self _getExemplarCharacterSet]; #if OS_API_VERSION(MAC_OS_X_VERSION_10_6, GS_API_LATEST) else if ([key isEqualToString: NSLocaleQuotationBeginDelimiterKey]) result = [self _getDelimiterWithType: ULOCDATA_QUOTATION_START]; else if ([key isEqualToString: NSLocaleQuotationEndDelimiterKey]) result = [self _getDelimiterWithType: ULOCDATA_QUOTATION_END]; else if ([key isEqualToString: NSLocaleAlternateQuotationBeginDelimiterKey]) result = [self _getDelimiterWithType: ULOCDATA_ALT_QUOTATION_START]; else if ([key isEqualToString: NSLocaleAlternateQuotationEndDelimiterKey]) result = [self _getDelimiterWithType: ULOCDATA_ALT_QUOTATION_END]; #endif else if ([key isEqualToString: NSLocaleCalendar]) result = [self _getCalendar]; else if ([key isEqualToString: NSLocaleDecimalSeparator]) result = [self _getDecimalSeparator]; else if ([key isEqualToString: NSLocaleGroupingSeparator]) result = [self _getGroupingSeparator]; else if ([key isEqualToString: NSLocaleCurrencySymbol]) result = [self _getCurrencySymbol]; else if ([key isEqualToString: NSLocaleCurrencyCode]) result = [self _getCurrencyCode]; [_components setValue: result forKey: key]; #endif return result; } - (NSString *) languageCode { return [self objectForKey: NSLocaleLanguageCode]; } - (NSString *) countryCode { return [self objectForKey: NSLocaleCountryCode]; } - (NSString *) scriptCode { return [self objectForKey: NSLocaleScriptCode]; } - (NSString *) variantCode { return [self objectForKey: NSLocaleVariantCode]; } - (NSCharacterSet *) exemplarCharacterSet { return [self objectForKey: NSLocaleExemplarCharacterSet]; } - (NSString *) collationIdentifier { return [self objectForKey: NSLocaleCollationIdentifier]; } - (NSString *) collatorIdentifier { return [self objectForKey: NSLocaleCollatorIdentifier]; } - (NSString *) description { return _localeId; } - (BOOL) isEqual: (id)obj { if ([obj isKindOfClass: [self class]]) { return [_localeId isEqual: [obj localeIdentifier]]; } return NO; } - (void) dealloc { RELEASE(_localeId); RELEASE(_components); [super dealloc]; } - (void) encodeWithCoder: (NSCoder*)encoder { [encoder encodeObject: _localeId]; } - (id) initWithCoder: (NSCoder*)decoder { NSString *s = [decoder decodeObject]; return [self initWithLocaleIdentifier: s]; } - (id) copyWithZone: (NSZone *) zone { NSLocale *result; if (NSShouldRetainWithZone(self, zone)) result = RETAIN(self); else { result = (NSLocale *)NSCopyObject(self, 0, zone); result->_localeId = [_localeId copyWithZone: zone]; } return result; } @end @implementation NSLocale (PrimateMethods) + (void) _updateCanonicalLocales { NSBundle *gbundle = [NSBundle bundleForLibrary: @"gnustep-base"]; NSString *file = [gbundle pathForResource: @"Locale" ofType: @"canonical" inDirectory: @"Languages"]; if (file != nil) canonicalLocales = [[NSDictionary alloc] initWithContentsOfFile: file]; } - (NSString *) _getMeasurementSystem { #if GS_USE_ICU == 1 const char *cLocaleId; ULocaleData *localeData; UMeasurementSystem msystem; UErrorCode err = U_ZERO_ERROR; NSString *result = nil; cLocaleId = [_localeId UTF8String]; localeData = ulocdata_open (cLocaleId, &err); if (U_FAILURE(err)) return nil; msystem = ulocdata_getMeasurementSystem (cLocaleId, &err); if (U_SUCCESS(err)) { if (msystem == UMS_SI) result = @"Metric"; else result = @"U.S."; } ulocdata_close (localeData); return result; #else return nil; #endif } - (NSCharacterSet *) _getExemplarCharacterSet { #if GS_USE_ICU == 1 const char *cLocaleId; int idx; int count; UChar buffer[1024]; // This is an arbitrary size, increase it if it's not enough. ULocaleData *localeData; USet *charSet; UErrorCode err = U_ZERO_ERROR; NSCharacterSet *result; NSMutableCharacterSet *mSet; cLocaleId = [_localeId UTF8String]; localeData = ulocdata_open(cLocaleId, &err); if (U_FAILURE(err)) { return nil; } charSet = ulocdata_getExemplarSet(localeData, NULL, USET_ADD_CASE_MAPPINGS, ULOCDATA_ES_STANDARD, &err); if (U_FAILURE(err)) { ulocdata_close(localeData); return nil; } ulocdata_close(localeData); mSet = [[NSMutableCharacterSet alloc] init]; if (mSet == nil) { uset_close(charSet); return nil; } count = uset_getItemCount(charSet); for (idx = 0 ; idx < count ; ++idx) { UChar32 start, end; int strLen; err = U_ZERO_ERROR; strLen = uset_getItem(charSet, idx, &start, &end, buffer, 1024, &err); if (U_FAILURE(err)) { uset_close(charSet); RELEASE(mSet); return nil; } if (strLen == 0) { [mSet addCharactersInRange: NSMakeRange(start, (end - start) + 1)]; } else if (strLen >= 2) { NSString *str = [NSString stringWithCharacters: buffer length: strLen]; [mSet addCharactersInString: str]; } // FIXME: The icu docs are a bit iffy and don't explain what len == 1 // means. So, if it is encountered, we simply skip it. } uset_close(charSet); result = [mSet copyWithZone: NULL]; RELEASE(mSet); return AUTORELEASE(result); #else return nil; #endif } - (NSString *) _getDelimiterWithType: (NSInteger) delimiterType { #if GS_USE_ICU == 1 const char *cLocaleId; int strLen; UErrorCode err = U_ZERO_ERROR; ULocaleData *localeData; UChar result[32]; // Arbritrary size cLocaleId = [_localeId UTF8String]; localeData = ulocdata_open (cLocaleId, &err); strLen = ulocdata_getDelimiter (localeData, delimiterType, result, 32, &err); ulocdata_close (localeData); if (U_SUCCESS(err)) return [NSString stringWithCharacters: (unichar *)result length: strLen]; #endif return nil; } - (NSCalendar *) _getCalendar { #if GS_USE_ICU == 1 NSCalendar *result; NSString *calId; int strLen; char buffer[ULOC_KEYWORDS_CAPACITY]; UErrorCode err = U_ZERO_ERROR; strLen = uloc_getKeywordValue ([_localeId UTF8String], ICUCalendarKeyword, buffer, ULOC_KEYWORDS_CAPACITY, &err); if (U_SUCCESS(err) && strLen > 0) calId = [NSString stringWithUTF8String: buffer]; else calId = NSGregorianCalendar; result = [[NSCalendar alloc] initWithCalendarIdentifier: calId]; return AUTORELEASE(result); #else return nil; #endif } - (NSString *) _getDecimalSeparator { NSNumberFormatter *nFor; NSString *result; nFor = [[NSNumberFormatter alloc] init]; [nFor setLocale: self]; [nFor setNumberStyle: NSNumberFormatterDecimalStyle]; result = [nFor decimalSeparator]; RELEASE(nFor); return result; } - (NSString *) _getGroupingSeparator { NSNumberFormatter *nFor; NSString *result; nFor = [[NSNumberFormatter alloc] init]; [nFor setLocale: self]; [nFor setNumberStyle: NSNumberFormatterDecimalStyle]; result = [nFor groupingSeparator]; RELEASE(nFor); return result; } - (NSString *) _getCurrencySymbol { NSNumberFormatter *nFor; NSString *result; nFor = [[NSNumberFormatter alloc] init]; [nFor setLocale: self]; [nFor setNumberStyle: NSNumberFormatterCurrencyStyle]; result = [nFor currencySymbol]; RELEASE(nFor); return result; } - (NSString *) _getCurrencyCode { NSNumberFormatter *nFor; NSString *result; nFor = [[NSNumberFormatter alloc] init]; [nFor setLocale: self]; [nFor setNumberStyle: NSNumberFormatterCurrencyStyle]; result = [nFor currencyCode]; RELEASE(nFor); return result; } @end