Index: ObjFW.xcodeproj/project.pbxproj ================================================================== --- ObjFW.xcodeproj/project.pbxproj +++ ObjFW.xcodeproj/project.pbxproj @@ -677,10 +677,14 @@ 4B44836D1D0497DE005D12A7 /* OFUndefinedKeyException.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B44836B1D0497DE005D12A7 /* OFUndefinedKeyException.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4B44836E1D0497DE005D12A7 /* OFUndefinedKeyException.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B44836C1D0497DE005D12A7 /* OFUndefinedKeyException.m */; }; 4B4483711D04989C005D12A7 /* RuntimeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B44836F1D049887005D12A7 /* RuntimeTests.m */; }; 4B45355313DCFE1E0037AB4D /* OFCountedSet.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B45355113DCFE1E0037AB4D /* OFCountedSet.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4B45355413DCFE1E0037AB4D /* OFCountedSet.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B45355213DCFE1E0037AB4D /* OFCountedSet.m */; }; + 4B46E2211E235EA700121ED6 /* OFLocalization.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B46E21F1E235EA700121ED6 /* OFLocalization.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4B46E2221E235EA700121ED6 /* OFLocalization.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B46E21F1E235EA700121ED6 /* OFLocalization.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4B46E2231E235EA700121ED6 /* OFLocalization.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B46E2201E235EA700121ED6 /* OFLocalization.m */; }; + 4B46E2241E235EA700121ED6 /* OFLocalization.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B46E2201E235EA700121ED6 /* OFLocalization.m */; }; 4B48B95414DC23B100546D39 /* OFXMLProcessingInstructions.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B48B95214DC23B100546D39 /* OFXMLProcessingInstructions.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4B48B95514DC23B100546D39 /* OFXMLProcessingInstructions.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B48B95314DC23B100546D39 /* OFXMLProcessingInstructions.m */; }; 4B49EA66143B39CE0005BBC6 /* OFXMLNodeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B49EA65143B39CE0005BBC6 /* OFXMLNodeTests.m */; }; 4B49EA6D143B3A090005BBC6 /* OFXMLCDATA.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B49EA67143B3A090005BBC6 /* OFXMLCDATA.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4B49EA6E143B3A090005BBC6 /* OFXMLCDATA.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B49EA68143B3A090005BBC6 /* OFXMLCDATA.m */; }; @@ -1250,10 +1254,12 @@ 4B44836B1D0497DE005D12A7 /* OFUndefinedKeyException.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFUndefinedKeyException.h; path = src/exceptions/OFUndefinedKeyException.h; sourceTree = ""; }; 4B44836C1D0497DE005D12A7 /* OFUndefinedKeyException.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFUndefinedKeyException.m; path = src/exceptions/OFUndefinedKeyException.m; sourceTree = ""; }; 4B44836F1D049887005D12A7 /* RuntimeTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RuntimeTests.m; path = tests/RuntimeTests.m; sourceTree = ""; }; 4B45355113DCFE1E0037AB4D /* OFCountedSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFCountedSet.h; path = src/OFCountedSet.h; sourceTree = ""; }; 4B45355213DCFE1E0037AB4D /* OFCountedSet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFCountedSet.m; path = src/OFCountedSet.m; sourceTree = ""; }; + 4B46E21F1E235EA700121ED6 /* OFLocalization.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFLocalization.h; path = src/OFLocalization.h; sourceTree = ""; }; + 4B46E2201E235EA700121ED6 /* OFLocalization.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFLocalization.m; path = src/OFLocalization.m; sourceTree = ""; }; 4B48B95214DC23B100546D39 /* OFXMLProcessingInstructions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFXMLProcessingInstructions.h; path = src/OFXMLProcessingInstructions.h; sourceTree = ""; }; 4B48B95314DC23B100546D39 /* OFXMLProcessingInstructions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFXMLProcessingInstructions.m; path = src/OFXMLProcessingInstructions.m; sourceTree = ""; }; 4B49EA65143B39CE0005BBC6 /* OFXMLNodeTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFXMLNodeTests.m; path = tests/OFXMLNodeTests.m; sourceTree = ""; }; 4B49EA67143B3A090005BBC6 /* OFXMLCDATA.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFXMLCDATA.h; path = src/OFXMLCDATA.h; sourceTree = ""; }; 4B49EA68143B3A090005BBC6 /* OFXMLCDATA.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFXMLCDATA.m; path = src/OFXMLCDATA.m; sourceTree = ""; }; @@ -1973,10 +1979,12 @@ 4BC176231D04963000C32718 /* OFKeyValueCoding.h */, 4BC176241D04963000C32718 /* OFObject+KeyValueCoding.h */, 4BC176251D04963000C32718 /* OFObject+KeyValueCoding.m */, 4B67996C1099E7C50041064A /* OFList.h */, 4B67996D1099E7C50041064A /* OFList.m */, + 4B46E21F1E235EA700121ED6 /* OFLocalization.h */, + 4B46E2201E235EA700121ED6 /* OFLocalization.m */, 4B6743F9163C395900EB1E59 /* OFLocking.h */, 4B3B0796166978780044E634 /* OFMapTable.h */, 4B3B0797166978780044E634 /* OFMapTable.m */, 4BC1C3EA184B5EB200BBF50F /* OFMapTable+Private.h */, 4BF1BCC211C9663F0025511F /* OFMD5Hash.h */, @@ -2343,10 +2351,11 @@ 4B2C21F61DA292BE00735907 /* OFIntrospection.h in Headers */, 4B2C21F71DA292BE00735907 /* OFJSONRepresentation.h in Headers */, 4B2C21F81DA292BE00735907 /* OFKernelEventObserver.h in Headers */, 4B2C21F91DA292BE00735907 /* OFKeyValueCoding.h in Headers */, 4B2C21FA1DA292BE00735907 /* OFList.h in Headers */, + 4B46E2221E235EA700121ED6 /* OFLocalization.h in Headers */, 4B2C21FB1DA292BE00735907 /* OFLocking.h in Headers */, 4B2C21FC1DA292BE00735907 /* OFMapTable.h in Headers */, 4B2C21FD1DA292BE00735907 /* OFMD5Hash.h in Headers */, 4B2C21FE1DA292BE00735907 /* OFMessagePackExtension.h in Headers */, 4B2C21FF1DA292BE00735907 /* OFMessagePackRepresentation.h in Headers */, @@ -2560,10 +2569,11 @@ 4BA49D9013DB113B00381CDB /* OFIntrospection.h in Headers */, 4BA02BA115041F5900002F84 /* OFJSONRepresentation.h in Headers */, 4B0EA9211898690E00F573A4 /* OFKernelEventObserver.h in Headers */, 4BC1762F1D04963000C32718 /* OFKeyValueCoding.h in Headers */, 4B3D23CB1337FCB000DD29B8 /* OFList.h in Headers */, + 4B46E2211E235EA700121ED6 /* OFLocalization.h in Headers */, 4B674402163C395900EB1E59 /* OFLocking.h in Headers */, 4B3B0798166978780044E634 /* OFMapTable.h in Headers */, 4B3D23CC1337FCB000DD29B8 /* OFMD5Hash.h in Headers */, 4BCAA9AF1772432F003EF859 /* OFMessagePackExtension.h in Headers */, 4B879A8E177231F000EBCEA4 /* OFMessagePackRepresentation.h in Headers */, @@ -3097,10 +3107,11 @@ 4B2C21401DA292BE00735907 /* OFKernelEventObserver.m in Sources */, 4B2C21411DA292BE00735907 /* OFKernelEventObserver_kqueue.m in Sources */, 4B2C21421DA292BE00735907 /* OFKernelEventObserver_poll.m in Sources */, 4B2C21431DA292BE00735907 /* OFKernelEventObserver_select.m in Sources */, 4B2C21441DA292BE00735907 /* OFList.m in Sources */, + 4B46E2241E235EA700121ED6 /* OFLocalization.m in Sources */, 4B2C21451DA292BE00735907 /* OFMapTable.m in Sources */, 4B2C21461DA292BE00735907 /* OFMD5Hash.m in Sources */, 4B2C21471DA292BE00735907 /* OFMessagePackExtension.m in Sources */, 4B2C21481DA292BE00735907 /* OFMutableArray.m in Sources */, 4B2C21491DA292BE00735907 /* OFMutableArray_adjacent.m in Sources */, @@ -3284,10 +3295,11 @@ 4B0EA9221898690E00F573A4 /* OFKernelEventObserver.m in Sources */, 4B0EA91C1898690E00F573A4 /* OFKernelEventObserver_kqueue.m in Sources */, 4B0EA91E1898690E00F573A4 /* OFKernelEventObserver_poll.m in Sources */, 4B0EA9201898690E00F573A4 /* OFKernelEventObserver_select.m in Sources */, 4B3D23991337FC0D00DD29B8 /* OFList.m in Sources */, + 4B46E2231E235EA700121ED6 /* OFLocalization.m in Sources */, 4B3B0799166978780044E634 /* OFMapTable.m in Sources */, 4B3D239A1337FC0D00DD29B8 /* OFMD5Hash.m in Sources */, 4BCAA9B01772432F003EF859 /* OFMessagePackExtension.m in Sources */, 4B3D239B1337FC0D00DD29B8 /* OFMutableArray.m in Sources */, 4B2B3E82140D430500EC2F7C /* OFMutableArray_adjacent.m in Sources */, Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -26,10 +26,11 @@ OFEnumerator.m \ OFGZIPStream.m \ OFHMAC.m \ OFIntrospection.m \ OFList.m \ + OFLocalization.m \ OFMapTable.m \ OFMD5Hash.m \ OFMessagePackExtension.m \ OFMutableArray.m \ OFMutableDictionary.m \ Index: src/OFApplication.m ================================================================== --- src/OFApplication.m +++ src/OFApplication.m @@ -18,21 +18,18 @@ #include #include #include -#ifdef HAVE_LANGINFO_H -# include -#endif #include #include #import "OFApplication.h" #import "OFString.h" #import "OFArray.h" #import "OFDictionary.h" -#import "OFSystemInfo.h" +#import "OFLocalization.h" #import "OFRunLoop.h" #import "OFRunLoop+Private.h" #import "OFThread.h" #import "OFThread+Private.h" @@ -70,11 +67,10 @@ #endif - (void)OF_run; @end static OFApplication *app = nil; -extern void of_system_info_parse_locale(char*); static void atexitHandler(void) { id delegate = [app delegate]; @@ -107,11 +103,11 @@ #ifdef OF_WINDOWS wchar_t **wargv, **wenvp; int wargc, si = 0; #endif - of_system_info_parse_locale(setlocale(LC_ALL, "")); + [[OFLocalization alloc] initWithLocale: setlocale(LC_ALL, "")]; if ([cls isSubclassOfClass: [OFApplication class]]) { fprintf(stderr, "FATAL ERROR:\n Class %s is a subclass of " "class OFApplication, but class\n %s was specified as " "application delegate!\n Most likely, you wanted to " @@ -246,15 +242,16 @@ } FreeEnvironmentStringsW(env0); #elif !defined(OF_IOS) if (env != NULL) { + const of_string_encoding_t encoding = + [OFLocalization encoding]; + for (; *env != NULL; env++) { OFString *key, *value; char *sep; - const of_string_encoding_t encoding = - [OFSystemInfo native8BitEncoding]; pool = objc_autoreleasePoolPush(); if ((sep = strchr(*env, '=')) == NULL) { fprintf(stderr, "Warning: Invalid " @@ -351,11 +348,11 @@ of_string_encoding_t encoding; _argc = argc; _argv = argv; - encoding = [OFSystemInfo native8BitEncoding]; + encoding = [OFLocalization encoding]; # ifndef OF_NINTENDO_DS if (*argc > 0) { # else if (__system_argv->argvMagic == ARGV_MAGIC && Index: src/OFFile.m ================================================================== --- src/OFFile.m +++ src/OFFile.m @@ -34,11 +34,11 @@ # include #endif #import "OFFile.h" #import "OFString.h" -#import "OFSystemInfo.h" +#import "OFLocalization.h" #import "OFInitializationFailedException.h" #import "OFInvalidArgumentException.h" #import "OFOpenItemFailedException.h" #import "OFOutOfRangeException.h" @@ -164,15 +164,15 @@ #if defined(OF_WINDOWS) if ((_fd = _wopen([path UTF16String], flags, DEFAULT_MODE)) == -1) #elif defined(OF_HAVE_OFF64_T) - if ((_fd = open64([path cStringWithEncoding: [OFSystemInfo - native8BitEncoding]], flags, DEFAULT_MODE)) == -1) + if ((_fd = open64([path cStringWithEncoding: + [OFLocalization encoding]], flags, DEFAULT_MODE)) == -1) #else - if ((_fd = open([path cStringWithEncoding: [OFSystemInfo - native8BitEncoding]], flags, DEFAULT_MODE)) == -1) + if ((_fd = open([path cStringWithEncoding: + [OFLocalization encoding]], flags, DEFAULT_MODE)) == -1) #endif @throw [OFOpenItemFailedException exceptionWithPath: path mode: mode errNo: errno]; Index: src/OFFileManager.m ================================================================== --- src/OFFileManager.m +++ src/OFFileManager.m @@ -32,10 +32,11 @@ #import "OFFile.h" #import "OFString.h" #import "OFArray.h" #import "OFDate.h" #import "OFSystemInfo.h" +#import "OFLocalization.h" #ifdef OF_HAVE_THREADS # import "OFMutex.h" #endif @@ -97,15 +98,15 @@ of_stat(OFString *path, of_stat_t *buffer) { #if defined(OF_WINDOWS) return _wstat64([path UTF16String], buffer); #elif defined(OF_HAVE_OFF64_T) - return stat64([path cStringWithEncoding: - [OFSystemInfo native8BitEncoding]], buffer); + return stat64([path cStringWithEncoding: [OFLocalization encoding]], + buffer); #else - return stat([path cStringWithEncoding: - [OFSystemInfo native8BitEncoding]], buffer); + return stat([path cStringWithEncoding: [OFLocalization encoding]], + buffer); #endif } int of_lstat(OFString *path, of_stat_t *buffer) @@ -112,23 +113,23 @@ { #if defined(OF_WINDOWS) return _wstat64([path UTF16String], buffer); #elif defined(HAVE_LSTAT) # ifdef OF_HAVE_OFF64_T - return lstat64([path cStringWithEncoding: - [OFSystemInfo native8BitEncoding]], buffer); + return lstat64([path cStringWithEncoding: [OFLocalization encoding]], + buffer); # else - return lstat([path cStringWithEncoding: - [OFSystemInfo native8BitEncoding]], buffer); + return lstat([path cStringWithEncoding: [OFLocalization encoding]], + buffer); # endif #else # ifdef OF_HAVE_OFF64_T - return stat64([path cStringWithEncoding: - [OFSystemInfo native8BitEncoding]], buffer); + return stat64([path cStringWithEncoding: [OFLocalization encoding]], + buffer); # else - return stat([path cStringWithEncoding: - [OFSystemInfo native8BitEncoding]], buffer); + return stat([path cStringWithEncoding: [OFLocalization encoding]], + buffer); # endif #endif } @implementation OFFileManager @@ -180,11 +181,11 @@ @try { #ifndef OF_WINDOWS ret = [OFString stringWithCString: buffer - encoding: [OFSystemInfo native8BitEncoding]]; + encoding: [OFLocalization encoding]]; #else ret = [OFString stringWithUTF16String: buffer]; #endif } @finally { free(buffer); @@ -261,11 +262,11 @@ { if (path == nil) @throw [OFInvalidArgumentException exception]; #ifndef OF_WINDOWS - if (mkdir([path cStringWithEncoding: [OFSystemInfo native8BitEncoding]], + if (mkdir([path cStringWithEncoding: [OFLocalization encoding]], DIR_MODE) != 0) #else if (_wmkdir([path UTF16String]) != 0) #endif @throw [OFCreateDirectoryFailedException @@ -320,11 +321,11 @@ files = [OFMutableArray array]; #ifndef OF_WINDOWS DIR *dir; - encoding = [OFSystemInfo native8BitEncoding]; + encoding = [OFLocalization encoding]; if ((dir = opendir([path cStringWithEncoding: encoding])) == NULL) @throw [OFOpenItemFailedException exceptionWithPath: path errNo: errno]; @@ -431,12 +432,11 @@ { if (path == nil) @throw [OFInvalidArgumentException exception]; #ifndef OF_WINDOWS - if (chdir([path cStringWithEncoding: - [OFSystemInfo native8BitEncoding]]) != 0) + if (chdir([path cStringWithEncoding: [OFLocalization encoding]]) != 0) #else if (_wchdir([path UTF16String]) != 0) #endif @throw [OFChangeCurrentDirectoryPathFailedException exceptionWithPath: path @@ -522,11 +522,11 @@ { if (path == nil) @throw [OFInvalidArgumentException exception]; # ifndef OF_WINDOWS - if (chmod([path cStringWithEncoding: [OFSystemInfo native8BitEncoding]], + if (chmod([path cStringWithEncoding: [OFLocalization encoding]], permissions) != 0) # else if (_wchmod([path UTF16String], permissions) != 0) # endif @throw [OFChangePermissionsFailedException @@ -552,12 +552,11 @@ # ifdef OF_HAVE_THREADS [passwdMutex lock]; @try { # endif - of_string_encoding_t encoding = - [OFSystemInfo native8BitEncoding]; + of_string_encoding_t encoding = [OFLocalization encoding]; if (owner != NULL) { struct passwd *passwd = getpwuid(s.st_uid); *owner = [OFString stringWithCString: passwd->pw_name @@ -586,11 +585,11 @@ of_string_encoding_t encoding; if (path == nil || (owner == nil && group == nil)) @throw [OFInvalidArgumentException exception]; - encoding = [OFSystemInfo native8BitEncoding]; + encoding = [OFLocalization encoding]; # ifdef OF_HAVE_THREADS [passwdMutex lock]; @try { # endif @@ -798,11 +797,11 @@ exceptionWithSourcePath: source destinationPath: destination errNo: EEXIST]; #ifndef OF_WINDOWS - encoding = [OFSystemInfo native8BitEncoding]; + encoding = [OFLocalization encoding]; if (rename([source cStringWithEncoding: encoding], [destination cStringWithEncoding: encoding]) != 0) { #else if (_wrename([source UTF16String], [destination UTF16String]) != 0) { @@ -882,21 +881,21 @@ objc_autoreleasePoolPop(pool2); } #ifndef OF_WINDOWS if (rmdir([path cStringWithEncoding: - [OFSystemInfo native8BitEncoding]]) != 0) + [OFLocalization encoding]]) != 0) #else if (_wrmdir([path UTF16String]) != 0) #endif @throw [OFRemoveItemFailedException exceptionWithPath: path errNo: errno]; } else { #ifndef OF_WINDOWS if (unlink([path cStringWithEncoding: - [OFSystemInfo native8BitEncoding]]) != 0) + [OFLocalization encoding]]) != 0) #else if (_wunlink([path UTF16String]) != 0) #endif @throw [OFRemoveItemFailedException exceptionWithPath: path @@ -915,11 +914,11 @@ if (source == nil || destination == nil) @throw [OFInvalidArgumentException exception]; pool = objc_autoreleasePoolPush(); - encoding = [OFSystemInfo native8BitEncoding]; + encoding = [OFLocalization encoding]; if (link([source cStringWithEncoding: encoding], [destination cStringWithEncoding: encoding]) != 0) @throw [OFLinkFailedException exceptionWithSourcePath: source @@ -958,11 +957,11 @@ if (source == nil || destination == nil) @throw [OFInvalidArgumentException exception]; pool = objc_autoreleasePoolPush(); - encoding = [OFSystemInfo native8BitEncoding]; + encoding = [OFLocalization encoding]; if (symlink([source cStringWithEncoding: encoding], [destination cStringWithEncoding: encoding]) != 0) @throw [OFCreateSymbolicLinkFailedException exceptionWithSourcePath: source @@ -1004,11 +1003,11 @@ of_string_encoding_t encoding; if (path == nil) @throw [OFInvalidArgumentException exception]; - encoding = [OFSystemInfo native8BitEncoding]; + encoding = [OFLocalization encoding]; length = readlink([path cStringWithEncoding: encoding], destination, PATH_MAX); if (length < 0) @throw [OFStatItemFailedException exceptionWithPath: path ADDED src/OFLocalization.h Index: src/OFLocalization.h ================================================================== --- src/OFLocalization.h +++ src/OFLocalization.h @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 + * Jonathan Schleifer + * + * 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. + */ + +#import "OFObject.h" +#import "OFString.h" + +OF_ASSUME_NONNULL_BEGIN + +/*! @file */ + +#define OF_LOCALIZED(ID, ...) \ + [[OFLocalization sharedLocalization] \ + localizedStringForID: ID \ + fallback: __VA_ARGS__, nil] + +/*! + * @class OFLocalization OFLocalization.h ObjFW/OFLocalization.h + * + * @brief A class for querying the locale and retrieving localized strings. + */ +@interface OFLocalization: OFObject +{ + OFString *_language; + OFString *_territory; + of_string_encoding_t _encoding; + OFString *_decimalPoint; +} + +/** + * The language of the locale. + * + * If the language is unknown, it is `nil`. + */ +@property OF_NULLABLE_PROPERTY (readonly, copy) OFString *language; + +/*! + * The territory of the locale. + * + * If the territory is unknown, it is `nil`. + */ +@property OF_NULLABLE_PROPERTY (readonly, copy) OFString *territory; + +/*! + * The native 8-bit string encoding for the locale. + * + * This is useful to encode strings correctly for passing them to operating + * system calls. + * + * If the native 8-bit encoding is unknown, UTF-8 is assumed. + */ +@property (readonly) of_string_encoding_t encoding; + +/*! + * The decimal point of the system's locale. + */ +@property (readonly, copy) OFString *decimalPoint; + +/*! + * @brief Returns the shared OFLocalization instance. + * + * @warning If you don't use @ref OFApplication, this might be `nil`! In this + * case, you need to manually allocate an instance and call + * @ref initWithLocale: once, passing the locale used (as would be + * returned by `setlocale()`). + * + * @return The shared OFLocalization instance + */ ++ (instancetype)sharedLocalization; + +/** + * @brief Returns the language of the locale. + * + * If the language is unknown, `nil` is returned. + * + * @return The language of the locale. + */ ++ (nullable OFString*)language; + +/*! + * @brief Returns the territory of the locale. + * + * If the territory is unknown, `nil` is returned. + * + * @return The territory of the locale. + */ ++ (nullable OFString*)territory; + +/*! + * @brief Returns the native 8-bit string encoding for the locale. + * + * This is useful to encode strings correctly for passing them to operating + * system calls. + * + * If the native 8-bit encoding is unknown, UTF-8 is assumed. + * + * @return The native 8-bit string encoding for the locale + */ ++ (of_string_encoding_t)encoding; + +/*! + * @brief Returns the decimal point of the system's locale. + * + * @return The decimal point of the system's locale + */ ++ (OFString*)decimalPoint; + +/*! + * @brief Initializes the OFLocalization singleton with the specified locale. + * + * @warning You should never call this yourself, except if you do not use + * @ref OFApplication. In this case, you need to allocate exactly one + * instance of OFLocalization, which will be come the singleton, and + * call this method. + * + * @param locale The locale used, as returned from `setlocale()` + */ +- initWithLocale: (char*)locale; + +/*! + * @brief Returns the localized string for the specified ID, using the fallback + * string if it cannot be looked up or is missing. + * + * @note This takes a variadic argument, terminated by `nil`, that consists of + * pairs of variable names and variable values, which will be replaced + * inside the localized string. For example, you can pass + * `@"name", @"foo", nil`, causing `%[name]` to be replaced with `foo` in + * the localized string. + * + * @note Generally, you want to use @ref OF_LOCALIZED instead, which also takes + * care of the `nil` sentinel automatically. + * + * @param ID The ID for the localized string + * @param fallback The fallback to use in case the localized string cannot be + * looked up or is missing + * @return The localized string + */ +- (OFString*)localizedStringForID: (OFConstantString*)ID + fallback: (OFConstantString*)fallback, ... OF_SENTINEL; +/** + * @brief Returns the localized string for the specified ID, using the fallback + * string if it cannot be looked up or is missing. + * + * @note This takes a variadic argument, terminated by `nil` and passed as + * va_list, that consists of pairs of variable names and variable values, + * which will be replaced inside the localized string. For example, you + * can pass `@"name", @"foo", nil`, causing `%[name]` to be replaced with + * `foo` in the localized string. + * + * @note Generally, you want to use @ref OF_LOCALIZED instead, which also takes + * care of the `nil` sentinel automatically. + * + * @param ID The ID for the localized string + * @param fallback The fallback to use in case the localized string cannot be + * looked up or is missing + * @param arguments A va_list of arguments, consisting of pairs of variable + * names and values to replace in the localized string, + * terminated with `nil` + * @return The localized string + */ +- (OFString*)localizedStringForID: (OFConstantString*)ID + fallback: (OFConstantString*)fallback + arguments: (va_list)arguments; +@end + +OF_ASSUME_NONNULL_END ADDED src/OFLocalization.m Index: src/OFLocalization.m ================================================================== --- src/OFLocalization.m +++ src/OFLocalization.m @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 + * Jonathan Schleifer + * + * 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 + +#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 Index: src/OFObject.m ================================================================== --- src/OFObject.m +++ src/OFObject.m @@ -24,11 +24,11 @@ #include #import "OFObject.h" #import "OFArray.h" -#import "OFSystemInfo.h" +#import "OFLocalization.h" #import "OFTimer.h" #import "OFRunLoop.h" #import "OFThread.h" #import "OFAllocFailedException.h" @@ -102,11 +102,11 @@ static void uncaughtExceptionHandler(id exception) { OFString *description = [exception description]; OFArray *backtrace = nil; - of_string_encoding_t encoding = [OFSystemInfo native8BitEncoding]; + of_string_encoding_t encoding = [OFLocalization encoding]; fprintf(stderr, "\nRuntime error: Unhandled exception:\n%s\n", [description cStringWithEncoding: encoding]); if ([exception respondsToSelector: @selector(backtrace)]) @@ -198,11 +198,11 @@ const char* _NSPrintForDebugger(id object) { return [[object description] - cStringWithEncoding: [OFSystemInfo native8BitEncoding]]; + cStringWithEncoding: [OFLocalization encoding]]; } /* References for static linking */ void _references_to_categories_of_OFObject(void) Index: src/OFPlugin.m ================================================================== --- src/OFPlugin.m +++ src/OFPlugin.m @@ -23,22 +23,22 @@ # include #endif #import "OFPlugin.h" #import "OFString.h" -#import "OFSystemInfo.h" +#import "OFLocalization.h" #import "OFInitializationFailedException.h" typedef OFPlugin* (*init_plugin_t)(void); of_plugin_handle_t of_dlopen(OFString *path, int flags) { #ifndef OF_WINDOWS - return dlopen([path cStringWithEncoding: - [OFSystemInfo native8BitEncoding]], flags); + return dlopen([path cStringWithEncoding: [OFLocalization encoding]], + flags); #else if (path == nil) return GetModuleHandle(NULL); return LoadLibraryW([path UTF16String]); Index: src/OFProcess.m ================================================================== --- src/OFProcess.m +++ src/OFProcess.m @@ -39,11 +39,11 @@ #import "OFProcess.h" #import "OFString.h" #import "OFArray.h" #import "OFDictionary.h" #import "OFDataArray.h" -#import "OFSystemInfo.h" +#import "OFLocalization.h" #import "OFInitializationFailedException.h" #import "OFOutOfRangeException.h" #import "OFReadFailedException.h" #import "OFWriteFailedException.h" @@ -147,12 +147,11 @@ if (pipe(_readPipe) != 0 || pipe(_writePipe) != 0) @throw [OFInitializationFailedException exceptionWithClass: [self class]]; - path = [program cStringWithEncoding: - [OFSystemInfo native8BitEncoding]]; + path = [program cStringWithEncoding: [OFLocalization encoding]]; [self OF_getArgV: &argv forProgramName: programName andArguments: arguments]; @try { @@ -348,11 +347,11 @@ of_string_encoding_t encoding; *argv = [self allocMemoryWithSize: sizeof(char*) count: count + 2]; - encoding = [OFSystemInfo native8BitEncoding]; + encoding = [OFLocalization encoding]; (*argv)[0] = (char*)[programName cStringWithEncoding: encoding]; for (i = 0; i < count; i++) (*argv)[i + 1] = @@ -369,11 +368,11 @@ of_string_encoding_t encoding; if (environment == nil) return NULL; - encoding = [OFSystemInfo native8BitEncoding]; + encoding = [OFLocalization encoding]; count = [environment count]; envp = [self allocMemoryWithSize: sizeof(char*) count: count + 1]; Index: src/OFString.h ================================================================== --- src/OFString.h +++ src/OFString.h @@ -46,10 +46,14 @@ /*! * @brief The encoding of a string. */ typedef enum of_string_encoding_t { + /* + * UTF-8 *has* to be 0, so that if the @ref OFLocalization singleton is + * `nil`, `[OFLocalization encoding]` returns UTF-8. + */ /*! UTF-8 */ OF_STRING_ENCODING_UTF_8, /*! ASCII */ OF_STRING_ENCODING_ASCII, /*! ISO 8859-1 */ Index: src/OFString.m ================================================================== --- src/OFString.m +++ src/OFString.m @@ -35,11 +35,11 @@ #import "OFString_UTF8.h" #import "OFString_UTF8+Private.h" #import "OFArray.h" #import "OFDictionary.h" #import "OFDataArray.h" -#import "OFSystemInfo.h" +#import "OFLocalization.h" #ifdef OF_HAVE_FILES # import "OFFile.h" #endif #import "OFURL.h" #ifdef OF_HAVE_SOCKETS @@ -2359,11 +2359,11 @@ #else /* * If we have no strtof_l, we have no other choice but to replace "." * with the locale's decimal point. */ - OFString *decimalPoint = [OFSystemInfo decimalPoint]; + OFString *decimalPoint = [OFLocalization decimalPoint]; const char *UTF8String = [[self stringByReplacingOccurrencesOfString: @"." withString: decimalPoint] UTF8String]; #endif char *endPointer = NULL; @@ -2400,11 +2400,11 @@ #else /* * If we have no strtod_l, we have no other choice but to replace "." * with the locale's decimal point. */ - OFString *decimalPoint = [OFSystemInfo decimalPoint]; + OFString *decimalPoint = [OFLocalization decimalPoint]; const char *UTF8String = [[self stringByReplacingOccurrencesOfString: @"." withString: decimalPoint] UTF8String]; #endif char *endPointer = NULL; Index: src/OFSystemInfo.h ================================================================== --- src/OFSystemInfo.h +++ src/OFSystemInfo.h @@ -39,47 +39,10 @@ * * @return The number of CPUs installed in the system */ + (size_t)numberOfCPUs; -/*! - * @brief Returns the native 8-bit string encoding of the operating system. - * - * This is useful to encode strings correctly for passing them to operating - * system calls. - * - * If the native 8-bit encoding is unknown, UTF-8 is assumed. - * - * @return The native 8-bit string encoding of the operating system - */ -+ (of_string_encoding_t)native8BitEncoding; - -/*! - * @brief Returns the language of the locale. - * - * If the language is unknown, nil is returned. - * - * @return The language of the locale. - */ -+ (OFString*)language; - -/*! - * @brief Returns the territory of the locale. - * - * If the territory is unknown, nil is returned. - * - * @return The territory of the locale. - */ -+ (OFString*)territory; - -/*! - * @brief Returns the decimal point in the system's locale. - * - * @return The decimal point in the system's locale - */ -+ (OFString*)decimalPoint; - /*! * @brief Returns the path where user data for the application can be stored. * * On Unix systems, this adheres to the XDG Base Directory specification.@n * On Mac OS X and iOS, it uses the `NSApplicationSupportDirectory` directory.@n Index: src/OFSystemInfo.m ================================================================== --- src/OFSystemInfo.m +++ src/OFSystemInfo.m @@ -22,11 +22,10 @@ #include /* include any libc header to get the libc defines */ #ifdef __GLIBC__ # undef __USE_XOPEN #endif -#include #include #include "platform.h" #ifdef OF_MAC_OS_X @@ -64,79 +63,10 @@ }; #endif static size_t pageSize; static size_t numberOfCPUs; -static of_string_encoding_t native8BitEncoding = OF_STRING_ENCODING_UTF_8; -static OFString *language = nil; -static OFString *territory = nil; -static OFString *decimalPoint = @"."; - -void -of_system_info_parse_locale(char *locale) -{ - if (locale == NULL) - return; - - 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) - native8BitEncoding = OF_STRING_ENCODING_UTF_8; - else if (strcmp(tmp, "ascii") == 0 || - strcmp(tmp, "us-ascii") == 0) - native8BitEncoding = OF_STRING_ENCODING_ASCII; - else if (strcmp(tmp, "iso8859-1") == 0 || - strcmp(tmp, "iso-8859-1") == 0) - native8BitEncoding = - OF_STRING_ENCODING_ISO_8859_1; - else if (strcmp(tmp, "iso8859-15") == 0 || - strcmp(tmp, "iso-8859-15") == 0) - native8BitEncoding = - OF_STRING_ENCODING_ISO_8859_15; - /* Windows uses a codepage */ - else if (strcmp(tmp, "1252") == 0) - native8BitEncoding = - 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]; - } @finally { - free(locale); - } - - decimalPoint = [[OFString alloc] - initWithCString: localeconv()->decimal_point - encoding: native8BitEncoding]; -} #if defined(OF_X86_64) || defined(OF_X86) static OF_INLINE struct x86_regs OF_CONST_FUNC x86_cpuid(uint32_t eax, uint32_t ecx) { @@ -211,30 +141,10 @@ + (size_t)numberOfCPUs { return numberOfCPUs; } -+ (of_string_encoding_t)native8BitEncoding -{ - return native8BitEncoding; -} - -+ (OFString*)language -{ - return language; -} - -+ (OFString*)territory -{ - return territory; -} - -+ (OFString*)decimalPoint -{ - return decimalPoint; -} - + (OFString*)userDataPath { #if defined(OF_MAC_OS_X) || defined(OF_IOS) void *pool = objc_autoreleasePoolPush(); char pathC[PATH_MAX]; Index: src/ObjFW.h ================================================================== --- src/ObjFW.h +++ src/ObjFW.h @@ -98,12 +98,13 @@ #import "OFXMLElementBuilder.h" #import "OFMessagePackExtension.h" #import "OFApplication.h" -#import "OFOptionsParser.h" #import "OFSystemInfo.h" +#import "OFLocalization.h" +#import "OFOptionsParser.h" #import "OFTimer.h" #import "OFRunLoop.h" #import "OFAllocFailedException.h" #import "OFException.h" Index: src/exceptions/OFException.m ================================================================== --- src/exceptions/OFException.m +++ src/exceptions/OFException.m @@ -26,11 +26,11 @@ #endif #import "OFException.h" #import "OFString.h" #import "OFArray.h" -#import "OFSystemInfo.h" +#import "OFLocalization.h" #import "OFInitializationFailedException.h" #import "OFLockFailedException.h" #import "OFUnlockFailedException.h" @@ -188,21 +188,21 @@ #ifdef HAVE_STRERROR_R if (strerror_r(errNo, buffer, 256) != 0) return @"Unknown error (strerror_r failed)"; ret = [OFString stringWithCString: buffer - encoding: [OFSystemInfo native8BitEncoding]]; + encoding: [OFLocalization encoding]]; #else # ifdef OF_HAVE_THREADS if (!of_mutex_lock(&mutex)) @throw [OFLockFailedException exception]; @try { # endif ret = [OFString stringWithCString: strerror(errNo) - encoding: [OFSystemInfo native8BitEncoding]]; + encoding: [OFLocalization encoding]]; # ifdef OF_HAVE_THREADS } @finally { if (!of_mutex_unlock(&mutex)) @throw [OFUnlockFailedException exception]; } Index: src/of_asprintf.m ================================================================== --- src/of_asprintf.m +++ src/of_asprintf.m @@ -31,11 +31,11 @@ #endif #include #import "OFString.h" -#import "OFSystemInfo.h" +#import "OFLocalization.h" #import "OFInitializationFailedException.h" #define MAX_SUBFORMAT_LEN 64 @@ -560,11 +560,11 @@ @try { OFMutableString *tmpStr = [OFMutableString stringWithUTF8String: tmp length: tmpLen]; OFString *decimalPoint = - [OFSystemInfo decimalPoint]; + [OFLocalization decimalPoint]; [tmpStr replaceOccurrencesOfString: decimalPoint withString: @"."]; if ([tmpStr UTF8StringLength] > INT_MAX) return false; tmpLen = (int)[tmpStr UTF8StringLength]; Index: utils/ofhttp/OFHTTP.m ================================================================== --- utils/ofhttp/OFHTTP.m +++ utils/ofhttp/OFHTTP.m @@ -28,10 +28,11 @@ #import "OFOptionsParser.h" #import "OFStdIOStream.h" #import "OFSystemInfo.h" #import "OFTCPSocket.h" #import "OFURL.h" +#import "OFLocalization.h" #import "OFAddressTranslationFailedException.h" #import "OFConnectionFailedException.h" #import "OFHTTPRequestFailedException.h" #import "OFInvalidFormatException.h" @@ -70,16 +71,17 @@ OF_APPLICATION_DELEGATE(OFHTTP) static void help(OFStream *stream, bool full, int status) { - [of_stderr writeFormat: - @"Usage: %@ -[cehHmoOPqv] url1 [url2 ...]\n", - [OFApplication programName]]; + [of_stderr writeString: + OF_LOCALIZED(@"usage", + @"Usage: %[prog] -[cehHmoOPqv] url1 [url2 ...]\n", + @"prog", [OFApplication programName])]; if (full) - [stream writeString: + [stream writeString: OF_LOCALIZED(@"full_usage", @"\nOptions:\n " @"-b --body " @" Specify the file to send as body\n " @"-c --continue " @" Continue download of existing file\n " @@ -98,11 +100,11 @@ @"-P --proxy " @" Specify SOCKS5 proxy\n " @"-q --quiet " @" Quiet mode (no output, except errors)\n " @"-v --verbose " - @" Verbose mode (print headers)\n"]; + @" Verbose mode (print headers)\n")]; [OFApplication terminateWithStatus: status]; } @implementation OFHTTP @@ -133,13 +135,13 @@ { size_t pos = [header rangeOfString: @":"].location; OFString *name, *value; if (pos == OF_NOT_FOUND) { - [of_stderr writeFormat: @"%@: Headers must to be in format " - "name:value!\n", - [OFApplication programName]]; + [of_stderr writeString: OF_LOCALIZED(@"invalid_input_header", + @"%[prog]: Headers must to be in format name:value!\n", + @"prog", [OFApplication programName])]; [OFApplication terminateWithStatus: 1]; } name = [header substringWithRange: of_range(0, pos)]; name = [name stringByDeletingEnclosingWhitespaces]; @@ -183,13 +185,14 @@ else if ([method isEqual: @"DELETE"]) _method = OF_HTTP_REQUEST_METHOD_DELETE; else if ([method isEqual: @"TRACE"]) _method = OF_HTTP_REQUEST_METHOD_TRACE; else { - [of_stderr writeFormat: @"%@: Invalid request method %@!\n", - [OFApplication programName], - method]; + [of_stderr writeString: OF_LOCALIZED(@"invalid_input_method", + @"%[prog]: Invalid request method %[method]!\n", + @"prog", [OFApplication programName], + @"method", method)]; [OFApplication terminateWithStatus: 1]; } objc_autoreleasePoolPop(pool); } @@ -214,13 +217,13 @@ @throw [OFOutOfRangeException exception]; [OFTCPSocket setSOCKS5Host: host]; [OFTCPSocket setSOCKS5Port: (uint16_t)port]; } @catch (OFInvalidFormatException *e) { - [of_stderr writeFormat: @"%@: Proxy must to be in format " - "host:port!\n", - [OFApplication programName]]; + [of_stderr writeString: OF_LOCALIZED(@"invalid_input_proxy", + @"%[prog]: Proxy must to be in format host:port!\n", + @"prog", [OFApplication programName])]; [OFApplication terminateWithStatus: 1]; } } - (void)applicationDidFinishLaunching @@ -261,41 +264,56 @@ case 'P': [self setProxy: [optionsParser argument]]; break; case ':': if ([optionsParser lastLongOption] != nil) - [of_stderr writeFormat: - @"%@: Argument for option --%@ missing\n", - [OFApplication programName], - [optionsParser lastLongOption]]; - else - [of_stderr writeFormat: - @"%@: Argument for option -%C missing\n", - [OFApplication programName], + [of_stderr writeString: + OF_LOCALIZED(@"long_argument_missing", + @"%[prog]: Argument for option --%[opt] " + "missing\n" + @"prog", [OFApplication programName], + @"opt", [optionsParser lastLongOption])]; + else { + OFString *optStr = [OFString + stringWithFormat: @"%c", [optionsParser lastOption]]; + [of_stderr writeString: + OF_LOCALIZED(@"argument_missing", + @"%[prog]: Argument for option -%[opt] " + "missing\n", + @"prog", [OFApplication programName], + @"opt", optStr)]; + } [OFApplication terminateWithStatus: 1]; break; case '=': - [of_stderr writeFormat: @"%@: Option --%@ takes no " - @"argument\n", - [OFApplication programName], - [optionsParser lastLongOption]]; + [of_stderr writeString: + OF_LOCALIZED(@"takes_no_argument", + @"%[prog]: Option --%[opt] takes no argument\n", + @"prog", [OFApplication programName], + @"opt", [optionsParser lastLongOption])]; [OFApplication terminateWithStatus: 1]; break; case '?': if ([optionsParser lastLongOption] != nil) - [of_stderr writeFormat: - @"%@: Unknown option: --%@\n", - [OFApplication programName], - [optionsParser lastLongOption]]; - else - [of_stderr writeFormat: - @"%@: Unknown option: -%C\n", - [OFApplication programName], + [of_stderr writeString: + OF_LOCALIZED(@"unknown_long_option", + @"%[prog]: Unknown option: --%[opt]\n", + @"prog", [OFApplication programName], + @"opt", [optionsParser lastLongOption])]; + else { + OFString *optStr = [OFString + stringWithFormat: @"%c", [optionsParser lastOption]]; + [of_stderr writeString: + OF_LOCALIZED(@"unknown_option", + @"%[prog]: Unknown option: -%[opt]\n", + @"prog", [OFApplication programName], + @"opt", optStr)]; + } [OFApplication terminateWithStatus: 1]; break; } } @@ -305,21 +323,23 @@ if ([_URLs count] < 1) help(of_stderr, false, 1); if (_quiet && _verbose) { - [of_stderr writeFormat: @"%@: -q / --quiet and -v / --verbose " - @"are mutually exclusive!\n", - [OFApplication programName]]; + [of_stderr writeString: OF_LOCALIZED(@"quiet_xor_verbose", + @"%[prog]: -q / --quiet and -v / --verbose are mutually " + @"exclusive!\n", + @"prog", [OFApplication programName])]; [OFApplication terminateWithStatus: 1]; } if (_outputPath != nil && [_URLs count] > 1) { - [of_stderr writeFormat: @"%@: Cannot use -o / --output when " - @"more than one URL has been " - @"specified!\n", - [OFApplication programName]]; + [of_stderr writeString: + OF_LOCALIZED(@"output_only_with_one_url", + @"%[prog]: Cannot use -o / --output when more than one URL " + @"has been specified!\n", + @"prog", [OFApplication programName])]; [OFApplication terminateWithStatus: 1]; } [self performSelector: @selector(downloadNextURL) afterDelay: 0]; @@ -365,62 +385,82 @@ response = [_HTTPClient performRequest: request]; } @catch (OFAddressTranslationFailedException *e) { if (!_quiet) [of_stdout writeString: @"\n"]; - [of_stderr writeFormat: @"%@: Failed to download <%@>!\n" - @" Address translation failed: %@\n", - [OFApplication programName], - [[request URL] string], e]; + [of_stderr writeString: + OF_LOCALIZED(@"download_failed_address_translation", + @"%[prog]: Failed to download <%[url]>!\n" + @" Address translation failed: %[exception]\n", + @"prog", [OFApplication programName], + @"url", [[request URL] string], + @"exception", e)]; } @catch (OFConnectionFailedException *e) { if (!_quiet) [of_stdout writeString: @"\n"]; - [of_stderr writeFormat: @"%@: Failed to download <%@>!\n" - @" Connection failed: %@\n", - [OFApplication programName], - [[request URL] string], e]; + [of_stderr writeString: + OF_LOCALIZED(@"download_failed_connection_failed", + @"%[prog]: Failed to download <%[url]>!\n" + @" Connection failed: %[exception]\n", + @"prog", [OFApplication programName], + @"url", [[request URL] string], + @"exception", e)]; } @catch (OFInvalidServerReplyException *e) { if (!_quiet) [of_stdout writeString: @"\n"]; - [of_stderr writeFormat: @"%@: Failed to download <%@>!\n" - @" Invalid server reply!\n", - [OFApplication programName], - [[request URL] string]]; + [of_stderr writeString: + OF_LOCALIZED(@"download_failed_invalid_server_reply", + @"%[prog]: Failed to download <%[url]>!\n" + @" Invalid server reply!\n", + @"prog", [OFApplication programName], + @"url", [[request URL] string])]; } @catch (OFUnsupportedProtocolException *e) { if (!_quiet) [of_stdout writeString: @"\n"]; - [of_stderr writeFormat: @"%@: No SSL library loaded!\n" - @" In order to download via https, " - @"you need to preload an SSL library " - @"for ObjFW\n such as ObjOpenSSL!\n", - [OFApplication programName]]; + [of_stderr writeString: OF_LOCALIZED(@"no_ssl_library", + @"%[prog]: No SSL library loaded!\n" + @" In order to download via https, you need to preload an " + @"SSL library for ObjFW\n" + "such as ObjOpenSSL!\n", + @"prog", [OFApplication programName])]; } @catch (OFReadOrWriteFailedException *e) { - OFString *action = @"Read or write"; + OFString *error = OF_LOCALIZED( + @"download_failed_read_or_write_failed_any", + @"Read or write failed"); if (!_quiet) [of_stdout writeString: @"\n"]; if ([e isKindOfClass: [OFReadFailedException class]]) - action = @"Read"; + error = OF_LOCALIZED( + @"download_failed_read_or_write_failed_read", + @"Read failed"); else if ([e isKindOfClass: [OFWriteFailedException class]]) - action = @"Write"; + error = OF_LOCALIZED( + @"download_failed_read_or_write_failed_write", + @"Write failed"); - [of_stderr writeFormat: @"%@: Failed to download <%@>!\n" - @" %@ failed: %@\n", - [OFApplication programName], - [[request URL] string], action, e]; + [of_stderr writeString: + OF_LOCALIZED(@"download_failed_read_or_write_failed", + @"%[prog]: Failed to download <%[url]>!\n" + @" %[error]: %[exception]\n", + @"prog", [OFApplication programName], + @"url", [[request URL] string], + @"error", error, + @"exception", e)]; } @catch (OFHTTPRequestFailedException *e) { if (!_quiet) [of_stdout writeFormat: @" ➜ %d\n", [[e response] statusCode]]; - [of_stderr writeFormat: @"%@: Failed to download <%@>!\n", - [OFApplication programName], - [[request URL] string]]; + [of_stderr writeString: OF_LOCALIZED(@"download_failed", + @"%[prog]: Failed to download <%[url]>!\n", + @"prog", [OFApplication programName], + @"url", [[request URL] string])]; } if (!_quiet && response != nil) [of_stdout writeFormat: @" ➜ %d\n", [response statusCode]]; @@ -562,12 +602,16 @@ if (!_quiet) [of_stdout writeString: @"\n Error!\n"]; URL = [_URLs objectAtIndex: _URLIndex - 1]; - [of_stderr writeFormat: @"%@: Failed to download <%@>: %@\n", - [OFApplication programName], URL, e]; + [of_stderr writeString: + OF_LOCALIZED(@"download_failed_exception", + @"%[prog]: Failed to download <%[url]>: %[exception]\n", + @"prog", [OFApplication programName], + @"url", URL, + @"exception", e)]; _errorCode = 1; goto next; } @@ -584,11 +628,12 @@ [_progressBar draw]; [_progressBar release]; _progressBar = nil; if (!_quiet) - [of_stdout writeString: @"\n Done!\n"]; + [of_stdout writeString: + OF_LOCALIZED(@"download_done", @"\n Done!\n")]; goto next; } return true; @@ -622,23 +667,25 @@ @try { URLString = [_URLs objectAtIndex: _URLIndex++]; URL = [OFURL URLWithString: URLString]; } @catch (OFInvalidFormatException *e) { - [of_stderr writeFormat: @"%@: Invalid URL: <%@>!\n", - [OFApplication programName], - URLString]; + [of_stderr writeString: OF_LOCALIZED(@"invalid_url", + @"%[prog]: Invalid URL: <%[url]>!\n", + @"prog", [OFApplication programName], + @"url", URLString)]; _errorCode = 1; goto next; } if (![[URL scheme] isEqual: @"http"] && ![[URL scheme] isEqual: @"https"]) { - [of_stderr writeFormat: @"%@: Invalid scheme: <%@:>!\n", - [OFApplication programName], - URLString]; + [of_stderr writeString: OF_LOCALIZED(@"invalid_scheme", + @"%[prog]: Invalid scheme: <%[scheme]:>!\n", + @"prog", [OFApplication programName], + @"scheme", URLString)]; _errorCode = 1; goto next; } @@ -706,62 +753,87 @@ if (lengthString != nil) _length = [lengthString decimalValue]; if (!_quiet) { if (type == nil) - type = @"unknown"; + type = OF_LOCALIZED(@"type_unknown", @"unknown"); if (_length >= 0) { - if (_resumedFrom + _length >= GIBIBYTE) + if (_resumedFrom + _length >= GIBIBYTE) { lengthString = [OFString stringWithFormat: - @"%,.2f GiB", + @"%,.2f", (float)(_resumedFrom + _length) / GIBIBYTE]; - else if (_resumedFrom + _length >= MEBIBYTE) + lengthString = OF_LOCALIZED(@"size_gib", + @"%[num] GiB", + @"num", lengthString); + } else if (_resumedFrom + _length >= MEBIBYTE) { lengthString = [OFString stringWithFormat: - @"%,.2f MiB", + @"%,.2f", (float)(_resumedFrom + _length) / MEBIBYTE]; - else if (_resumedFrom + _length >= KIBIBYTE) + lengthString = OF_LOCALIZED(@"size_mib", + @"%[num] MiB", + @"num", lengthString); + } else if (_resumedFrom + _length >= KIBIBYTE) { lengthString = [OFString stringWithFormat: - @"%,.2f KiB", + @"%,.2f", (float)(_resumedFrom + _length) / KIBIBYTE]; - else + lengthString = OF_LOCALIZED(@"size_kib", + @"%[num] KiB", + @"num", lengthString); + } else { lengthString = [OFString stringWithFormat: - @"%jd bytes", _resumedFrom + _length]; + @"%jd", _resumedFrom + _length]; + lengthString = OF_LOCALIZED(@"size_bytes", + @"%[num] bytes", + @"num", lengthString); + } } else - lengthString = @"unknown"; - - [of_stdout writeFormat: @" Name: %@\n", fileName]; + lengthString = + OF_LOCALIZED(@"size_unknown", @"unknown"); if (_verbose) { void *pool = objc_autoreleasePoolPush(); OFDictionary OF_GENERIC(OFString*, OFString*) *headers = [response headers]; OFEnumerator *keyEnumerator = [headers keyEnumerator]; OFEnumerator *objectEnumerator = [headers objectEnumerator]; OFString *key, *object; + + [of_stdout writeString: OF_LOCALIZED(@"info_name_nopad", + @" Name: %[name]\n", + @"name", fileName)]; while ((key = [keyEnumerator nextObject]) != nil && (object = [objectEnumerator nextObject]) != nil) [of_stdout writeFormat: @" %@: %@\n", key, object]; objc_autoreleasePoolPop(pool); } else { - [of_stdout writeFormat: @" Type: %@\n", type]; - [of_stdout writeFormat: @" Size: %@\n", lengthString]; + [of_stdout writeString: OF_LOCALIZED(@"info_name", + @" Name: %[name]\n", + @"name", fileName)]; + [of_stdout writeString: OF_LOCALIZED(@"info_type", + @" Type: %[type]\n", + @"type", type)]; + [of_stdout writeString: OF_LOCALIZED(@"info_size", + @" Size: %[size]\n", + @"size", lengthString)]; } } if ([_outputPath isEqual: @"-"]) _output = of_stdout; else { if (!_continue && !_force && [fileManager fileExistsAtPath: fileName]) { - [of_stderr writeFormat: - @"%@: File %@ already exists!\n", - [OFApplication programName], fileName]; + [of_stderr writeString: + OF_LOCALIZED(@"ouput_already_exists", + @"%[prog]: File %[filename] already exists!\n", + @"prog", [OFApplication programName], + @"filename", fileName)]; _errorCode = 1; goto next; } @@ -769,13 +841,17 @@ OFString *mode = ([response statusCode] == 206 ? @"ab" : @"wb"); _output = [[OFFile alloc] initWithPath: fileName mode: mode]; } @catch (OFOpenItemFailedException *e) { - [of_stderr writeFormat: - @"%@: Failed to open file %@!\n", - [OFApplication programName], fileName]; + [of_stderr writeString: + OF_LOCALIZED(@"failed_to_open_output", + @"%[prog]: Failed to open file %[filename]: " + @"%[exception]\n", + @"prog", [OFApplication programName], + @"filename",fileName, + @"exception", e)]; _errorCode = 1; goto next; } } Index: utils/ofhttp/ProgressBar.m ================================================================== --- utils/ofhttp/ProgressBar.m +++ utils/ofhttp/ProgressBar.m @@ -19,10 +19,11 @@ #include #import "OFDate.h" #import "OFStdIOStream.h" #import "OFTimer.h" +#import "OFLocalization.h" #import "ProgressBar.h" #define GIBIBYTE (1024 * 1024 * 1024) #define MEBIBYTE (1024 * 1024) @@ -136,57 +137,105 @@ _ETA = timeInterval; } if (isinf(_ETA)) [of_stdout writeString: @"--:--:-- "]; - else if (_ETA >= 99 * 3600) - [of_stdout writeFormat: @"%,4.2f d ", _ETA / (24 * 3600)]; - else + else if (_ETA >= 99 * 3600) { + OFString *num = [OFString stringWithFormat: + @"%,4.2f", _ETA / (24 * 3600)]; + [of_stdout writeString: OF_LOCALIZED(@"eta_days", + @"%[num] d ", + @"num", num)]; + } else [of_stdout writeFormat: @"%2u:%02u:%02u ", (uint8_t)(_ETA / 3600), (uint8_t)(_ETA / 60) % 60, (uint8_t)_ETA % 60]; - if (_BPS >= GIBIBYTE) - [of_stdout writeFormat: @"%,7.2f GiB/s", _BPS / GIBIBYTE]; - else if (_BPS >= MEBIBYTE) - [of_stdout writeFormat: @"%,7.2f MiB/s", _BPS / MEBIBYTE]; - else if (_BPS >= KIBIBYTE) - [of_stdout writeFormat: @"%,7.2f KiB/s", _BPS / KIBIBYTE]; - else - [of_stdout writeFormat: @"%,7.2f B/s ", _BPS]; + if (_BPS >= GIBIBYTE) { + OFString *num = [OFString stringWithFormat: + @"%,7.2f", _BPS / GIBIBYTE]; + [of_stdout writeString: OF_LOCALIZED(@"progress_gibs", + @"%[num] GiB/s", + @"num", num)]; + } else if (_BPS >= MEBIBYTE) { + OFString *num = [OFString stringWithFormat: + @"%,7.2f", _BPS / MEBIBYTE]; + [of_stdout writeString: OF_LOCALIZED(@"progress_mibs", + @"%[num] MiB/s", + @"num", num)]; + } else if (_BPS >= KIBIBYTE) { + OFString *num = [OFString stringWithFormat: + @"%,7.2f", _BPS / KIBIBYTE]; + [of_stdout writeString: OF_LOCALIZED(@"progress_kibs", + @"%[num] KiB/s", + @"num", num)]; + } else { + OFString *num = [OFString stringWithFormat: + @"%,7.2f", _BPS]; + [of_stdout writeString: OF_LOCALIZED(@"progress_bps", + @"%[num] B/s ", + @"num", num)]; + } } - (void)_drawReceived { - if (_resumedFrom + _received >= GIBIBYTE) - [of_stdout writeFormat: - @"\r %,7.2f GiB ", - (float)(_resumedFrom + _received) / GIBIBYTE]; - else if (_resumedFrom + _received >= MEBIBYTE) - [of_stdout writeFormat: - @"\r %,7.2f MiB ", - (float)(_resumedFrom + _received) / MEBIBYTE]; - else if (_resumedFrom + _received >= KIBIBYTE) - [of_stdout writeFormat: - @"\r %,7.2f KiB ", - (float)(_resumedFrom + _received) / KIBIBYTE]; - else - [of_stdout writeFormat: - @"\r %jd bytes ", _resumedFrom + _received]; + if (_resumedFrom + _received >= GIBIBYTE) { + OFString *num = [OFString stringWithFormat: + @"%,7.2f", (float)(_resumedFrom + _received) / GIBIBYTE]; + [of_stdout writeString: OF_LOCALIZED(@"progress_gib", + @"\r %[num] GiB ", + @"num", num)]; + } else if (_resumedFrom + _received >= MEBIBYTE) { + OFString *num = [OFString stringWithFormat: + @"%,7.2f", (float)(_resumedFrom + _received) / MEBIBYTE]; + [of_stdout writeString: OF_LOCALIZED(@"progress_mib", + @"\r %[num] MiB ", + @"num", num)]; + } else if (_resumedFrom + _received >= KIBIBYTE) { + OFString *num = [OFString stringWithFormat: + @"%,7.2f", (float)(_resumedFrom + _received) / KIBIBYTE]; + [of_stdout writeString: OF_LOCALIZED(@"progress_kib", + @"\r %[num] KiB ", + @"num", num)]; + } else { + OFString *num = [OFString stringWithFormat: + @"%jd", _resumedFrom + _received]; + [of_stdout writeString: OF_LOCALIZED(@"progress_bytes", + @"\r %[num] bytes ", + @"num", num)]; + } if (_stopped) _BPS = (float)_received / -(float)[_startDate timeIntervalSinceNow]; - if (_BPS >= GIBIBYTE) - [of_stdout writeFormat: @"%,7.2f GiB/s", _BPS / GIBIBYTE]; - else if (_BPS >= MEBIBYTE) - [of_stdout writeFormat: @"%,7.2f MiB/s", _BPS / MEBIBYTE]; - else if (_BPS >= KIBIBYTE) - [of_stdout writeFormat: @"%,7.2f KiB/s", _BPS / KIBIBYTE]; - else - [of_stdout writeFormat: @"%,7.2f B/s ", _BPS]; + if (_BPS >= GIBIBYTE) { + OFString *num = [OFString stringWithFormat: + @"%,7.2f", _BPS / GIBIBYTE]; + [of_stdout writeString: OF_LOCALIZED(@"progress_gibs", + @"%[num] GiB/s", + @"num", num)]; + } else if (_BPS >= MEBIBYTE) { + OFString *num = [OFString stringWithFormat: + @"%,7.2f", _BPS / MEBIBYTE]; + [of_stdout writeString: OF_LOCALIZED(@"progress_mibs", + @"%[num] MiB/s", + @"num", num)]; + } else if (_BPS >= KIBIBYTE) { + OFString *num = [OFString stringWithFormat: + @"%,7.2f", _BPS / KIBIBYTE]; + [of_stdout writeString: OF_LOCALIZED(@"progress_kibs", + @"%[num] KiB/s", + @"num", num)]; + } else { + OFString *num = [OFString stringWithFormat: + @"%,7.2f", _BPS]; + [of_stdout writeString: OF_LOCALIZED(@"progress_bps", + @"%[num] B/s ", + @"num", num)]; + } } - (void)draw { if (_length > 0)