Adding general and specific localizable files for targets in Xcode - ios

I have two targets in my project: target1 and target2. Each of them uses their localizable.strings resource for localization. But their localizable.strings files are same for 90%.
I want to realize such behavior:
Create general localizable file with common strings
Create localizable files for each target with their special strings that are different (e.x. app_name)
Application should search the string in target's localizable.strings file at first and if this string is not exists, search it in common file
I tried to add localizable.string files for each target and then created common localizable.string file(with membership for target1 and target2), but application uses only common file :(
Does anybody know how to fix it?
Thank you for help!

Maybe you can create a pre-build script to concat the specific strings inside global strings.
Or an other solution, you can create a function to search first in global and after in a specific file (this is an example, not tested) :
- (NSString*) localizedString:(NSString*) key {
//search in global file
NSString *transcription = NSLocalizedString(key, "");
if ([transcription isEqualToString:key]) {
//search in specific file
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"specific_file" ofType:#"strings"];
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:filePath];
return [dict objectForKey:key];
}
return transcription;
}

With thanks to lafalex, here is code for swift 2:
if let path = NSBundle.mainBundle().pathForResource("specific_file", ofType: "strings"),
let dict : NSDictionary = NSDictionary(contentsOfFile: path) {
if let localizedString = dict.objectForKey(key) as? String {
return localizedString
}
}

Related

Concatenating Two strings in Localization Objective-c

