How to force NSLocalizedString to use a specific language iOS APP内国际化
2016-03-16 00:00
741 查看
http://stackoverflow.com/questions/1669645/how-to-force-nslocalizedstring-to-use-a-specific-language?lq=1
activeoldestvotes
add
a comment
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 ? ios objective-c cocoa localization internationalization
| |||
add a comment |
22 Answers
activeoldestvotesup vote177down voteaccepted | 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) 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: [[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
| ||||||||||||||||||||
comments |
up vote111down vote | 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
| ||||||||||||||||||||
comments |
up vote82down vote | 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. 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: [NSBundle setLanguage:nil]; Enjoy...
| ||||||||||||||||||||
comments |
up vote36down vote | Do not use on iOS 9. This returns nil for all strings passed through it. I have found another solution that allows you to update the language strings, w/o restarting the app and compatible with genstrings: Put this macro in the 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, @""));
| ||||||||||||||||||||
comments |
up vote30down vote | 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(...).
| ||||||||||||||||||||
a comment |
up vote30down vote | 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 , here is the post i have written for this learning advance localization in ios apps and here is the code of one sample app advance localization in ios apps
| ||||||||
a comment |
up vote11down vote | I like best Mauro Delrio's method. I also have added the following in my 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.
| ||||
a comment |
up vote9down vote | 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. But as William Denniss mentions, that seems to have an effect only after the app is restarted, which is kind of useless. It seems to work fine if I put the code in the main function, though: 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.
| ||||||||||||||||
a comment |
up vote7down vote | I came up with a solution that allows you to use NSLocalizedString. I create a category of NSBundlecall NSBundle+RunTimeLanguage. The interface is like this. // 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 The implementation is like this. // 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 Than just add import NSBundle+RunTimeLanguage.hinto the files that use NSLocalizedString. As you can see I store my languageCode in a property of AppDelegate. This could be stored anywhere you'd like. This only thing I don't like about it is a Warning that NSLocalizedStringmarco redefined. Perhaps someone could help me fix this part.
| ||||||||||||||||
a comment |
up vote5down vote | 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. However, all values of 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. 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): 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.
| ||
add a comment |
up vote4down vote | In file .pch to define:#define currentLanguageBundle [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:[[NSLocale preferredLanguages] objectAtIndex:0] ofType:@"lproj"]]
| ||||||||
a comment |
up vote4down vote |
|
shareimprove this answer | edited Apr 23 '15 at 10:49 | answered Apr 22 '15 at 18:31 user3465357 412 |
We ask that you not just link to a solution in your answer; the link might stop working some day. While you don't have to remove the link from your answer, we do ask that you edit your answer to include a summary of the relevant section of the linked article. – Oblivious Sage Apr 22 '15 at 18:51 |
a comment
up vote3down vote | You could build a sub-bundle with the set of localized strings that you want to do this with, and then use NSLocalizedStringFromTableInBundle()to load them. (I'm assuming that this is content separate from the normal UI localization you might be doing on the app.)
| ||
add a comment |
up vote3down vote | Maybe you should complement with this (on .pch file after #import ):extern NSBundle* bundle; // Declared on Language.m #ifdef NSLocalizedString #undef NSLocalizedString // Delete this line to avoid warning #warning "Undefining NSLocalizedString" #endif #define NSLocalizedString(key, comment) \ [bundle localizedStringForKey:(key) value:@"" table:nil]
| ||||
a comment |
up vote2down vote | for my case i have two localized file , ja and en and i would like to force it to en if the preferred language in the system neither en or ja i'm going to edit the main.m file i 'll check whether the first preferred is en or ja , if not then i 'll change the second preferred language to en. 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])); } }
| ||||||||
a comment |
up vote2down vote | swift version NSUserDefaults.standardUserDefaults().setObject(["fr"], forKey: "AppleLanguages") NSUserDefaults.standardUserDefaults().synchronize()
| ||
add a comment |
up vote1down vote | This function will try to get localized string for current language and if it's not found it will get it using english language.- (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; }
| ||
add a comment |
up vote1down vote | You can do something like this:NSString *bundlePath = [[NSBundle mainBundle] pathForResource:@"Localizable" ofType:@"strings" inDirectory:nil forLocalization:@"es"]; NSBundle *spanishBundle = [[NSBundle alloc] initWithPath:[bundlePath stringByDeletingLastPathComponent]]; NSLocalizedStringFromTableInBundle(@"House", nil, spanishBundle, nil):
| ||
add a comment |
up vote0down vote | whatever you all do, the best way is to take the short_name for the specified language, i.e.: fr, en, nl, de, it, etc... and assign the same to a global value. make a picker view to pop up like a drop down menu (combination of a button on click of which a picker view appears from below with a list of languages) and select the language you desire. let the short name be stored internally. make a .h + .m file named LocalisedString. Set the global value of short_name to be equal to the obtained value in LocalisedString.m When the required language is selected assign the NSBundlePath to create project sub-directories for the needed language. for eg, nl.proj, en.proj. When the particular proj folder is selected call the localised string for the respective language and change the language dynamically. no rules broken.
| ||
add a comment |
up vote0down vote | I wanted to add support for a language that isn't officially supported by iOS (not listed in Language section under system settings). By following the Apple's Internationalization Tutorial and few hints here by Brian Webster and geon, I came up with this piece of code (put it in main.m): int main(int argc, char * argv[]) { @autoreleasepool { // Grab regional settings locale, for Slovenian this is either sl_SI or en_SI NSLocale *locale = [NSLocale currentLocale]; NSString *ll = [locale localeIdentifier]; // sl_SI // Grab the first part of language identifier NSArray *comp = [ll componentsSeparatedByString:@"_"]; NSString *ll1 = @"en"; if (comp.count > 0) { ll1 = comp[0]; // sl, en, ... } // Check if we already saved language (user can manually change it inside app for example) if (![[NSUserDefaults standardUserDefaults] objectForKey:@"SelectedLanguage"]) { // Slovenian (Slovenia), Slovenia if ([ll isEqualToString:@"sl_SI"] || [ll isEqualToString:@"en_SI"]) { ll1 = @"sl-SI"; // This is the part of localized path for Slovenian language that Xcode generates } // Add more unsupported languages here... [[NSUserDefaults standardUserDefaults] setObject:ll1 forKey:@"SelectedLanguage"]; // Save language } else { // Restore language as we have previously saved it ll1 = [[NSUserDefaults standardUserDefaults] objectForKey:@"SelectedLanguage"]; } // Overwrite NSLocalizedString and StoryBoard language preference [[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:ll1, @"en", @"fr", nil] forKey:@"AppleLanguages"]; // Make sure settings are stored to disk [[NSUserDefaults standardUserDefaults] synchronize]; return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } } This works well for both Storyboard and NSLocalizedString code. The code assumes that user will have an option to manually change language inside app later on. Of course, don't forget to add proper Storyboard translations and Localizable.strings translations (see link to Apple page above for how to do that).
| ||
add a comment |
up vote0down vote | Here is a decent solution for this problem, and it does not require application restart. https://github.com/cmaftuleac/BundleLocalization This implementation works by tweaking inside NSBundle. The idea is that you override the method localizedStringForKey on the instance of NSBundle object, and then call this method on a different bundle with a different language. Simple and elegant fully compatible with all types of resources.
| ||
add a comment |
up vote0down vote | Based on Tudorizer's answer to change language without leaving or restarting the application. Instead of a macro, use a class for accessing the preferred language in order to check if a specific language code is present. Below is a class used to obtain the current language bundle that is working for iOS 9: @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. [[NSUserDefaults standardUserDefaults] setObject:@[@"es"] forKey:@"AppleLanguages"]; |
相关文章推荐
- iOS 终端常用命令
- Android快速开发框架
- 记录一下iOS开发中琐碎的点点_1
- 你知道android textView文字怎么竖向排列吗
- android getItemViewType注意点
- Object-C基础-01-概述
- IOS导航栏颜色渐变与惯用属性
- cdh使用公平调度策略FairScheduler,调大最大运行app个数
- iOS提交应用至APP Store流程
- Android ViewPager
- 基础 Android数据存储方式
- Android ArrayList
- Android ListView 使用
- Android Activity的使用,持续更新
- Android AlertDialog
- Android 自定义ScrollView
- Android的进程与线程
- 时钟动画
- iOS 应用程序内部国际化,不跟随系统语言
- IOS APP 国际化 程序内切换语言实现 不重新启动系统(支持项目中stroyboard 、xib 混用。完美解决方案)