NSLocalizedString
返回iPhone所使用的语言对应的字符串。是否有可能强制NSLocalizedString
使用指定的语言,使得应用程序与设备语言不同?NSLocalizedString
返回iPhone所使用的语言对应的字符串。是否有可能强制NSLocalizedString
使用指定的语言,使得应用程序与设备语言不同?NSLocalizedString()
(及其变体)访问NSUserDefaults
中的“AppleLanguages”键,以确定用户首选语言设置。这将返回一个语言代码数组,其中第一个是用户为其手机设置的语言,后续的语言代码用作备用选项,如果在首选语言中找不到资源,则使用这些备用选项。(在桌面上,用户可以在系统偏好设置中指定多个语言并进行自定义排序)setObject:forKey:
方法。这将优先于全局设置值,并返回到执行本地化的应用程序中的任何代码中。此操作的代码类似于:[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:@"de", @"en", @"fr", nil] forKey:@"AppleLanguages"];
[[NSUserDefaults standardUserDefaults] synchronize]; //to make the change immediate
这将使德语成为您的应用程序首选语言,英语和法语作为备选项。您需要在应用程序启动早期调用此功能。您可以在此处阅读有关语言/区域设置首选项的更多信息:国际化编程主题:获取当前语言和区域设置
我最近也遇到了同样的问题,我不想修改整个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:@"en"];
[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)
}
}
我通常会以这种方式处理,但是你需要在项目中拥有所有的本地化文件。
@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
不要在iOS 9上使用。这会对通过它传递的所有字符串返回nil。
我找到了另一种解决方案,可以更新语言字符串,而无需重新启动应用程序,并且与genstrings兼容:
将此宏放入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, @""));
如前所述,只需执行:
[[NSUserDefaults standardUserDefaults] setObject: [NSArray arrayWithObjects:@"el", nil] forKey:@"AppleLanguages"];
但是为了避免重新启动应用程序,将该行代码放在main.m
的主方法中,在UIApplicationMain
(...)之前。
[[NSBundle mainBundle] URLForResource:withExtension:]
之前,此操作将无法执行。 - KofUIApplicationMain(argc
之前添加了这个代码,但是语言更改只有在重新启动后才会生效。有什么方法可以避免这种情况吗? - user3752049使用应用程序中的特定语言的技巧是通过强制NSLocalizedString
根据所选语言选择特定的包来使用。
这里是我为此撰写的帖子学习iOS应用程序中的高级本地化
这是一个示例应用程序的代码iOS应用程序中的高级本地化
您认为这个适用于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)
}
}
简单使用:
"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.
正如Brian Webster所提到的那样,语言需要“在应用程序启动的早期某个时候”设置。我认为AppDelegate
的applicationDidFinishLaunching:
方法应该是一个适合的地方,因为在这里我进行了所有其他的初始化。
但正如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;
}
我很感激对此的任何评论。
setObject:forKey:
后加上 [[NSUserDefaults standardUserDefaults] synchronize];
即可。该操作会使数据立即写入磁盘。 - Ben BaronNSLocalizedString()
从标准用户默认项([NSUserDefaults standardUserDefaults]
)中读取键 AppleLanguages
的值。它使用该值在运行时从所有现有本地化中选择适当的本地化。当 Apple 在应用程序启动时构建用户默认项字典时,它们在系统偏好设置中查找首选语言键,并将其复制到那里。这也解释了为什么在 OS X 中更改语言设置对正在运行的应用程序没有影响,只对随后启动的应用程序生效。一旦复制,该值就不会因设置更改而更新。这就是为什么如果您更改了语言,则 iOS 会重新启动所有应用程序。
但是,命令行参数可以覆盖用户默认项字典的所有值。请参阅NSUserDefaults
文档上关于NSArgumentDomain
的说明。这甚至包括从应用程序首选项 (.plist) 文件加载的那些值。如果您想要进行一次性测试并更改某个值,这真的非常重要。
因此,如果您只想测试更改语言,则可能不希望更改代码(如果您忘记稍后删除此代码...),而是告诉Xcode使用命令行参数启动应用程序(例如,使用西班牙语本地化):
完全不需要修改代码。只需为不同的语言创建不同的方案,并通过切换方案一次在一个语言中快速启动应用程序。
Options
中的语言,因为在较新版本的Xcode中,Apple也提供此选项。 - Mecki我最喜欢Mauro Delrio的方法。 我还在我的Project_Prefix.pch中添加了以下内容。
#import "Language.h"
#define MyLocalizedString(key, alt) [Language get:key alter:alt]
所以,如果你想使用标准方法(使用 NSLocalizedString),你可以在所有文件中快速进行语法替换。