My Previous Code without Localization. It worked perfect.
case LOGIN_LOGOUT: ((Cell*)cell).lbl.text = [self isLoggedIn] ?
[NSString stringWithFormat:#"Logout %#", email]
:NSLocalizedString(#"Login", #"Message");
break;
But when I implement Localization in Logout the email will not show.
case LOGIN_LOGOUT: ((Cell*)cell).lbl.text = [self isLoggedIn] ?
[NSString stringWithFormat:NSLocalizedString(#"Logout", #"Message") ,"%#",
email] :NSLocalizedString(#"Login", #"Message");
break;
I know I am missing some basics in stringWithFormat but can anyone offer some guidance to me?
Let's assume, that you have .strings file and it contains entry named "Logout". You have:
[NSString stringWithFormat:NSLocalizedString(#"Logout", #"Message") ,"%#", email]
here you try to load format string via NSLocalizedString and use it with NSString. That means that you have to put correct format string into your .strings file, so, if currently you have:
"Logout" = "Logout";
In order to make it just like before localization, you need:
"Logout" = "Logout %#";
If you don't have a .strings file or don't have entry named "Logout", NSLocalizedString will return the key, i.e.
NSLocalizedString(#"key", #"comment") // returns "key"
That means, that your NSLocalizedString(#"Logout", #"Message") may return "Logout" if NSLocalizedString can't find correct entry in your .strings file.
There are more things that may go wrong, if you want some deeper insides on that, I have written great article on the whole topic: Understanding iOS internationalization.
Also I'd suggest to use +localizedStringWithFormat: instead of just plain +stringWithFormat:, because the former uses current locale.
You are looking up the localisation of "Logout". You are using that as a format string. That's not likely to work. Don't make statements that are too complex, it makes it impossible to debug.
I'd write
case LOGIN_LOGOUT: {
NSString* labelText;
if ([self isLoggedIn]) {
NSString* formatString = NSLocalizedString(#"Logout", #"Message");
labelText = [NSString stringWithFormat:formatString, "%#", email];
} else {
labelText = NSLocalizedString(#"Login", #"Message");
}
((Cell*)cell).lbl.text = labelText;
break;
}
And now you can actually debug that whole mess. The stringWithFormat parameters look very, very dodgy.

share excel file from one application to another application

I have added export UTI in shareDocument Application
Another application which will import excel file
Now while sharing from shareDocument Application i don't see my application name in the list. So whats wrong here if somebody can share.
Firstly need to add Document Type. Here its for xls and xlsx format document
Now you need specify Import UTI or Export UTI
For more reference check How do I get my application to show up in the “Open in...” menu on iOS for a specific document type?
For All System-DeclaredUniformTypeIdentifiers
Note : To get exact UTI for specific file format :
NSArray *extensionArray = [NSArray arrayWithObjects:#"doc", #"docx", #"ppt", #"pptx", #"xls", #"xlsx",#"mp3",#"mp4",#"rft",#"rtf",#"pages",#"key",#"numbers",nil];
for (int i=0; i<[extensionArray count]; i++) {
NSString *fileExtension = [extensionArray objectAtIndex:i];
NSString *utiString = (__bridge NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension,(__bridge CFStringRef)fileExtension,NULL);
NSLog(#"Extension: %# UTI:%#",fileExtension,utiString);
}

What is wrong with this NSLocalizableString code?

I have this code on Viewcontroller.m, on Xcode:
NSString *language = NSLocalizedString(#"es", #"language");
NSString *connector = NSLocalizedString(#"de", #"connector to link words");
And this one on the "Localizable.strings (English)":
"language" = "en";
"connector to link words" = "of";
The problem is that with every language I change on the iOs Simulator, I always get the first value, the value of the Viewcontroller.m, instead of get the strings values.
Does anyone know what is wrong?? Thank you so much!
UPDATE:
I have this:
NSString *language = NSLocalizedString(#"es", #"language");
NSString *connector = NSLocalizedString(#"de", #"connector to link words");
But it still doesn't work!! Why????
It only shows the key values!! In the strings I have:
"es" = "en";
"de" = "of";
on the english file, and on the spanish file:
"es" = "es";
"de" = "de";
SOLUTION:
I think I have already done everything right, so the problem must to be in the iOs simulator. If anyone can take advantage of that, my solution has been edit the scheme clicking in the image of the project in the superior task bar, and in the tab "Options" (on the Run part) set "Spanish" as my language by default.
Thanks everybody anyway.
The syntax of NSLocalizedString goes like the below.
NSString * NSLocalizedString(
NSString *key,
NSString *comment
)
The key should be used in your .strings file. The value of the key will be different for different languages .
So when you run the key will be replaced by the value provided in the language .strings file you set.
Look at this tutorial for more explanation.
syntax is NSLocalizedString(key, comment)
And this one on the "Localizable.strings (English)":
"language" = "en";
"connector to link words" = "of";
so "language" is the key and "en" is the value
so
NSString *language = NSLocalizedString(#"language", #"");
NSString *connector = NSLocalizedString(#"connector to link words",#"");

How to perform Localization using sqlite stored data-iOS App?

In my app i am doing localization to support mutiple languages. For that i am creating .string file and using the following method:
-(NSString*) languageSelectedStringForKey:(NSString*)key;
I am using the Key_value concept for this. its working fine. But My question is:
Let say i have a sqlite database which has all the different languages string which needs to be localized.
For eg: for spanish
"Username" = "nombre de usuario";
"Password" = "contraseña";
"Submit" = "presentar";
for french
"Username" = "nom d'utilisateur";
"Password" = "mot de passe";
"Submit" = "soumettre";
all this is insert in a sqlite database.
How can we perform localization with the app that way?
Since you seem to have all strings traduction within one db, you need to have an intermediate data structure that will act as a bridge between the db and your method to localize the string.
You could make use of a dictionary for e.g to store the key string identifier and as a value an array that contain all the languages traduction. You will need to feed this data structure once then.
Note that the recommended way is to use .strings files for localisation so you need to provide us good raison why you can't take this approach.
If those are titles/texts for UIButtons and UILabels use the following syntax in your viewController:
NSString *buttonString = NSLocalizedStringFromTable(#"viewController-title-button-username", #"Localizable", nil);
self.username_label.text = buttonString;
And in your Localizable.strings file you do(you must add localization to your project and set this file to be localized to spanish):
"viewController-title-button-username" = "nombre de usuario";
"tabbar-item1" = "item1 in spanish";
"tabbar-item2" = "item2 in spanish";
"tabbar-item3" = "item3 in spanish";
And the same for the Localizable.strings you have set to be the french:
"viewController-title-button-username" = "nom d'utilisateur";
"tabbar-item1" = "item1 in french";
"tabbar-item2" = "item2 in french";
"tabbar-item3" = "item3 in french";
For your tabbar name, put this in your viewDidLoad for each viewController you have. Just change 1, 2, 3 etc.:
NSString *name = NSLocalizedStringFromTable(#"tabbar-item1", #"Main", nil);
self.title = name;
On the other hand if you are getting either an .xml or retrieving using REST you can do the following:
//Get the name of what you want eg. spanish-data or french-data
NSString *name = NSLocalizedStringFromTable(#"xml-locations-string", #"Main", nil);
//What to add at the end
NSString *append = #".xml";
//Merge strings to what you want to retrieve: spanish-data.xml
NSMutableString *file = [NSMutableString stringWithFormat:#"%#%#", name, append];
//From which URL
NSString *urlString = #"http://www.myDomain.com/ProjectName/";
//Create full URL http://www.myDomain.com/ProjectName/spanish-data.xml
NSString *fileUrl = [NSString stringWithFormat:#"%#%#", urlString, file];
NSURL *url = [NSURL URLWithString:fileUrl];
The data you get back will now be the localized version. And you can use that to set table data or whatever you need.
This is the super simple and therefor not optimized way of doing it. Works for REST as well, you just need to reconstruct to fit your project.

Fallback language iOS (with incomplete Localizable.strings file)

I have an iOS project that is localized into 16 languages. Only some words are not localized (Mainly those that go into an update and the localization office did not deliver in time).
For my keys I do not use the english wording, as this can also change if a translator wishes.
So now if I just don't have a translation for a language, if falls back to the key that I used. But as this key is not 'human readable' or at least not 'human enjoyable to read' this is a problem.
I did some research but couldn't find a solution to my exact problem.
I have fe.:
Localizable.strings in en.lproj
#"Key1" = #"Value 1"
#"Key2" = #"Value 2"
Localizable.strings in de.lproj
#"Key1" = #"Wert 1"
// Note that #"Key2" is missing here in my de.lproj
I would expect that if I make NSLocalizedString(#"Key2", ...)
and am running on a german phone, it falls back to the english
translation for this key as it exists...
So for now i just copied the english translation into the missing Localizable.strings files. But this is a big hack!
But also using the english words as keys seems to be a hack to me!
Is there any way to tell my app, that it should use f.e. english as the fallback if there is no value for a given key? I tried adding a base localization but this doesn't help...
Thanks a lot
As far as I know, there's no "official" way to do it, but I have implemented functions such as this before:
NSString * L(NSString * translation_key) {
NSString * s = NSLocalizedString(translation_key, nil);
if (![[[NSLocale preferredLanguages] objectAtIndex:0] isEqualToString:#"en"] && [s isEqualToString:translation_key]) {
NSString * path = [[NSBundle mainBundle] pathForResource:#"en" ofType:#"lproj"];
NSBundle * languageBundle = [NSBundle bundleWithPath:path];
s = [languageBundle localizedStringForKey:translation_key value:#"" table:nil];
}
return s;
}
borrowed from: https://stackoverflow.com/a/8784451/1403046
Basically, instead of NSLocalizedString(), which will return the input string, this version will fallback to English if necessary.
Inspired by this and this, my Swift code version:
public func LS(_ key: String) -> String {
let value = NSLocalizedString(key, comment: "")
if value != key || NSLocale.preferredLanguages.first == "en" {
return value
}
// Fall back to en
guard
let path = Bundle.main.path(forResource: "en", ofType: "lproj"),
let bundle = Bundle(path: path)
else { return value }
return NSLocalizedString(key, bundle: bundle, comment: "")
}
Many developers expect an incomplete translation to fallback on the development language.. but that's not the way Apple choose to behave. I have a pseudocode to help better understand how Apple choose to fallback.
For Swift project SwiftGen tool can be used. It generates string constants that will contain fallback strings from base localisation language. If a string key is not found in the localization file for currently selected language then the fallback string will be used.
Example of a generated constant for CommonTextClose localization key from Localizable.strings file:
internal static let commonTextClose = L10n.tr("Localizable", "CommonTextClose", fallback: "Close")
You can use a Base localization and all unlocalized strings will be taken from this.

Resources