Comment forcer NSLocalizedString à utiliser une langue spécifique

Réponses:

261

NSLocalizedString()(et ses variantes) accédez à la clé "AppleLanguages" NSUserDefaultspour déterminer quels sont les paramètres de l'utilisateur pour les langues préférées. Cela renvoie un tableau de codes de langue, le premier étant celui défini par l'utilisateur pour son téléphone, et les suivants utilisés comme solutions de rechange si une ressource n'est pas disponible dans la langue préférée. (sur le bureau, l'utilisateur peut spécifier plusieurs langues avec une commande personnalisée dans les Préférences Système)

Vous pouvez remplacer le paramètre global de votre propre application si vous le souhaitez en utilisant la méthode setObject: forKey: pour définir votre propre liste de langues. Cela aura priorité sur la valeur définie globalement et sera renvoyé à tout code de votre application qui effectue la localisation. Le code pour cela ressemblerait à quelque chose comme:

[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:@"de", @"en", @"fr", nil] forKey:@"AppleLanguages"];
[[NSUserDefaults standardUserDefaults] synchronize]; //to make the change immediate

Cela ferait de l'allemand la langue préférée pour votre application, avec l'anglais et le français comme solutions de rechange. Vous voudriez l'appeler quelque temps au début du démarrage de votre application. Vous pouvez en savoir plus sur les préférences de langue / paramètres régionaux ici: Rubriques de programmation d'internationalisation: obtenir la langue et les paramètres régionaux actuels

Brian Webster
la source
4
Cela ne fonctionne pas pour moi, il utilisera la langue par défaut quoi qu'il arrive.
quano
50
Denniss; Cela semble mieux fonctionner si vous définissez la préférence de langue avant le lancement de l'application. Je le fais dans la fonction main (), avant d'appeler UIApplicationMain (). Une fois qu'il s'exécute, il ne change pas la langue utilisée, mais définit simplement la préférence pour la prochaine fois.
geon
3
Vous devez définir la langue avant d'initialiser UIKit et vous devez spécifier une langue complète + paramètres régionaux de région - vérifiez cela pour un exemple complet blog.federicomestrone.com/2010/09/15/…
fedmest
18
définir AppleLanguages ​​changera une langue au moment de l'exécution si cela est fait dans main () - vrai! mais ... la PREMIÈRE FOIS que l'application est installée, les nsuserdefaults sont initialisés APRÈS UIApplicationMain () est appelé, qui ignorera les AppleLanguages ​​précédemment définis et nécessitera un laid-redémarrage forcé de l'application pour voir la langue souhaitée.
JohnPayne
3
Cela ne fonctionne pas avec plusieurs éléments définis dans Localizable.stringsdict. Une idée s'il y a une solution possible?
Mihai Fratu
147

J'ai eu le même problème récemment et je ne voulais pas démarrer et corriger l'intégralité de ma NSLocalizedString ni forcer l'application à redémarrer pour que la nouvelle langue fonctionne. Je voulais que tout fonctionne tel quel.

Ma solution était de changer dynamiquement la classe du bundle principal et d'y charger le bundle approprié:

En tête de fichier

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

la mise en oeuvre

#import <objc/runtime.h>

static const char _bundle=0;

@interface BundleEx : NSBundle
@end

@implementation BundleEx
-(NSString*)localizedStringForKey:(NSString *)key value:(NSString *)value table:(NSString *)tableName
{
    NSBundle* bundle=objc_getAssociatedObject(self, &_bundle);
    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],[BundleEx class]);
    });
    objc_setAssociatedObject([NSBundle mainBundle], &_bundle, language ? [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:language ofType:@"lproj"]] : nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end

Donc, fondamentalement, lorsque votre application démarre et avant de charger votre premier contrôleur, appelez simplement:

[NSBundle setLanguage:@"en"];

Lorsque votre utilisateur change sa langue préférée dans votre écran de configuration, il vous suffit de l'appeler à nouveau:

[NSBundle setLanguage:@"fr"];

Pour rétablir les valeurs par défaut du système, passez simplement nil:

[NSBundle setLanguage:nil];

Prendre plaisir...

Pour ceux qui ont besoin d'une version Swift:

var bundleKey: UInt8 = 0

class AnyLanguageBundle: Bundle {

