@@ -18,10 +18,12 @@ #include #import "OFLocalization.h" #import "OFString.h" +#import "OFArray.h" +#import "OFDictionary.h" #import "OFInvalidArgumentException.h" static OFLocalization *sharedLocalization = nil; @@ -51,14 +53,26 @@ + (OFString*)decimalPoint { return [sharedLocalization decimalPoint]; } + ++ (void)addLanguageDirectory: (OFString*)path +{ + [sharedLocalization addLanguageDirectory: path]; +} - initWithLocale: (char*)locale { self = [super init]; + + @try { + _localizedStrings = [[OFMutableArray alloc] init]; + } @catch (id e) { + [self release]; + @throw e; + } if (locale == NULL) { _encoding = OF_STRING_ENCODING_UTF_8; _decimalPoint = @"."; return self; @@ -66,19 +80,18 @@ locale = of_strdup(locale); @try { char *tmp; + size_t tmpLen; /* We don't care for extras behind the @ */ if ((tmp = strrchr(locale, '@')) != NULL) *tmp = '\0'; /* Encoding */ if ((tmp = strrchr(locale, '.')) != NULL) { - size_t tmpLen; - *tmp++ = '\0'; tmpLen = strlen(tmp); for (size_t i = 0; i < tmpLen; i++) tmp[i] = of_ascii_tolower(tmp[i]); @@ -101,18 +114,29 @@ } /* Territory */ if ((tmp = strrchr(locale, '_')) != NULL) { *tmp++ = '\0'; + + tmpLen = strlen(tmp); + for (size_t i = 0; i < tmpLen; i++) + tmp[i] = of_ascii_tolower(tmp[i]); + _territory = [[OFString alloc] initWithCString: tmp - encoding: OF_STRING_ENCODING_ASCII]; + encoding: OF_STRING_ENCODING_ASCII + length: tmpLen]; } + tmpLen = strlen(tmp); + for (size_t i = 0; i < tmpLen; i++) + tmp[i] = of_ascii_tolower(tmp[i]); + _language = [[OFString alloc] initWithCString: locale - encoding: OF_STRING_ENCODING_ASCII]; + encoding: OF_STRING_ENCODING_ASCII + length: tmpLen]; _decimalPoint = [[OFString alloc] initWithCString: localeconv()->decimal_point encoding: _encoding]; } @catch (id e) { @@ -124,10 +148,48 @@ sharedLocalization = self; return self; } + +- (void)dealloc +{ + [_language release]; + [_territory release]; + [_decimalPoint release]; + [_localizedStrings release]; + + [super dealloc]; +} + +- (void)addLanguageDirectory: (OFString*)path +{ + void *pool = objc_autoreleasePoolPush(); + OFString *mapPath = + [path stringByAppendingPathComponent: @"languages.json"]; + OFDictionary *map = + [[OFString stringWithContentsOfFile: mapPath] JSONValue]; + OFString *languageFile; + + languageFile = [[map objectForKey: _language] objectForKey: _territory]; + if (languageFile == nil) + languageFile = [[map objectForKey: _language] + objectForKey: @""]; + + if (languageFile == nil) { + objc_autoreleasePoolPop(pool); + return; + } + + languageFile = [path stringByAppendingPathComponent: + [languageFile stringByAppendingString: @".json"]]; + + [_localizedStrings addObject: + [[OFString stringWithContentsOfFile: languageFile] JSONValue]]; + + objc_autoreleasePoolPop(pool); +} - (OFString*)localizedStringForID: (OFConstantString*)ID fallback: (OFConstantString*)fallback, ... { OFString *ret; @@ -146,15 +208,35 @@ fallback: (OFConstantString*)fallback arguments: (va_list)arguments { OFMutableString *ret = [OFMutableString string]; void *pool = objc_autoreleasePoolPush(); - const char *UTF8String = [fallback UTF8String]; - size_t UTF8StringLength = [fallback UTF8StringLength]; - size_t last = 0; + const char *UTF8String = NULL; + size_t last, UTF8StringLength; int state = 0; + for (OFDictionary *strings in _localizedStrings) { + id string = [strings objectForKey: ID]; + + if (string == nil) + continue; + + if ([string isKindOfClass: [OFArray class]]) + string = [string componentsJoinedByString: @""]; + + UTF8String = [string UTF8String]; + UTF8StringLength = [string UTF8StringLength]; + break; + } + + if (UTF8String == NULL) { + UTF8String = [fallback UTF8String]; + UTF8StringLength = [fallback UTF8StringLength]; + } + + state = 0; + last = 0; for (size_t i = 0; i < UTF8StringLength; i++) { switch (state) { case 0: if (UTF8String[i] == '%') { [ret appendUTF8String: UTF8String + last