2012-02-23 7 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- on-iphone –

答えて

6

あなたのnibファイルに設定されている文字列に依存しないでください。ビューのレイアウト&のレイアウトにのみペン先を使用してください。ユーザーに表示される任意の文字列(ボタンテキストなど)は、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を作成する必要がありますあなたが使用したいと思うry。

+0

しかし、xibファイルのローカライズされた画像はどうですか?例:ボタンイメージ。また、異なるローカリゼーションのxibファイルのレイアウトやラベルのサイズはどうですか? –

+0

画像の読み込みの例については、http://stackoverflow.com/questions/3787751/loading-a-localized-uiimageをご覧ください。 – mamills

+0

ラベルなどのサイズを動的に変更する必要があるかもしれません。たとえば、 - [NSString sizeWithFont:constrainedToSize:lineBreakMode:]を使用して特定のテキストに必要な高さ(または幅)を決定し、それに応じてフレームを設定する必要があります。 – mamills

2

あなたはNSLocalizedStringに、独自のマクロ類似したを作成するが、それはあなたが設定NSUserDefaults値上から文字列を選択したバンドルを基づかなければならない(つまり、りんご言語のデフォルト値の値が何であるかを心配しないでください)

ときビューコントローラ、ビューなどが自分自身のためにリッスンしてリフレッシュする必要がある通知を送信する言語を変更する

+1

サンプルコードを提供できますか? – Developer

10

タブ付きナビゲーションを備えたキオスクモードのiPadアプリと同様の要件がありました。アプリは即座に言語の変更をサポートする必要があるだけでなく、アプリが(平均して)1週間に約1回再起動してから、ほとんどのタブがすでにペン先からロードされていたことを知っていなければなりませんでしたバージョンがロードされました。

現行のアップルのローカリゼーションメカニズムを活用するためにいくつかの提案を試みましたが、ローカライズされたnibsのXCode 4.2での驚異的なサポートを含む重大な欠点がありました。IBoutlet接続変数はIBでは正しく設定されているようですが、多くの場合nullになるだろう!

アップルのNSLocalizedStringクラスを模倣したクラスを実装しましたが、ランタイムの変更を処理することができました。また、ユーザーが言語を変更したときにクラスが通知を送信しました。変更するローカライズされた文字列(および画像)が必要な画面は、viewDidLoadで呼び出されたhandleLocaleChangeメソッドが宣言され、LocaleChangedNotificationがポストされたときに宣言されました。

すべてのボタンとグラフィックスは言語に依存しないように設計されていますが、タイトルテキストとラベルテキストは通常​​、ロケールの変更に応じて更新されます。画像を変更しなければならない場合は、各画面のhandleLocaleChangeメソッドで行うことができました。

ここにコードがあります。これには、最終的なプロジェクトで実際に使用していないnib/bundleパスのサポートが含まれています。

MyLanguage.h // // MyLanguage。時間 // //

#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ファイルは、esGUIStrings.plistのようなGUIStings.plistに続く短い言語のIDであり、plistのルートオブジェクトは"languageDictionary"という名前の辞書。辞書のエントリは、キーとして翻訳される文字列(「Unlock」や「Log in」など)で構成され、値は翻訳された文字列(「Desbloquear」や「Iniciar la sesion」など)です。 –

+0

Valet.hとは何ですか? – KKendall

+0

バレットは私が作成したヘルパークラスで、NSFileManagerよりも高レベルのファイルシステムインターフェイスとして機能します。コードを投稿する前にすべての参照を削除しようとしましたが、見逃してしまったようです。 NSString * path = [[NSBundle mainBundle] pathForResource:sh​​ortLanguageIdentifier ofType:@ "lproj"];コードの後半に、Valetを使用しない同様の行が表示されます。 –

4

私がやったこと。私はそのトリックがNSLocalizedStringの代わりにNSLocalizedStringFromTableInBundleを使うことだったと思います。

すべての文字列の場合、あなたはおそらく新しいの文字列を更新するために、どんなアップデートのコードを呼び出すことになるでしょう、この後

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"); 

を使用言語、例えばこれをもう一度実行してください。

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

アプリを再起動する必要はありません。システム設定にも対応しています(iOS設定で言語を設定しても動作します)。外部ライブラリは必要ありません。脱獄は必要ありません。そしてそれもgenstringsで動作します。もちろん

アプリの設定を永続化するために、あなたはまだ通常の作業を行う必要があります。

[[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

これは私のために動作します: スウィフト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() 
} 
関連する問題