2012-02-23 187 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() 
} 
相關問題