web-dev-qa-db-fra.com

Changer la langue de l'application dans iOS sans redémarrer l'application

J'ai semble que certaines applications peuvent changer la langue en interne au sein de l'application sans avoir besoin de redémarrer l'application, je me demande comment elles sont mises en œuvre.

Par exemple, pour nous qui utilisons NSLocalizedString, je sais qu’il est possible de définir la langue lors de l’exécution à main.m lorsque votre AppDelegate n’est pas initialisée, mais qu’elle est initialisée (en particulier votre contrôleur de vue créé), elle n’a redémarrer

[[NSUserDefaults standardUserDefaults] 
    setObject:[NSMutableArray arrayWithObjects:language, nil] 
    forKey:@"AppleLanguages"];

Quelqu'un a-t-il une idée de la façon dont ces changements de langue dynamiques peuvent être effectués sans redémarrer l'application?

29
Ryan

On discute d'autres approches ici, en particulier une approche basée sur la notification:

iOS: Comment changer le langage de l'application par programme SANS redémarrer l'application?

À mon avis, il s’agit en réalité de trois tâches: (1) la relocalisation de ressources automatiquement chargées à partir de nibs. (par exemple, si vous instanciez de manière dynamique une autre UIView personnalisée à partir d'une nib, les anciennes chaînes et paramètres de langue (images, direction du texte) seront toujours chargés) (2) la relocalisation des chaînes actuellement affichées à l'écran. (3) re-localisation de chaînes insérées par le développeur (vous) dans le code du programme.

Commençons par (3). Si vous recherchez la définition, vous remarquerez que NSLocalizedString est une macro. Donc, si vous ne voulez pas trop modifier le code existant, vous pouvez probablement résoudre le problème de (3) en créant un nouveau fichier d’en-tête. Dans ce fichier d'en-tête, #undef et ensuite re -#define NSLocalizedString pour sélectionner la chaîne localisée à l'emplacement approprié - pas celle qui est définie par défaut pour iOS, mais celle que vous suivez dans une variable globale (par exemple, dans un délégué d'application ivar). . Si vous ne voulez pas redéfinir NSLocalizedString mais que vous créez toujours votre propre alternative, vous devriez probablement toujours #undef NSLocalizedString si vous ne voulez pas que les futurs développeurs l'appellent accidentellement à la place de la macro par laquelle vous la remplacez. Pas une solution idéale, mais peut-être la plus pratique.

Pour ce qui est de (1), si vous n'avez pas fait votre localisation dans Interface Builder, mais plutôt vous le faites de manière dynamique dans viewDidLoad, etc., pas de problème. Vous pouvez utiliser le même comportement que nous venons de décrire (c’est-à-dire le NSLocalizedString modifié, etc.). Sinon, vous pouvez soit (a) mettre en œuvre un système de notification comme décrit dans le lien ci-dessus (compliqué), ou (b) envisager de déplacer la localisation de IB vers viewDidLoad, ou (c) essayer de remplacer initWithNibName: et d'échanger l'objet chargé avec l'ancienne langue. ressources, avec une chargée avec les nouvelles ressources linguistiques. C'est une approche mentionnée par Mohamed au bas de la discussion: http://learning-ios.blogspot.ca/2011/04/advance-localization-in-ios-apps.html . Il affirme que cela cause des problèmes (viewDidLoad n'est pas appelé). Même si cela ne fonctionne pas, l'essayer peut vous conduire à quelque chose qui fonctionne.

Enfin, (2) est probablement la tâche la plus facile: il suffit de supprimer et de rajouter la vue actuelle (ou, dans certains cas, de la redessiner).

5
Merk

l'idée est d'écrire une nouvelle macro comme NSLocalizedString qui devrait vérifier si la traduction doit être prise à partir d'un autre paquet spécifique ou non.

Le méthode 2 dans cet article explique exactement comment le faire . Dans ce cas particulier, l'auteur n'utilise pas une nouvelle macro mais directement set une classe personnalisée pour [NSBundle mainBundle].

J'espère que @holex comprendra le problème en lisant ceci.

2
Matteo Gobbi

Mon implémentation utilise une classe pour changer de langue et accéder au groupe de langues actuel. C'est un exemple, donc si vous utilisiez des langues différentes de celles que je suis, changez les méthodes pour utiliser vos codes de langue exacts.

Cette classe accédera aux langues préférées depuis NSLocale et prendra le premier objet qui est la langue utilisée.

@implementation OSLocalization

+ (NSBundle *)currentLanguageBundle
{
    // Default language incase an unsupported language is found
    NSString *language = @"en";

    if ([NSLocale preferredLanguages].count) {
        // Check first object to be of type "en","es" etc
        // Codes seen by my eyes: "en-US","en","es-US","es" etc

        NSString *letterCode = [[NSLocale preferredLanguages] objectAtIndex:0];

        if ([letterCode rangeOfString:@"en"].location != NSNotFound) {
            // English
            language = @"en";
        } else if ([letterCode rangeOfString:@"es"].location != NSNotFound) {
            // Spanish
            language = @"es";
        } else if ([letterCode rangeOfString:@"fr"].location != NSNotFound) {
            // French
            language = @"fr";
        } // Add more if needed
    }

    return [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:language ofType:@"lproj"]];
}

