Artifact fbc5149bde87ea988783b04ba3ca4075a6d24ea169fab5b9a023dc30ac425fc0:
- File
src/OFLocalization.m
— part of check-in
[06bcb21fc7]
at
2017-01-09 06:26:04
on branch trunk
— Add OFLocalization
This singleton gives access to all things locale, including the ability
to get localized strings.This also adds the OF_LOCALIZED() macro. Its first argument is an ID for
the string to be localized and its second argument is the fallback
string to be used if it cannot retrieve the localized string. Following
that are variable name / value pairs to be replaced in the localized
string.Getting translated strings is not implemented yet: Instead, it always
uses the fallback string.This also switches ofhttp to localized strings. (user: js, size: 5034) [annotate] [blame] [check-ins using]
/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 * Jonathan Schleifer <js@heap.zone> * * All rights reserved. * * This file is part of ObjFW. It may be distributed under the terms of the * Q Public License 1.0, which can be found in the file LICENSE.QPL included in * the packaging of this file. * * Alternatively, it may be distributed under the terms of the GNU General * Public License, either version 2 or 3, which can be found in the file * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this * file. */ #include "config.h" #include <locale.h> #import "OFLocalization.h" #import "OFString.h" #import "OFInvalidArgumentException.h" static OFLocalization *sharedLocalization = nil; @implementation OFLocalization @synthesize language = _language, territory = _territory, encoding = _encoding; @synthesize decimalPoint = _decimalPoint; + (instancetype)sharedLocalization { return sharedLocalization; } + (OFString*)language { return [sharedLocalization language]; } + (OFString*)territory { return [sharedLocalization territory]; } + (of_string_encoding_t)encoding { return [sharedLocalization encoding]; } + (OFString*)decimalPoint { return [sharedLocalization decimalPoint]; } - initWithLocale: (char*)locale { self = [super init]; if (locale == NULL) { _encoding = OF_STRING_ENCODING_UTF_8; _decimalPoint = @"."; return self; } locale = of_strdup(locale); @try { char *tmp; /* 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]); if (strcmp(tmp, "utf8") == 0 || strcmp(tmp, "utf-8") == 0) _encoding = OF_STRING_ENCODING_UTF_8; else if (strcmp(tmp, "ascii") == 0 || strcmp(tmp, "us-ascii") == 0) _encoding = OF_STRING_ENCODING_ASCII; else if (strcmp(tmp, "iso8859-1") == 0 || strcmp(tmp, "iso-8859-1") == 0) _encoding = OF_STRING_ENCODING_ISO_8859_1; else if (strcmp(tmp, "iso8859-15") == 0 || strcmp(tmp, "iso-8859-15") == 0) _encoding = OF_STRING_ENCODING_ISO_8859_15; /* Windows uses a codepage */ else if (strcmp(tmp, "1252") == 0) _encoding = OF_STRING_ENCODING_WINDOWS_1252; } /* Territory */ if ((tmp = strrchr(locale, '_')) != NULL) { *tmp++ = '\0'; _territory = [[OFString alloc] initWithCString: tmp encoding: OF_STRING_ENCODING_ASCII]; } _language = [[OFString alloc] initWithCString: locale encoding: OF_STRING_ENCODING_ASCII]; _decimalPoint = [[OFString alloc] initWithCString: localeconv()->decimal_point encoding: _encoding]; } @catch (id e) { [self release]; @throw e; } @finally { free(locale); } sharedLocalization = self; return self; } - (OFString*)localizedStringForID: (OFConstantString*)ID fallback: (OFConstantString*)fallback, ... { OFString *ret; va_list args; va_start(args, fallback); ret = [self localizedStringForID: ID fallback: fallback arguments: args]; va_end(args); return ret; } - (OFString*)localizedStringForID: (OFConstantString*)ID 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; int state = 0; for (size_t i = 0; i < UTF8StringLength; i++) { switch (state) { case 0: if (UTF8String[i] == '%') { [ret appendUTF8String: UTF8String + last length: i - last]; last = i + 1; state = 1; } break; case 1: if (UTF8String[i] == '[') { last = i + 1; state = 2; } else { [ret appendString: @"%"]; state = 0; } break; case 2: if (UTF8String[i] == ']') { va_list argsCopy; OFConstantString *name; OFString *var = [OFString stringWithUTF8String: UTF8String + last length: i - last]; /* * We loop, as most of the time, we only have * one or maybe two variables, meaning looping * is faster than constructing a dictionary. */ va_copy(argsCopy, arguments); while ((name = va_arg(argsCopy, OFConstantString*)) != nil) { id value = va_arg(argsCopy, id); if (value == nil) @throw [OFInvalidArgumentException exception]; if ([name isEqual: var]) { [ret appendString: [value description]]; break; } } last = i + 1; state = 0; } break; } } switch (state) { case 1: [ret appendString: @"%"]; /* Explicit fall-through */ case 0: [ret appendUTF8String: UTF8String + last length: UTF8StringLength - last]; break; } objc_autoreleasePoolPop(pool); [ret makeImmutable]; return ret; } @end