web-dev-qa-db-fra.com

Changer de langue à la volée, sous iOS, par programme

Je suis entrain de patauger et de googler pendant des heures. Et je suis un peu désespéré maintenant ... Je voudrais changer la langue de mon application dans l'application, pas seulement avec la langue par défaut.

De ce que j'ai essayé, je suis resté bloqué comme tout le monde avec l'étape de redémarrage. Signification, pommes vous oblige à redémarrer l'application manuellement. Cela signifie que vous devez quitter l'application, puis la redémarrer.

Après avoir googlé, j’essayais de configurer une alarme, puis de forcer plus tard l’application à quitter avec

exit(0);

Ma mauvaise, Apple semble ne pas aimer cela et empêcher le développeur de l'utiliser ... Je suppose que je ne pointe pas dans la bonne direction.

Enfin, malgré tout le problème, je pourrais rencontrer je voudrais discuter à ce sujet.

Des allusions?


EDIT, infos de Apple

En général, vous ne devriez pas changer le Langue du système iOS (via l’utilisation de la clé de préfixe AppleLanguages) depuis ton application. Cela va contre le modèle utilisateur iOS de base pour la commutation langues dans l'application Paramètres, et utilise également une clé de préférence qui n'est pas documenté, ce qui signifie qu'à un moment donné à l'avenir, le nom de la clé pourrait changer, ce qui briserait votre application.

Si vous voulez changer de langue en votre application, vous pouvez le faire via chargement manuel des fichiers de ressources dans votre paquet. Vous pouvez utiliser NSBundle: pathForResource: ofType: inDirectory: forLocalization: à cette fin, mais gardez à l'esprit que votre application serait responsable de tout le chargement de données localisées. 

En ce qui concerne la question de sortie (0), Apple DTS ne peut pas commenter l'approbation de l'application processus. Vous devriez contacter [email protected] pour obtenir une réponse pour cette question.

Eh bien, je dois choisir jusqu'à présent.

30
gabrielstuff

C'est une question assez ancienne, mais je me débattais avec le même problème et j'ai trouvé cette solution:

http://aggressive-mediocrity.blogspot.com/2010/03/custom-localization-system-for-your.html

Ce qui fait exactement ce dont vous avez besoin (et pourrait être utile pour ceux qui ont le même problème :)

21
Swissdude

Le lien ci-dessous présente une implémentation de Nice consistant à avoir un langage personnalisé à partir de dans l'application.

sélection manuelle de la langue dans une application iOS (iPhone et iPad)

Tentative de trouver une version de Swift ici LanguageSettings_Swift

 LanguageChange

-anoop

5
anoop4real

oui, j'ai eu le même problème, alors je l'ai géré avec mon propre paramètre de langue dans mon prefFile, où j'ai défini une variable pour le paramètre de langue:

// write a new value in file and set the var
- (void)changeLangInPrefFile:(NSString *)newLanguage {
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *path = [documentsDirectory stringByAppendingPathComponent:@"myPreference.plist"];
    NSMutableDictionary *data = [[NSMutableDictionary alloc] initWithContentsOfFile: path];
    //here add elements to data file and write data to file
    [data setObject:newLanguage forKey:@"language"];
    [data writeToFile:path atomically:YES];
    [data release];

// NSString *chosenLang; <- declared in .h file
    if (chosenLang != nil){
        [chosenLang release];
        chosenLang = nil;
    }
    chosenLang = [[NSString alloc] initWithString:(@"%@",newLanguage)];

}

// read the language from file and set the var:
- (void)readFromFileInBundleDocuments {
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *path = [documentsDirectory stringByAppendingPathComponent:@"myPreference.plist"];
    NSMutableDictionary *savedStock = [[NSMutableDictionary alloc] initWithContentsOfFile:path];

    NSString *chosenLangTemp = [savedStock objectForKey:@"language"];
    NSLog (@"read in file: %@", chosenLangTemp);
    if (chosenLang != nil){
        [chosenLang release];
        chosenLang = nil;
    }
    chosenLang = [[NSString alloc] initWithString:(@"%@",chosenLangTemp)];
    [savedStock release];
}