/// Check if preferred language is English
+ (BOOL)isCurrentLanguageEnglish
{
    if (![NSLocale preferredLanguages].count) {
        // Just incase check for no items in array
        return YES;
    }

    if ([[[NSLocale preferredLanguages] objectAtIndex:0] rangeOfString:@"en"].location == NSNotFound) {
        // No letter code for english found
        return NO;
    } else {
        // Tis English
        return YES;
    }
}

/*  Swap language between English & Spanish
 *  Could send a string argument to directly pass the new language
 */
+ (void)changeCurrentLanguage
{
    if ([self isCurrentLanguageEnglish]) {
        [[NSUserDefaults standardUserDefaults] setObject:@[@"es"] forKey:@"AppleLanguages"];
    } else {
        [[NSUserDefaults standardUserDefaults] setObject:@[@"en"] forKey:@"AppleLanguages"];
    }
}
@end

Utilisez la classe ci-dessus pour référencer un fichier chaîne/image/video/etc:

// Access a localized image
[[OSLocalization currentLanguageBundle] pathForResource:@"my_image_name.png" ofType:nil]
// Access  a localized string from Localizable.strings file
NSLocalizedStringFromTableInBundle(@"StringKey", nil, [OSLocalization currentLanguageBundle], @"comment")

Changez de langue en ligne comme ci-dessous ou mettez à jour la méthode "changeCurrentLanguage" de la classe ci-dessus pour prendre un paramètre de chaîne faisant référence au nouveau code de langue.

// Change the preferred language to Spanish
[[NSUserDefaults standardUserDefaults] setObject:@[@"es"] forKey:@"AppleLanguages"];
1
Antonioni

J'utilise toujours cette méthode, cela fonctionne parfaitement, cela pourrait aussi vous aider.


vous devez définir tous les textes avec NSLocalizableString(...) pour la UI pour la langue actuelle dans la méthode -viewWillAppear: de votre chaque UIViewController.

en utilisant cette façon, vous (je veux dire les utilisateurs) vous n'avez pas besoin de redémarrer l'application après avoir changé la langue de iOS dans Settings.


bien sûr, j'utilise l'architecture de localisation standard d'Apple.

MISE À JOUR le (24 octobre 2013)

J'ai constaté que la méthode –viewWillAppear: ne sera pas exécutée pour la vue réelle lorsque l'application entre au premier plan. pour résoudre ce problème, j'engage également la procédure (voir ci-dessus) lorsque je reçois une notification UIApplicationWillEnterForegroundNotification dans la vue.

1
holex

J'étais coincé dans le même problème, mon exigence était "L'utilisateur peut sélectionner la langue du menu déroulant et l'application doit fonctionner selon la langue sélectionnée (anglais ou arabe)". De cette façon, l'utilisateur peut sélectionner la langue. J'ai utilisé NSBundle pour la même chose. comme 

Pour XIB

self.homeScreen =  [[HomeScreen alloc] initWithNibName:@"HomeScreen" bundle:[CommonData sharedCommonData].languageBundle];

Pour le texte 

_lblHeading.text = [self languageSelectedStringForKey:@"ViewHeadingInfo"];

/**
 This method is responsible for selecting language bundle according to user's selection. 
 @param: the string which is to be converted in selected language. 
 @return: the converted string.
 @throws:
 */

-(NSString*) languageSelectedStringForKey:(NSString*) key

{

    NSString* str=[[CommonData sharedCommonData].languageBundle localizedStringForKey:key value:@"" table:nil];

    return str;

}
0
Mangesh