如何强制NSLocalizedString使用特定语言

Cod*_*kes 261 localization objective-c internationalization nslocalizedstring ios

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

Bri*_*ter 259

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

如果您希望使用setObject:forKey:方法设置自己的语言列表,则可以覆盖自己应用程序的全局设置.这将优先于全局设置值,并返回到应用程序中执行本地化的任何代码.这个代码看起来像这样:

[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:@"de", @"en", @"fr", nil] forKey:@"AppleLanguages"];
[[NSUserDefaults standardUserDefaults] synchronize]; //to make the change immediate
Run Code Online (Sandbox Code Playgroud)

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

  • 丹尼斯; 如果您在启动应用程序之前设置语言首选项*,它似乎会更好.我在main()函数中执行它,然后调用UIApplicationMain().一旦它实际运行,它将不会更改使用的语言,而只是设置下一次的首选项. (50认同)
  • 如果在main()中设置,AppleLanguages将在运行时更改语言 - true!但是......安装应用程序的第一时间,在调用UIApplicationMain()之后初始化nsuserdefaults,这将忽略之前设置的AppleLanguages,并且需要对应用程序进行丑陋的强制重启才能看到所需的语言. (18认同)
  • 这对我不起作用,无论如何都会使用默认语言. (4认同)
  • 您必须在初始化UIKit之前设置语言,并且必须指定完整的语言+区域区域设置 - 请查看完整示例http://blog.federicomestrone.com/2010/09/15/iphone-apps-and-装置语言设定/ (3认同)
  • 这不适用于Localizable.stringsdict中定义的多个项目.知道是否有可能的修复? (3认同)
  • 这对我不起作用,所以我用[,[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:@"el",nil] forKey:@"AppleLanguages"]; (2认同)

Gil*_*vik 146

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

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

头文件

@interface NSBundle (Language)
+(void)setLanguage:(NSString*)language;
@end
Run Code Online (Sandbox Code Playgroud)

履行

#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
Run Code Online (Sandbox Code Playgroud)

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

[NSBundle setLanguage:@"en"];
Run Code Online (Sandbox Code Playgroud)

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

[NSBundle setLanguage:@"fr"];
Run Code Online (Sandbox Code Playgroud)

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

[NSBundle setLanguage:nil];
Run Code Online (Sandbox Code Playgroud)

请享用...

对于那些需要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)
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 嗨@ Wirsing,到目前为止这对我很有用.我甚至上传了一个应用程序到商店,没有苹果抱怨. (7认同)
  • 这是一个很好的解决方案,我也在这里引用它:https://medium.com/ios-apprentice/905e4052b9de (7认同)
  • @Gilad你的方法非常适合动态字符串(我在localizable.strings中定义的字符串),但对于storyboard按钮和标签,它只适用于main方法.那么你可以扩展你的答案,包括UI控件本地化?我的意思是在我调用[NSBundle setLanguage:@"??"]后,如何刷新(不关闭)故事板;? (3认同)
  • 感谢您的解决方案,但它不适用于xib视图 (2认同)
  • 对于XIB/storyboard:您需要确保在加载视图之前调用此方法.到目前为止更新的视图直播:在我的情况下,我只是重新加载了视图控制器(我把它放在导航控制器中,只是更换了控制器.重新加载它 - 它得到了新的翻译字符串).我正在为它做一个cocoapod,我可能也会在那里加一个例子.敬请关注... (2认同)

Mau*_*rio 137

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

@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
Run Code Online (Sandbox Code Playgroud)

  • 优秀的毛罗.我注意到它也可以用于项目之外的文件.如果出于某种原因(如我的情况),您需要从网络下载字符串文件,并将它们存储在"Documents"目录中(文件夹结构为Documents/en.lproj/Localizable.strings,Documents/fr.lproj/Localizable.strings,...).你甚至可以制作一个NSBundle.只需将此代码用于路径:NSString*path = [NSHomeDirectory()stringByAppendingPathComponent:[NSString stringWithFormat:@"/ Documents /%@.lproj",l,nil]]; (4认同)

Tud*_*dor 41

不要在iOS 9上使用.对于通过它传递的所有字符串,它返回nil.

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

将此宏放在Prefix.pch中:

#define currentLanguageBundle [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:[[NSLocale preferredLanguages] objectAtIndex:0] ofType:@"lproj"]]
Run Code Online (Sandbox Code Playgroud)

以及在哪里需要使用本地化字符串:

NSLocalizedStringFromTableInBundle(@"GalleryTitleKey", nil, currentLanguageBundle, @"")
Run Code Online (Sandbox Code Playgroud)

要设置语言使用:

[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObject:@"de"] forKey:@"AppleLanguages"];
Run Code Online (Sandbox Code Playgroud)