puis je charge tout le contenu de différents fichiers en fonction de la langue Par exemple, je peux charger "an_image_fra.png" ou "an_image_ita.png", .__ ou avoir 2 fichiers .xib différents et pour le texte pour charger, j'utilise différents fichiers de dictionnaire, un pour chaque langue, avec tous les mots/expressions traduits, je charge juste celui qui a été choisi et je lis la bonne expression pour chaque texte à charger (le code pour le charger est similaire au méthode que j'ai écrite dans cet exemple, vous pouvez simplement l'arranger pour lire le mot correct pour chaque expression: il suffit de regarder la valeur de objectForKey dans le fichier de dictionnaire correct, où objectForKey est le mot à traduire et sa valeur est le mot traduit). ..

3
meronix

En règle générale, la langue que l'utilisateur voit est déterminée par les paramètres régionaux, qui sont définis à l'échelle du système. Seul l'utilisateur peut le modifier et, le cas échéant, SpringBoard et toutes les applications en cours d'exécution sur le périphérique doivent redémarrer. Il n’ya aucun moyen de contourner ce problème, car toutes les applications et tous les frameworks système supposent que les paramètres régionaux ne changent pas une fois lancés. Il serait très difficile pour Apple de modifier les applications et les infrastructures pour ne pas nécessiter de redémarrage. 

J'imagine que vous voulez modifier la langue de l'interface de votre application de manière totalement indépendante des paramètres régionaux du système ou utiliser les paramètres régionaux du système par défaut tout en permettant à l'utilisateur de les remplacer uniquement pour votre application. 

Vous pouvez obtenir les paramètres régionaux actuels et examiner ses différentes valeurs à l'aide de +[NSLocale currentLocale]. Pour afficher l'interface utilisateur de votre application dans une langue indépendante des paramètres régionaux du système, vous devez absolument éviter d'utiliser NSLocalizedString() et utiliser une sorte d'état personnalisé permettant de déterminer les chaînes à afficher pour l'utilisateur et comment procéder. modifiez l'interface pour l'adapter à la langue de votre application. Il vous appartient de conserver l'état de la langue de votre application et de modifier son interface utilisateur en conséquence.

1
Ryan

