Localization fallback language - ios

I want to localize a couple of strings in my iPhone app and it works so far for the 5 languages I've selected. However, I have one issue, if I haven't defined a key for a specific language I want it to choose english as a fallback because I know that I have defined every string for sure in english. The thing is that it's not very important that all keys are translated why it's okay to show english even if the iPhone is set to spanish, for instance.
Example:
en:
"HELLO" = "hello" ;
"PERSON" = "my friend" ;
es:
"HELLO" = "hola" ;
Expected Outcome:
If the iPhone is set to english I want it to say: hello, my friend and if the languange is set to spanish I want it to say hola, my friend. Currently the app will output hola, PERSON. (I use the NSLocalizedString method).
There is probably a really easy solution but I couldn't find any good ones.
Thanks for the help,
Mattias

The other answers were definitely correct but it wasn't exactly what I wanted. Instead I found another way to do it and wrote a static method that works perfectly for me in my environment and I wanted to add it here if anybody else wants to acheive the same thing:
+ (NSString *)localizedStringForKey:(NSString *)key table:(NSString *)table
{
NSString *str = [[NSBundle mainBundle] localizedStringForKey:key value:#"NA" table:table];
if ([str isEqualToString:#"NA"])
{
NSString *enPath = [[NSBundle mainBundle] pathForResource:#"en" ofType:#"lproj"];
return [[NSBundle bundleWithPath:enPath] localizedStringForKey:key value:#"NA" table:table];
}
return str;
}

the documentation of the function NSLocalizedString says:
Return Value
The result of invoking localizedStringForKey:value:table: on the main bundle and a nil table.
and for localizedStringForKey:value:table: says:
Return Value
A localized version of the string designated by key in table tableName. If value is nil or an empty string, and a localized string is not found in the table, returns key. If key and value are both nil, returns the empty string.
so you have localize the string for all your languages or change the value of the key you are using

The easy solution is to use the English text as the key. For example:
[NSString stringWithFormat:NSLocalizedString(#"%#, %#", "format for greeting"),
NSLocalizedString(#"Hello", "salutation"),
NSLocalizedString(#"my friend", "person")]
If there's no localized version of the string, the English key is used.

Related

iOS applications Localization.strings file name change

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

how to change app language inside app in ios sdk

I am thinking how to update all UILabel having storyboard and UITableviewController as subview while i change language in select language page of my app not device language.
can anyone help me how can i resolved this issues
You can manage this using Micro Like this
open .pch file in supporting file and write down like this and one more thing you need to decide languageId first that you pass as "number"
#define kCancelText(number) (number == 1 ? #"Cancel" : (number == 2 ? #"取消" : (number == 4 ? #"cancelar" :#"Cancel" )))
after define this you need to call like this
NSLog(#"Varible Print :: %#", kCancelText(1)); //Note : here 1 is language id .. I have define three language . 1. English 2. Mandarin 3. Spanish
You can use according to language id .. One more thing you don't need for import any this for this because its always global in app.
For this you can use NSNotificationCenter through which you can post your Notification with a key, now in all the classes where you need to make changes after posting this notification, you can just add a NSNotificationObserver with the same key, from where you can call a method and do changes what so ever you require.
For using NSNotification use this link.
For that you have to this steps:
1>first if you have two language like arabic and English then add two .strings file
Messages_en.strings
Messages_ar.strings
// this is Messages_en.strings file in this declare key and value
Messages_en.strings
"answer" = "Answer";
Messages_ar.strings
"answer" = "الإجابة";
2> make all label custom label like MyUILabel_custom and add all labels in all screens of this class so if you want to modify something in viewwillappear like in arabic you want to change alignment you can do in that
3> when button is clicked language should be stored in NSUserDefaults
and then you can access it everywhere
[[NSUserDefaults standardUserDefaults]setValue:#"en" forKey:#"lang"];
4> In Constants.h file add this line
#define IsArabic ([[[NSUserDefaults standardUserDefaults] valueForKey:#"lang"] isEqualToString:#"ar"]? YES : NO)
#define getString(key) NSLocalizedStringFromTable(key, (IsArabic ? #"Messages_ar" : #"Messages_en"), #"Fix it:-(")
5> you can check this way in all screens viewwillappear this way
self.lblRewardsPoint.text = getString(#"rewards_point");
6>In tableview also you can do this way you have to reload all data and if your data came from api then you have to recall the api .
-(NSString *)GetLocalString:(NSString *)text{
NSString *path;
path = [[NSBundle mainBundle] pathForResource:currentLanguageDocumentPath ofType:#"lproj"];
NSBundle* languageBundle = [NSBundle bundleWithPath:path];
NSString* str=[languageBundle localizedStringForKey:text value:#"" table:nil];
return str;
}
Here just add string files of languages in project and pass there file name in 'currentLanguageDocumentPath'.
text -> key of text .

localizedStringForKey:value:table ignores Base localization

My application need to be localised in 2 languages: German and English. German should be Base language. It means that app should always localize to German except the language on device is english (in this case it should be on english)
I have custom *.string files for localisation and use localizedStringForKey:value:table to localise strings.
When I have only base localisation everything works fine. But in case if I add english localisation, in some reason localizedStringForKey:value:table just ignore Base localisation and always use English (for all languages)
Here how it looks like after I've added english:
and here is how I localise strings:
[[NSBundle mainBundle] localizedStringForKey:#"key" value:#"" table:#"Shared"]
I'm testing on simulator and here is my language screen:
Does anybody know what could be a problem? Thanks in advance!
As I mentioned in the comments, you need to set the Localization native development region (CFBundleDevelopmentRegion) in the Info.plist to your language code. Xcode seems to set it to a en_GB or de_DE style region code, setting it to de (no region) will fix it. Note that by default it is en, but selecting United Kingdom or Germany will change it to use the longer codes.
I have another solution, I think it works for you:
NSString* NSCustomLocalizedString( NSString *key , NSString *comment)
{
NSString *rs = nil;
if( [[NSUserDefaults standardUserDefaults] integerForKey:KEY_LANGUAGE ] == e_language_japanese)
{
rs = NSLocalizedStringFromTable(key,#"Localizable.strings-ja",nil);
}
else
{
rs = NSLocalizedStringFromTable(key,#"Localizable.strings-en",nil);
}
return rs;
}

iOS - NSLocalizedString is only returning the key string

I'm having some trouble debugging my NSLocalizedString implementation. Should be simple, but whatever I do, the function only returns the KEY string.
I'm using XCode 4.5 and iOS6, so I:
Added a new file called File.strings.
In my project settings I added English and Spanish as language settings.
Clicked "Make Localized" in the file inspector, and made sure that both English and Spanish options were selected, and also that the Target membership to my target was selected.
Added "KEY" = "TestEnglish"; to my english File.strings
Added "KEY" = "TestSpanish"; to my spanish File.strings
Added NSLog(#"Localization: %#\n", NSLocalizedString(#"KEY", nil)); to my .m file.
When I run the app, the value "KEY" is always displayed printed in the NSLog.
To jump into this a bit more, I tried this as well:
NSString *path = [[NSBundle mainBundle] pathForResource:#"en" ofType:#"lproj"];
NSString *str = [[NSBundle bundleWithPath:path] localizedStringForKey:#"KEY" value:#"" table:nil];
NSLog(#"Localization: %#\n", str);
and still the value "KEY" is printed, yet, path is a valid path.
Does anyone have any clue how to debug this? I feel like I've read every SO question/answer out there, but none of the suggestions help.
I realize that NSLocalizedString returns the KEY string when it cannot match a key, but I don't see how I can debug why my app might not be matching the KEY.
I've also deleted/cleaned the app about a dozen times.
If you specify table:nil, then NSBundle will try to fetch the localization from the default table (the one in SOMELANG.lproj/Localizable.strings). If you have the localization elsewhere, you should explicitly specify the table using table:#"File" (or use the NSLocalizedStringFromTable() macro in a similar manner:
NSString *value = NSLocalizedStringFromTable(#"key", #"File", nil);
Rename the InfoPlist.strings file to Localizable.strings (double clic) and then you will get the correct string for that key.
In my case the issue was with the case of the string:
"bla.bla.blabla.BookSlot" whereas the Localizable.strings had it defined as "bla.bla.blabla.Bookslot"
So, double-check that the key string is in the correct case. Better yet, copy-paste.

Change app language in iOS without restarting the app

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.

Resources