switching app language without restarting the app swift - ios

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.

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.

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

App not showing in external display extension for any iPhone

I created a new app in Xcode and added the following code in the AppDelegate file
func updateCarWindow()
{
guard let screen = UIScreen.screens.first(where: { $0.traitCollection.userInterfaceIdiom == .carPlay })
else
{
// CarPlay is not connected
self.carWindow = nil;
return
}
// CarPlay is connected
let carWindow = UIWindow(frame: screen.bounds)
carWindow.screen = screen
carWindow.makeKeyAndVisible()
carWindow.rootViewController = CarViewController(nibName: nil, bundle: nil)
self.carWindow = carWindow
}
and called the function in function application. The app is not showing in the CarPlay external display.
You don’t have direct access to the carplay screen, carplay manages everything using the CPInterfaceController class that is able to display so called templates (such as CPListTemplate and a handful of others). Your ability to draw on the screen is pretty much limited to drawing maps in a CPMapContentWindow.
I recommend you read the Apple docs first starting here:
https://developer.apple.com/carplay/documentation/CarPlay-Navigation-App-Programming-Guide.pdf
Don’t forget to set the correct app permissions and carplay entitlements othereise it simply won’t work and it might not tell you why.
And a final word that the Carplay framework is only supposed to work with navigation apps. Everything else would require a lot of workarounds, not to mention it would never pass app review.
Hope this helps

iOS Notification Content Extension - How to pass data to app?

I wrote a custom Notification Content Extension for my Push Notifications like this:
The thing is, whenever the user is on a certain item in the carousel, I want the GO TO APP button to send a String to the app when it's opening, and from there, handle that string to move the user to the correct ViewController.
I already have the handling part inside the app, I just need to know how to pass that String from the Notification Content Extension to the container app.
Thanks! :)
Enable app groups in capabilities and use suite userDefaults to write the key and read it in the app
NSUserDefaults*defaults= [[NSUserDefaults alloc] initWithSuiteName:#"group.com.company.appName"];
// Write in extension
[defaults setObject:#"anyThing" forKey:#"sharedContent"];
// Read in app
[defaults objectForKey:#"sharedContent"];
If your app is configured for Universal Links or you have defined a Custom URL Scheme for your app, you can also open your app's URL (e.g. with data in query parameters) by calling
extensionContext?.open(url)
in your NotificationViewController.
iOS 13, Swift 5.
Based on the answer by Sh_Khan, here is some Swift Syntax. Obviously I have added App Group as a capability to the target of the main app + the target of the extension, naming the group as "group.ch.Blah" for this example.
Setting your app group, saving a string in our case, needed to set the type as Any cause strings not a type that is available in groups.
let localK = getPrivateKey64() as Any
let defaults = UserDefaults.init(suiteName: "group.ch.Blah")
defaults?.set(localK, forKey: "privateK")
Setting your app group, and reading the string back, needed to recast it back to string.
let defaults = UserDefaults.init(suiteName: "group.ch.Blah")
let localK = defaults?.object(forKey: "privateK") as? String
Worked perfectly with a notification service extension.

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

Resources