无需重新启动应用程序即可在 iOS 中更改应用程序语言

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12192151/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 19:56:47  来源:igfitidea点击:

Change app language in iOS without restarting the app

iosobjective-cxcodecocoalocalization

提问by Ryan

I have seems some apps can change the language internally within the app without the need of restarting the app, I am wondering how they are implemented.

我似乎有些应用程序可以在应用程序内部更改语言而无需重新启动应用程序,我想知道它们是如何实现的。

For example, for us using NSLocalizedString, I know it is possible to set the language at runtime at main.mwhen your AppDelegateis not initialized, but once it is initialized (particularly your view controller is created), change it has not effect until the next restart

例如,对于我们使用NSLocalizedString,我知道可以在未初始化main.m时在运行时设置语言AppDelegate,但是一旦初始化(特别是创建了您的视图控制器),更改它直到下次重新启动时才会生效

[[NSUserDefaults standardUserDefaults] 
    setObject:[NSMutableArray arrayWithObjects:language, nil] 
    forKey:@"AppleLanguages"];

Anyone have idea how those dynamic language change can be done without restarting the app?

任何人都知道如何在不重新启动应用程序的情况下完成这些动态语言更改?

回答by Merk

There's some discussion of other approaches here, in particular a notification based approach:

这里有一些其他方法的讨论,特别是基于通知的方法:

iOS: How to change app language programmatically WITHOUT restarting the app?

iOS:如何在不重新启动应用程序的情况下以编程方式更改应用程序语言?

In my view there are really three tasks here: (1) re-localization of resources automatically loaded from nibs. (for example if you dynamically instantiate another custom UIView from a nib, the "old" language strings and settings (images, text direction) will still be loaded) (2) re-localization of strings currently displayed on the screen. (3) re-localization of strings inserted by the developer (you) in program code.

在我看来,这里确实有三项任务:(1)重新本地化从笔尖自动加载的资源。(例如,如果你动态地从笔尖实例化另一个自定义的UIView,“老字号”的语言字符串和设置(图片,文字方向)将仍然被加载)的字符串(2)重新定位当前显示在屏幕上。(3) 重新本地化开发者(你)在程序代码中插入的字符串。

Let's start with (3). If you look for the definition you will notice that NSLocalizedString is a macro. So if you don't want to change existing code too much, you can probably solve the problem of (3) by creating a new header file. In that header file, #undefand then re-#define NSLocalizedStringto pick the localized string from the appropriate place--not the one that iOS defaults to, but one that you keep track of in some global variable (e.g., in an app delegate ivar). If you don't want to redefine NSLocalizedString but you still make your own alternative , you should probably still #undef NSLocalizedStringif you don't want future developers to accidentally call it instead of the macro you replace it with. Not an ideal solution, but maybe the most practical.

让我们从(3)开始。如果您查找定义,您会注意到 NSLocalizedString 是一个宏。所以如果不想对现有代码改动太多,大概可以通过新建一个头文件来解决(3)的问题。在那个头文件中,#undef然后#define NSLocalizedString从适当的地方重新选择本地化的字符串——不是 iOS 默认的那个,而是你在某个全局变量中跟踪的那个(例如,在应用程序委托 ivar 中)。如果你不想重新定义 NSLocalizedString 但你仍然自己做替代,#undef NSLocalizedString如果你不希望未来的开发人员不小心调用它而不是你替换它的宏,你可能应该仍然这样做。不是一个理想的解决方案,但也许是最实用的。

As for (1), if you haven't done your localization in Interface Builder, but rather you do it dynamically in viewDidLoad, etc., no problem. You can use the same behavior just discussed (i.e., the modified NSLocalizedString, etc.). Otherwise you can either (a) implement a notification system as described in the link above (complicated), or (b) consider moving localization from IB to viewDidLoad, or (c) try overriding initWithNibName:and swap out the object loaded with the old language resources, with one loaded with the new language resources. This was an approach mentioned by Mohamed at the very bottom of this discussion: http://learning-ios.blogspot.ca/2011/04/advance-localization-in-ios-apps.html. He claims it causes problems (viewDidLoad isn't called). Even if it doesn't work, trying it out might point you towards something that does.

至于(1),如果你没有在Interface Builder中做本地化,而是在viewDidLoad等中动态做,没问题。您可以使用刚才讨论的相同行为(即修改后的 NSLocalizedString 等)。否则,您可以 (a) 实现上述链接中所述的通知系统(复杂),或 (b) 考虑将本地化从 IB 移至 viewDidLoad,或 (c) 尝试覆盖initWithNibName:并换出加载有旧语言资源的对象,其中一个加载了新的语言资源。这是由穆罕默德在最底层的讨论提到的方法:http://learning-ios.blogspot.ca/2011/04/advance-localization-in-ios-apps.html. 他声称这会导致问题(未调用 viewDidLoad)。即使它不起作用,尝试它也可能会为您指明一些行之有效的方法。

Finally, (2) is presumably the easiest task: just remove and re-add the current view (or in some cases, just redraw it).

最后,(2)大概是最简单的任务:只需删除并重新添加当前视图(或者在某些情况下,只需重新绘制它)。

