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.m
when your AppDelegate
is 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?
有人知道动态语言的改变是如何在不重新启动应用程序的情况下完成的吗?
5
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)从nib自动加载的资源的重新定位。(例如,如果从nib动态实例化另一个自定义的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, #undef
and then re-#define NSLocalizedString
to 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 NSLocalizedString
if 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然后re-#define NSLocalizedString从适当的位置选择本地化的字符串——不是iOS默认的字符串,而是在全局变量(例如,在app delegate 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),如果您没有在接口构建器中进行本地化,而是在viewDidLoad中动态地进行本地化,那么没有问题。您可以使用刚才讨论的相同行为(例如。,修改后的NSLocalizedString,等等)。否则,您可以(a)实现上面链接(复杂)中描述的通知系统,或者(b)考虑将本地化从IB迁移到viewDidLoad,或者(c)尝试重写initWithNibName:并交换加载旧语言资源的对象,其中一个对象加载新语言资源。这是Mohamed在讨论的最后提到的方法:http://learning- ios.blogspot.ca/2011/04/advancelocaliz-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)可能是最简单的任务:只需删除并重新添加当前视图(在某些情况下,只需重新绘制它)。
2
the idea is to write a new macro like NSLocalizedString
which should check if to take the translation from another specific bundle or not.
其想法是编写一个新的宏,比如NSLocalizedString,它应该检查是否从另一个特定的bundle中获取翻译。
The method 2 in this article explain exactly how to do it. In this particular case, the author doesn't use a new macro, but directly set a custom class for [NSBundle mainBundle]
.
本文中的方法2详细地解释了如何实现它。在本例中,作者不使用新的宏,而是直接为[NSBundle mainBundle]设置自定义类。
I hope that @holex will understand the problem reading this.
我希望@holex能理解阅读本文的问题。
1
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 UI
for 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 restart the application after changing the language of iOS
in the Settings.
使用这种方式,您(我的意思是,用户)无需在设置中更改iOS语言后重新启动应用程序。
of course, I'm using the Apple's standard localisation architecture.
当然,我使用的是苹果的标准本地化架构。
UPDATE on (24 Oct 2013)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 UIApplicationWillEnterForegroundNotification
notification in the view.
我经历过-viewWillAppear:当应用程序进入前台时,不会对实际视图执行方法;为了解决这个问题,当我收到uiapplicationwillentergroundnotification通知时,我也提交了这个过程(见上面)。
1
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"];
0
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;
}
-1
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]本地化]列出所有应用程序本地化(包括“Base”)。
Also I wrote helper class which 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作为依赖项)。它允许无需重新启动就进行语言更改,并在每次更改时发送当前语言环境。