Case Insensitive Localization in iOS - ios

While implementing localization I realize that localization is case sensitive, what means:
- "Cookie Selection" = "Sélection de biscuit";
- "COOKIE SELECTION" = "SÉLECTION DE BISCUIT";
above two are different entities. If you localize like NSLocalizedString("cookie selection", comment: ""), this will not get localized as the key value mapping given for "cookie selection" is either for capitalized string or for Uppercased string.
Query:
If I can make it case insensitive, in the sense like I will add only one pair for localization as
"Cookie Selection" = "Sélection de biscuit";
and this should work for all cases like below
1. NSLocalizedString("Cookie Selection", comment: "")
2. NSLocalizedString("COOKIE SELECTION", comment: "")
3. NSLocalizedString("cookie selection", comment: "")
Note: -> without any custom method, use only NSLocalization.
Happy Coding

Nope....Better use a constant file where you will store all the localization constants and its values so by mistake localization constant value does not change by mistake(suppose you type cookie Selection instead of Cookie Selection) while calling. You will use then values in localizable files as key.
LocalizationConstants.swift
let CookieSelectionKey = "Cookie Selection"
Localizable.strings(English)
"Cookie Selection" = "Selection Of cookie";
Now call it like
NSLocalizedString(CookieSelectionKey, comment: "")
Since it returns a string you can use string instance methods such as uppercased, capitalized(etc.) to change case accordingly.

You could always remove the possibility of getting it wrong by making a struct to encapsulate your localised logic etc...
enum Strings: String {
case cookieSelection
var localized: String {
return NSLocalizedString(self.rawValue, comment: "Something here")
}
}
With something like this you can now keep track of all your localised strings in one place and also remove any chance of them being used incorrectly.
For usage do something like...
label.text = Strings.cookieSelection.localized
If you want a default value for the string you can add that to the case...
enum Strings: String {
case cookieSelection = "Cookie Selection"
}

The simplest and most raw solution would be to always capitalize the key like so:
NSLocalizedString("cookie selection".uppercased(), comment: "Comment")
More cleaner approach would be to create a category on String class and use that method instead:
extension String {
public static func LocalizedString(key: String, comment: String) -> String {
return NSLocalizedString(key.uppercased(), comment: comment)
}
}
// Usage
String.LocalizedString(key: "cookie selection", comment: "")
Another nice approach would be to make a file of constants and use them instead of using hard coded strings.

Related

is there a good practice to use a localization that is then complemented with String interpolation?

I have a translation whose phrase is static but then it is complemented with a piece of information that varies according to the user. It would be something like this:
"" The person resides in "+ (data that varies)".
"The person resides in" would be the static text for Localization.
Data that varies would be the interpolation of a string.
I know there is %d for numbers in Localization, does this apply to a string? Thanks!
If the variable data doesn't depend on current locale you could just use the regular %# for string arguments. Let's say you have this line in the .strings file:
"stringKey" = "The person resides in %#";
And then you can localize it like this:
String(format: NSLocalizedString("stringKey", comment: ""), data)
You may want to wrap it into an extension:
extension String {
func localized(_ arg: String) -> String {
String(format: NSLocalizedString(self, comment: ""), arg)
}
}
In order to keep your code cleaner:
"stringKey".localized(data)
Or just use the R.swift library which does it for yourself, so you would have it like this:
R.string.localizable.stringKey(data)

Return a string if localized string not found

