iOS:如何以编程方式更改应用程序语言无需重新启动应用程序?

当我使用设备语言独立更改应用程序语言时,只有closures应用程序并重新启动后才能生效。 如何不要求应用程序重新加载所有的nib文件和.strings文件取决于所选的语言?

我使用它在运行时更改语言:

NSArray* languages = [NSArray arrayWithObjects:@"ar", @"en", nil]; [[NSUserDefaults standardUserDefaults] setObject:languages forKey:@"AppleLanguages"]; 

对于带有标签导航的Kiosk模式iPad应用,我有类似的要求。 不仅应用程序需要支持dynamic语言更改,而且必须知道大多数选项卡已经从笔尖加载,因为应用程序只是每周重新启动(平均)大约一次版本被加载。

我尝试了几个build议来利用现有的苹果本地化机制,它们都有严重的缺陷,包括在XCode 4.2中针对本地化的笔尖的不可靠的支持 – 我的IBoutlet连接variables似乎在IB中被正确设置,但是在运行时它们通常是空的!?

我最终实现了一个模仿Apple的NSLocalizedString类的类,但是它可以处理运行时更改,而且当用户进行语言更改时,我的类发布了通知。 需要本地化string(和图像)的屏幕声明一个handleLocaleChange方法,该方法在viewDidLoad处被调用,并且只要LocaleChangedNotification被发布。

我的所有button和graphics都被devise为独立于语言,但标题文本和标签文本通常会根据语言环境更改进行更新。 如果我必须改变图像,我可以在每个屏幕的handleLocaleChange方法中这样做,我想。

这是代码。 它包含了一些我在最终项目中实际不使用的笔尖/束path的支持。

MyLanguage.h // // MyLanguage.h // //

 #import <Foundation/Foundation.h> #define DEFAULT_DICTIONARY_FOR_STRINGS @"" #define ACCESSING_ALTERNATE_DICTIONARY_SETS_DEFAULT 1 #define LANGUAGE_ENGLISH_INT 0 #define LANGUAGE_SPANISH_INT 1 #define LANGUAGE_ENGLISH_SHORT_ID @"en" #define LANGUAGE_SPANISH_SHORT_ID @"es" #define LANGUAGE_CHANGED_NOTIFICATION @"LANGUAGE_CHANGED" @interface MyLanguage : NSObject { NSString *currentLanguage; NSDictionary *currentDictionary; NSBundle *currentLanguageBundle; } +(void) setLanguage:(NSString *)languageName; +(NSString *)stringFor:(NSString *)srcString forLanguage:(NSString *)languageName; +(NSString *)stringFor:(NSString *)srcString; + (MyLanguage *)singleton; @property (nonatomic, retain) NSBundle *currentLanguageBundle; @property (nonatomic, retain) NSString *currentLanguage; @property (nonatomic, retain) NSDictionary *currentDictionary; @end 

MyLanguage.m:// // MyLanguage.m

 #import "MyLanguage.h" #import "Valet.h" #define GUI_STRING_FILE_POSTFIX @"GUIStrings.plist" @implementation MyLanguage @synthesize currentLanguage; @synthesize currentDictionary; @synthesize currentLanguageBundle; +(NSDictionary *)getDictionaryNamed:(NSString *)languageName { NSDictionary *results = nil; // for now, we store dictionaries in a PLIST with the same name. NSString *dictionaryPlistFile = [languageName stringByAppendingString:GUI_STRING_FILE_POSTFIX]; NSString *plistBundlePath = [Valet getBundlePathForFileName:dictionaryPlistFile]; if ( [[NSFileManager defaultManager] fileExistsAtPath:plistBundlePath] ) { // read it into a dictionary NSDictionary *newDict = [NSDictionary dictionaryWithContentsOfFile:plistBundlePath]; results = [newDict valueForKey:@"languageDictionary"]; }// end if return results; } +(NSString *)stringFor:(NSString *)srcString forDictionary:(NSString *)languageName; { MyLanguage *gsObject = [MyLanguage singleton]; // if default dictionary matches the requested one, use it. if ([gsObject.currentLanguage isEqualToString:languageName]) { // use default return [MyLanguage stringFor:srcString]; }// end if else { // get the desired dictionary NSDictionary *newDict = [MyLanguage getDictionaryNamed:languageName]; // default is not desired! if (ACCESSING_ALTERNATE_DICTIONARY_SETS_DEFAULT) { gsObject.currentDictionary = newDict; gsObject.currentLanguage = languageName; return [MyLanguage stringFor:srcString]; }// end if else { // use current dictionary for translation. NSString *results = [gsObject.currentDictionary valueForKey:srcString]; if (results == nil) { return srcString; }// end if return results; } } } +(void) setLanguage:(NSString *)languageName; { MyLanguage *gsObject = [MyLanguage singleton]; // for now, we store dictionaries in a PLIST with the same name. // get the desired dictionary NSDictionary *newDict = [MyLanguage getDictionaryNamed:languageName]; gsObject.currentDictionary = newDict; gsObject.currentLanguage = languageName; // now set up the bundle for nibs NSString *shortLanguageIdentifier = @"en"; if ([languageName contains:@"spanish"] || [languageName contains:@"espanol"] || [languageName isEqualToString:LANGUAGE_SPANISH_SHORT_ID]) { shortLanguageIdentifier = LANGUAGE_SPANISH_SHORT_ID; }// end if else shortLanguageIdentifier = LANGUAGE_ENGLISH_SHORT_ID; // NSArray *languages = [NSArray arrayWithObject:shortLanguageIdentifier]; // [[NSUserDefaults standardUserDefaults] setObject:languages forKey:@"AppleLanguages"]; // NSString *path= [[NSBundle mainBundle] pathForResource:shortLanguageIdentifier ofType:@"lproj"]; NSBundle *languageBundle = [NSBundle bundleWithPath:path]; gsObject.currentLanguageBundle = languageBundle; [[NSNotificationCenter defaultCenter] postNotificationName:LANGUAGE_CHANGED_NOTIFICATION object:nil]; } +(NSString *)stringFor:(NSString *)srcString; { MyLanguage *gsObject = [MyLanguage singleton]; // default is to do nothing. if (gsObject.currentDictionary == nil || gsObject.currentLanguage == nil || [gsObject.currentLanguage isEqualToString:DEFAULT_DICTIONARY_FOR_STRINGS] ) { return srcString; }// end if // use current dictionary for translation. NSString *results = [gsObject.currentDictionary valueForKey:srcString]; if (results == nil) { return srcString; }// end if return results; } #pragma mark - #pragma mark Singleton methods static MyLanguage *mySharedSingleton = nil; -(void) lateInit; { } // PUT THIS METHOD DECLARATION INTO THE HEADER + (MyLanguage *)singleton; { if (mySharedSingleton == nil) { mySharedSingleton = [[super allocWithZone:NULL] init]; [mySharedSingleton lateInit]; } return mySharedSingleton; } + (id)allocWithZone:(NSZone *)zone { return [[self singleton] retain]; } - (id)copyWithZone:(NSZone *)zone { return self; } - (id)retain { return self; } - (NSUInteger)retainCount //denotes an object that cannot be released { return NSUIntegerMax; } - (oneway void)release //do nothing { } - (id)autorelease { return self; } @end 

