Using the wrong Localizable.string - ios

I have an iOS app with a default language (English).
It also has a French localization and works as expected in both languages.
Here is the problem I am having:
When setting the device to a language other than English(default) or a language for which localization is provided (for example setting the device to Japanese); the app does not fall as expected in the default language. But it keeps the last language used for the app.
Why is that? And how can I fix it?
I have found a few post with a similar issue, but the solutions proposed did not work for me. For example this one, where the problem is similar to mine.
I am using Xcode version 11.1 and iOS version 12.4.2.

iOS will have a list of preferred language order based on the previously selected languages.
So, if you changed from English then to an unmapped language (e.g ko), the application will have as AppleLanguages the array:
[ko-(ZoneCode), en-(ZoneCode)].
You can avoid this procedure using this code below:
let defaultCultureCode: String = "en"
let defaults = UserDefaults.standard
let currentAppleLanguages = defaults.stringArray(forKey: "AppleLanguages")
if let currentLanguages = currentAppleLanguages {
if(!currentLanguages.isEmpty && !(currentLanguages.first?.contains(["en", "fr"]))!) {
defaults.removeObject(forKey: "AppleLanguages")
defaults.set([defaultCultureCode], forKey: "AppleLanguages")
defaults.synchronize()
}
}
I used this string extension to check if the current languages contain the available language:
extension String {
public func contains(_ elements: [String]) -> Bool {
var haveElementOnString = false
if elements.count == 0 { return false }
else { elements.forEach{ element in haveElementOnString = haveElementOnString || self.contains(element)} }
return haveElementOnString
}
}
The next step it's up to you to define the best strategy.
In my application context, it will always be defined as English by default because it is the only .strings file available. All the other supported strings will be downloaded and then a message will be displayed to the user warning that new languages are available and will be applied next time.
There's a lot of information on StackOverflow about bundle.localizedStringForKey or NSLocalizedString that could help you to found the best solution for what you need.
Also, this is only the validation and update for the current locale and the default language. this doesn't include the Region section (e.g. ko-KR (KR)).
I found an interesting text on this topic that could be useful
How not to do localization.
I hope it solves your problem!

Related

Swift: How to change the language inside of the app?

I have an app with localized string files.
Here are what my localized string files look like.
I also have a tableView with cells containing accessoryType (check marks). I want to let the user change the language of my app when changing the check in the tableView.
Here are the codes I have in the tableView of the settings of the app:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.setValues(value: languageArray[indexPath.row]) // Upload new language settings to the server
// Here I want to set the language of my app to the selected value: languageArray[indexPath.row]
// For example, languageArray[indexPath.row] is "en"
var array = tableView.visibleCells
for i in 0 ..< array.count {
let cells: UITableViewCell = array[i]
cells.accessoryType = .none
}
let cell:UITableViewCell! = tableView.cellForRow(at: indexPath)
cell.accessoryType = .checkmark
}
Thanks!
As most of answer saying "this is not possible" but,
It is possible and can be done it by simple thing.
In iOS application there are bundle for each languages, like en.lproj for english. You just need to load that specific bundle from selected language and get localizable stuff like: string, images etc. from this bundle.
var selectedLanguage = "en"
let path = Bundle.main.path(forResource: selectedLanguage, ofType: "lproj")
let bundle = Bundle(path: path!)
let localizedText = bundle!.localizedString(forKey: YOUR_TEXT_KEY, value: YOUR_TEXT_COMMENT, table: nil)
For reference, i cerated an LanguageDemo app, you can found all implementation and complete source code.
There is a way to do it, but as the previous answers mentioned it's not the best idea and might no be approved by Apple app review. But here is what I've tried and it worked:
UserDefaults.standard.set(["en"], forKey: "AppleLanguages")
UserDefaults.standard.synchronize()
Whenever the user select the language you need to apply this code, just replace ["en"] with your selected language. Keep in mind the user need to exit the app in order for this to work properly.
I know "this is not possible" answers are not really popular in Stack Overflow, but anyhow..
What you want to do is not possible. iOS doesn't allow you to change the language of the app from within the app. There is no API for that, and even if you somehow manage to achieve this thing by hacking (monkey patching) iOS, Apple wouldn't allow your app to be published to the itunes store.
There is a language setting within the "Settings" app (a system app), and there the user can set the language. All installed apps will use this language setting.
It is impossible to change the language of the iphone, but if you just want to change your app's language, I think there is the way: you can save the map as a plist or json file but the system .strings. And do not use the func NSLocalizedString, just encapsulation a func to get it, all the thing will be on the control.
This is so untrue about publishing etc, there is a way and you are not hacking anything, it is true there is not api for that but it is only a few lines of code, and it is nothing complicated... http://aplus.rs/2017/in-app-language-change-in-ios-app/
there is the link for your question, we have that o0n academy and this guy is our professor, if there is something you don't understand i am glad to help you with this because i got the same problem 2 years a go...

