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

Ios 如何强制NSLocalizedString使用特定语言,ios,objective-c,localization,internationalization,nslocalizedstring,Ios,Objective C,Localization,Internationalization,Nslocalizedstring,在iPhone上NSLocalizedString返回iPhone语言的字符串。 是否可以强制NSLocalizedString使用特定语言来创建应用程序 使用与设备不同的语言?您可以使用要使用的本地化字符串集构建子捆绑包,然后使用来加载它们。(我假设这是与您可能在应用程序上执行的普通UI本地化不同的内容。)NSLocalizedString()(及其变体)访问NSUserDefaults中的“AppleLanguages”键,以确定用户对首选语言的设置。这将返回一组语言代码,第一个代码是用户为

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

您可以使用要使用的本地化字符串集构建子捆绑包,然后使用来加载它们。(我假设这是与您可能在应用程序上执行的普通UI本地化不同的内容。)

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

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

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

这将使德语成为应用程序的首选语言,而英语和法语则是备用语言。您可能希望在应用程序启动的早期调用它。您可以在此处阅读有关语言/区域设置首选项的更多信息:

我通常是这样做的,但您的项目中必须包含所有本地化文件

@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

如前所述,只要做:

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

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

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

但正如威廉·丹尼斯(William Denniss)提到的,这似乎只有在应用程序重新启动后才会产生效果,这是一种无用的做法

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

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;
}

我非常感谢您对这一点的评论。

我最喜欢毛罗·德里奥的方法。 我还在我的Project_Prefix.pch中添加了以下内容

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

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

从应用程序中选择特定语言的诀窍是强制
NSLocalizedString
根据所选语言使用特定捆绑包

这是我为此写的帖子


下面是一个示例应用程序的代码

也许您应该用它来补充(导入后在.pch文件中):


无论你们做什么,最好的方法是为指定的语言取一个简短的名称,例如:fr、en、nl、de、it等等。。。并将其指定给全局值

使选取器视图像下拉菜单一样弹出(点击按钮时,选取器视图会从下方显示,并显示语言列表),然后选择所需的语言。让短名称存储在内部。 制作一个名为LocalisedString的.h+.m文件

将short_name的全局值设置为等于LocalisedString.m中获得的值 选择所需语言后,分配NSBundlePath为所需语言创建项目子目录。例如,nl.proj,en.proj

选择特定项目文件夹后,调用相应语言的本地化字符串并动态更改语言


没有违反规则。

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

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

将此宏置于Prefix.pch中:

#define currentLanguageBundle [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:[[NSLocale preferredLanguages] objectAtIndex:0] ofType:@"lproj"]]
如果需要本地化字符串,请使用:

NSLocalizedStringFromTableInBundle(@"GalleryTitleKey", nil, currentLanguageBundle, @"")
要设置语言使用,请执行以下操作:

[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObject:@"de"] forKey:@"AppleLanguages"];
即使在连续的语言跳跃中也能工作,例如:

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

在文件.pch中定义:

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


#define NSLocalizedString(str,nil) NSLocalizedStringFromTableInBundle(str, nil, currentLanguageBundle, @"")

此函数将尝试获取当前语言的本地化字符串,如果找不到,将使用英语获取

- (NSString*)L:(NSString*)key
{
    static NSString* valueNotFound = @"VALUE_NOT_FOUND";
    static NSBundle* enBundle = nil;

    NSString* pl = [NSLocale preferredLanguages][0];
    NSString* bp = [[NSBundle mainBundle] pathForResource:pl ofType:@"lproj"];
    NSBundle* b = [NSBundle bundleWithPath:bp];

    NSString* s = [b localizedStringForKey:key value:valueNotFound table:nil];
    if ( [s isEqualToString:valueNotFound] ) {
        if ( !enBundle ) {
            bp = [[NSBundle mainBundle] pathForResource:@"en" ofType:@"lproj"];
            enBundle = [NSBundle bundleWithPath:bp];
        }
        s = [enBundle localizedStringForKey:key value:key table:nil];
    }

    return s;
}
[[NSUserDefaults standardUserDefaults] setObject:@[@"es"] forKey:@"AppleLanguages"];

