2012-02-23 189 views
28

当我使用设备语言独立更改应用程序语言时,它将不会生效,除非关闭应用程序并重新启动它。如何不要求应用程序重新启动以根据所选语言再次加载所有nib文件和.strings文件?iOS:如何以编程方式更改应用程序语言而无需重新启动应用程序?

我用这个在运行时改变语言:

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

检查这个答案它的工作原理http://stackoverflow.com/questions/5912018/language-change-only-after-restart- iphone- – 2013-05-01 15:43:15

回答

6

不要依赖于您在笔尖文件中设置字符串。只使用你的笔尖来设置视图的布局&。任何显示给用户的字符串(按钮文本等)都需要位于Localizable.strings文件中,并且在加载笔尖时,需要相应地在相应的视图/控件上设置文本。

要得到束当前语言:

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

,并使用捆绑,以获得您的本地化字符串:

NSLocalizedStringFromTableInBundle(stringThatNeedsToBeLocalized, nil, localeBundle, nil); 

而且日期格式,你可能要考虑

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

要使用该功能,您需要为相应的语言/计数创建一个NSLocale你想使用哪种。

+0

但是,xib文件中的本地化图像呢?例如:按钮图像。那么xib文件中不同的布局和标签大小对于不同的本地化呢? – 2012-02-23 18:16:26

+0

有关加载图像的示例,请参阅http://stackoverflow.com/questions/3787751/loading-a-localized-uiimage。 – mamills 2012-02-24 15:46:59

+0

您可能需要动态调整标签大小等。例如,您可能需要使用 - [NSString sizeWithFont:constrainedToSize:lineBreakMode:]来确定某些文本所需的高度(或宽度),然后相应地设置框架。 – mamills 2012-02-24 15:58:51

2

您应该创建自己的宏类似NSLocalizedString但基地束它选择一个字符串从设置NSUserDefaults的一个值(即不用担心什么苹果语言默认值的值)

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

+1

你能提供任何示例代码吗? – Developer 2017-06-05 08:41:24

10

我有一个类似的需求为Kiosk模式的iPad应用程序与选项卡式导航。该应用程序不仅需要支持动态语言更改,而且必须知道大部分选项卡已从nib加载,因为应用程序仅在每周重新启动(平均)时才会重新启动一次版本被加载。

我尝试了一些建议,以利用现有的Apple本地化机制,它们都有严重的缺陷,包括对XCode 4.2本地化笔尖的不可靠支持 - 我的IBoutlet连接变量似乎在IB中正确设置,但在运行时它们通常会是空的!?

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

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

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

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 
+0

如果有人想重新使用我的代码,只需要注意一点(这可能并不明显):语言环境PLIST文件是GUIStings.plist中的语言短ID,如esGUIStrings.plist中所示,并且plist中的根对象是一个名为“languageDictionary”的字典。词典中的条目包括将被翻译为键(例如“解锁”和“登录”)的字符串,并且值是翻译后的字符串(例如“Desbloquear”和“Iniciar lasion”)。 – 2012-09-12 15:03:33

+0

什么是Valet.h? – KKendall 2012-11-20 20:28:51

+0

代客是我创建的助手类,它的作用是比NSFileManager更高级的文件系统接口。在发布代码之前,我尝试删除所有对它的引用,但看起来我错过了一个。你可以在后面的代码中看到一个类似的行,它不使用代客:NSString * path = [[NSBundle mainBundle] pathForResource:sh​​ortLanguageIdentifier ofType:@“lproj”]; – 2012-11-26 23:35:35

4

继承人我做了什么。我猜诀窍是使用NSLocalizedStringFromTableInBundle而不是NSLocalizedString。

的所有字符串,用这个

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"] ]; 
    } 

在此之后,你可能会想打电话给任何更新代码更新字符串新语言,例如再次运行这个

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"] ]; 
    } 
0

这对我的作品: Swift 4:

创建一个名为BundleExtension.swift的文件并向其添加以下代码 -

var bundleKey: UInt8 = 0 

class AnyLanguageBundle: Bundle { 

override func localizedString(forKey key: String, 
           value: String?, 
           table tableName: String?) -> String { 

    guard let path = objc_getAssociatedObject(self, &bundleKey) as? String, 
     let bundle = Bundle(path: path) else { 

      return super.localizedString(forKey: key, value: value, table: tableName) 
    } 

    return bundle.localizedString(forKey: key, value: value, table: tableName) 
    } 
} 

extension Bundle { 

class func setLanguage(_ language: String) { 

    defer { 

     object_setClass(Bundle.main, AnyLanguageBundle.self) 
    } 

    objc_setAssociatedObject(Bundle.main, &bundleKey, Bundle.main.path(forResource: language, ofType: "lproj"), .OBJC_ASSOCIATION_RETAIN_NONATOMIC) 
    } 
} 

现在只要您需要更改语言调用此方法:

func languageButtonAction() { 
    // This is done so that network calls now have the Accept-Language as "hi" (Using Alamofire) Check if you can remove these 
    UserDefaults.standard.set(["hi"], forKey: "AppleLanguages") 
    UserDefaults.standard.synchronize() 

    // Update the language by swaping bundle 
    Bundle.setLanguage("hi") 

    // Done to reintantiate the storyboards instantly 
    let storyboard = UIStoryboard.init(name: "Main", bundle: nil) 
    UIApplication.shared.keyWindow?.rootViewController = storyboard.instantiateInitialViewController() 
} 
相关问题