即使连续语言跳跃也能正常工作:

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, @""));
Run Code Online (Sandbox Code Playgroud)

  • @ powerj1984:不,它将仅更改源文件中的字符串。如果要更改xib语言,则必须从所选语言包中手动重新加载xib。 (2认同)

小智 32

如前所述,只需:

[[NSUserDefaults standardUserDefaults] setObject: [NSArray arrayWithObjects:@"el", nil] forKey:@"AppleLanguages"];
Run Code Online (Sandbox Code Playgroud)

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

  • 如果使用Swift,那么没有main.m? (3认同)
  • 非常有用的答案!PS对于非初学者来说听起来很明显,但是你应该在`NSAutoreleasePool*pool ..`之后插入那行或者一些自动释放的对象会泄漏. (2认同)

obj*_*2.0 31

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

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

这里是ios应用程序中一个示例应用程序预先本地化的代码


Bar*_*zyk 13

您对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)
    }
}
Run Code Online (Sandbox Code Playgroud)

用法简单:

"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.
Run Code Online (Sandbox Code Playgroud)


geo*_*eon 12

正如Brian Webster所提到的那样,语言需要"在应用程序启动的早期"设置.我想到applicationDidFinishLaunching:AppDelegate应该是一个合适的地方做,因为这就是我做的所有其他初始化.

但正如William Denniss所提到的那样,只有应用程序重新启动才会产生影响,这有点无用.

如果我把代码放在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;
}
Run Code Online (Sandbox Code Playgroud)