我想添加对iOS不支持的语言的支持(未在“系统设置”下的“语言”部分列出)。通过遵循Brian Webster和geon在此提供的一些提示,我得到了这段代码(放在main.m中):

这适用于故事板和NSLocalizedString代码。代码假定用户稍后可以选择手动更改应用程序内部的语言


当然,别忘了添加正确的故事板翻译和本地化.strings翻译(请参阅上面苹果页面的链接了解如何操作)。

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

我的解决方案是动态更改主捆绑包的类并在那里加载相应的捆绑包:

头文件

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

#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
当用户在设置屏幕中更改其首选语言时,只需再次调用:

[NSBundle setLanguage:@"fr"];
要重置回系统默认值,只需传递nil:

[NSBundle setLanguage:nil];
享受

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

对于我的案例,我有两个本地化文件,ja和en

如果系统中的首选语言不是en或ja,我想强制它使用en

我要编辑main.m文件

我会检查第一个首选语言是英语还是日语,如果不是,我会将第二个首选语言改为英语

int main(int argc, char *argv[])
{

    [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"AppleLanguages"];
    [[NSUserDefaults standardUserDefaults] synchronize];

    NSString *lang = [[NSLocale preferredLanguages] objectAtIndex:0];

    if (![lang isEqualToString:@"en"]  &&  ![lang isEqualToString:@"ja"]){

        NSMutableArray *array = [[NSMutableArray alloc] initWithArray:[NSLocale preferredLanguages]];
        [array replaceObjectAtIndex:1 withObject:@"en"];

        [[NSUserDefaults standardUserDefaults] setObject:array forKey:@"AppleLanguages"];
        [[NSUserDefaults standardUserDefaults] synchronize];


    }

    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));


    }


}
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)
    }
}
int main(int argc, char *argv[])
{

    [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"AppleLanguages"];
    [[NSUserDefaults standardUserDefaults] synchronize];

    NSString *lang = [[NSLocale preferredLanguages] objectAtIndex:0];

    if (![lang isEqualToString:@"en"]  &&  ![lang isEqualToString:@"ja"]){

        NSMutableArray *array = [[NSMutableArray alloc] initWithArray:[NSLocale preferredLanguages]];
        [array replaceObjectAtIndex:1 withObject:@"en"];

        [[NSUserDefaults standardUserDefaults] setObject:array forKey:@"AppleLanguages"];
        [[NSUserDefaults standardUserDefaults] synchronize];


    }

    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));


    }


}
// 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
// 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
NSString *bundlePath = [[NSBundle mainBundle] pathForResource:@"Localizable" ofType:@"strings" inDirectory:nil forLocalization:@"es"];


NSBundle *spanishBundle = [[NSBundle alloc] initWithPath:[bundlePath stringByDeletingLastPathComponent]];

NSLocalizedStringFromTableInBundle(@"House", nil, spanishBundle, nil):
NSUserDefaults.standardUserDefaults().setObject(["fr"], forKey: "AppleLanguages")
NSUserDefaults.standardUserDefaults().synchronize()
- (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];
}
- (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];
}
@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
// 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")
[[NSUserDefaults standardUserDefaults] setObject:@[@"es"] forKey:@"AppleLanguages"];
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)
    }
}
"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.
let languages = ["bs", "zh-Hant", "en", "fi", "ko", "lv", "ms", "pl", "pt-BR", "ru", "sr-Latn", "sk", "es", "tr"]
UserDefaults.standard.set([languages[0]], forKey: "AppleLanguages")
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
    }
}
Locale.preferredLanguage = "uk"

label.text = "localizedKey".localized
    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)
    }
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!)!
    }
}
NSLocalizedString ("StringToLocalize", comment: “")
let customLang = CustomLanguage() //declare at top
let bundleLanguage = customLang.createBundle()

NSLocalizedString("StringToLocalize", tableName: nil, bundle: bundleLanguage, value: "", comment: “”) //use in each String
import Foundation
import UIKit

// Your initialisation code here
let langCultureCode: String = "LANGUAGE_CODE"

UserDefaults.standard.set([langCultureCode], forKey: "AppleLanguages")
UserDefaults.standard.synchronize()

UIApplicationMain(CommandLine.argc, CommandLine.unsafeArgv, nil, NSStringFromClass(AppDelegate.self))