145 votes

Meilleure pratique pour utiliser NSLocalizedString

J'utilise (comme tous les autres) NSLocalizedString pour localiser mon application.

Malheureusement, il existe plusieurs "inconvénients" (qui ne sont pas nécessairement le fait de NSLocalizedString elle-même), notamment

  • Pas d'autocomplétion pour les chaînes de caractères dans Xcode. Cela rend le travail non seulement source d'erreurs mais aussi fastidieux.
  • Vous pourriez finir par redéfinir une chaîne simplement parce que vous ne saviez pas qu'il existait déjà une chaîne équivalente (par exemple, "Veuillez entrer le mot de passe" ou "Entrez d'abord le mot de passe").
  • Comme pour le problème de l'autocomplétion, vous devez vous "souvenir"/copier les chaînes de commentaires, sinon genstring se retrouvera avec plusieurs commentaires pour une seule chaîne
  • Si vous voulez utiliser genstring après avoir déjà localisé certaines chaînes, vous devez faire attention à ne pas perdre vos anciennes localisations.
  • Les mêmes ficelles sont dispersées dans tout votre projet. Par exemple, vous avez utilisé NSLocalizedString(@"Abort", @"Cancel action") partout, et ensuite Code Review vous demande de renommer la chaîne en NSLocalizedString(@"Cancel", @"Cancel action") pour rendre le code plus cohérent.

Ce que je fais (et après quelques recherches sur SO, je me suis rendu compte que beaucoup de personnes font cela), c'est d'avoir un fichier séparé strings.h où je #define tout le code de localisation. Par exemple

// In strings.h
#define NSLS_COMMON_CANCEL NSLocalizedString(@"Cancel", nil)
// Somewhere else
NSLog(@"%@", NSLS_COMMON_CANCEL);

Cela permet essentiellement la complétion du code, un endroit unique pour changer les noms de variables (donc plus besoin de genstring), et un mot-clé unique pour l'auto-réfactorisation. Cependant, cela a pour conséquence de se retrouver avec tout un tas de fichiers #define qui ne sont pas intrinsèquement structurées (par exemple, LocString.Common.Cancel ou quelque chose comme ça).

Bien que cela fonctionne assez bien, je me demandais comment vous procédiez dans vos projets. Existe-t-il d'autres approches pour simplifier l'utilisation de NSLocalizedString ? Existe-t-il même un cadre qui l'encapsule ?

0 votes

Je fais presque la même chose que toi. Mais j'utilise la makro NSLocalizedStringWithDefaultValue pour créer différents fichiers de chaînes de caractères pour différents problèmes de localisation (comme les contrôleurs, les modèles, etc.) et pour créer une valeur par défaut initiale.

0 votes

Il semble que l'exportation vers la localisation de xcode6 ne prenne pas en compte les chaînes de caractères qui sont définies comme des macros dans un fichier d'en-tête. Quelqu'un peut-il confirmer ou me dire ce que je pourrais manquer ? Merci... !

0 votes

@Juddster, je peux confirmer, même avec le nouveau fonds Éditeur->Exportation pour la localisation, il n'est pas repris dans le fichier d'en-tête.

101voto

ndfred Points 1866

NSLocalizedString a quelques limitations, mais il est si central à Cocoa qu'il est déraisonnable d'écrire du code personnalisé pour gérer la localisation, ce qui signifie que vous devrez l'utiliser. Cela dit, un peu d'outillage peut aider, voici comment je procède :

Mise à jour du fichier des chaînes de caractères

genstrings écrase vos fichiers de chaînes de caractères, abandonnant toutes vos traductions précédentes. J'ai écrit update_strings.py pour analyser l'ancien fichier de chaînes de caractères, exécutez genstrings et de remplir les blancs afin que vous ne deviez pas restaurer manuellement vos traductions existantes. Le script essaie de faire correspondre les fichiers de chaînes existants aussi étroitement que possible pour éviter d'avoir une trop grande différence lors de leur mise à jour.

Nommer vos chaînes de caractères

Si vous utilisez NSLocalizedString comme annoncé :

NSLocalizedString(@"Cancel or continue?", @"Cancel notice message when a download takes too long to proceed");