    override func localizedString(forKey key: String,
                                  value: String?,
                                  table tableName: String?) -> String {

        guard let path = objc_getAssociatedObject(self, &bundleKey) as? String,
              let bundle = Bundle(path: path) else {

            return super.localizedString(forKey: key, value: value, table: tableName)
            }

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

extension Bundle {

    class func setLanguage(_ language: String) {

        defer {

            object_setClass(Bundle.main, AnyLanguageBundle.self)
        }

        objc_setAssociatedObject(Bundle.main, &bundleKey,    Bundle.main.path(forResource: language, ofType: "lproj"), .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
    }
}
Gilad Novik
la source
7
Salut @ Wirsing, cela fonctionne très bien pour moi jusqu'à présent. J'ai même téléchargé une application sur la boutique et aucune plainte d'Apple.
Gilad Novik
7
Ceci est une excellente solution, je l'ai également référencé ici: medium.com/ios-apprentice/905e4052b9de
James Tang
1
Salut @JamesTang, Merci pour la référence et votre message - J'ai trouvé beaucoup d'informations utiles concernant la localisation.
Gilad Novik
3
@Gilad votre méthode fonctionne parfaitement pour les chaînes dynamiques (chaînes que j'ai définies dans localisable.strings) mais comme pour les boutons et les étiquettes du storyboard, elle ne fonctionne qu'avec la méthode principale uniquement. pouvez-vous donc étendre votre réponse pour inclure la localisation des contrôles de l'interface utilisateur? je veux dire comment actualiser (sans fermer) le storyboard après avoir invoqué [NSBundle setLanguage: @ "??"] ;?
Jawad Al Shaikh
2
Comme pour XIB / storyboard: vous devez vous assurer d'appeler cette méthode AVANT de charger la vue. En ce qui concerne la mise à jour des vues en direct: dans mon cas, j'ai simplement rechargé le contrôleur de vue (je l'ai mis dans un contrôleur de navigation et je l'ai simplement remplacé. Lors du rechargement, il a obtenu les nouvelles chaînes traduites). Je travaille sur un cocoapod pour cela, je vais probablement y inclure un exemple également. Restez à l'écoute ...
Gilad Novik
137

Je fais généralement cela de cette façon, mais vous DEVEZ avoir tous les fichiers de localisation dans votre projet.

@implementation Language

static NSBundle *bundle = nil;

+(void)initialize 
{
    NSUserDefaults* defs = [NSUserDefaults standardUserDefaults];
    NSArray* languages = [defs objectForKey:@"AppleLanguages"];
    NSString *current = [[languages objectAtIndex:0] retain];
    [self setLanguage:current];
}

/*
  example calls:
    [Language setLanguage:@"it"];
    [Language setLanguage:@"de"];
*/
+(void)setLanguage:(NSString *)l
{
    NSLog(@"preferredLang: %@", l);
    NSString *path = [[ NSBundle mainBundle ] pathForResource:l ofType:@"lproj" ];
    bundle = [[NSBundle bundleWithPath:path] retain];
}

+(NSString *)get:(NSString *)key alter:(NSString *)alternate 
{
    return [bundle localizedStringForKey:key value:alternate table:nil];
}

@end
Mauro Delrio
la source
1
+1. C'est un truc vraiment sympa que je n'ai vu nulle part ailleurs. En créant un "sous-ensemble" à partir de l'un des dossiers de localisation, vous pouvez faire fonctionner correctement la chaîne tant que vous encapsulez NSLocalizedString avec quelque chose qui détourne ici.
Ben Zotto
4
Excellent Mauro. J'ai remarqué que cela peut également fonctionner pour les fichiers de votre projet. Si pour une raison quelconque (comme dans mon cas), vous devez télécharger des fichiers de chaînes à partir du réseau et les stocker dans votre répertoire 'Documents' (avec la structure de dossiers Documents / en.lproj / Localizable.strings, Documents / fr.lproj / Chaînes localisables, ...). Vous pouvez même créer un NSBundle. Utilisez simplement ce code pour le chemin: NSString * path = [NSHomeDirectory () stringByAppendingPathComponent: [NSString stringWithFormat: @ "/ Documents /% @. Lproj", l, nil]];
arnaud del.
1
Il me semble que c'est la meilleure approche, avec la contribution d'Alessandro ci-dessous. Ce n'est pas si intrusif et ne devrait pas nécessiter de redémarrage.
PKCLsoft
1
Qu'est-ce que je fais mal? J'ai créé une classe Language, héritant de NSObject, je l'ai mis dans l'implémentation, j'ai mis les noms de méthode dans .h, puis j'ai mis [Language setLanguage: @ "es"]; dans mon bouton Changer de langue, le code s'exécute (la méthode du bouton est appelée et passe à la classe Language), mais ne fait rien. J'ai également mis en place mes cordes localisables (en espagnol). Mais cela semble fonctionner pour beaucoup d'autres personnes. Je vous en prie, bête pour moi.
SirRupertIII
1
@KKendall vous l'appelez comme ceci: [Language setLanguage: @ "es"]; NSString * stringToUse = [Language get: @ "Your Text" alter: nil];
Mikrasya
41

Ne l'utilisez pas sur iOS 9. Cela renvoie zéro pour toutes les chaînes qui y sont passées.

J'ai trouvé une autre solution qui vous permet de mettre à jour les chaînes de langue, sans redémarrer l'application et compatible avec genstrings:

Mettez cette macro dans Prefix.pch:

#define currentLanguageBundle [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:[[NSLocale preferredLanguages] objectAtIndex:0] ofType:@"lproj"]]

et partout où vous avez besoin d'une utilisation de chaîne localisée:

NSLocalizedStringFromTableInBundle(@"GalleryTitleKey", nil, currentLanguageBundle, @"")

Pour définir l'utilisation de la langue:

[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObject:@"de"] forKey:@"AppleLanguages"];

Fonctionne même avec un saut de langue consécutif comme:

NSLog(@"test %@", NSLocalizedStringFromTableInBundle(@"NewKey", nil, currentLanguageBundle, @""));
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObject:@"fr"] forKey:@"AppleLanguages"];
NSLog(@"test %@", NSLocalizedStringFromTableInBundle(@"NewKey", nil, currentLanguageBundle, @""));
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObject:@"it"] forKey:@"AppleLanguages"];
NSLog(@"test %@", NSLocalizedStringFromTableInBundle(@"NewKey", nil, currentLanguageBundle, @""));
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObject:@"de"] forKey:@"AppleLanguages"];
NSLog(@"test %@", NSLocalizedStringFromTableInBundle(@"NewKey", nil, currentLanguageBundle, @""));
Tudor
la source
2
@ powerj1984: non, cela ne changera que les chaînes de vos fichiers source. Si vous souhaitez changer les langues xib, vous devez recharger les xibs à la main, à partir du bundle de votre langue sélectionnée.
gklka
@gklka J'ai suivi les étapes données par Tudozier mais mes XIB ne changent pas la langue, comme vous l'avez dit recharger XIB à la main, qu'est-ce que cela signifie réellement de recharger les XIB?
Dk Kumar
@DkKumar: Vous devez faire quelque chose de similaire à ceci: stackoverflow.com/questions/21304988/…
gklka
@gklka Votre réponse semble correcte et j'ai suivi les mêmes étapes que vous avez décrites dans votre réponse, mais le problème est que si nous modifions le chemin du bundle, il trouvera toutes les ressources (c'est-à-dire le fichier .png utilisé dans XIB) dans le même bundle, nous devons donc sur-copier le dossier images dans tous les bundles comme en.lproj, es.lproj et ainsi de suite .. cela aura donc un effet sur l'augmentation de la taille de l'IPA, donc y a-t-il un moyen de résoudre ce problème? comme vous l'avez dit dans votre article, permettez-moi d'essayer et de vous en dire plus. Merci de m'avoir aidé pour la même chose
Dk Kumar
1
Ceci est horriblement cassé dans iOS 9, renvoyant le null pour toutes les chaînes.
Alex Zavatone
32

