How to Change Locale Programmatically with Swift

Swift : How to change language inside app?

found my answer :

NSUserDefaults.standardUserDefaults().setObject(["language identifier"], forKey: "AppleLanguages") 
NSUserDefaults.standardUserDefaults().synchronize()

unfortunately user must restart the app!
if anyone could find a solution to not restart the application please inform me.

Change Language in the app programmatically in iOS

Usually when you support official languages that Apple supports in iOS, there is no reason to provide language switching within the app, just properly set up translations in your project and interface language will automatically switch with the system. But since you want it from the app, there are few ways to go about this:

1) You could force a specific language for just your app with the following code:

[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:@"zh-Hans", @"en", @"fr", nil] forKey:@"AppleLanguages"];
[[NSUserDefaults standardUserDefaults] synchronize];

I would suggest putting this code inside main.m file in "int main" function just before the "return UIApplicationMain". But this method requires you to kill the app or tell user to restart the app for it to take effect.

You can kill the app without having user to force quit the app using exit(0), but make sure user has chance to abort the action with UIAlertView or similar, or Apple might reject your app.

2) Alternative is implementing your own localization logic, where you just take translations from your own language file. One way is this example which takes translations from official lproj files. This way you can change the language on the fly without a restart, but you have to manually load all label texts from the code. When you change the translation, you have to repopulate the text on screen.

Changing language on the fly in swift

Basically you have to teach you bundle how to switch languages by loading different bundle files.

I translated my Objective-C code to Swift — with leaving the NSBundle category untouched.

Sample Image

The result is a view controller class that offers a languageDidChange() method for overriding.


NSBundle+Language.h

#import 

@interface NSBundle (Language)
+(void)setLanguage:(NSString*)language;

@end

NSBundle+Language.m

#import "NSBundle+Language.h"
#import

static const char associatedLanguageBundle=0;

@interface PrivateBundle : NSBundle
@end

@implementation PrivateBundle
-(NSString*)localizedStringForKey:(NSString *)key
value:(NSString *)value
table:(NSString *)tableName
{
NSBundle* bundle=objc_getAssociatedObject(self, &associatedLanguageBundle);
return bundle ? [bundle localizedStringForKey:key
value:value
table:tableName] : [super localizedStringForKey:key
value:value
table:tableName];
}
@end

@implementation NSBundle (Language)
+(void)setLanguage:(NSString*)language
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
object_setClass([NSBundle mainBundle],[PrivateBundle class]);
});

objc_setAssociatedObject([NSBundle mainBundle], &associatedLanguageBundle, language ?
[NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:language ofType:@"lproj"]] : nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end

AppDelegate.swift

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

var window: UIWindow?


func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {

NSNotificationCenter.defaultCenter().addObserver(self, selector: "languageWillChange:", name: "LANGUAGE_WILL_CHANGE", object: nil)

let targetLang = NSUserDefaults.standardUserDefaults().objectForKey("selectedLanguage") as? String

NSBundle.setLanguage((targetLang != nil) ? targetLang : "en")
return true
}

func languageWillChange(notification:NSNotification){
let targetLang = notification.object as! String
NSUserDefaults.standardUserDefaults().setObject(targetLang, forKey: "selectedLanguage")
NSBundle.setLanguage(targetLang)
NSNotificationCenter.defaultCenter().postNotificationName("LANGUAGE_DID_CHANGE", object: targetLang)
}
}

BaseViewController.swift

import UIKit



class BaseViewController: UIViewController {

@IBOutlet weak var englishButton: UIButton!
@IBOutlet weak var spanishButton: UIButton!

deinit{
NSNotificationCenter.defaultCenter().removeObserver(self)
}
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "languageDidChangeNotification:", name: "LANGUAGE_DID_CHANGE", object: nil)
languageDidChange()

}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}

@IBAction func switchLanguage(sender: UIButton) {

var localeString:String?
switch sender {
case englishButton: localeString = "en"
case spanishButton: localeString = "es"
default: localeString = nil
}


if localeString != nil {
NSNotificationCenter.defaultCenter().postNotificationName("LANGUAGE_WILL_CHANGE", object: localeString)
}
}


func languageDidChangeNotification(notification:NSNotification){
languageDidChange()
}

func languageDidChange(){

}


}

ViewController.swift

import UIKit

class ViewController: BaseViewController {

@IBOutlet weak var helloLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}

override func languageDidChange() {
super.languageDidChange()
self.helloLabel.text = NSLocalizedString("Hello", comment: "")

}
}

instead of using subclasses of BaseViewController, your viewcontrollers could also post "LANGUAGE_WILL_CHANGE" and listen for "LANGUAGE_DID_CHANGE"

I pushed the complete project here: ImmediateLanguageSwitchSwift



Related Topics



Leave a reply



Submit