I am changing my iOS application preferred language dynamically using this setting:
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObject:#"ar"] forKey:#"AppleLanguages"];
Then I load a localised resource file from the main NSBundle object, but the loaded file isn't of the new language, it's loaded in the default english language until I restart the application totally then it loads the arabic localisation.
I want to force NSBundle to load the resource file in the new language #"ar" not the language been set when app starts. How?
Your method is a hacky way to get what you need, and requires app restart to take effect.
It is best to use NSLocalizedStringFromTableInBundle instead of NSLocalizedString, and provide the bundle for that language.
NSString* path = [[NSBundle mainBundle] pathForResource:#"ar" ofType:#"lproj"];
NSBundle* ar_bundle = [NSBundle bundleWithPath:path];
NSLocalizedStringFromTableInBundle(#"str", nil, ar_bundle, #"comment");
If you put the bundle in a global scope, you can create a macro for ease:
#define ARLocalizedString(str, cmt) NSLocalizedStringFromTableInBundle(str, nil, ar_bundle, cmt)
I have tried this and its working fine without restarting the app:
//Use this in constants
#ifdef NSLocalizedString
#undef NSLocalizedString
#endif
#define NSLocalizedString(str, cmt) NSLocalizedStringFromTableInBundle(str, nil, newLangbundle, cmt)
newLangbundle --> Define a global variable in .pch and vary it according to language selection using this,
NSString* path = [[NSBundle mainBundle] pathForResource:#"th" ofType:#"lproj"];
newLangbundle = [NSBundle bundleWithPath:path];
You need to synchronize user defaults after changing the language:
[[NSUserDefaults standardUserDefaults] default synchronize]
Related
enter image description hereIn iOS, can we change App localization in hindi?
I tried following examples but its only support info.plist values
https://code.tutsplus.com/tutorials/ios-sdk-localization-with-nslocalizedstring--mobile-11603
Yes, we can have any language in app.
You need to have language selection inside the app instead of device's setting language.
Define all keywords in localization bundle.
NSString *selected_lng_code = #"hi"
NSString *path = [[NSBundle mainBundle] pathForResource:selected_lng_code ofType:#"lproj" ];
NSBundle *localizedBundle = [NSBundle bundleWithPath:path];
NSString *translated_word = [localizedBundle localizedStringForKey:#"Hello" value:#"" table:nil];
You have to create on InfoPlist.strings file for localizing Info.plist value. To do so, go to File -> New -> File, choose Strings File under Resource tab of iOS, name it InfoPlist, and create it. Now open that file and add Info.plist values you want to localize like:
"CFBundleDisplayName" = "<app name>". This will be your Base file.
For your desired language, select your localized files and add translated strings.
IOS application provide the support of localization through the Localizable.strings file. If I want to change the file name for some obvious reasons where would I have to put that reference.
Can anyone please help.
How iOS localization works:
As you would already know, iOS provides a nice API for getting localized string as following.
NSString *stringValue = [[NSBundle mainBundle] localizedStringForKey:key
value:#""
table:nil];
And it also provides a macro for quick access as:
#define NSLocalizedString(key, comment) \
[[NSBundle mainBundle] localizedStringForKey:(key) value:#"" table:nil]
iOS, by default, looks for strings in Localizable.strings file. However, we can also provide a custom file for iOS to look for strings into. And this is where things get interesting.
To provide a custom file, we can use the API as mentioned above in following manner.
NSString *localizedString = [[NSBundle mainBundle] localizedStringForKey:key
value:#""
table:#"AnotherLocalizableStringsFile"];
The table parameter takes a string argument AnotherLocalizableStringsFile which is the file containing the strings.
Another interesting parameter is the value parameter that takes in a string that should be returned in case no string is found matching the given key.
So following piece of code would return Invalid Key assuming the provided key does not exist in the file.
NSString *stringValue = [[NSBundle mainBundle] localizedStringForKey:#"Wrong_key_passed"
value:#"Invalid Key"
table:#"TargetLocalizable"];
The solution:
By using the combination of these two interesting parameters, we can devise a method to suit our requirements. We will ask iOS to look for strings specific to target in target specific strings file and fall back to Localizablestrings file when it comes to loading generic strings common to all targets.
Here’s how the piece of code looks like.
NSString *stringValue = [[NSBundle mainBundle] localizedStringForKey:#"Key"
value:NSLocalizedString(#"Key", nil)
table:#"TargetLocalizable"];
So, iOS looks for the string first in the given TargetLocalizable.strings file. If it doesn’t find it there, it would search in the base Localizable.strings file.
So all I had to do was to place all the strings common to all targets in Localizable.strings file and put the additional and overridden strings specific to the target in TargetLocalizable.strings file.
The name is 'fixed' - a localizable is named Localizable. you can only decide to not use NSBundle localization and roll your own stuff
I would like to be able to set app preferences at build time in my iOS project. I know that I can create different targets in xcode but I think with the amount of preferences I might end of making I could end up with a nightmarish amount of targets in my project.
An easy example is setting the a default integer for a default called 'amount'. Currently 'amount' is defined in a plist file in my app called 'preferences.plist'. I load that plist file and register defaults with that plist on NSUserDefaults.
NSURL *preferencesFile = [[NSBundle mainBundle] URLForResource:#"preferences" withExtension:#"plist"];
NSDictionary *defaultPreferences = [NSDictionary dictionaryWithContentsOfURL:defaultPreferencesFile];
[[NSUserDefaults standardUserDefaults] registerDefaults:preferences];
I assume I could write a script to modify the preferences.plist file before I build, then build it. However I think might become a nightmare when I need to mod a bunch of different preferences.
End game is to have jenkins build my IPAs. I would like to easily create multiple jenkins builds that will point to the same code based but build my app with different preferences.
Android has flavors, and the ability to set resource values. Does iOS have a something similar that I can use to build these different 'flavors' of apps?
I use a Jenkins build action to inject the appropriate variables into the plist before the Xcode build:
plutil -replace MyBuildBranch -string ${BRANCH} MyProj/MyProj-Info.plist
I then read that value at runtime using something like:
NSBundle * bundle = [NSBundle bundleForClass:[AppDelegate class]];
NSString * myBuildBranch = bundle.infoDictionary[#"MyBuildBranch"]
I don't have enough working experience with Android.
I'd approach this with multiple plists.. One for each flavor..
And i'll try either of the below options --
I'd have Jenkins swap the plist based on the flavor i am trying to build.. the script will pick the right plist for a given flavor
I will define compile-time MACROS for each flavor and load the appropriate plist.. something like this
#ifdef FLAVOUR1
NSURL *preferencesFile = [[NSBundle mainBundle] URLForResource:#"preferences-flavour1" withExtension:#"plist"];
NSDictionary *defaultPreferences = [NSDictionary dictionaryWithContentsOfURL:defaultPreferencesFile];
[[NSUserDefaults standardUserDefaults] registerDefaults:preferences];
#endif
#ifdef FLAVOUR2
NSURL *preferencesFile = [[NSBundle mainBundle] URLForResource:#"preferences-flavour2" withExtension:#"plist"];
NSDictionary *defaultPreferences = [NSDictionary dictionaryWithContentsOfURL:defaultPreferencesFile];
[[NSUserDefaults standardUserDefaults] registerDefaults:preferences];
#endif
#ifdef FLAVOUR2
NSURL *preferencesFile = [[NSBundle mainBundle] URLForResource:#"preferences-flavour3" withExtension:#"plist"];
NSDictionary *defaultPreferences = [NSDictionary dictionaryWithContentsOfURL:defaultPreferencesFile];
[[NSUserDefaults standardUserDefaults] registerDefaults:preferences];
#endif
in my application I must have two languages, but the problem is that they should be switched by pressing a button in the application (on the first screen, or screen settings). As I understand, all the methods of localization (Localizing the Storyboard, Localizing Dynamic Strings) based on the language settings of the iPhone. The only option that comes to my mind - do it by the record in NSUserDefault about language preference, and in ViewDidLoad methods of all ViewControllers check the record about language and in accordance with it set strings, picture and so on. Can it be done on a more clever way?
Try this:
If de is the new language selected by the user. Also assure that you are reinitiating the current view.
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:#"de", nil]
forKey:#"AppleLanguages"];
See the below link and source code
http://learning-ios.blogspot.com/2011/04/advance-localization-in-ios-apps.html
and here is the code of one sample app https://github.com/object2dot0/Advance-Localization-in-ios-apps
All the best.
this tutorial & sample app helpful you more
localization-tutorial-for-ios
ios-programming-tutorial-localization-apps
Ya The way you are following for Localization is not up to Mark.
Please Follow this beautiful tutorial http://www.raywenderlich.com/2876/localization-tutorial-for-ios
You have to manually mange the currently selected language into user default and load the bundle folder according to selected language key like, i have done this and worked like charm
if ([currentLanguage isEqualToString:#"fr"]) { // If French.
languageBundle = nil;
NSString* path = [[NSBundle mainBundle] pathForResource:#"fr" ofType:#"lproj"];
languageBundle = [NSBundle bundleWithPath:path];
}
I have seems some apps can change the language internally within the app without the need of restarting the app, I am wondering how they are implemented.
For example, for us using NSLocalizedString, I know it is possible to set the language at runtime at main.m when your AppDelegate is not initialized, but once it is initialized (particularly your view controller is created), change it has not effect until the next restart
[[NSUserDefaults standardUserDefaults]
setObject:[NSMutableArray arrayWithObjects:language, nil]
forKey:#"AppleLanguages"];
Anyone have idea how those dynamic language change can be done without restarting the app?
There's some discussion of other approaches here, in particular a notification based approach:
iOS: How to change app language programmatically WITHOUT restarting the app?
In my view there are really three tasks here:
(1) re-localization of resources automatically loaded from nibs. (for example if you dynamically instantiate another custom UIView from a nib, the "old" language strings and settings (images, text direction) will still be loaded)
(2) re-localization of strings currently displayed on the screen.
(3) re-localization of strings inserted by the developer (you) in program code.
Let's start with (3). If you look for the definition you will notice that NSLocalizedString is a macro. So if you don't want to change existing code too much, you can probably solve the problem of (3) by creating a new header file. In that header file, #undef and then re-#define NSLocalizedString to pick the localized string from the appropriate place--not the one that iOS defaults to, but one that you keep track of in some global variable (e.g., in an app delegate ivar). If you don't want to redefine NSLocalizedString but you still make your own alternative , you should probably still #undef NSLocalizedString if you don't want future developers to accidentally call it instead of the macro you replace it with. Not an ideal solution, but maybe the most practical.
As for (1), if you haven't done your localization in Interface Builder, but rather you do it dynamically in viewDidLoad, etc., no problem. You can use the same behavior just discussed (i.e., the modified NSLocalizedString, etc.). Otherwise you can either (a) implement a notification system as described in the link above (complicated), or (b) consider moving localization from IB to viewDidLoad, or (c) try overriding initWithNibName: and swap out the object loaded with the old language resources, with one loaded with the new language resources. This was an approach mentioned by Mohamed at the very bottom of this discussion: http://learning-ios.blogspot.ca/2011/04/advance-localization-in-ios-apps.html. He claims it causes problems (viewDidLoad isn't called). Even if it doesn't work, trying it out might point you towards something that does.
Finally, (2) is presumably the easiest task: just remove and re-add the current view (or in some cases, just redraw it).
the idea is to write a new macro like NSLocalizedString which should check if to take the translation from another specific bundle or not.
The method 2 in this article explain exactly how to do it.
In this particular case, the author doesn't use a new macro, but directly set a custom class for [NSBundle mainBundle].
I hope that #holex will understand the problem reading this.
I'm always using this way, it works perfectly, it might help you as well.
you should set all the texts with NSLocalizableString(...) for the UI for the current language in the -viewWillAppear: method of your every UIViewController.
using this way you (I mean, the users) don't need to restart the application after changing the language of iOS in the Settings.
of course, I'm using the Apple's standard localisation architecture.
UPDATE on (24 Oct 2013)
I've experienced the –viewWillAppear: method won't be performed for the actual view when the application enters to foreground; to solve that issue I also commit the procedure (see above) when I receive UIApplicationWillEnterForegroundNotification notification in the view.
My implementation uses a class to change the language and access the current language bundle. It's an example so if you were to use different languages than I am then change the methods to use your exact language codes.
This class will access the preferred languages from NSLocale and take the first object which is the language being used.
#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 code.
// Change the preferred language to Spanish
[[NSUserDefaults standardUserDefaults] setObject:#[#"es"] forKey:#"AppleLanguages"];
I was stuck in same issue, my requirement was "User can select language from drop down & application have to work according selected language (English or arabic)" What i have done i create two XIB and fetch XIB and Text according selected language. In this way user can select language. I used NSBundle for the same. like
For XIB
self.homeScreen = [[HomeScreen alloc] initWithNibName:#"HomeScreen" bundle:[CommonData sharedCommonData].languageBundle];
For Text
_lblHeading.text = [self languageSelectedStringForKey:#"ViewHeadingInfo"];
/**
This method is responsible for selecting language bundle according to user's selection.
#param: the string which is to be converted in selected language.
#return: the converted string.
#throws:
*/
-(NSString*) languageSelectedStringForKey:(NSString*) key
{
NSString* str=[[CommonData sharedCommonData].languageBundle localizedStringForKey:key value:#"" table:nil];
return str;
}
You need to load another bundle like this(where #"en" could be locale you need):
NSString *path = [[NSBundle mainBundle] pathForResource:#"en" ofType:#"lproj"];
NSBundle *languageBundle = [NSBundle bundleWithPath:path];
and make macros/function like NSLocalizedString which use your loaded bundle or use methods on that bundle directly like this
[languageBundle localizedStringForKey:key value:value table:tableName];
[[NSBundle mainBundle] localizations] lists all app localizations(including "Base").
Also I wrote helper class which does this(note that it has ReactiveCocoa as a dependency). It allows language change without app restart and sends current locale each time it's changed.