Comme dit précédemment, faites simplement:

[[NSUserDefaults standardUserDefaults] setObject: [NSArray arrayWithObjects:@"el", nil] forKey:@"AppleLanguages"];

Mais pour éviter d'avoir à redémarrer l'application, mettez la ligne dans la méthode principale de main.m, juste avant UIApplicationMain(...).

Frédéric Feytons
la source
2
Réponse très utile! PS NSAutoreleasePool * pool ..Cela peut sembler évident pour les non-débutants, mais vous devez insérer cette ligne après ou quelques objets libérés automatiquement fuiront.
pt2ph8
1
IMPORTANT: cela ne fonctionnera pas si vous appelez [[NSBundle mainBundle] URLForResource:withExtension:]avant.
Kof
3
Et si vous utilisez Swift, il n'y a pas de main.m?
testée
@turingtested avez-vous essayé sur swift et storyboard, ça marche?
iDevAmit
Salut, Comment localiser le texte dynamique qui vient du serveur, dans mon application chaque texte que je dois afficher en chinois simplifié, Mais comment afficher le texte en chinois qui vient en anglais. Aidez moi.
Mahesh Narla
31

L'astuce pour utiliser une langue spécifique en la sélectionnant dans l'application consiste à forcer l' NSLocalizedStringutilisation d'un ensemble spécifique en fonction de la langue sélectionnée,

voici le post que j'ai écrit pour cette localisation avancée de l'apprentissage dans les applications ios

et voici le code d'un exemple de localisation avancée de l' application dans les applications ios

object2.0
la source
Cela fonctionne également sur iOS7, je dois passer de la solution de Brian à cela car il semble qu'iOS remplace à nouveau l'option de langues, donc je suis resté avec le premier réglage de la langue. Votre solution fonctionne comme un charme!
haxpor
14

Que pensez-vous de cette solution pour Swift 3?

extension String {

    func localized(forLanguage language: String = Locale.preferredLanguages.first!.components(separatedBy: "-").first!) -> String {

        guard let path = Bundle.main.path(forResource: language == "en" ? "Base" : language, ofType: "lproj") else {

            let basePath = Bundle.main.path(forResource: "Base", ofType: "lproj")!

            return Bundle(path: basePath)!.localizedString(forKey: self, value: "", table: nil)
        }

        return Bundle(path: path)!.localizedString(forKey: self, value: "", table: nil)
    }
}

Utilisation simple:

"report".localized(forLanguage: "pl") //forced language
"report".localized() //default language selected by user in settings, in case when your app doesnt support selected lanaguage, the default one is selected, here is an english.
Bartłomiej Semańczyk
la source
Salut, Comment localiser le texte dynamique qui vient du serveur, dans mon application chaque texte que je dois afficher en chinois simplifié, Mais comment afficher le texte en chinois qui vient en anglais. Aidez moi.
Mahesh Narla
C'est une brillante réponse;)
Bartłomiej Semańczyk
grande réponse m'a aidé
Dilip Tiwari
13