I would like to know how could I return a default string value if localized string of key is not found. here is my function to return the localized String.
func localizedStr(key: String) {
return NSLocalizedString(key, comment: "")
}
I would like to return default string e.g. "foo" if the key is not found in localization file.
TL;DR
Use the full
NSLocalizedString(key: "key", value:"English string", comment: "")
If key is not found in your translated file, value is returned
Long version
Don't wrap NSLocalizedString in a function - while there's less code, you're losing valuable information for the localisation process.
you won't be able to use Xcode export an Xliff file for translation - only string literal keys in NSLocalizedString are included
Use a value: parameter with the English (or your base language) string. This is used when no translation is found, and means you don't need to have a base Localizable.strings file.
The comment: appears in the xliff file, and is useful for translators. Use this to give information about the context of the string being translated.
You could give it default value like this, using NSLocalizedString(key, comment: "") would return an Empty String if the Key was not found so checking on it like this should do the trick.
func localizedStr(key: String) {
if NSLocalizedString(key, comment: "") == key {
return "foo"
}
return NSLocalizedString(key, comment: "")

Change language without restart on-the-fly does not work on global declarations

The goal is to be able to change language on the fly without having to restart the iOS Swift app.
I am using method swizzling like below to achieve it.
method_exchangeImplementations(
class_getInstanceMethod(Bundle.self, #selector(Bundle.localizedString(forKey:value:table:)))!,
class_getInstanceMethod(Bundle.self, #selector(Bundle.kd_localizedString(key:value:table:)))!
)
This works quite well for NSLocalized Strings for example like below
pickOneLabel.text = NSLocalizedString("Please choose one to continue..", comment: "")
because it is being processed after the language change.
However for global declarations for example like below. It does not work
let requestStatuses = [
"open" : NSLocalizedString("Open Request", comment: ""),
"accepted" : NSLocalizedString("Booking - Confirmed", comment: ""),
"rejected" : NSLocalizedString("Booking - Rejected", comment: ""),
"expired" : NSLocalizedString("Request - Expired", comment: ""),
"cancelled" : NSLocalizedString("Booking - Cancelled", comment: ""),
"completed" : NSLocalizedString("Request - Completed", comment: "")
]
because above dictionary was declared already, before the labgauge was changed and it stored the original language strings in it. I do not really want to redeclare it because it is redundant and error prone. Is there a better solution?
Haven't tested, but how about using struct?
struct RequestStatus {
let open = NSLocalizedString("Open Request", comment: "")
// ...
}
let localizedTextOpen = RequestStatus().open // localized string on the fly?

How to compare Localized string with the string from the database?

I have a to compare a string with the string from the db.
var variableFromDB = "test"
if "test" == variableFromDB{
print("Success")
}
It works fine in the English Language. I don't know how to compare it in the arabic language. Is the need to check in arabic language also. Please tell me know to check it.
In general it's a bad idea for your code to make decisions based on display strings. That goes double for display strings.
If your primary audience is Arabic-speaking, you could make your development language Arabic and then localize for other languages as needed.
In any case, I would suggest using a set of fixed strings as keys, and then calling NSLocalizedString(_:tableName:bundle:value:comment:) or one of it's variants to fetch a display string. Example:
Put this code somewhere central so the keys can be shared:
let screen1Prompt = "screen1Prompt"
Then when you need a localized string for display:
let prompt = NSLocalizedString(screen1Prompt)
Where the actual prompt string might be "Please select the date for your payment." in English, Arabic, etc.
Then if you need to match something in your database, look it up using the unlocalized key, not the localized display string.
That way if you later change the display string, your code still works.
var language: String
UserDefaults.standard.set("AE", forKey: "Apple") // manually set language
UserDefaults.standard.synchronize()
self.language = UserDefaults.standard.object(forKey: "Apple")as! String
// self.language = Locale.current.languageCode // your device language
extension String {
func stringlocalized(lang:String) ->String {
let path = Bundle.main.path(forResource: lang, ofType: "lproj")
let bundle = Bundle(path: path!)
return NSLocalizedString(self, tableName: nil, bundle: bundle!, value: "", comment: "")
}}
// Check You Localization string from your current language
let str = “ test”.localized(lang: self.language! )
if str ==variableFromDB
{ // Your logic here
}

How to capitalize each word in a string using Swift iOS

Is there a function to capitalize each word in a string or is this a manual process?
For e.g. "bob is tall"
And I would like "Bob Is Tall"
Surely there is something and none of the Swift IOS answers I have found seemed to cover this.
Are you looking for capitalizedString
Discussion
A string with the first character in each word changed to its corresponding uppercase value, and all remaining characters set to their corresponding lowercase values.
and/or capitalizedStringWithLocale(_:)
Returns a capitalized representation of the receiver using the specified locale.
For strings presented to users, pass the current locale ([NSLocale currentLocale]). To use the system locale, pass nil.
Swift 3:
var lowercased = "hello there"
var stringCapitalized = lowercased.capitalized
//prints: "Hello There"
Since iOS 9 a localised capitalization function is available as capitalised letters may differ in languages.
if #available(iOS 9.0, *) {
"istanbul".localizedCapitalizedString
// In Turkish: "İstanbul"
}
An example of the answer provided above.
var sentenceToCap = "this is a sentence."
println(sentenceToCap.capitalizedStringWithLocale(NSLocale.currentLocale()) )
End result is a string "This Is A Sentence"
For Swift 3 it has been changed to capitalized .
Discussion
This property performs the canonical (non-localized) mapping. It is suitable for programming operations that require stable results not depending on the current locale.
A capitalized string is a string with the first character in each word changed to its corresponding uppercase value, and all remaining characters set to their corresponding lowercase values. A “word” is any sequence of characters delimited by spaces, tabs, or line terminators (listed under getLineStart(_:end:contentsEnd:for:)). Some common word delimiting punctuation isn’t considered, so this property may not generally produce the desired results for multiword strings.
Case transformations aren’t guaranteed to be symmetrical or to produce strings of the same lengths as the originals. See lowercased for an example.
There is a built in function for that
nameOfString.capitalizedString
This will capitalize every word of string. To capitalize only the first letter you can use:
nameOfString.replaceRange(nameOfString.startIndex...nameOfString.startIndex, with: String(nameOfString[nameOfString.startIndex]).capitalizedString)
Older Thread
Here is what I came up with that seems to work but I am open to anything that is better.
func firstCharacterUpperCase(sentenceToCap:String) -> String {
//break it into an array by delimiting the sentence using a space
var breakupSentence = sentenceToCap.componentsSeparatedByString(" ")
var newSentence = ""
//Loop the array and concatinate the capitalized word into a variable.
for wordInSentence in breakupSentence {
newSentence = "\(newSentence) \(wordInSentence.capitalizedString)"
}
// send it back up.
return newSentence
}
or if I want to use this as an extension of the string class.
extension String {
var capitalizeEachWord:String {
//break it into an array by delimiting the sentence using a space
var breakupSentence = self.componentsSeparatedByString(" ")
var newSentence = ""
//Loop the array and concatinate the capitalized word into a variable.
for wordInSentence in breakupSentence {
newSentence = "\(newSentence) \(wordInSentence.capitalizedString)"
}
// send it back up.
return newSentence
}
}
Again, anything better is welcome.
Swift 5 version of Christopher Wade's answer
let str = "my string"
let result = str.capitalized(with: NSLocale.current)
print(result) // prints My String

Resources