如何强制NSLocalizedString使用特定的语言
在iPhone上, NSLocalizedString
返回iPhone语言的string。 是否有可能强制NSLocalizedString
使用特定的语言使应用程序在不同的语言比设备?
NSLocalizedString()
及其变体访问NSLocalizedString()
的“AppleLanguages”键,以确定用户对首选语言的设置。 这将返回一组语言代码,第一个是用户为其电话设置的语言代码,如果资源不是首选语言,则会使用后续语言代码。 (在桌面上,用户可以在“系统偏好设置”中使用自定义顺序指定多种语言)
如果您希望使用setObject:forKey:方法来设置您自己的语言列表,则可以覆盖您自己的应用程序的全局设置。 这将优先于全局设置值,并返回到正在执行本地化的应用程序中的任何代码。 这个代码看起来像这样:
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:@"de", @"en", @"fr", nil] forKey:@"AppleLanguages"]; [[NSUserDefaults standardUserDefaults] synchronize]; //to make the change immediate
这将使德语成为您的应用程序的首选语言,英语和法语作为后备。 您可能想在应用程序启动的早些时候调用它。 您可以在这里阅读更多关于语言/区域设置的偏好: 国际化编程主题:获取当前语言和区域设置
我通常这样做,但是你必须在你的项目中有所有的本地化文件。
@implementation Language static NSBundle *bundle = nil; +(void)initialize { NSUserDefaults* defs = [NSUserDefaults standardUserDefaults]; NSArray* languages = [defs objectForKey:@"AppleLanguages"]; NSString *current = [[languages objectAtIndex:0] retain]; [self setLanguage:current]; } /* example calls: [Language setLanguage:@"it"]; [Language setLanguage:@"de"]; */ +(void)setLanguage:(NSString *)l { NSLog(@"preferredLang: %@", l); NSString *path = [[ NSBundle mainBundle ] pathForResource:l ofType:@"lproj" ]; bundle = [[NSBundle bundleWithPath:path] retain]; } +(NSString *)get:(NSString *)key alter:(NSString *)alternate { return [bundle localizedStringForKey:key value:alternate table:nil]; } @end
我最近有同样的问题,我不想开始和修补我的整个NSLocalizedString也不强迫应用程序重新启动新语言工作。 我希望一切都按原样工作。
我的解决scheme是dynamic更改主包的类,并在那里加载适当的包:
头文件
@interface NSBundle (Language) +(void)setLanguage:(NSString*)language; @end
履行
#import <objc/runtime.h> static const char _bundle=0; @interface BundleEx : NSBundle @end @implementation BundleEx -(NSString*)localizedStringForKey:(NSString *)key value:(NSString *)value table:(NSString *)tableName { NSBundle* bundle=objc_getAssociatedObject(self, &_bundle); return bundle ? [bundle localizedStringForKey:key value:value table:tableName] : [super localizedStringForKey:key value:value table:tableName]; } @end @implementation NSBundle (Language) +(void)setLanguage:(NSString*)language { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^ { object_setClass([NSBundle mainBundle],[BundleEx class]); }); objc_setAssociatedObject([NSBundle mainBundle], &_bundle, language ? [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:language ofType:@"lproj"]] : nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } @end
所以基本上,当你的应用程序启动,并加载你的第一个控制器之前,只需调用:
[NSBundle setLanguage:@"en"];
当用户在设置屏幕上更改他的首选语言时,只需再次调用它:
[NSBundle setLanguage:@"fr"];
要重置回系统默认值,只需传递nil:
[NSBundle setLanguage:nil];
请享用…
不要在iOS 9上使用。对于通过它的所有string,这将返回nil。
我发现了另一个解决scheme,允许您更新语言string,而不是重新启动应用程序,并兼容genstrings:
把这个macros放在Prefix.pch:
#define currentLanguageBundle [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:[[NSLocale preferredLanguages] objectAtIndex:0] ofType:@"lproj"]]
以及哪里你需要一个本地化的string使用:
NSLocalizedStringFromTableInBundle(@"GalleryTitleKey", nil, currentLanguageBundle, @"")
设置语言使用:
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObject:@"de"] forKey:@"AppleLanguages"];
即使连续的语言跳转,如:
NSLog(@"test %@", NSLocalizedStringFromTableInBundle(@"NewKey", nil, currentLanguageBundle, @"")); [[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObject:@"fr"] forKey:@"AppleLanguages"]; NSLog(@"test %@", NSLocalizedStringFromTableInBundle(@"NewKey", nil, currentLanguageBundle, @"")); [[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObject:@"it"] forKey:@"AppleLanguages"]; NSLog(@"test %@", NSLocalizedStringFromTableInBundle(@"NewKey", nil, currentLanguageBundle, @"")); [[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObject:@"de"] forKey:@"AppleLanguages"]; NSLog(@"test %@", NSLocalizedStringFromTableInBundle(@"NewKey", nil, currentLanguageBundle, @""));
如前所述,只要做到:
[[NSUserDefaults standardUserDefaults] setObject: [NSArray arrayWithObjects:@"el", nil] forKey:@"AppleLanguages"];
但为了避免重新启动应用程序,请在UIApplicationMain
(…)之前将行放在main.m
的main方法中。
从应用程序中select使用特定语言的技巧是强制NSLocalizedString
根据所选语言使用特定的包,
这里是我写的这个学习提前本地化在ios应用程序中的post
这里是一个示例应用程序提前本地化在iOS应用程序的代码
我最喜欢Mauro Delrio的方法。 我也在我的Project_Prefix.pch中添加了以下内容
#import "Language.h" #define MyLocalizedString(key, alt) [Language get:key alter:alt]
所以,如果你想要使用标准方法(使用NSLocalizedString),你可以在所有文件中进行快速的语法replace。
正如Brian Webster所说,语言需要在应用程序启动的早期进行设置。 我认为AppDelegate
applicationDidFinishLaunching:
应该是一个合适的地方,因为它是我做其他所有初始化的地方。
但正如威廉·丹尼斯(William Denniss)提到的那样,只有在应用程序重新启动后 ,这似乎才起作用,这是无用的。
它似乎工作正常,如果我把代码在主要function,但:
int main(int argc, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; // Force language to Swedish. [[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObject:@"sv"] forKey:@"AppleLanguages"]; int retVal = UIApplicationMain(argc, argv, nil, nil); [pool release]; return retVal; }
我会很感激这个评论。
NSLocalizedString()
从标准用户默认值( [NSUserDefaults standardUserDefaults]
)中读取AppleLanguages
的值。 它使用该值在运行时在所有现有本地化中select适当的本地化。 当Apple在应用程序启动时构build用户默认字典时,会在系统偏好设置中查找首选语言键,并从中复制该值。 这也解释了为什么在OS X中更改语言设置对于运行应用程序没有影响,仅在之后启动的应用程序中才有效。 一旦被复制,值不会仅仅因为设置改变而更新。 这就是为什么iOS会重新启动所有的应用程序,如果你改变语言。
但是,用户默认字典的所有值都可以被命令行参数覆盖。 请参阅NSUserDefaults
上的NSArgumentDomain
文档。 这甚至包括从应用程序首选项(.plist)文件加载的值。 如果您只想为testing更改一次值,这真的很好。
所以,如果你想改变语言只是为了testing,你可能不想改变你的代码(如果你以后忘了删除这个代码…),而是告诉Xcode使用命令行参数启动你的应用程序(如使用西class牙语本地化):
根本不需要触摸你的代码。 只需为不同的语言创build不同的scheme,您就可以通过切换scheme快速启动一种语言的应用程序,一次就可以启动应用程序。
Swift版本:
NSUserDefaults.standardUserDefaults().setObject(["fr"], forKey: "AppleLanguages") NSUserDefaults.standardUserDefaults().synchronize()
我想出了一个解决scheme,允许您使用NSLocalizedString
。 我创build了一个类NSBundle
调用NSBundle+RunTimeLanguage
。 界面是这样的。
// NSBundle+RunTimeLanguage.h #import <Foundation/Foundation.h> @interface NSBundle (RunTimeLanguage) #define NSLocalizedString(key, comment) [[NSBundle mainBundle] runTimeLocalizedStringForKey:(key) value:@"" table:nil] - (NSString *)runTimeLocalizedStringForKey:(NSString *)key value:(NSString *)value table:(NSString *)tableName; @end
实现是这样的。
// NSBundle+RunTimeLanguage.m #import "NSBundle+RunTimeLanguage.h" #import "AppDelegate.h" @implementation NSBundle (RunTimeLanguage) - (NSString *)runTimeLocalizedStringForKey:(NSString *)key value:(NSString *)value table:(NSString *)tableName { AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate; NSString *path= [[NSBundle mainBundle] pathForResource:[appDelegate languageCode] ofType:@"lproj"]; NSBundle *languageBundle = [NSBundle bundleWithPath:path]; NSString *localizedString=[languageBundle localizedStringForKey:key value:key table:nil]; return localizedString; } @end
只要将导入NSBundle+RunTimeLanguage.h
添加到使用NSLocalizedString
的文件。
正如你所看到的,我将我的languageCode存储在AppDelegate
的属性中。 这可以存储在任何你想要的地方。
我唯一不喜欢的是一个警告, NSLocalizedString
marco重新定义。 也许有人可以帮我解决这个问题。
简而言之 :
本地化您的应用程序
这是你必须做的第一件事是用至less两种语言本地化您的应用程序(在这个例子中,英文和法文)。
覆盖NSLocalizedString
在您的代码中,不是使用NSLocalizedString(key, comment)
,而是使用定义如下的macrosMYLocalizedString(key, comment)
: #define MYLocalizedString(key, comment) [[MYLocalizationSystem sharedInstance] localizedStringForKey:(key) value:(comment)];
这个MYLocalizationSystem
单MYLocalizationSystem
:
- 通过设置正确的本地化NSBundle用户请求来设置语言
- 根据之前设置的语言返回本地化的NSString
设置用户语言
当用户用法语改变应用语言时,调用[[MYLocalizationSystem sharedInstance] setLanguage:@"fr"];
- (void)setLanguage:(NSString *)lang { NSString *path = [[NSBundle mainBundle] pathForResource:lang ofType:@"lproj"]; if (!path) { _bundle = [NSBundle mainBundle]; NSLog(@"Warning: No lproj for %@, system default set instead !", lang); return; } _bundle = [NSBundle bundleWithPath:path]; }
在这个例子中, 这个方法把本地化的bundle设置为fr.lproj
返回本地化string
一旦你设置了本地化的bundle,你就可以用这个方法从他那里得到正确的本地化string:
- (NSString *)localizedStringForKey:(NSString *)key value:(NSString *)value { // bundle was initialized with [NSBundle mainBundle] as default and modified in setLanguage method return [self.bundle localizedStringForKey:key value:value table:nil]; }
希望这会帮助你。
您可以在NSWinery.io的本文中find更多详细信息
在文件.pch中定义:
#define currentLanguageBundle [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:[[NSLocale preferredLanguages] objectAtIndex:0] ofType:@"lproj"]] #define NSLocalizedString(str,nil) NSLocalizedStringFromTableInBundle(str, nil, currentLanguageBundle, @"")
也许你应该补充(在#import后面的.pch文件中):
extern NSBundle* bundle; // Declared on Language.m #ifdef NSLocalizedString #undef NSLocalizedString // Delete this line to avoid warning #warning "Undefining NSLocalizedString" #endif #define NSLocalizedString(key, comment) \ [bundle localizedStringForKey:(key) value:@"" table:nil]
你对Swift 3的这个解决scheme有什么看法?
extension String { func localized(forLanguage language: String = Locale.preferredLanguages.first!.components(separatedBy: "-").first!) -> String { guard let path = Bundle.main.path(forResource: language == "en" ? "Base" : language, ofType: "lproj") else { let basePath = Bundle.main.path(forResource: "Base", ofType: "lproj")! return Bundle(path: basePath)!.localizedString(forKey: self, value: "", table: nil) } return Bundle(path: path)!.localizedString(forKey: self, value: "", table: nil) } }
简单的用法:
"report".localized(forLanguage: "pl") //forced language "report".localized() //default language selected by user in settings, in case when your app doesnt support selected lanaguage, the default one is selected, here is an english.
你可以使用你想要做的本地化string来创build一个子包,然后使用NSLocalizedStringFromTableInBundle()
来加载它们。 (我假设这是独立于正常的UI本地化,你可能会在应用程序上做的内容。)
对于我的情况,我有两个本地化的文件,ja和en
如果系统中的首选语言不是en或ja,我想强制它
我要编辑main.m文件
我会检查第一个首选是en还是ja,如果不是的话我会把第二个首选语言改成en。
int main(int argc, char *argv[]) { [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"AppleLanguages"]; [[NSUserDefaults standardUserDefaults] synchronize]; NSString *lang = [[NSLocale preferredLanguages] objectAtIndex:0]; if (![lang isEqualToString:@"en"] && ![lang isEqualToString:@"ja"]){ NSMutableArray *array = [[NSMutableArray alloc] initWithArray:[NSLocale preferredLanguages]]; [array replaceObjectAtIndex:1 withObject:@"en"]; [[NSUserDefaults standardUserDefaults] setObject:array forKey:@"AppleLanguages"]; [[NSUserDefaults standardUserDefaults] synchronize]; } @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } }
这个函数将尝试获取当前语言的本地化string,如果没有find,它会使用英语。
- (NSString*)L:(NSString*)key { static NSString* valueNotFound = @"VALUE_NOT_FOUND"; static NSBundle* enBundle = nil; NSString* pl = [NSLocale preferredLanguages][0]; NSString* bp = [[NSBundle mainBundle] pathForResource:pl ofType:@"lproj"]; NSBundle* b = [NSBundle bundleWithPath:bp]; NSString* s = [b localizedStringForKey:key value:valueNotFound table:nil]; if ( [s isEqualToString:valueNotFound] ) { if ( !enBundle ) { bp = [[NSBundle mainBundle] pathForResource:@"en" ofType:@"lproj"]; enBundle = [NSBundle bundleWithPath:bp]; } s = [enBundle localizedStringForKey:key value:key table:nil]; } return s; }
我想添加对iOS不正式支持的语言的支持(未在系统设置下的语言部分列出)。 通过遵循苹果的国际化教程和Brian Webster和geon的一些提示,我想出了这段代码(放在main.m中):
int main(int argc, char * argv[]) { @autoreleasepool { // Grab regional settings locale, for Slovenian this is either sl_SI or en_SI NSLocale *locale = [NSLocale currentLocale]; NSString *ll = [locale localeIdentifier]; // sl_SI // Grab the first part of language identifier NSArray *comp = [ll componentsSeparatedByString:@"_"]; NSString *ll1 = @"en"; if (comp.count > 0) { ll1 = comp[0]; // sl, en, ... } // Check if we already saved language (user can manually change it inside app for example) if (![[NSUserDefaults standardUserDefaults] objectForKey:@"SelectedLanguage"]) { // Slovenian (Slovenia), Slovenia if ([ll isEqualToString:@"sl_SI"] || [ll isEqualToString:@"en_SI"]) { ll1 = @"sl-SI"; // This is the part of localized path for Slovenian language that Xcode generates } // Add more unsupported languages here... [[NSUserDefaults standardUserDefaults] setObject:ll1 forKey:@"SelectedLanguage"]; // Save language } else { // Restore language as we have previously saved it ll1 = [[NSUserDefaults standardUserDefaults] objectForKey:@"SelectedLanguage"]; } // Overwrite NSLocalizedString and StoryBoard language preference [[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:ll1, @"en", @"fr", nil] forKey:@"AppleLanguages"]; // Make sure settings are stored to disk [[NSUserDefaults standardUserDefaults] synchronize]; return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } }
这适用于Storyboard和NSLocalizedString代码。 该代码假定用户将有一个选项,以后手动更改应用程序中的语言。
当然,不要忘记添加适当的Storyboard翻译和Localizable.strings翻译(请参阅上面的苹果页面的链接如何做到这一点)。
你可以做这样的事情:
NSString *bundlePath = [[NSBundle mainBundle] pathForResource:@"Localizable" ofType:@"strings" inDirectory:nil forLocalization:@"es"]; NSBundle *spanishBundle = [[NSBundle alloc] initWithPath:[bundlePath stringByDeletingLastPathComponent]]; NSLocalizedStringFromTableInBundle(@"House", nil, spanishBundle, nil):
这是一个体面的解决scheme,这个问题,它不需要重新启动应用程序。
https://github.com/cmaftuleac/BundleLocalization
这个实现通过调整NSBundle内部工作。 这个想法是你重写NSBundle对象的实例上的方法localStringForKey,然后用不同的语言在不同的bundle上调用这个方法。 简单和优雅完全兼容所有types的资源。
根据Tudorizer的回答改变语言,不用离开或重新启动应用程序。
为了检查是否存在特定的语言代码,可以使用类来访问首选语言,而不是macros。
以下是用于获取适用于iOS 9的当前语言包的类:
@implementation OSLocalization + (NSBundle *)currentLanguageBundle { // Default language incase an unsupported language is found NSString *language = @"en"; if ([NSLocale preferredLanguages].count) { // Check first object to be of type "en","es" etc // Codes seen by my eyes: "en-US","en","es-US","es" etc NSString *letterCode = [[NSLocale preferredLanguages] objectAtIndex:0]; if ([letterCode rangeOfString:@"en"].location != NSNotFound) { // English language = @"en"; } else if ([letterCode rangeOfString:@"es"].location != NSNotFound) { // Spanish language = @"es"; } else if ([letterCode rangeOfString:@"fr"].location != NSNotFound) { // French language = @"fr"; } // Add more if needed } return [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:language ofType:@"lproj"]]; } /// Check if preferred language is English + (BOOL)isCurrentLanguageEnglish { if (![NSLocale preferredLanguages].count) { // Just incase check for no items in array return YES; } if ([[[NSLocale preferredLanguages] objectAtIndex:0] rangeOfString:@"en"].location == NSNotFound) { // No letter code for english found return NO; } else { // Tis English return YES; } } /* Swap language between English & Spanish * Could send a string argument to directly pass the new language */ + (void)changeCurrentLanguage { if ([self isCurrentLanguageEnglish]) { [[NSUserDefaults standardUserDefaults] setObject:@[@"es"] forKey:@"AppleLanguages"]; } else { [[NSUserDefaults standardUserDefaults] setObject:@[@"en"] forKey:@"AppleLanguages"]; } } @end
Use the class above to reference a string file / image / video / etc:
// Access a localized image [[OSLocalization currentLanguageBundle] pathForResource:@"my_image_name.png" ofType:nil] // Access a localized string from Localizable.strings file NSLocalizedStringFromTableInBundle(@"StringKey", nil, [OSLocalization currentLanguageBundle], @"comment")
Change language in-line like below or update the "changeCurrentLanguage" method in the class above to take a string parameter referencing the new language.
[[NSUserDefaults standardUserDefaults] setObject:@[@"es"] forKey:@"AppleLanguages"];
Swift 3 solution:
let languages = ["bs", "zh-Hant", "en", "fi", "ko", "lv", "ms", "pl", "pt-BR", "ru", "sr-Latn", "sk", "es", "tr"] UserDefaults.standard.set([languages[0]], forKey: "AppleLanguages")
Gave some examples of language codes that can be used. 希望这可以帮助
Swift 3 extensions:
extension Locale { static var preferredLanguage: String { get { return self.preferredLanguages.first ?? "en" } set { UserDefaults.standard.set([newValue], forKey: "AppleLanguages") UserDefaults.standard.synchronize() } } } extension String { var localized: String { var result: String let languageCode = Locale.preferredLanguage //en-US var path = Bundle.main.path(forResource: languageCode, ofType: "lproj") if path == nil, let hyphenRange = languageCode.range(of: "-") { let languageCodeShort = languageCode.substring(to: hyphenRange.lowerBound) // en path = Bundle.main.path(forResource: languageCodeShort, ofType: "lproj") } if let path = path, let locBundle = Bundle(path: path) { result = locBundle.localizedString(forKey: self, value: nil, table: nil) } else { result = NSLocalizedString(self, comment: "") } return result } }
用法:
Locale.preferredLanguage = "uk" label.text = "localizedKey".localized
whatever you all do, the best way is to take the short_name for the specified language, ie: fr, en, nl, de, it, etc… and assign the same to a global value.
make a picker view to pop up like a drop down menu (combination of a button on click of which a picker view appears from below with a list of languages) and select the language you desire. let the short name be stored internally. make a .h + .m file named LocalisedString.
Set the global value of short_name to be equal to the obtained value in LocalisedString.m When the required language is selected assign the NSBundlePath to create project sub-directories for the needed language. for eg, nl.proj, en.proj.
When the particular proj folder is selected call the localised string for the respective language and change the language dynamically.
no rules broken.