Comme le mentionne Brian Webster, la langue doit être définie "quelque temps au début du démarrage de votre application". J'ai pensé que applicationDidFinishLaunching:le AppDelegatedevrait être un endroit approprié pour le faire, car c'est là que je fais toutes les autres initialisations.

Mais comme William Denniss le mentionne, cela ne semble avoir d'effet qu'après le redémarrage de l'application, ce qui est en quelque sorte inutile.

Cela semble bien fonctionner si je mets le code dans la fonction principale, cependant:

int main(int argc, char *argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    // Force language to Swedish.
    [[NSUserDefaults standardUserDefaults]
     setObject:[NSArray arrayWithObject:@"sv"]
     forKey:@"AppleLanguages"];

    int retVal = UIApplicationMain(argc, argv, nil, nil);
    [pool release];
    return retVal;
}

J'apprécierais tout commentaire à ce sujet.

geon
la source
dans mon implémentation, c'est une chose configurable par l'utilisateur - donc je viens de faire apparaître une boîte de dialogue leur disant qu'ils devront redémarrer :)
William Denniss
Cela fonctionne, presque pour tous - sauf pour l'image Default.png localisée.
Lukasz
2
Si vous laissez les utilisateurs définir une langue, puis leur dites de redémarrer l'application, n'oubliez pas que les NSUserDefaults risquent de ne pas être enregistrés s'ils redémarrent trop rapidement. La plupart des utilisateurs ne sont pas aussi rapides, mais lorsque vous testez l'application, vous risquez d'être trop rapide et de voir un comportement incohérent en conséquence. Il m'a fallu quelques heures pour comprendre pourquoi mon changement de langue fonctionnait parfois et parfois non!
arlomedia
3
Ce n'est pas un problème, utilisez-le [[NSUserDefaults standardUserDefaults] synchronize];après avoir appelésetObject:forKey:
Ben Baron
11

J'aime mieux la méthode de Mauro Delrio. J'ai également ajouté ce qui suit dans mon Project_Prefix.pch

#import "Language.h"    
#define MyLocalizedString(key, alt) [Language get:key alter:alt]

Donc, si vous souhaitez utiliser la méthode standard (qui utilise NSLocalizedString), vous pouvez effectuer une substitution de syntaxe rapide dans tous les fichiers.

dnaxxx
la source
1
J'aime aussi sa méthode et j'ajoute une méthode pour charger les images png: + (UIImage ) imageNamed: (NSString *) imageFileName {NSString fullPath = [bundle pathForResource: imageFileName ofType: @ "png"]; UIImage * imageObj = [[UIImage alloc] initWithContentsOfFile: fullPath]; return [imageObj autorelease]; }
Jagie
11

NSLocalizedString()lit la valeur de la clé à AppleLanguagespartir des valeurs par défaut de l'utilisateur standard ( [NSUserDefaults standardUserDefaults]). Il utilise cette valeur pour choisir une localisation appropriée parmi toutes les localisations existantes au moment de l'exécution. Quand Apple crée le dictionnaire des valeurs par défaut de l'utilisateur au lancement de l'application, il recherche la clé de langue préférée dans les préférences système et copie la valeur à partir de là. Cela explique également par exemple pourquoi la modification des paramètres de langue dans OS X n'a ​​aucun effet sur l'exécution des applications, uniquement sur les applications démarrées par la suite. Une fois copiée, la valeur n'est pas mise à jour simplement parce que les paramètres changent. C'est pourquoi iOS redémarre toutes les applications si vous changez de langue.

Cependant, toutes les valeurs du dictionnaire par défaut de l'utilisateur peuvent être remplacées par des arguments de ligne de commande. Voir la NSUserDefaultsdocumentation sur le NSArgumentDomain. Cela inclut même les valeurs chargées à partir du fichier de préférences de l'application (.plist). C'est vraiment bon de savoir si vous voulez changer une valeur une seule fois pour le tester .

Donc, si vous voulez changer la langue juste pour les tests, vous ne voudrez probablement pas modifier votre code (si vous oubliez de supprimer ce code plus tard ...), dites plutôt à Xcode de démarrer votre application avec des paramètres de ligne de commande ( par exemple utiliser la localisation espagnole):

entrez la description de l'image ici

Pas besoin de toucher à votre code. Il suffit de créer différents schémas pour différentes langues et vous pouvez rapidement démarrer l'application une fois dans une langue et une fois dans une autre en changeant simplement le schéma.