Xcode: How to change the language for the app when the user selects the language?

I would like to make my application multilingual, so I have been looking about how to add other languages in an app in Xcode, however I saw the language changes based on the language of your phone.
Is there a way to set a language when a user selects it in the application? If so, is it also possible to remember the selected language for the future? So the user will not have to select it every time when he or she starts the application again.
Thank you in advance
Well first you need to have a Localization.Strings file that have multiple languages strings.
Read about them here
Second of all you have multiple ways to detect what language user selected when the app starts, the common one for this case is userDefualts read about them here.
Therefore, you can implement the Localization file and use the value saved to detect what language to use from the userDefualts.
Localization is simply the process of translating your app into multiple languages.Internationalization is the process of making your app able to adapt to different languages, regions, and culture. 
Refer the link to implement:-
https://codeburst.io/localization-of-ios-app-in-swift-4-and-xcode-9-3c7c7d53ae11
You need to save the application language selected by user in userDefaults by example, this example is using the third party library SwiftyUserDefaults
Using this way you need to add the .strings with the "Localizable_" + initials of language of regular localization way, example
your .string file for Spanish should be named
"Localizable_es" but you can customize that in code too
this are the steps:
Save the app language selected by the user:
func setupAppLanguage(lang:String) {
Defaults[.appLanguage] = lang
}
Get saved language:
static func getCurrentLang() ->String
{
if(Defaults[.appLanguage] == nil)
{
if(NSLocale.current.languageCode == nil)
{
return "en"
}
return NSLocale.current.languageCode!
}else
{
return Defaults[.appLanguage] as String!
}
}
Get localized tableName language:
static func getLocalizedTableName() ->String
{
return "Localizable_\(Client.getCurrentLang())"
}
Method to localize:
//MARK: Localization Util
static func getLocalizedText(toLocalizeText:String) ->String{
return NSLocalizedString(toLocalizeText,tableName: Client.getLocalizedTableName(), comment: "")
}
Then you can use the getLocalizedText method the same way as you use NSLocalizedString, replacing it
Example of use
self.labelText.text = Client.getLocalizedText(toLocalizeText: "k_glossary")

How to get OS language and locale?

