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

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...

Related

can we release same app with 1 or 2 different functionality for 2 different countries?

I want to release my app in only 2 country and i want to do 2 different functionality for both country.
For example.
ViewController1 functionality is different in Jamaica.
ViewController1 functionality is different in Kenya.
Different functionality means content is different, or input forms are different.
Is it possible? if yes then please refer some document.
Thanks in advance
You should have a screen that allows user to select their country, after that, store selected country in our app (by UserDefault or Keychain, etc...).
Based on the selected country then you can switch logic/layout to adapt the requirement above
some notes about App Store:
1) language should / must be selected by user on Prefs, NOT in Apps.
Chances Apple will refuse apps not following above logic.
2) You could test current language / Zone using code (see below for language)
BUT I think Apple can refuse as you use a different behaviour
3) if really you need it, You can load a different controller using Storyboards (I suggest using different storyboards AND lod them at runtime using segues and "*.soryboard" as in:
func ViewControllerFromStoryboardWith( name: String ) -> UIViewController {
// we use an identifier equal to filename for now.
let storyboard = UIStoryboard(name: name, bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: name) as UIViewController
return vc
}
// test lang:
func currHWLanguage()->String{
let defs : UserDefaults = UserDefaults.standard
let languages : NSArray = defs.object(forKey: "AppleLanguages") as! NSArray
let current = languages[0] as! String
// since 9.0 we get en-US etc.. so cut to 2:
let result = (current as NSString).substring(to: 2)
#if DEBUG
// force to IT as a bug in simulator
// return "IT"
#endif
return result.uppercased()
//NSLog("%#", current)
}
This is a problem many applications are trying to solve. Basically, you have the following options:
Let the user choose. This is the safest option if one application contains two different configuration.
Try to detect location of the user. Language/Locale is unsafe because many people will have English (or different) locale set up. Very unsafe. You shouldn't ask for GPS location for this. The safest option is to create a server request and check the location using the IP address. A bit complicated and won't work if a VPN is used (e.g. antivirus apps create VPNs).
Create two different apps. In the end, this is the best option. Add a second application target to your project and release two separate apps with separate configuration.

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")

switching app language without restarting the app swift

I want my app language to change on button click..
My code is:
#IBAction func convertlang(_ sender: Any) {
if L102Language.currentAppleLanguage() == "en" {
L102Language.setAppleLAnguageTo(lang: "ar")
UIView.appearance().semanticContentAttribute = .forceRightToLeft
} else {
L102Language.setAppleLAnguageTo(lang: "en")
UIView.appearance().semanticContentAttribute = .forceLeftToRight
}
}
let APPLE_LANGUAGE_KEY = "AppleLanguages"
class L102Language {
class func currentAppleLanguage() -> String{
let userdef = UserDefaults.standard
let langArray = userdef.object(forKey: APPLE_LANGUAGE_KEY) as! NSArray
let current = langArray.firstObject as! String
return current
}
class func setAppleLAnguageTo(lang: String) {
let userdef = UserDefaults.standard
userdef.set([lang,currentAppleLanguage()], forKey: APPLE_LANGUAGE_KEY)
userdef.synchronize()
}}
and this works fine and convert the language when i click on the button..
the problem is i need to restart the app to see the language change..
i searched for this but most of them were for objective-c and tried some for swift but didn't work..
how to do this?
You can't change the language at runtime with normal button click. If you really need that, you need to use a custom localization system instead.
To change the app language during runtime, I have done this manually by creating two different storyboards. I saved the language preference in NSUserDefaults but NOT with the key AppleLanguages and then called AppDelegate's didFinishLaunchingWithOptions method to select storyboard.
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainEnglish" bundle:nil];
UINavigationController *navigationcontroller=[[UINavigationController alloc]init];
RegistorViewController *registor=[storyboard instantiateViewControllerWithIdentifier:#"Registor"];
[self.window setRootViewController:navigationcontroller];
[self.window makeKeyAndVisible];
[navigationvontroller pushViewController:registor animated:NO];
Though this is not what apple wants a developer to do in runtime.
Someone even asked an Apple engineer and this is the response:
In general, you should not change the iOS system language (via use of the AppleLanguages pref key) from within your application. This goes against the basic iOS user model for switching languages in the Settings app, and also uses a preference key that is not documented, meaning that at some point in the future, the key name could change, which would break your application.
If you want to switch languages in your application, you can do so via manually loading resource files in your bundle. You can use NSBundle:pathForResource:ofType:inDirectory:forLocalization: for this purpose, but keep in mind that your application would be responsible for all loading of localized data.
I had faced the same problem in my project recently. You are using the correct code to change app language but it change only string file data not the storyboard. For complete localisation you have to restart the app again, because there is no any another way to do this. You can show a popup to user "Please restart your app to change language completely". Because we can kill the app programatically, but after doing this there is no way to restart the app again.
This might not be the most iOS-typical way of doing things, but I tend to treat static content like any other data in the app, especially when I'm using VIPER (although other patterns would work) and a unidirectional data flow.
With that in mind, for this I would have a service accessing a store of static content keyed of the user's language preference. When that changes I would trigger a refresh of the content.
This has worked well for projects when I also have server data as well as local data that needs to be localised.
To be fair, I've never used the standard tools much. And I suspect this may not be ideal for right-to-left language types.

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

Pass data from TodayExtension to app

Is it possible to pass data from a today extension to an app? (Even when its not currently running). I wish to pass an array of objects to the main app and instantiate a viewController based on the objects passed from the today extension. I know how to open the app from the extension just not to too sure how to send data to the app.
var arrayToBePassed: [MyDataSource]
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
var url = ""
if currentSort == .recent{
url = "ext://recent"
}else{
url = "ext://popular"
}
//HOW DO I PASS arrayToBePassed to my app?
let myAppUrl = NSURL(string: url)!
extensionContext?.openURL(myAppUrl, completionHandler: { (success) in
if (!success) {
}else{
}
})
}
There's a few routes you could go down:
• In your app, implement a URL scheme that can quite literally take that array of objects, formatted as some sort of URL parameter. e.g. myAwesomeApp://objectArray=[these, are, strings]. You'll have to parse that URL yourself which should be a bit of fun.
• Look into NSUserDefaults. You can initialise a 'shared' user defaults object that both your app and your today extension can use, see here for more info. You could then store your array in here, and access it from the app when opened.
It really depends on what you're trying to do, but from the impression I get, I feel like the first option may be the best answer. I haven't provided any code, just an outline of how I'd go about it - but hopefully that should be enough to get you off to a good start.
Sharing of data is achieved through a new concept called “App Groups”. App Groups are allowed to share some data, including files, but it is worth noting that file access needs to be marshalled to avoid concurrent writes and so forth. This can be achieved through NSFileCoordination, but CoreData and NSUserDefaults handle this out of the box.
You can find the detial Steps here

Resources