ios 如何强制 NSLocalizedString 使用特定语言

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1669645/
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 16:34:55  来源:igfitidea点击:

How to force NSLocalizedString to use a specific language

iosobjective-clocalizationinternationalizationnslocalizedstring

提问by CodeFlakes

On iPhone NSLocalizedStringreturns the string in the language of the iPhone. Is it possible to force NSLocalizedStringto use a specific language to have the app in a different language than the device ?

在 iPhone 上NSLocalizedString返回iPhone语言的字符串。是否可以强制NSLocalizedString使用特定语言来让应用程序使用与设备不同的语言?

采纳答案by Brian Webster

NSLocalizedString()(and variants thereof) access the "AppleLanguages" key in NSUserDefaultsto determine what the user's settings for preferred languages are. This returns an array of language codes, with the first one being the one set by the user for their phone, and the subsequent ones used as fallbacks if a resource is not available in the preferred language. (on the desktop, the user can specify multiple languages with a custom ordering in System Preferences)

NSLocalizedString()(及其变体)访问“AppleLanguages”键NSUserDefaults以确定用户对首选语言的设置。这将返回一组语言代码,第一个是用户为其手机设置的语言代码,如果资源在首选语言中不可用,则后续代码用作后备。(在桌面上,用户可以通过系统偏好设置中的自定义排序来指定多种语言)

You can override the global setting for your own application if you wish by using the setObject:forKey: method to set your own language list. This will take precedence over the globally set value and be returned to any code in your application that is performing localization. The code for this would look something like:

如果您愿意,您可以通过使用 setObject:forKey: 方法设置您自己的语言列表来覆盖您自己的应用程序的全局设置。这将优先于全局设置值并返回到您的应用程序中执行本地化的任何代码。代码如下所示:

[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:@"de", @"en", @"fr", nil] forKey:@"AppleLanguages"];
[[NSUserDefaults standardUserDefaults] synchronize]; //to make the change immediate

This would make German the preferred language for your application, with English and French as fallbacks. You would want to call this sometime early in your application's startup. You can read more about language/locale preferences here: Internationalization Programming Topics: Getting the Current Language and Locale

这将使德语成为您应用程序的首选语言,英语和法语作为后备语言。您可能希望在应用程序启动的早期某个时候调用它。您可以在此处阅读有关语言/区域设置首选项的更多信息:国际化编程主题:获取当前语言和区域设置

回答by Gilad Novik

I had the same problem recently and I didn't want to start and patch my entire NSLocalizedStringnor force the app to restart for the new language to work. I wanted everything to work as-is.

我最近遇到了同样的问题,我不想启动和修补我的整个NSLocalizedString,也不想强制应用程序重新启动以使新语言工作。我希望一切都按原样工作。

My solution was to dynamically change the main bundle's class and load the appropriate bundle there:

我的解决方案是动态更改主包的类并在那里加载适当的包:

Header file

头文件

@interface NSBundle (Language)
+(void)setLanguage:(NSString*)language;
@end

Implementation

执行

#import <objc/runtime.h>

static const char _bundle=0;

@interface BundleEx : NSBundle
@end

@implementation BundleEx
-(NSString*)localizedStringForKey:(NSString *)key value:(NSString *)value table:(NSString *)tableName
{
    NSBundle* bundle=objc_getAssociatedObject(self, &_bundle);
    return bundle ? [bundle localizedStringForKey:key value:value table:tableName] : [super localizedStringForKey:key value:value table:tableName];
}
@end

@implementation NSBundle (Language)
+(void)setLanguage:(NSString*)language
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^
    {
        object_setClass([NSBundle mainBundle],[BundleEx class]);
    });
    objc_setAssociatedObject([NSBundle mainBundle], &_bundle, language ? [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:language ofType:@"lproj"]] : nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end

So basically, when your app starts and before you load your first controller, simply call:

所以基本上,当你的应用程序启动时,在你加载你的第一个控制器之前,只需调用:

[NSBundle setLanguage:@"en"];

When your user changes his preferred language in your setting screen, simply call it again:

当您的用户在您的设置屏幕中更改他的首选语言时,只需再次调用它:

[NSBundle setLanguage:@"fr"];

To reset back to system defaults, simply pass nil:

要重置回系统默认值,只需传递 nil:

[NSBundle setLanguage:nil];

Enjoy...

享受...

For those who need a Swift version:

对于那些需要 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)
    }
}

回答by Mauro Delrio

I usually do this in this way, but you MUST have all localization files in your project.

我通常以这种方式执行此操作,但是您必须在项目中拥有所有本地化文件。

@implementation Language

static NSBundle *bundle = nil;

+(void)initialize 
{
    NSUserDefaults* defs = [NSUserDefaults standardUserDefaults];
    NSArray* languages = [defs objectForKey:@"AppleLanguages"];
    NSString *current = [[languages objectAtIndex:0] retain];
    [self setLanguage:current];
}

/*
  example calls:
    [Language setLanguage:@"it"];
    [Language setLanguage:@"de"];
*/
+(void)setLanguage:(NSString *)l
{
    NSLog(@"preferredLang: %@", l);
    NSString *path = [[ NSBundle mainBundle ] pathForResource:l ofType:@"lproj" ];
    bundle = [[NSBundle bundleWithPath:path] retain];
}

+(NSString *)get:(NSString *)key alter:(NSString *)alternate 
{
    return [bundle localizedStringForKey:key value:alternate table:nil];
}

@end

回答by Tudor

Do not use on iOS 9. This returns nil for all strings passed through it.

不要在 iOS 9 上使用。这对通过它的所有字符串返回 nil。

I have found another solution that allows you to update the language strings, w/o restarting the app and compatible with genstrings:

我找到了另一种解决方案,它允许您更新语言字符串,而无需重新启动应用程序并与 genstrings 兼容:

Put this macro in the Prefix.pch:

将此宏放在 Prefix.pch 中:

#define currentLanguageBundle [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:[[NSLocale preferredLanguages] objectAtIndex:0] ofType:@"lproj"]]

and where ever you need a localized string use:

在需要本地化字符串的地方使用:

NSLocalizedStringFromTableInBundle(@"GalleryTitleKey", nil, currentLanguageBundle, @"")

To set the language use:

要设置语言使用:

[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObject:@"de"] forKey:@"AppleLanguages"];

Works even with consecutive language hopping like:

即使在连续语言跳跃的情况下也能工作,例如:

NSLog(@"test %@", NSLocalizedStringFromTableInBundle(@"NewKey", nil, currentLanguageBundle, @""));
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObject:@"fr"] forKey:@"AppleLanguages"];
NSLog(@"test %@", NSLocalizedStringFromTableInBundle(@"NewKey", nil, currentLanguageBundle, @""));
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObject:@"it"] forKey:@"AppleLanguages"];
NSLog(@"test %@", NSLocalizedStringFromTableInBundle(@"NewKey", nil, currentLanguageBundle, @""));
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObject:@"de"] forKey:@"AppleLanguages"];
NSLog(@"test %@", NSLocalizedStringFromTableInBundle(@"NewKey", nil, currentLanguageBundle, @""));

回答by Frédéric Feytons

As said earlier, just do:

如前所述,只需执行以下操作:

[[NSUserDefaults standardUserDefaults] setObject: [NSArray arrayWithObjects:@"el", nil] forKey:@"AppleLanguages"];

But to avoid having to restart the app, put the line in the main method of main.m, just before UIApplicationMain(...).

但是为了避免重新启动应用程序,请将该行放在 的 main 方法中main.m,就在UIApplicationMain(...)之前。

回答by object2.0

The trick to use specific language by selecting it from the app is to force the NSLocalizedStringto use specific bundle depending on the selected language ,

通过从应用程序中选择特定语言来使用特定语言的技巧是NSLocalizedString根据所选语言强制使用特定包,

here is the post i have written for this learning advance localization in ios apps

这是我为在 ios 应用程序中学习高级本地化而写的帖子

and here is the code of one sample app advance localization in ios apps

这是ios应用程序中一个示例应用程序高级本地化的代码

回答by Bart?omiej Semańczyk

What do you think about this solution for Swift 3?

您如何看待 Swift 3 的这个解决方案?

extension String {

    func localized(forLanguage language: String = Locale.preferredLanguages.first!.components(separatedBy: "-").first!) -> String {

        guard let path = Bundle.main.path(forResource: language == "en" ? "Base" : language, ofType: "lproj") else {

            let basePath = Bundle.main.path(forResource: "Base", ofType: "lproj")!

            return Bundle(path: basePath)!.localizedString(forKey: self, value: "", table: nil)
        }

        return Bundle(path: path)!.localizedString(forKey: self, value: "", table: nil)
    }
}