My app changes the language at runtime and advises the user to restart the app. I do it like this:
typealias LanguageLocaleType = (language: String?, locale: String?, title: String)
let elements: [LanguageLocaleType] = [
(nil, nil, "System Default"),
("en", "en_US", "English"),
("ar", "ar_SA", "Arabic"),
]
//...func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)...
let element = elements[indexPath.row]
guard let language = element.language else {
// How to really reset app back to OS language/locale???
UserDefaults.standard.removeObject(forKey: "AppleLanguages")
UserDefaults.standard.removeObject(forKey: "AppleLocale")
return
}
UserDefaults.standard.set([language], forKey: "AppleLanguages")
if let locale = element.locale, !locale.isEmpty,
Locale.current.identifier != locale,
!Locale.current.identifier.hasPrefix("\(language)_") {
UserDefaults.standard.set(locale, forKey: "AppleLocale")
}
I want to offer to set languages in a list with the right one selected, one of which being offered it to set language back to System Default. However, there's no way to find the OS-level language and locale that I could find. Since after I mess around with setting the UserDefaults, Bundle.main.preferredLocalizations is not reliable and do not match the system default (event when I remove the key from the User Default).
Is there a way to get the OS-level language and locale instead of the app-level?
You should be able to get currently selected OS language like this.
let language = NSLocale.preferredLanguages[0]
if this is not what you are looking for, refer to this answer
https://stackoverflow.com/a/30750120/809821
I will recommend you to use this library from Cocoa pods.
In your Podfile add this line:
pod 'Localize-Swift', '~> 1.7'
An then install your pods using this command:
pod install
Then if you don't have Localizable.strings, for example Localizable.strings (English) file add this lines.
"LD" = "Loading ...";
"CAMP" = "Campus";
"MAP" = "MAP";
The first word is like your TAG and the next word is your value .
Then in your ViewController you can use your string like this.
Example of use:
let progressHUD = ProgressHUD(text: "LD".localized())
let progressHUD = ProgressHUD(text: "CAMP".localized())
let progressHUD = ProgressHUD(text: "MAP".localized())
And that is all.
I hope this help.
I found an answer which seems to do something similar, by playing with the Bundle language files of your app. As in setting the proper one.
While you state that playing with Bundle properties seems to fiddle with your use case, I'd look into saving the setted language with Core Data, inside an app file, or inside and XML (.plist). You could set up a checking method in didFinishWithOptions which checks the presence of this variable then runs the language prompt if absent or leaves as is if present (adding a check to make sure the right language is set would be wise). Since this does not alter the Settings-set language, you'd always have a quick reference point outside of your app through NSLocale to switch back to it with ease if the user so chooses.
Here's the post
Disclaimer 1 : i haven't tested this, but it seems like a plausible, and Apple-Tolerated thing to do.
Disclaimer 2 : as the OP in the link states, and as some have mentioned here, changing app language is not recommended by Apple. Notably due to NSLocale keys, and how frequently Swift is updated which could lead to your code being unuseable or requiring massive overhaul.. As i mentioned in a comment above, i'd reconsider this use-case sadly :(
In reference to your question above you can either as stated above create your own bundle or use a prebuilt class like the following:https://github.com/mumensh/iOS-Localization

Localise Application for English and Arabic Language without restarting app

I want to localise application for two languages one is LTR & Another is RTL without restarting application.
class func changeLanguageForceFully(currLan : String){
let langCultureCode: String = currLan
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setObject([langCultureCode], forKey: "AppleLanguages")
defaults.synchronize()
}
Above function is working fine but it takes effect after restarting the application.
Are you supporting Arabic internationalisation in your project info?
Also are you setting constraints using leading/trailing?

String localization for framework

I'm building a framework that includes UI components. I'm retrieving localized strings with:
var bundle = Bundle(identifier: "com.my.identifier")
bundle.localizedString(forKey: "myKey", value: nil, table: nil)
The problem I'm seeing is whatever parent application integrates with my framework, must also have localization enabled for my same localizations (e.g. if I have localizations for es-ES, the parent application must also turn on localization for es-ES in project settings:
Otherwise my localized strings always default to english.
I have found a workaround by manually grabbing a language bundle based on the device's preferred language:
var bundle = Bundle(identifier: "com.my.identifier")
guard let deviceLanguage = Locale.preferredLanguages.first else {
return notFound
}
guard let languagePath = bundle.path(forResource: deviceLanguage, ofType: "lproj") else {
return notFound
}
guard let languageBundle = Bundle(path: languagePath) else {
return notFound
}
return NSLocalizedString(self, tableName: "Localizable", bundle: languageBundle, value:"KEY_NOT_FOUND", comment: "")
But the issue with this workaround solution is that I lose the built-in iOS language fallback (e.g. if the device preferred language is es-MX it will auto-choose the es-ES string over the en string I have).
For my framework, is there a way to avoid forcing parent applications to turn on localization for all my localized strings, yet still keep iOS language fallback logic?
Why do you want your framework to use a localization not supported by the app? This is a terrible user experience.
Imagine app that uses your framework only supports English and Spanish. Now imagine that a user of the app have their device setup to use German first, and English second.
Since the app doesn't support German, the app shows English. The entire app should show English. If your app framework manages to show German in this case, the app's user experience is now very confusing because much of the app is in English but the parts supported by your framework appear in German.
tl;dr - Don't do anything. Let the default behavior work as-is. It makes for a much better user experience.

Resources