iOS localizable string with default values - ios

I want to understand how localizable base string works in iOS. For example, in Android if I have got a default localizable file strings (base localization on iOS) like:
"title_app" = "Title"
"Copy" = "Copy";
"Edit = "Edit;"
And then I have got a Spanish localizable file like:
"Copy" = "Copiar";
"Edit" = "Editar";
Why on iOS if I set Spanish language on my device the key "title_app" doesn't appear? Because Android if doesn't find a key, it takes the key from the default language.
Sometines there are words that they don't need a translation. Or sometimes I have 10 languages and maybe one language needs a translation from a non translatable word. For example "title_app" = "My app". It will be the same in English, French, Italian, Spanish... but in chinese no. It is not efficient write the key on 10 files, repeating... imagine 10, 20 o 50 words.
Always Apple/iOS is far behind in matters of translation/localization compared to Android... :S
Talking about translation; sorry my bad english.

The NSLocalizedString macro takes two parameters, a key and a comment. The key will be looked up in Localizable.strings file, which is a simple key-value pair collection.
As #TheEye pointed out in the comments via the blog post link, you can exercise more control, and explicitly include a default value by using the NSLocalizedStringWithDefaultValue macro instead.
What you have to realize is that you have a choice: you can either use the simpler macro and treat your key as the default value (and iOS will fall back to that) or use the more specialized macro that is more verbose but gives you more control instead.
Note, this has already been discussed here: Fallback language iOS (with incomplete Localizable.strings file)

Related

Validation Check for non-english strings