不要依赖您在nib文件中设置的string。 只使用你的笔尖布局和设置的意见。 任何显示给用户的string(button文本等)都需要在你的Localizable.strings文件中,当你加载你的笔尖时,你需要相应地在相应的视图/控件上设置文本。

要获取当前语言的包:

 NSString *path = [[NSBundle mainBundle] pathForResource:currentLanguage ofType:@"lproj"]; if (path) { NSBundle *localeBundle = [NSBundle bundleWithPath:path]; } 

并使用该包来获取您的本地化string:

 NSLocalizedStringFromTableInBundle(stringThatNeedsToBeLocalized, nil, localeBundle, nil); 

另外对于date格式,您可能需要查看

 [NSDateFormatter dateFormatFromTemplate:@"HH:mm:ss"" options:0 locale:locale]; 

要使用它,你需要为你想使用的相应语言/国家创build一个NSLocale。

只需使用这个库, https://github.com/Baglan/MCLocalization ,你可以很容易地改变选定的朗,飞:)

下面是我做的。 我猜诀窍是使用NSLocalizedStringFromTableInBundle而不是NSLocalizedString。

对于所有的string,使用这个

 someLabel.text = NSLocalizedStringFromTableInBundle(@"Your String to be localized, %@",nil,self.localeBundle,@"some context for translators"); 

要更改语言,请运行此代码

  NSString * language = @"zh-Hans"; //or whatever language you want NSString *path = [[NSBundle mainBundle] pathForResource:language ofType:@"lproj"]; if (path) { self.localeBundle = [NSBundle bundleWithPath:path]; } else { self.localeBundle = [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:@"en" ofType:@"lproj"] ]; } 

在此之后,您可能需要调用任何更新代码来将string更新为新语言,例如再次运行

 someLabel.text = NSLocalizedStringFromTableInBundle(@"Your String to be localized, %@",nil,self.localeBundle,@"some context for translators"); 

就这样。 不需要重启应用程序。 与系统设置兼容(如果您通过iOS设置设置语言,它也可以)。 不需要外部库。 不需要越狱。 它也适用于起跳。

当然,你仍然应该按照惯例为你的应用程序设置保持:

 [[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:@"zh-Hans", nil] forKey:@"AppleLanguages"]; [[NSUserDefaults standardUserDefaults] synchronize]; 

(并检查你的viewDidLoad或东西)

 NSString * language = [[NSLocale preferredLanguages] objectAtIndex:0]; NSString *path = [[NSBundle mainBundle] pathForResource:language ofType:@"lproj"]; if (path) { self.localeBundle = [NSBundle bundleWithPath:path]; } else { self.localeBundle = [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:@"en" ofType:@"lproj"] ]; } 

你应该创build类似于NSLocalizedString的自己的macros,但是根据你设置的NSUserDefaults值来select一个string的bundle(也就是不用担心苹果语言默认值的值是多less)

当你改变语言,你应该发出一个通知,哪些视图控制器,视图等应该听和刷新自己