Vous pouvez finir par définir la même chaîne de caractères dans une autre partie de votre code, ce qui peut entraîner des conflits car le même terme anglais peut avoir une signification différente dans des contextes différents ( OK y Cancel me viennent à l'esprit). C'est pourquoi j'utilise toujours une chaîne de caractères en majuscules sans signification, avec un préfixe spécifique au module, et une description très précise :

NSLocalizedString(@"DOWNLOAD_CANCEL_OR_CONTINUE", @"Cancel notice window title when a download takes too long to proceed");

Utiliser la même chaîne à différents endroits

Si vous utilisez la même chaîne plusieurs fois, vous pouvez soit utiliser une macro comme vous l'avez fait, soit la mettre en cache en tant que variable d'instance dans votre contrôleur de vue ou votre source de données. De cette façon, vous n'aurez pas à répéter la description, qui peut devenir obsolète et incohérente entre les instances de la même localisation, ce qui est toujours déroutant. Comme les variables d'instance sont des symboles, vous pourrez utiliser l'autocomplétion pour les traductions les plus courantes et utiliser des chaînes "manuelles" pour les traductions spécifiques, qui n'apparaîtront de toute façon qu'une seule fois.

J'espère que vous serez plus productif avec la localisation de Cocoa grâce à ces conseils !

0 votes

Merci pour votre réponse, je vais certainement jeter un coup d'œil à votre fichier python. Je suis d'accord avec vos conventions de nommage. J'ai discuté avec d'autres développeurs iOS récemment, et ils ont recommandé l'utilisation de chaînes statiques au lieu de macros, ce qui est logique. J'ai upvoted votre réponse, mais je vais attendre un peu avant de l'accepter, parce que la solution est encore un peu maladroite. Peut-être que quelque chose de mieux arrivera. Merci encore !

0 votes

Vous êtes les bienvenus. La localisation est un processus fastidieux. Disposer des bons outils et du bon flux de travail fait toute la différence.

19 votes

Je n'ai jamais compris pourquoi les fonctions de localisation de type gettext utilisent l'une des traductions comme clé. Que se passe-t-il si votre texte original change ? Votre clé change et tous vos fichiers localisés utilisent l'ancien texte pour leur clé. Cela n'a jamais eu de sens pour moi. J'ai toujours utilisé des clés comme "home_button_text" pour qu'elles soient uniques et ne changent jamais. J'ai également écrit un bash script pour analyser tous mes fichiers Localizable.strings et générer un fichier de classe avec des méthodes statiques qui chargeront la chaîne appropriée. Cela me permet de compléter le code. Un jour, il se peut que je mette ce programme en open source.

31voto

hiroshi Points 1796

En ce qui concerne l'autocomplétion pour les chaînes de caractères dans Xcode, vous pouvez essayer https://github.com/questbeat/Lin .

3 votes

C'est en fait assez étonnant. Pas besoin de créer des macros.

1 votes

Page non trouvée_

1 votes

@Juanmi Merci d'avoir mentionné le lien mort. J'ai remplacé le lien par l'url de github.

25voto

Pascal Points 2023

Je suis d'accord avec ndfred, mais je voudrais ajouter ceci :

Le deuxième paramètre peut être utilisé comme valeur par défaut !

(NSLocalizedStringWithDefaultValue ne fonctionne pas correctement avec genstring, c'est pourquoi j'ai proposé cette solution)

Voici mon implémentation personnalisée qui utilise NSLocalizedString qui utilise le commentaire comme valeur par défaut :

1 . Dans votre en-tête pré-compilé (fichier .pch), redéfinissez la macro 'NSLocalizedString' :

// cutom NSLocalizedString that use macro comment as default value
#import "LocalizationHandlerUtil.h"

#undef NSLocalizedString
#define NSLocalizedString(key,_comment) [[LocalizationHandlerUtil singleton] localizedString:key  comment:_comment]

2. créer une classe pour implémenter le gestionnaire de localisation

#import "LocalizationHandlerUtil.h"

@implementation LocalizationHandlerUtil

static LocalizationHandlerUtil * singleton = nil;

+ (LocalizationHandlerUtil *)singleton
{
    return singleton;
}

__attribute__((constructor))
static void staticInit_singleton()
{
    singleton = [[LocalizationHandlerUtil alloc] init];
}

- (NSString *)localizedString:(NSString *)key comment:(NSString *)comment
{
    // default localized string loading
    NSString * localizedString = [[NSBundle mainBundle] localizedStringForKey:key value:key table:nil];

    // if (value == key) and comment is not nil -> returns comment
    if([localizedString isEqualToString:key] && comment !=nil)
        return comment;

    return localizedString;
}

@end

3. Utilisez-le !

Assurez-vous d'ajouter un Run script dans vos App Build Phases afin que votre fichier Localizable.strings soit mis à jour à chaque build, c'est-à-dire que les nouvelles chaînes localisées seront ajoutées dans votre fichier Localized.strings :

Ma phase de construction script est un shell script :

Shell: /bin/sh
Shell script content: find . -name \*.m | xargs genstrings -o MyClassesFolder

Donc quand vous ajoutez cette nouvelle ligne dans votre code :

self.title = NSLocalizedString(@"view_settings_title", @"Settings");

Puis effectuez un build, votre fichier ./Localizable.scripts contiendra cette nouvelle ligne :

/* Settings */
"view_settings_title" = "view_settings_title";

Et puisque key == value pour 'view_settings_title', le LocalizedStringHandler personnalisé retournera le commentaire, c'est-à-dire 'Settings'.

Voilà :-)

0 votes

Obtention d'erreurs ARC, No known instance method for selector 'localizedString:comment: :(

0 votes

Je suppose que c'est parce que LocalizationHandlerUtil.h est manquant. Je n'arrive pas à retrouver le code... Essayez juste de créer le fichier d'en-tête LocalizationHandlerUtil.h et ça devrait être bon.

0 votes

J'ai créé les fichiers. Je pense que c'est dû à un problème de chemin de dossier.

2voto

hiroshi Points 1796

J'ai écrit un script pour aider à maintenir Localizable.strings dans plusieurs langues. Bien qu'il n'aide pas à l'autocomplétion, il aide à fusionner les fichiers .strings en utilisant la commande :

merge_strings.rb ja.lproj/Localizable.strings en.lproj/Localizable.strings

Pour plus d'informations, voir : https://github.com/hiroshi/merge_strings

J'espère que certains d'entre vous le trouveront utile.

0voto

baozhifei Points 1
#define PBLocalizedString(key, val) \

[[NSBundle mainBundle] localizedStringForKey:(key) value:(val) table:nil]

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X