Mecki
la source
Il ne fonctionne que pour la 1ère fois après cela, il se charge à nouveau dans l'appareil pref. langue ..
Aks
@Aks fonctionne à chaque fois pour moi. Assurez-vous que vous démarrez le bon schéma, assurez-vous que vous démarrez à partir de Xcode (relancements, fourches, etc., vous n'aurez pas l'argument) et assurez-vous que vous ne remplacez pas la langue Optionscomme dans les versions Xcode plus récentes qu'Apple propose ainsi que.
Mecki
Merci pour votre réponse @Mecki .. Pour moi, cela ne fonctionne pas dans xcode mais quand je le mentionne dans le code, cela fonctionne .. Mais quand même si j'ai configuré mon appareil avec d'autres langues préférées, il doit redémarrer l'application pour se charger dans ma langue préférée. langue que je passe en arguments ....
Aks
@Aks Lorsque vous l'utilisez sur un appareil, cela ne fonctionnera qu'au démarrage directement à partir de Xcode, cela ne fonctionnera pas lorsque vous redémarrerez l'application en appuyant sur son icône sur un appareil et cela ne fonctionnera pas non plus lorsque l'application est tuée (par exemple car il est passé en arrière-plan) et est redémarré par le système (car ce redémarrage n'est pas un redémarrage effectué par Xcode) - donc le passage à une autre application et vice versa peut ne pas fonctionner si iOS doit tuer votre application entre les deux (par exemple parce qu'elle a besoin de la mémoire).
Mecki
10

J'ai trouvé une solution qui vous permet d'utiliser NSLocalizedString. Je crée une catégorie d' NSBundleappel NSBundle+RunTimeLanguage. L'interface est comme ça.

// NSBundle+RunTimeLanguage.h
#import <Foundation/Foundation.h>
@interface NSBundle (RunTimeLanguage)
#define NSLocalizedString(key, comment) [[NSBundle mainBundle] runTimeLocalizedStringForKey:(key) value:@"" table:nil]
- (NSString *)runTimeLocalizedStringForKey:(NSString *)key value:(NSString *)value table:(NSString *)tableName;
@end

L'implémentation est comme ça.

// NSBundle+RunTimeLanguage.m
#import "NSBundle+RunTimeLanguage.h"
#import "AppDelegate.h"

@implementation NSBundle (RunTimeLanguage)

- (NSString *)runTimeLocalizedStringForKey:(NSString *)key value:(NSString *)value table:(NSString *)tableName
{
    AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
    NSString *path= [[NSBundle mainBundle] pathForResource:[appDelegate languageCode] ofType:@"lproj"];
    NSBundle *languageBundle = [NSBundle bundleWithPath:path];
    NSString *localizedString=[languageBundle localizedStringForKey:key value:key table:nil];
    return localizedString;
}
@end

Ensuite, ajoutez simplement l'importation NSBundle+RunTimeLanguage.hdans les fichiers qui utilisent NSLocalizedString.

Comme vous pouvez le voir, je stocke mon languageCode dans une propriété de AppDelegate. Cela pourrait être stocké où vous le souhaitez.

Cette seule chose que je n'aime pas, c'est un avertissement que NSLocalizedStringmarco a redéfini. Peut-être que quelqu'un pourrait m'aider à réparer cette partie.

qman64
la source
1
Ajouter #undef NSLocalizedStringjuste avant #definepour désactiver l'avertissement
béryllium
Est-ce que cela fonctionne sur la localisation du fichier xib? J'ai un fichier .xib et des chaînes de caractères pour traduire le nom des interfaces utilisateur dans une langue particulière. J'ai essayé et cela ne fonctionne pas avec XIB mais je ne sais pas si j'ai fait les choses correctement
Kong Hantrakool
Kong désolé pour la réponse tardive. Cela fonctionne partout où vous utilisez NSLocalizedString. Il ne fonctionnera donc pas directement dans un XIB. Vous auriez besoin d'IBOutlets pour les chaînes dans le XIB, puis vous devriez définir par programme les valeurs de chaîne dans le code.
qman64
Cette approche a fonctionné pour moi et j'ai pu changer de langue à la volée. Notez que les codes de langue comme "es", fr "et" en "ont bien fonctionné. Mais" zh "(pour le chinois simplifié) a échoué et m'a renvoyé des chaînes nulles. J'ai fait une recherche globale sur mon système pour" es.lproj "pour voir où se trouvaient les fichiers auxquels j'accédais et j'ai découvert que "zh.lproj" est en fait "zh-Hans.lproj".
Gallymon
9

Version rapide:

NSUserDefaults.standardUserDefaults().setObject(["fr"], forKey: "AppleLanguages")
NSUserDefaults.standardUserDefaults().synchronize()
daaniaal
la source
8

En un mot :

Localisez votre application

C'est la première chose que vous devez faire est de localiser votre application avec au moins deux langues (anglais et français dans cet exemple).

Remplacer NSLocalizedString

Dans votre code, au lieu d'utiliser NSLocalizedString(key, comment), utilisez une macro MYLocalizedString(key, comment)définie comme ceci: #define MYLocalizedString(key, comment) [[MYLocalizationSystem sharedInstance] localizedStringForKey:(key) value:(comment)];

Ce MYLocalizationSystemsingleton:

  • Définissez le langage en définissant le bon utilisateur NSBundle localisé
  • Renvoie la NSString localisée selon cette langue précédemment définie

Définir la langue de l'utilisateur

Lorsque l'utilisateur a changé la langue de l'application en français, appelez [[MYLocalizationSystem sharedInstance] setLanguage:@"fr"];