Selon Apple guidelines, ce n'est pas une bonne idée de changer de langue dans l'application par programme, mais si vous n'avez pas le pouvoir de changer le comportement demandé, vous pouvez faire quelque chose comme ceci:

  1. Préparez un service pour gérer votre langue même après le redémarrage de l'application

    enum LanguageName: String {
        case undefined
        case en
        case es
    }
    
    let DynamicLanguageServiceDidDetectLanguageSwitchNotificationKey = "DynamicLanguageServiceDidDetectLanguageSwitchNotificationKey"
    
    func dynamicLocalizableString(_ key: String) -> String {
        return LanguageService.service.dynamicLocalizedString(key)
    }
    
    class LanguageService {
    
        private struct Defaults {
            static let keyAppleLanguage = "AppleLanguages"
            static let keyCurrentLanguage = "KeyCurrentLanguage"
        }
    
        static let service:LanguageService = LanguageService()
    
        var languageCode: String {
            get {
                return language.rawValue
            }
        }
    
        var currentLanguage:LanguageName {
            get {
                var currentLanguage = UserDefaults.standard.object(forKey: Defaults.keyCurrentLanguage)
                if let currentLanguage = currentLanguage as? String {
                    UserDefaults.standard.set([currentLanguage], forKey: Defaults.keyAppleLanguage)
                    UserDefaults.standard.synchronize()
                } else {
                    if let languages = UserDefaults.standard.object(forKey: Defaults.keyAppleLanguage) as? [String] {
                        currentLanguage = languages.first
                    }
                }
                if let currentLanguage = currentLanguage as? String, 
                    let lang = LanguageName(rawValue: currentLanguage) {
                    return lang
                }
                return LanguageName.undefined
            }
        }
    
        func switchToLanguage(_ lang:LanguageName) {
            language = lang
            NotificationCenter.default.post(name: NSNotification.Name(rawValue: DynamicLanguageServiceDidDetectLanguageSwitchNotificationKey), object: nil)
        }
    
        private var localeBundle:Bundle?
    
        fileprivate var language: LanguageName = LanguageName.en {
            didSet {
                let currentLanguage = language.rawValue
    
                UserDefaults.standard.set([currentLanguage], forKey:Defaults.keyAppleLanguage)
                UserDefaults.standard.setValue(currentLanguage, forKey:Defaults.keyCurrentLanguage)
                UserDefaults.standard.synchronize()
    
                setLocaleWithLanguage(currentLanguage)            
            }
        }
    
        // MARK: - LifeCycle
    
        private init() {
            prepareDefaultLocaleBundle()
        }
    
        //MARK: - Private
    
        fileprivate func dynamicLocalizedString(_ key: String) -> String {
            var localizedString = key
            if let bundle = localeBundle {
                localizedString = NSLocalizedString(key, bundle: bundle, comment: "")
            } else {
                localizedString = NSLocalizedString(key, comment: "")
            }
            return localizedString
        }
    
        private func prepareDefaultLocaleBundle() {
            var currentLanguage = UserDefaults.standard.object(forKey: Defaults.keyCurrentLanguage)
            if let currentLanguage = currentLanguage as? String {
                UserDefaults.standard.set([currentLanguage], forKey: Defaults.keyAppleLanguage)
                UserDefaults.standard.synchronize()
            } else {
                if let languages = UserDefaults.standard.object(forKey: Defaults.keyAppleLanguage) as? [String] {
                    currentLanguage = languages.first
                }
            }
    
            if let currentLanguage = currentLanguage as? String {
                updateCurrentLanguageWithName(currentLanguage)
            }
        }
    
        private func updateCurrentLanguageWithName(_ languageName: String) {
            if let lang = LanguageName(rawValue: languageName) {
                language = lang
            }
        }
    
        private func setLocaleWithLanguage(_ selectedLanguage: String) {
            if let pathSelected = Bundle.main.path(forResource: selectedLanguage, ofType: "lproj"),
                let bundleSelected = Bundle(path: pathSelected)  {
                localeBundle = bundleSelected
            } else if let pathDefault = Bundle.main.path(forResource: LanguageName.en.rawValue, ofType: "lproj"),
                let bundleDefault = Bundle(path: pathDefault) {
                localeBundle = bundleDefault
            }
        }
    }
    
  2. Ajoutez des règles pour vous assurer que vos composants d'interface utilisateur seront toujours mis à jour:

    protocol Localizable {
        func localizeUI()
    }
    
  3. Les mettre en œuvre

    class LocalizableViewController: UIViewController {
    
        // MARK: - LifeCycle
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            NotificationCenter.default.addObserver(self, selector: #selector(self.localizeUI), name: NSNotification.Name(rawValue:DynamicLanguageServiceDidDetectLanguageSwitchNotificationKey), object: nil)
        }
    
        override func viewWillAppear(_ animated: Bool) {
            super.viewWillAppear(animated)
    
            localizeUI()
        }
    
        deinit {
            NotificationCenter.default.removeObserver(self)
        }
    }
    
    extension LocalizableViewController: Localizable {
        // MARK: - Localizable
    
        func localizeUI() {
            fatalError("Must Override to provide inApp localization functionality")
        }
    }
    
  4. Héritez de tout contrôleur. Vous souhaitez vous conformer à la fonctionnalité de commutateur d'application dynamique et mettre en œuvre localizeUI()fonction

    final class WelcomeTableViewController: LoadableTableViewController
    
  5. Changer de langue au besoin:

    LanguageService.service.switchToLanguage(.en)
    
  6. Toutes les chaînes localisées doivent être définies comme suit:

    label.text = dynamicLocalizableString(<KEY_IN_STRINGS>)
    

Remarque: n'oubliez pas d'ajouter Localizable.strings avec les mêmes codes que dans LanguageName

 enter image description here

0
gbk

C'est une vieille question, mais je développais un assistant qui me notifie lorsque la langue change à la volée.

Regardez le code de helper:

import Foundation

class LocalizableLanguage {

    // MARK: Constants

    fileprivate static let Apple_LANGUAGE_KEY = "AppleLanguages"

    /// Notification Name to observe when language change
    static let ApplicationDidChangeLanguage = Notification.Name("ApplicationDidChangeLanguage")

    // MARK: Properties

    /// An array with all available languages as String
    static var availableLanguages: [String]? = {
        return UserDefaults.standard.object(forKey: Apple_LANGUAGE_KEY) as? [String]
    }()

    /// The first element of available languages that is the current language
    static var currentLanguageCode: String? = {
        return availableLanguages?.first
    }()