回答by Matteo Gobbi

the idea is to write a new macro like NSLocalizedStringwhich should check if to take the translation from another specific bundle or not.

这个想法是编写一个新的宏,比如NSLocalizedString应该检查是否从另一个特定的包中获取翻译。

The method 2in this articleexplain exactly how to do it. In this particular case, the author doesn't use a new macro, but directly set a custom classfor [NSBundle mainBundle].

方法2这篇文章中解释究竟是如何做到这一点。在这种特殊情况下,笔者不使用新的宏,而是直接设置自定义类[NSBundle mainBundle]

I hope that @holex will understand the problem reading this.

我希望@holex 能理解阅读这篇文章的问题。

回答by Antonioni

My implementation uses a class to change the language and access the current language bundle. It's an example so if you were to use different languages than I am then change the methods to use your exact language codes.

我的实现使用一个类来更改语言并访问当前的语言包。这是一个示例,因此如果您要使用与我不同的语言,请更改方法以使用您的确切语言代码。

This class will access the preferred languages from NSLocale and take the first object which is the language being used.

此类将从 NSLocale 访问首选语言并获取第一个对象,即正在使用的语言。

@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 code.

如下所示在线更改语言或更新上面类中的“changeCurrentLanguage”方法以获取引用新语言代码的字符串参数。

// Change the preferred language to Spanish
[[NSUserDefaults standardUserDefaults] setObject:@[@"es"] forKey:@"AppleLanguages"];

回答by holex

I'm always using this way, it works perfectly, it might help you as well.

我一直在使用这种方式,它工作得很好,它也可能对您有所帮助。



you should set all the texts with NSLocalizableString(...)for the UIfor the current language in the -viewWillAppear:method of your every UIViewController.

你应该将所有与文本NSLocalizableString(...)UI在当前语言-viewWillAppear:你的每一个方法UIViewController

using this way you (I mean, the users) don't need to restartthe application after changing the language of iOSin the Settings.

使用这种方式,你(我的意思是,用户)不需要重新启动改变的语言应用后iOS设置



of course, I'm using the Apple's standard localisation architecture.

当然,我使用的是 Apple 的标准本地化架构。

UPDATE on (24 Oct 2013)

更新(2013 年 10 月 24 日)

I've experienced the –viewWillAppear:method won't be performed for the actual view when the application enters to foreground; to solve that issue I also commit the procedure (see above) when I receive UIApplicationWillEnterForegroundNotificationnotification in the view.

我经历过–viewWillAppear:当应用程序进入前台时,不会对实际视图执行该方法;为了解决这个问题,当我UIApplicationWillEnterForegroundNotification在视图中收到通知时,我也会提交该过程(见上文)。

回答by Mangesh

I was stuck in same issue, my requirement was "User can select language from drop down & application have to work according selected language (English or arabic)" What i have done i create two XIB and fetch XIB and Text according selected language. In this way user can select language. I used NSBundle for the same. like

我遇到了同样的问题,我的要求是“用户可以从下拉列表中选择语言,应用程序必须根据所选语言(英语或阿拉伯语)工作”我所做的我创建了两个 XIB 并根据所选语言获取 XIB 和文本。这样用户可以选择语言。我同样使用了 NSBundle。喜欢

For XIB

对于 XIB

self.homeScreen =  [[HomeScreen alloc] initWithNibName:@"HomeScreen" bundle:[CommonData sharedCommonData].languageBundle];

For Text

对于文本

_lblHeading.text = [self languageSelectedStringForKey:@"ViewHeadingInfo"];

/**
 This method is responsible for selecting language bundle according to user's selection. 
 @param: the string which is to be converted in selected language. 
 @return: the converted string.
 @throws:
 */

-(NSString*) languageSelectedStringForKey:(NSString*) key

{

    NSString* str=[[CommonData sharedCommonData].languageBundle localizedStringForKey:key value:@"" table:nil];

    return str;

}

回答by Timur Kuchkarov

You need to load another bundle like this(where @"en" could be locale you need):

您需要像这样加载另一个包(其中 @"en" 可能是您需要的语言环境):

NSString *path = [[NSBundle mainBundle] pathForResource:@"en" ofType:@"lproj"];
NSBundle *languageBundle = [NSBundle bundleWithPath:path];

and make macros/function like NSLocalizedString which use your loaded bundle or use methods on that bundle directly like this

并制作像 NSLocalizedString 这样的宏/函数,它们使用您加载的包或直接像这样使用该包上的方法

[languageBundle localizedStringForKey:key value:value table:tableName];

[[NSBundle mainBundle] localizations]lists all app localizations(including "Base").

[[NSBundle mainBundle] localizations]列出所有应用本地化(包括“基础”)。

Also I wrote helper classwhich does this(note that it has ReactiveCocoa as a dependency). It allows language change without app restart and sends current locale each time it's changed.

我还编写了执行此操作的帮助程序类(请注意,它具有 ReactiveCocoa 作为依赖项)。它允许在不重启应用程序的情况下更改语言,并在每次更改时发送当前语言环境。