- (void)setLanguage:(NSString *)lang
{
    NSString *path = [[NSBundle mainBundle] pathForResource:lang ofType:@"lproj"];
    if (!path)
    {
        _bundle = [NSBundle mainBundle];
        NSLog(@"Warning: No lproj for %@, system default set instead !", lang);
        return;
    }

    _bundle = [NSBundle bundleWithPath:path];
}

Dans cet exemple, cette méthode définit le bundle localisé sur fr.lproj

Retourne une chaîne localisée

Une fois que vous avez défini le bundle localisé, vous pourrez obtenir la bonne chaîne localisée de lui avec cette méthode:

- (NSString *)localizedStringForKey:(NSString *)key value:(NSString *)value
{
    // bundle was initialized with [NSBundle mainBundle] as default and modified in setLanguage method
    return [self.bundle localizedStringForKey:key value:value table:nil];
}

J'espère que ceci vous aidera.

Vous trouverez plus de détails dans cet article de NSWinery.io

user3465357
la source
Nous vous demandons de ne pas simplement lier à une solution dans votre réponse; le lien pourrait cesser de fonctionner un jour. Bien que vous n'ayez pas à supprimer le lien de votre réponse, nous vous demandons de modifier votre réponse pour inclure un résumé de la section pertinente de l'article lié.
Oblivious Sage
7

Extensions Swift 3:

extension Locale {
    static var preferredLanguage: String {
        get {
            return self.preferredLanguages.first ?? "en"
        }
        set {
            UserDefaults.standard.set([newValue], forKey: "AppleLanguages")
            UserDefaults.standard.synchronize()
        }
    }
}

extension String {
    var localized: String {

    var result: String

    let languageCode = Locale.preferredLanguage //en-US

    var path = Bundle.main.path(forResource: languageCode, ofType: "lproj")

    if path == nil, let hyphenRange = languageCode.range(of: "-") {
        let languageCodeShort = languageCode.substring(to: hyphenRange.lowerBound) // en
        path = Bundle.main.path(forResource: languageCodeShort, ofType: "lproj")
    }

    if let path = path, let locBundle = Bundle(path: path) {
        result = locBundle.localizedString(forKey: self, value: nil, table: nil)
    } else {
        result = NSLocalizedString(self, comment: "")
    }
        return result
    }
}

Usage:

Locale.preferredLanguage = "uk"

label.text = "localizedKey".localized
ChikabuZ
la source
Merci. Fonctionne bien.
Krunal Nagvadia
5

Dans le fichier .pch à définir:

#define currentLanguageBundle [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:[[NSLocale preferredLanguages] objectAtIndex:0] ofType:@"lproj"]]


#define NSLocalizedString(str,nil) NSLocalizedStringFromTableInBundle(str, nil, currentLanguageBundle, @"")
MrPiliffo
la source
Comment puis-je me débarrasser de l'avertissement `` passer outre ... ''?
Idan Moshe
1
Une solution simple fait tout ce dont j'ai besoin. A pu utiliser cela comme un guide pour résoudre mon problème. Si pathForResource renvoie nil parce que je n'ai pas de fichier de localisation, j'utilise pathForResource: @ "Base" car rien d'autre que j'ai essayé ne tire des chaînes de la localisation de base. Je vous remercie.
GRW
C'est hacky (ainsi que toutes les autres réponses), et c'était plus facile à implémenter dans le test unitaire que j'écrivais.
Max
4

Vous devriez peut-être compléter avec ceci (sur le fichier .pch après #import):

extern NSBundle* bundle; // Declared on Language.m

#ifdef NSLocalizedString
    #undef NSLocalizedString
    // Delete this line to avoid warning
    #warning "Undefining NSLocalizedString"
#endif

#define NSLocalizedString(key, comment) \
    [bundle localizedStringForKey:(key) value:@"" table:nil]
D33pN16h7
la source
/Users/pwang/Desktop/Myshinesvn/Myshine/viewController/OrientationReadyPagesView.m:193:31: Utilisation d'un identifiant non déclaré 'bundle'
pengwang
4

Solution Swift 3:

let languages = ["bs", "zh-Hant", "en", "fi", "ko", "lv", "ms", "pl", "pt-BR", "ru", "sr-Latn", "sk", "es", "tr"]
UserDefaults.standard.set([languages[0]], forKey: "AppleLanguages")

Donné quelques exemples de codes de langue qui peuvent être utilisés. J'espère que cela t'aides

Jeremy Bader
la source
3

Vous pouvez créer un sous-ensemble avec l'ensemble de chaînes localisées avec lesquelles vous souhaitez effectuer cette opération, puis les utiliser NSLocalizedStringFromTableInBundle()pour les charger. (Je suppose qu'il s'agit d'un contenu distinct de la localisation normale de l'interface utilisateur que vous effectuez sur l'application.)

Sixten Otto
la source
3

pour mon cas j'ai deux fichiers localisés, ja et en

et je voudrais le forcer à en si la langue préférée dans le système n'est ni en ni ja

je vais éditer le fichier main.m

je vais vérifier si la première préférence est en ou ja, sinon je changerai la deuxième langue préférée en en.