我对此表示感谢.

  • 这不是问题,只需使用`[[NSUserDefaults standardUserDefaults] synchronize];``之后调用`setObject:forKey:` (3认同)
  • 如果要让用户设置一种语言然后告诉他们重新启动应用程序,请记住,如果用户重新启动的速度过快,则可能不会保存NSUserDefaults。大多数用户的运行速度并不快,但是当您测试应用程序时,您可能会过快并看到不一致的行为。我花了几个小时才意识到为什么我的语言切换有时会起作用而有时却没有起作用! (2认同)

小智 11

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

#import "Language.h"    
#define MyLocalizedString(key, alt) [Language get:key alter:alt]
Run Code Online (Sandbox Code Playgroud)

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


Mec*_*cki 11

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

但是,用户默认字典的所有值都可以被命令行参数覆盖.请参阅有关的NSUserDefaults文档NSArgumentDomain.这甚至包括从app首选项(.plist)文件加载的值.如果您想要仅为测试更改一次值,这真的很好.

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

在此输入图像描述

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


小智 10

我想出了一个允许你使用的解决方案NSLocalizedString.我创建了一个NSBundle电话类别NSBundle+RunTimeLanguage.界面是这样的.

// NSBundle+RunTimeLanguage.h
#import <Foundation/Foundation.h>
@interface NSBundle (RunTimeLanguage)
#define NSLocalizedString(key, comment) [[NSBundle mainBundle] runTimeLocalizedStringForKey:(key) value:@"" table:nil]
- (NSString *)runTimeLocalizedStringForKey:(NSString *)key value:(NSString *)value table:(NSString *)tableName;
@end
Run Code Online (Sandbox Code Playgroud)

实现是这样的.

// NSBundle+RunTimeLanguage.m
#import "NSBundle+RunTimeLanguage.h"
#import "AppDelegate.h"

@implementation NSBundle (RunTimeLanguage)

- (NSString *)runTimeLocalizedStringForKey:(NSString *)key value:(NSString *)value table:(NSString *)tableName
{
    AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
    NSString *path= [[NSBundle mainBundle] pathForResource:[appDelegate languageCode] ofType:@"lproj"];
    NSBundle *languageBundle = [NSBundle bundleWithPath:path];
    NSString *localizedString=[languageBundle localizedStringForKey:key value:key table:nil];
    return localizedString;
}
@end
Run Code Online (Sandbox Code Playgroud)

而不仅仅是将import添加NSBundle+RunTimeLanguage.h到使用的文件中NSLocalizedString.

如您所见,我将languageCode存储在属性中AppDelegate.这可以存储在您喜欢的任何地方.

我唯一不喜欢的是NSLocalizedString马克重新定义的警告.也许有人可以帮我解决这个问题.


daa*_*aal 9

Swift版本:

NSUserDefaults.standardUserDefaults().setObject(["fr"], forKey: "AppleLanguages")
NSUserDefaults.standardUserDefaults().synchronize()
Run Code Online (Sandbox Code Playgroud)


use*_*357 8

简而言之 :

本地化您的应用程序

您要做的第一件事就是使用至少两种语言(本例中为英语和法语)本地化您的应用程序.

覆盖NSLocalizedString

在您的代码中NSLocalizedString(key, comment),使用如下MYLocalizedString(key, comment)定义的宏而不是使用: #define MYLocalizedString(key, comment) [[MYLocalizationSystem sharedInstance] localizedStringForKey:(key) value:(comment)];

这个MYLocalizationSystem单身人士将:

  • 通过设置正确的本地化NSBundle用户请求来设置语言
  • 根据此先前设置的语言返回本地化的NSString

设置用户语言

当用户用法语更改应用程序语言时,请致电 [[MYLocalizationSystem sharedInstance] setLanguage:@"fr"];

- (void)setLanguage:(NSString *)lang
{
    NSString *path = [[NSBundle mainBundle] pathForResource:lang ofType:@"lproj"];
    if (!path)
    {
        _bundle = [NSBundle mainBundle];
        NSLog(@"Warning: No lproj for %@, system default set instead !", lang);
        return;
    }

    _bundle = [NSBundle bundleWithPath:path];
}
Run Code Online (Sandbox Code Playgroud)

在此示例中,此方法将本地化的bundle设置为fr.lproj

返回本地化字符串

设置本地化包后,您将能够使用此方法从他那里获取正确的本地化字符串:

- (NSString *)localizedStringForKey:(NSString *)key value:(NSString *)value
{
    // bundle was initialized with [NSBundle mainBundle] as default and modified in setLanguage method
    return [self.bundle localizedStringForKey:key value:value table:nil];
}
Run Code Online (Sandbox Code Playgroud)

希望这会帮助你.

您可以在NSWinery.io的这篇文章中找到更多细节


Chi*_*buZ 7

Swift 3扩展:

extension Locale {
    static var preferredLanguage: String {
        get {
            return self.preferredLanguages.first ?? "en"
        }
        set {
            UserDefaults.standard.set([newValue], forKey: "AppleLanguages")
            UserDefaults.standard.synchronize()
        }
    }
}

extension String {
    var localized: String {

    var result: String

    let languageCode = Locale.preferredLanguage //en-US

    var path = Bundle.main.path(forResource: languageCode, ofType: "lproj")

    if path == nil, let hyphenRange = languageCode.range(of: "-") {
        let languageCodeShort = languageCode.substring(to: hyphenRange.lowerBound) // en
        path = Bundle.main.path(forResource: languageCodeShort, ofType: "lproj")
    }

    if let path = path, let locBundle = Bundle(path: path) {
        result = locBundle.localizedString(forKey: self, value: nil, table: nil)
    } else {
        result = NSLocalizedString(self, comment: "")
    }
        return result
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

Locale.preferredLanguage = "uk"

label.text = "localizedKey".localized
Run Code Online (Sandbox Code Playgroud)


小智 6

在 swift 4 中,我已经解决了它而无需重新启动或使用库。

在尝试了很多选项后,我找到了这个函数,在那里你传递了你想翻译的 stringToLocalize(Localizable.String,字符串文件),以及你想翻译的语言,它返回的是你在字符串文件中的字符串:

    func localizeString (stringToLocalize: String, language: String) -> String
    {
        let path = Bundle.main.path (forResource: language, ofType: "lproj")
        let languageBundle = Bundle (path: path!)
        return languageBundle! .localizedString (forKey: stringToLocalize, value: "", table: nil)
    }
Run Code Online (Sandbox Code Playgroud)

考虑到这个函数,我在一个 Swift 文件中创建了这个函数:

struct CustomLanguage {

    func createBundlePath () -> Bundle {
        let selectedLanguage = //recover the language chosen by the user (in my case, from UserDefaults)
        let path = Bundle.main.path(forResource: selectedLanguage, ofType: "lproj")
        return Bundle(path: path!)!
    }
}
Run Code Online (Sandbox Code Playgroud)

要从整个应用程序以及在其余 ViewControllers 的每个字符串中访问,而不是放置:

NSLocalizedString ("StringToLocalize", comment: “")
Run Code Online (Sandbox Code Playgroud)

我已将其替换为

let customLang = CustomLanguage() //declare at top
let bundleLanguage = customLang.createBundle()

NSLocalizedString("StringToLocalize", tableName: nil, bundle: bundleLanguage, value: "", comment: “”) //use in each String
Run Code Online (Sandbox Code Playgroud)

我不知道这是否是最好的方法,但我发现它非常简单,并且对我有用,希望对您有所帮助!


小智 5

在文件.pch中定义:

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


#define NSLocalizedString(str,nil) NSLocalizedStringFromTableInBundle(str, nil, currentLanguageBundle, @"")
Run Code Online (Sandbox Code Playgroud)


归档时间:

查看次数:

139926 次

最近记录:

5 年,11 月 前