I am trying to build an iOS app and I wish to validate whether a particular string that I have, is a valid email or not. The catch is, the string can be a non-english lanuage as well. Also, lets take Arabic for example, a language that is written Right to Left. So I wish to know how could I validate a non english string. (for the sake of simplicity, lets just assume that a valid email has the format "string+#+.+string").
Programming Language - Swift
I know that we can validate this using regEx
validEmail = "[A-Z0-9a-z\\._%+-]+#([A-Za-z0-9-]+\\.)+[A-Za-z]{2,4}"
.
.
Now I am not sure how to do this if the language is not indian, and it is written Right to Left(like in Arabic, urdu, etc.)
Here's the validation to check if the string is in English language or not.
validEnglishAlphabets = CharacterSet(charactersIn: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")

String(localized:) has no separate key and value?

New in iOS 15, we are invited to use this String initializer method to make localizable strings in our Swift code:
init(localized keyAndValue: String.LocalizationValue,
table: String? = nil, bundle: Bundle? = nil,
locale: Locale = .current, comment: StaticString? = nil)
The trouble is that the first parameter is, as the internal name suggests, used for both the key and the value. You can see that from this localized French strings file:
/* Alert message: Report a tap */
"You tapped me!" = "Vous m'avez tapé!";
That resulted from my saying
String(localized:"You tapped me!", comment: "Alert message: Report a tap")
and localizing for French.
That's totally wrong! This is supposed to be a list of key–value pairs; we shouldn't be using the English user-facing text as a key.
For one thing, if we now change the English text in our String(localized:comment:) call, our French translation will break. Also, we would be unable to have different French translations for the same English text used in different contexts.
What are we supposed to do about this?
I regard this as a major bug in String(localizable:). If we were using NSLocalizedString, we would have individual key: and value: parameters. String(localizable:) needs that.
I can think of two workarounds. One is: don't use String(localizable:). Just keep on using NSLocalizedString.
The other is to localize explicitly for English. Instead of entering the English user-facing text as the localized: parameter, enter a key string. Then, to prevent the keys from appearing in the user interface, export the English localization and "translate" the keys into the desired English user-facing text. Now import the localization to generate the correct English .strings files.
(If your development language isn't English, substitute the development language into those instructions.)
Now when you export a different localization, such as French, the <trans-unit> element's id value is the key, to which the translator pays no attention, and the <source> is the English, which the translator duly translates.
To change the English user-facing text later on, edit the English Localizable.strings file — not the code. Nothing will break because the key remains constant.
If you want to separate the key and value you can call String.init(localized:defaultValue:table:bundle:locale:comment:). This allows you to specify a default value to use if the key does not exist in your strings file, and is used as the default translation when using Xcode's Export Localisations feature.
For example:
let alertMessage = String(localized: "alert.message.report-a-tap", defaultValue: "You tapped me!")
// Xcode's Export Localisations generates the following:
"alert.message.report-a-tap" = "You tapped me!";
I was experiencing the same problem using SwiftGen to produce my localized strings and the solution for me was to ensure that my generated localized strings file is inside of the appropriate language folder rather than the separate Generated folder I would otherwise use.
The explicit localization approach using keys is imo the correct way here, I think the parameter name keyAndValue is just misleading.
See another initializer using String.LocalizationValue, for AttributedString:
https://developer.apple.com/documentation/foundation/attributedstring/3867590-init
On that one the parameter is just named key:
init(localized key: String.LocalizationValue, options: AttributedString.FormattingOptions = [], table: String? = nil, bundle: Bundle? = nil, locale: Locale? = nil, comment: StaticString? = nil)
After all that seems to align well with SwiftUI's usage of LocalizedStringKey, where no value is given next to the key too.
Beside the current lack of documentation, I don't understand the need to introduce String.LocalizationValue having the mentioned LocalizedStringKey but at least it seems to be aligned from the way it's done in SwiftUI, but not from the previous localizedString(forKey:value:table) / NSLocalizedString way.
In my recent projects we always used key-based translations even for the development language, so this new initializer would work pretty well. But Apple should change the parameter name and update the documentation accordingly, as they did for AttributedString.

Should I use unique ids or the whole sentences when localizing software?

I need to translate a website to a couple languages, and I've already read how to do it:
Mark strings for translation
Generate messages file
Translate messages file
The problem is, if I use whole sentences as the message ids, in english, for example, then if later I decide to modify the text, I'll have to change it in the code and on each message file . Or I could just change the english translation, but then my english message file will look weird, with translations from english to english which do not match.
Example:
Original: "I don't know what to do."
Translation: "I'm not sure what to do."
An alternative is to use unique message ids such as:
Original: "INDECISION_MESSAGE"
Translation: "I'm not sure what to do."
The advantage is that I can change translations without changing the id and things will still be consistent. But then there is no easy way for a translator to know what the message should be like as there is no context except by looking at the code.
What would you use?
In PHP people normally use the first approach you mentioned. In ASP.NET the second.
I think it's more of a personal taste and a matter of the framework that you use.
Personally I prefer the second option. Since you normally already have the ID/translation pair somewhere in a list, the translator can just take that list to translate.
What framework do you use?

Mapping API error codes to localised strings in iOS

I'm writing an iOS app which download some statistics from our company server. In case of error the APIs provides an error code and an error description. I would like to keep the error description (which is always in english) for the internal log and to map the error codes to some localised strings. Which would be the best approach for solving this problem? I was thinking of executing a mapping using a .plist file,but not 100% sure.
Using a plist file with an NSDictionary is fine, as long as the memory footprint is low. I've done something similar.
However, also be aware of the standard method which is NSLocalizedString and using .Strings files for each language.
Here's an example of how to use NSLocalizedString:
// Set the label using the localized string
self.label.text = NSLocalizedString(#"Select choice:", #"Prompt to make a selection.");
The first part is the key, which you define in the file Localizable.strings. If no entry exists in the strings file, then the key name is used, so I make the key equal the default text. In the example above, if no entry is found for the default language, it will just use the key name, which is #"Select choice:".
Then, you create a Localizable.string file and press the Localize button, then create one for each language. Your spanish one might look like this:
/* Contents of Localizable.strings */
"Select choice:" = "Selecciona la opción:";
Of course, you could have an English one, which would look like this:
/* Contents of Localizable.strings */
"Select choice:" = "Select choice:";
The second parameter to NSLocalizedString() is a comment, which is optional, but Apple provides tools to find all of the NSLocalizedString() entries in your code and generate lines in your Strings resource files for you, complete with the comment.
I'll add that if your API takes a language parameter and returns messages in that language, you can use its available languages like this (Objective C):
NSArray *availableLanguages = #[#"en", #"es"]; // API's available languages
NSString *preferredLanguage = [NSBundle preferredLocalizationsFromArray:availableLanguages].firstObject;
Then pass preferredLanguage to the API.
(The API might even have a call to get available languages that it supports.)
See https://developer.apple.com/library/content/technotes/tn2418/_index.html

How should I localize content in an ASP.NET MVC application?

This is a question about setting our website's Language and Culture settings with regards to the settings we read from the user visiting the site.
Let's assume our website supports 2 languages, English (en) and German (de). Let's also assume we want to disregard locale (region) (at least on the server side, so we only know that we support "en" and "de", so we have that specified either in application code, config file or somewhere elese). So we don't care if a user comes from US or UK.
What we are doing is matching "en" or "de" to possible matches in user's browser defined languages/cultures.
The issue I am having is that if I do this
/* Gets Languages from Browser */
IList<string> BrowserLanguages = filterContext.RequestContext
.HttpContext
.Request
.UserLanguages;
we get all sorts of results.
We might receive lists like
en, (for instance Firefox has this), - en-US, - en-UK.
en-US, - en-UK.
en, - de, - it-IT.
de, - en-US, - en.
What I would like to ask here is:
Is it ok to use compare strings here (checking whether "en" exists as a substring)? See sample list 2
Do we have to take the order into account or would you just disregard it?
Am I overcomplicating this? The problem is though that IE and Firefox (and others) have different strings for regional settings (for instance, "sl" in Firefox and "sl-SI" in IE8)
I just want to direct all visitors for which language does not exist to English and all others to their appropriate language (disregarding their location), you might think of it like if we support Portugese (pt) and our visitors come from Portugal and Brazil we will redirect them to Portugese version of the site even if the match is not 100% perfect (we would rather redirect them to Portugese version than English version).
Interesting question. Let me try to answer...
Is it ok to use compare strings here (checking whether "en" exists as a substring)?
You could something like this. Note, I am just providing a way that does not use strings, however, I think that in this case substring approach will also work since its simpler.
CultureInfo enCulture = new CultureInfo("en"); // use "de"
var langPref = Request.UserLanguages[0];
var userCulture = CultureInfo.GetCultureInfo(langPref);
var baseCulture = CultureInfo.GetCultureInfo(cult.TwoLetterISOLanguageName); // get the base culture
var isSame = baseCulture.Equals(enCulture);
What about using the Headers["Accept-Language"]. Section 14.4 Accept-Language of RFC 2616. There may be a bit more work involved using this, but off hand it seems that that it can hold more valuable information.
Do we have to take the order into account or would you just disregard it?
The UserLanguages array is sorted by preference (MSDN). Having said that, I would assume that each browser has its own specific way to create the Language String (I stand under correction, but I think that FF4 is considering removal of this part of the user-agent string). You could check each language and decide when the correct language is found using the approach described above.
Am I overcomplicating this? The problem is though that IE and Firefox (and others) have different strings for regional settings (for instance, "sl" in Firefox and "sl-SI" in IE8)
To me localisation is tricky. I would suggest having read through RFC 1766 and RFC 2616 (HTTP Protocol, Section 3.10.
I hope this helps.

Resources