int main(int argc, char *argv[])
{

    [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"AppleLanguages"];
    [[NSUserDefaults standardUserDefaults] synchronize];

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

    if (![lang isEqualToString:@"en"]  &&  ![lang isEqualToString:@"ja"]){

        NSMutableArray *array = [[NSMutableArray alloc] initWithArray:[NSLocale preferredLanguages]];
        [array replaceObjectAtIndex:1 withObject:@"en"];

        [[NSUserDefaults standardUserDefaults] setObject:array forKey:@"AppleLanguages"];
        [[NSUserDefaults standardUserDefaults] synchronize];


    }

    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));


    }


}
chings228
la source
Merci @ chings228 c'est utile pour moi, mais cela ne change pas l'image de lancement par défaut (splash) pour l'application J'ai différents splashs pour chaque langue. savez-vous comment en faire la demande ??
JERC
je pense que le splash s'exécute avant que le programme principal ne démarre, donc je ne sais pas comment le remplacer, peut être plist peut aider mais cela peut fonctionner pour la prochaine fois que l'application s'ouvre
chings228
2

Vous pouvez faire quelque chose comme ça:

NSString *bundlePath = [[NSBundle mainBundle] pathForResource:@"Localizable" ofType:@"strings" inDirectory:nil forLocalization:@"es"];


NSBundle *spanishBundle = [[NSBundle alloc] initWithPath:[bundlePath stringByDeletingLastPathComponent]];

NSLocalizedStringFromTableInBundle(@"House", nil, spanishBundle, nil):
JERC
la source
2

Basé sur la réponse de Tudorizer pour changer de langue sans quitter ni redémarrer l'application.

Au lieu d'une macro, utilisez une classe pour accéder à la langue préférée afin de vérifier si un code de langue spécifique est présent.

Vous trouverez ci-dessous une classe utilisée pour obtenir le pack de langue actuel qui fonctionne pour iOS 9:

@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 de chaîne / image / vidéo / 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")

Modifiez la langue en ligne comme ci-dessous ou mettez à jour la méthode "changeCurrentLanguage" dans la classe ci-dessus pour prendre un paramètre de chaîne référençant la nouvelle langue.

[[NSUserDefaults standardUserDefaults] setObject:@[@"es"] forKey:@"AppleLanguages"];
Antonioni
la source
2

Dans swift 4, je l'ai résolu sans avoir besoin de redémarrer ou d'utiliser des bibliothèques.

Après avoir essayé de nombreuses options, j'ai trouvé cette fonction, où vous passez la chaîneToLocalize (de Localizable.String, le fichier de chaînes) que vous souhaitez traduire, et la langue dans laquelle vous souhaitez la traduire, et ce qu'elle renvoie est la valeur de cette chaîne que vous avez dans le fichier Strings:

    func localizeString (stringToLocalize: String, language: String) -> String
    {
        let path = Bundle.main.path (forResource: language, ofType: "lproj")
        let languageBundle = Bundle (path: path!)
        return languageBundle! .localizedString (forKey: stringToLocalize, value: "", table: nil)
    }

Compte tenu de cette fonction, j'ai créé cette fonction dans un fichier Swift:

struct CustomLanguage {

    func createBundlePath () -> Bundle {
        let selectedLanguage = //recover the language chosen by the user (in my case, from UserDefaults)
        let path = Bundle.main.path(forResource: selectedLanguage, ofType: "lproj")
        return Bundle(path: path!)!
    }
}

Pour accéder à partir de l'application entière, et dans chaque chaîne du reste de ViewControllers, au lieu de mettre:

NSLocalizedString ("StringToLocalize", comment: ")

Je l'ai remplacé par

let customLang = CustomLanguage() //declare at top
let bundleLanguage = customLang.createBundle()

NSLocalizedString("StringToLocalize", tableName: nil, bundle: bundleLanguage, value: "", comment: “”) //use in each String

Je ne sais pas si c'est le meilleur moyen, mais je l'ai trouvé très simple, et ça marche pour moi, j'espère que ça vous aide!

ainareta685
la source
1

Cette fonction essaiera d'obtenir une chaîne localisée pour la langue actuelle et si elle n'est pas trouvée, elle l'obtiendra en utilisant la langue anglaise.

- (NSString*)L:(NSString*)key
{
    static NSString* valueNotFound = @"VALUE_NOT_FOUND";
    static NSBundle* enBundle = nil;

    NSString* pl = [NSLocale preferredLanguages][0];
    NSString* bp = [[NSBundle mainBundle] pathForResource:pl ofType:@"lproj"];
    NSBundle* b = [NSBundle bundleWithPath:bp];

    NSString* s = [b localizedStringForKey:key value:valueNotFound table:nil];
    if ( [s isEqualToString:valueNotFound] ) {
        if ( !enBundle ) {
            bp = [[NSBundle mainBundle] pathForResource:@"en" ofType:@"lproj"];
            enBundle = [NSBundle bundleWithPath:bp];
        }
        s = [enBundle localizedStringForKey:key value:key table:nil];
    }

    return s;
}
Cherpak Evgeny
la source
1

Je voulais ajouter la prise en charge d'une langue qui n'est pas officiellement prise en charge par iOS (non répertoriée dans la section Langue sous les paramètres système). En suivant le tutoriel d'internationalisation d'Apple et quelques conseils ici de Brian Webster et geon, j'ai trouvé ce morceau de code (mettez-le dans main.m):

int main(int argc, char * argv[]) {
    @autoreleasepool {
        // Grab regional settings locale, for Slovenian this is either sl_SI or en_SI
        NSLocale *locale = [NSLocale currentLocale];
        NSString *ll = [locale localeIdentifier]; // sl_SI

        // Grab the first part of language identifier
        NSArray *comp = [ll componentsSeparatedByString:@"_"];
        NSString *ll1 = @"en";
        if (comp.count > 0) {
            ll1 = comp[0]; // sl, en, ...
        }
        // Check if we already saved language (user can manually change it inside app for example)
        if (![[NSUserDefaults standardUserDefaults] objectForKey:@"SelectedLanguage"]) {
            //   Slovenian (Slovenia),            Slovenia
            if ([ll isEqualToString:@"sl_SI"] || [ll isEqualToString:@"en_SI"]) {
                ll1 = @"sl-SI"; // This is the part of localized path for Slovenian language that Xcode generates
            }
            // Add more unsupported languages here...

            [[NSUserDefaults standardUserDefaults] setObject:ll1 forKey:@"SelectedLanguage"]; // Save language
        }
        else {
            // Restore language as we have previously saved it
            ll1 = [[NSUserDefaults standardUserDefaults] objectForKey:@"SelectedLanguage"];
        }
        // Overwrite NSLocalizedString and StoryBoard language preference
        [[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:ll1, @"en", @"fr", nil] forKey:@"AppleLanguages"];
        // Make sure settings are stored to disk
        [[NSUserDefaults standardUserDefaults] synchronize];

        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

Cela fonctionne bien pour le code Storyboard et NSLocalizedString. Le code suppose que l'utilisateur aura la possibilité de changer manuellement la langue dans l'application ultérieurement.

Bien sûr, n'oubliez pas d'ajouter les traductions de Storyboard et les traductions de Localizable.strings appropriées (voir le lien vers la page Apple ci-dessus pour savoir comment faire).

frin
la source
Je vois que vous rencontrez également des problèmes avec la langue slovène :) Pour améliorer cette réponse pour les langues non prises en charge par iOS, avez-vous déterminé si nous pouvons définir des chaînes localisables personnalisées qui seront utilisées pour les contrôles système dans l'application (Modifier, Terminé, Plus, ...)?
miham
1

Voici une solution décente à ce problème, et elle ne nécessite pas de redémarrage de l'application.

https://github.com/cmaftuleac/BundleLocalization

Cette implémentation fonctionne en modifiant l'intérieur de NSBundle. L'idée est que vous remplacez la méthode localizedStringForKey sur l'instance de l'objet NSBundle, puis appelez cette méthode sur un bundle différent avec un langage différent. Simple et élégant entièrement compatible avec tous les types de ressources.

Corneliu Maftuleac
la source
Ce code du projet m'a beaucoup aidé. Comment trouver un paquet pour un code de langue spécifique ---NSString *path = [[NSBundle mainBundle] pathForResource:lang ofType:@"lproj" ];
Eonil
Cette classe fonctionne bien, mais après avoir changé de langue en une autre, cela ne fonctionne pas.
Anilkumar iOS ReactNative
0

quoi que vous fassiez tous, la meilleure façon est de prendre le short_name pour la langue spécifiée, c'est-à-dire: fr, en, nl, de, it, etc ... et de l'attribuer à une valeur globale.

faire apparaître une vue de sélecteur comme un menu déroulant (combinaison d'un bouton au clic duquel une vue de sélecteur apparaît ci-dessous avec une liste de langues) et sélectionner la langue souhaitée. laissez le nom court être stocké en interne. créez un fichier .h + .m nommé LocalisedString.

Définissez la valeur globale de short_name pour qu'elle soit égale à la valeur obtenue dans LocalisedString.m Lorsque la langue requise est sélectionnée, affectez NSBundlePath pour créer des sous-répertoires de projet pour la langue requise. par exemple, nl.proj, en.proj.

Lorsque le dossier proj particulier est sélectionné, appelez la chaîne localisée pour la langue respective et changez la langue dynamiquement.

pas de règles enfreintes.

Abhijeet
la source
0

Pour Swift, vous pouvez remplacer le main.swiftfichier et y définir la chaîne UserDefaults avant l'exécution de l'application. De cette façon, vous n'avez pas à redémarrer l'application pour voir l'effet souhaité.

import Foundation
import UIKit

// Your initialisation code here
let langCultureCode: String = "LANGUAGE_CODE"

UserDefaults.standard.set([langCultureCode], forKey: "AppleLanguages")
UserDefaults.standard.synchronize()

UIApplicationMain(CommandLine.argc, CommandLine.unsafeArgv, nil, NSStringFromClass(AppDelegate.self))

jumelé avec la suppression de @UIApplicationMaindans votre AppDelegate.swiftfichier.

Andreas
la source