Simple usage:

简单用法:

"report".localized(forLanguage: "pl") //forced language
"report".localized() //default language selected by user in settings, in case when your app doesnt support selected lanaguage, the default one is selected, here is an english.

回答by geon

As Brian Webster mentions, the language needs to be set "sometime early in your application's startup". I thought applicationDidFinishLaunching:of the AppDelegateshould be a suitable place to do it, since it's where I do all other initialization.

正如布赖恩·韦伯斯特(Brian Webster)所提到的,需要“在应用程序启动的早期某个时候”设置语言。我认为applicationDidFinishLaunching:AppDelegate应该是一个合适的地方,因为它是我进行所有其他初始化的地方。

But as William Denniss mentions, that seems to have an effect only afterthe app is restarted, which is kind of useless.

但正如威廉丹尼斯提到的,这似乎只有应用程序重新启动才会生效,这没什么用。

It seems to work fine if I put the code in the main function, though:

不过,如果我将代码放在 main 函数中,它似乎可以正常工作:

int main(int argc, char *argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    // Force language to Swedish.
    [[NSUserDefaults standardUserDefaults]
     setObject:[NSArray arrayWithObject:@"sv"]
     forKey:@"AppleLanguages"];

    int retVal = UIApplicationMain(argc, argv, nil, nil);
    [pool release];
    return retVal;
}

I'd appreciate any comments on this.

我很感激对此的任何评论。

回答by dnaxxx

I like best Mauro Delrio's method. I also have added the following in my Project_Prefix.pch

我最喜欢 Mauro Delrio 的方法。我还在 Project_Prefix.pch 中添加了以下内容

#import "Language.h"    
#define MyLocalizedString(key, alt) [Language get:key alter:alt]

So if you ever want to use the standard method (that uses NSLocalizedString) you can make a quick syntax substitution in all files.

因此,如果您想使用标准方法(使用 NSLocalizedString),您可以在所有文件中进行快速语法替换。

回答by Mecki

NSLocalizedString()reads the value for the key AppleLanguagesfrom the standard user defaults ([NSUserDefaults standardUserDefaults]). It uses that value to choose an appropriate localization among all existing localizations at runtime. When Apple builds the user defaults dictionary at app launch, they look up the preferred language(s) key in the system preferences and copy the value from there. This also explains for example why changing the language settings in OS X has no effect on running apps, only on apps started thereafter. Once copied, the value is not updated just because the settings change. That's why iOS restarts all apps if you change then language.

NSLocalizedString()AppleLanguages从标准用户默认值 ( [NSUserDefaults standardUserDefaults]) 中读取键的值。它使用该值在运行时在所有现有本地化中选择适当的本地化。当 Apple 在应用程序启动时构建用户默认字典时,他们会在系统首选项中查找首选语言键并从那里复制值。例如,这也解释了为什么在 OS X 中更改语言设置对运行的应用程序没有影响,只会影响此后启动的应用程序。复制后,值不会因为设置更改而更新。这就是为什么如果您更改语言,iOS 会重新启动所有应用程序的原因。

However, all valuesof the user defaults dictionary can be overwritten by command line arguments. See NSUserDefaultsdocumentation on the NSArgumentDomain. This even includes those values that are loaded from the app preferences (.plist) file. This is really good to know if you want to change a value just once for testing.

但是,用户默认字典的所有值都可以被命令行参数覆盖。请参阅NSUserDefaults有关NSArgumentDomain. 这甚至包括从应用首选项 (.plist) 文件加载的那些值。知道您是否只想更改一次值以进行测试真的很好。

So if you want to change the language just for testing, you probably don't want to alter your code (if you forget to remove this code later on ...), instead tell Xcode to start your app with a command line parameters (e.g. use Spanish localization):

因此,如果您只想更改语言以进行测试,您可能不想更改您的代码(如果您稍后忘记删除此代码...),而是告诉 Xcode 使用命令行参数启动您的应用程序(例如使用西班牙语本地化):

enter image description here

在此处输入图片说明

No need to touch your code at all. Just create different schemes for different languages and you can quickly start the app once in one language and once in another one by just switching the scheme.

根本不需要触摸您的代码。只需为不同的语言创建不同的方案,只需切换方案,您就可以快速启动应用程序一次,一种语言,另一种语言。