    /// The current language code with just 2 characters
    static var currentShortLanguageCode: String? = {
        guard let currentLanguageCode = currentLanguageCode else {
            return nil
        }

        let strIndex = currentLanguageCode.index(currentLanguageCode.startIndex, offsetBy: 2)
        return currentLanguageCode.substring(to: strIndex)
    }()

    // MARK: Handle functions

    /// This accepts the short language code or full language code
    /// Setting this will send a notification with name "ApplicationDidChangeLanguage", that can be observed in order to refresh your localizable strings
    class func setLanguage(withCode langCode: String) {

        let matchedLangCode = availableLanguages?.filter {
            $0.contains(langCode)
        }.first

        guard let fullLangCode = matchedLangCode else {
            return
        }

        var reOrderedArray = availableLanguages?.filter {
            $0.contains(langCode) == false
        }

        reOrderedArray?.insert(fullLangCode, at: 0)

        guard let langArray = reOrderedArray else {
            return
        }

        UserDefaults.standard.set(langArray, forKey: Apple_LANGUAGE_KEY)
        UserDefaults.standard.synchronize()

        LocalizableLanguage.refreshAppBundle()

        NotificationCenter.default.post(name: ApplicationDidChangeLanguage, object: fullLangCode)
    }
}

// MARK: Refresh Bundle Helper

private extension LocalizableLanguage {

    class func refreshAppBundle() {
        MethodSwizzleGivenClassName(cls: Bundle.self, originalSelector: #selector(Bundle.localizedString(forKey:value:table:)), overrideSelector: #selector(Bundle.specialLocalizedStringForKey(_:value:table:)))
    }

    class func MethodSwizzleGivenClassName(cls: AnyClass, originalSelector: Selector, overrideSelector: Selector) {
        let origMethod: Method = class_getInstanceMethod(cls, originalSelector);
        let overrideMethod: Method = class_getInstanceMethod(cls, overrideSelector);
        if (class_addMethod(cls, originalSelector, method_getImplementation(overrideMethod), method_getTypeEncoding(overrideMethod))) {
            class_replaceMethod(cls, overrideSelector, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
        } else {
            method_exchangeImplementations(origMethod, overrideMethod);
        }
    }
}

extension Bundle {

    func specialLocalizedStringForKey(_ key: String, value: String?, table tableName: String?) -> String {

        let availableLanguages = UserDefaults.standard.object(forKey: LocalizableLanguage.Apple_LANGUAGE_KEY) as? [String]
        let currentLanguageCode = availableLanguages?.first ?? "en-US"
        let currentShortLanguageCode = currentLanguageCode.substring(to: currentLanguageCode.index(currentLanguageCode.startIndex, offsetBy: 2))

        let path =
                Bundle.main.path(forResource: currentLanguageCode, ofType: "lproj") ??
                Bundle.main.path(forResource: currentShortLanguageCode, ofType: "lproj") ??
                Bundle.main.path(forResource: "Base", ofType: "lproj")

        guard
            self == Bundle.main,
            let bundlePath = path,
            let bundle = Bundle(path: bundlePath)
        else {
            return self.specialLocalizedStringForKey(key, value: value, table: tableName)
        }

        return bundle.specialLocalizedStringForKey(key, value: value, table: tableName)
    }
}

Il vous suffit de copier ce code et de l'insérer dans votre projet.

Ensuite, vous implémentez simplement l’auditeur comme ceci:

NotificationCenter.default.addObserver(forName: LocalizableLanguage.ApplicationDidChangeLanguage, object: nil, queue: nil) { notification in
            guard let langCode = notification.object as? String else {
                return
            }
            self.accountStore.languageCode.value = langCode
        } 

Notez que cette ligne self.accountStore.languageCode.value = langCode correspond à ce que j’ai besoin d’actualiser lorsque la langue de l’application a été modifiée. Je peux alors facilement modifier toutes les chaînes de mes ViewModels afin de modifier immédiatement la langue de l’utilisateur.

Pour changer de langue, vous pouvez simplement appeler:

LocalizableLanguage.setLanguage(withCode: "en")

Une autre aide qui pourrait être gentille avec vous est:

import Foundation

extension String {

    var localized: String {
        return NSLocalizedString(self, comment: "")
    }

}

Donc, si vous avez dans vos fichiers localisables quelque chose comme ça:

main.view.title = "Title test";

Vous pouvez appeler simplement:

"main.view.title".localized

Et vous avez votre chaîne traduite.

0
Pincha