Bonnes pratiques de traduction

muqaddarmuqaddar Administrateur
janvier 2012 modifié dans Objective-C, Swift, C, C++ #1
Hello,

Je venais voir si il y avait des bonnes habitudes à  prendre sur les chaà®nes côté traductions.

Vous êtes plutôt:

NSLocalizedString(@&quot;NewProduct&quot;, @&quot;&quot;);<br />NSLocalizedString(@&quot;New Product&quot;, @&quot;&quot;);<br />NSLocalizedString(@&quot;kNewProduct&quot;, @&quot;&quot;);<br />NSLocalizedString(@&quot;New_Product&quot;, @&quot;&quot;);


J'avais pris l'habitude de mettre un "k", comme NSLocalizedString(@kNewProduct, @";");
mais ça peut être un problème sur les chaà®nes sans majuscule NSLocalizedString(@kproduct, @";"); qui sont peu lisibles.

Par contre j'aimais bien l'idée de voir ce "k" tout de suite dans l'app, afin que la chaà®ne non traduite nous saute aux yeux.

Réponses

  • 03:31 modifié #2

    /** Titles */
    "ARTISTS_TITLE" = "Artists";
    "ALBUMS_TITLE" = "Albums";
    "AUDIOBOOKS_TITLE" = "Audiobooks";
    "COMPILATIONS_TITLE" = "Compilations";
    "GENRES_TITLE" = "Genres";
    "PLAYLISTS_TITLE" = "Playlists";
    "PODCASTS_TITLE" = "Podcasts";
    "COMPOSERS_TITLE" = "Composers";
    "SONGS_TITLE"          = "Songs";



    C'est une partie d'un de mes localizable.strings.

    Enfin c'est assez vieux, maintenant je met plutôt "TITLE_" en préfixe. (OK, c'est un léger changement :p)
    En fait l'intérêt c'est surtout de préciser au futur traducteur le genre de chaque morceau à  traduire.
    Par exemple, pour des actions je fais:

    /** Actions */
    "PLAY_ACTION"          = "Play";
    "SHUFFLE_ACTION"        = "Shuffle";
    "PLAYNEXT_ACTION"      = "Play next";
    "VIEWALL_ACTION" = "View all";
    "BACK_ACTION"          = "Back";


    Comme ça il sait que ça fait partie des actions.. histoire qu'il n'y ait pas de quiproquo dans certaines langues..


    Surtout pas de constantes, ça ferait bien trop lourd à  gérer je trouve.. D'ailleurs tu t'es planté je pense dans tes exemples.. le "kMachinChose" ne devrait pas avoir les double-quote vu que c'est une constante.. enfin si tu as voulu le faire passer pour une constante.. sinon je trouve ça bizarre de mettre @kMachinChose... C'est plutôt NSString *const kMachinChose = @MachinChose; (enfin c'est ce que je fais)
    <br />NSString *const kECSwitchBarArtistsItemIdenfier&nbsp; &nbsp; &nbsp; = @&quot;ECSwitchBarArtistsItemIdenfier&quot;;<br />NSString *const kECSwitchBarAlbumsItemIdenfier&nbsp; &nbsp; &nbsp;  = @&quot;ECSwitchBarAlbumsItemIdenfier&quot;;<br />NSString *const kECSwitchBarSongsItemIdenfier&nbsp; &nbsp; &nbsp; &nbsp; = @&quot;ECSwitchBarSongsItemIdenfier&quot;;<br />NSString *const kECSwitchBarPlaylistsItemIdenfier&nbsp; &nbsp; = @&quot;ECSwitchBarPlaylistsItemIdenfier&quot;;<br />NSString *const kECSwitchBarCompilationsItemIdenfier = @&quot;ECSwitchBarCompilationsItemIdenfier&quot;;<br />NSString *const kECSwitchBarComposersItemIdenfier&nbsp; &nbsp; = @&quot;ECSwitchBarComposersItemIdenfier&quot;;<br />NSString *const kECSwitchBarGenresItemIdenfier&nbsp; &nbsp; &nbsp;  = @&quot;ECSwitchBarGenresItemIdenfier&quot;;<br />
    
  • muqaddarmuqaddar Administrateur
    janvier 2012 modifié #3
    Oui, je voulais plutôt parler de clés que de constante
    k étant le k de Key.

    L'inconvénient avec ta méthode c'est que tu n'indiques pas au traducteur les minuscules et majuscules dans la clé. Tu me diras si tu files le fichier en anglais il verra ce qu'on a fait dans la valeur.
  • AliGatorAliGator Membre, Modérateur
    janvier 2012 modifié #4
    Moi j'utilise pas NSLocalizedString mais une macro maison _T() pour les chaà®nes dans le code, et sinon mon "AutoNibL10n" qui traduit automatiquement à  la volée mes XIBs sans avoir à  écrire une ligne de code. Le tout faisant plein de trucs sympas :
    • Si la chaà®ne n'est pas traduite, il m'affiche un "$" devant le nom de la chaà®ne, pour que ça me saute aux yeux. De plus, avec mon fichier magique AutoNibL10n (qui s'autoload), il me log les chaà®nes qui ne sont pas traduites pour que je les vois dans mes logs. Du coup pas besoin de "k"
    • Avec ma macro [tt]_Tfn(@dish.count,n,...)[/tt] il "pluralise" automatiquement les chaà®nes en utilisant la clé "dish.count" si n≤1 et "dish.count+" si n>1 et si cette clé existe, me permettant d'avoir automatiquement "1 plat" et "3 plats" au singulier et au pluriel sans avoir à  écrire des lignes de code en plus pour gérer ces cas. Le "+" à  la fin est évidemment de ma convention personnelle.
    • Pour mieux m'y retrouver dans mes constantes, j'utilise la notation URL-inversée, un peu comme des packages : "mydish.comment.alert.title", "mydish.comment.alert.text", "mydish.comment.notif.text", ... ce qui permet à  la fois de mieux savoir de quoi parle la constante je trouve, de mieux les catégoriser/grouper
    • En plus j'ai un script qui me sort toutes les clés déclarées dans mes divers fichiers .strings (un par langue) et compare ces listes de clés, me permettant ainsi tout de suite de savoir si je n'ai pas oublié une clé dans une des langues que j'aurais ajouté dans les autres (courant quand vite fait on rajoute une clé en français et qu'on oublie en anglais et espagnol et allemand, etc). Du coup en plus ma notation URL-inversée me permet bcp plus facilement de savoir de quelle clé il s'agit et à  peu près où elle se trouve dans mon fichier .strings qui est organisé proprement par "groupes" selon ces notations.
    • En plus pour aider encore à  avoir une organisation propre, je n'hésite pas à  utiliser des commentaires "// MARK:" dans mes fichiers .strings (équivalent / synonyme des "[tt]#pragma mark[/tt]" pour ceux qui ne sauraient pas), ce qui me permet d'avoir des marqueurs dans le menu déroulant adéquat et ainsi sauter facilement à  ma section contenant toutes les traductions relatives aux chaà®nes de type "dish.comment" (commentaires sur un plat), ou à  la section relative à  "friends.find" pour la recherche.invitation d'amis (friends.find.facebook, friends.find.twitter, friends.find.invite, friends.find.follow, ...)


    Bref tout ceci a beaucoup d'avantages :
    - des fichiers .strings proprement organisés, aérés, et facilement navigables depuis Xcode avec le menu
    - des constantes organisées par groupes
    - une facilité pour détecter les clés manquantes dans certaines langues, une détection aisiée des constantes qui n'ont pas de traduction
    - une meilleure compréhension et lisibilité


    Par contre en dehors des chaà®nes localisables, par exemple pour mon code quand j'ai une constante à  définir pour indiquer par exemple je sais pas, la durée d'une animation, là  je la déclare (directement dans le code donc) souvent avec un k au début de son nom genre [tt]const float kAnimationDuration = 2.0f;[/tt].
  • AliGatorAliGator Membre, Modérateur
    janvier 2012 modifié #5
    Extrait de mon Localizable.strings :
    /*********************************<br />&nbsp;  MARK: Nearby Feed<br />&nbsp;  (Feed of Nearby Restaurants)<br /> *********************************/<br /><br />&quot;feed.nearby.cell.nbrates&quot; = &quot;%d note&quot;; /* number of notes/reviews on a given restaurant (if ≤1) */<br />&quot;feed.nearby.cell.nbrates+&quot; = &quot;%d notes&quot;; /* number of notes/reviews on a given restaurant (if &gt;1) */<br />&quot;feed.nearby.cell.nbdishes&quot; = &quot;%d plat&quot;; /* number of dishes on a given restaurant (if ≤1) */<br />&quot;feed.nearby.cell.nbdishes+&quot; = &quot;%d plats&quot;; /* number of dishes on a given restaurant (if &gt;1) */<br />&quot;feed.nearby.cell.nbfriends&quot; = &quot;%d ami a mangé là &quot;; /* number of friends that have eaten in this restaurant (if ≤1) */<br />&quot;feed.nearby.cell.nbfriends+&quot; = &quot;%d amis ont mangé là &quot;; /* number of friends that have eaten in this restaurant (if &gt;1) */<br />&quot;feed.nearby.cell.nbnofriends&quot; = &quot;%d personne a mangé là &quot;; /* number of non-friends that have eaten in this restaurant (if ≤1) */<br />&quot;feed.nearby.cell.nbnofriends+&quot; = &quot;%d personnes ont mangé là &quot;; /* number of non-friends that have eaten in this restaurant (if &gt;1) */<br /><br /><br />/**************************<br />&nbsp;  MARK: LocationChooser<br />&nbsp;  (Choose Location around which to search)<br /> **************************/<br /><br />&quot;locationChooser.edit.button&quot; = &quot;Modifier&quot;; /* Title of the button to change the location */<br />&quot;locationChooser.title&quot; = &quot;Choix du lieu&quot;; /* Title of the screen to change the location */<br />&quot;locationChooser.placeholder&quot; = &quot;Tapez une adresse, une ville, un code postal&quot;; /* Placeholder for the field in which to enter the place name */<br />&quot;locationChooser.location.format&quot; = &quot;Près de : %@&quot;; /* Format used to display the currently used location in the header of the feed */<br />&quot;locationChooser.currentlocation&quot; = &quot;Ma position actuelle&quot;; /* Name of the special location indicating the &quot;current position&quot; */<br />&quot;locationChooser.notfound&quot; = &quot;Cette adresse n&#039;a pu être trouvée.&quot;; /* Error message if the requested place name was not found */
    
  • muqaddarmuqaddar Administrateur
    03:31 modifié #6
    J'aime bien quelques une de tes idées, comme la notation inversée.
    J'utilise aussi la macro _T() qui est pratique.
    Je vais utiliser MARK également pour classer.

    Merci pour tous ces détails.


  • AliGatorAliGator Membre, Modérateur
    janvier 2012 modifié #7
    Note que je m'étais à  une époque lancé dans une autre idée, qui était de mettre des "%d" ou "%@" à  la fin de mes chaà®nes traduites qui contenaient des formatteurs, pour que quand je les utilise dans le code je sache à  quoi m'attendre en les utilisant dans le code.

    Ainsi si j'avais suivi cette idée j'aurais nommé mes clés "[tt]feed.nearby.cell.nbrates.%d[/tt]" plutôt que juste "[tt]feed.nearby.cell.nbrates[/tt]" ou "[tt]locationChooser.location.%@[/tt]"; plutôt que "[tt]locationChooser.location.format[/tt]".

    Et comme ça en les utilisant avec
    _Tf(@&quot;locationChooser.location.%@&quot; , self.currentLocation.address);
    
    on a une cohérence dans la chaà®ne de format, retrouvant que je dois m'attendre à  un "[tt]%@[/tt]"; dans la chaà®ne traduite quelle que soit la langue, et donc ce qui attend un argument de type [tt]NSString[/tt] à  suivre dans ma macro.

    Du coup je risque pas d'oublier que j'ai un argument à  mettre quand j'utilise cette chaà®ne (au risque d'avoir un crash au runtime si l'argument en question n'est pas mis dans mon code) et je ne risque pas de mettre un nombre au lieu d'une chaà®ne ou vice-versa.

    Si je prévoyais un affichage en coordonnées GPS à  la place d'afficher l'adresse, j'aurais pu alors avoir une autre clé
    &quot;locationChooser.location.%f.%f&quot; = &quot;Position GPS: Lat %.3f, Lng %.3f&quot;;
    
    par exemple et en l'utilisant dans mon code, le nom de la clé m'aurait rappelé que je devais passer 2 float en paramètre après cette clé pour que ça marche (et pas une NSString ou quoi que ce soit d'autre)
    _Tf(@&quot;locationChooser.location.%f.%f&quot;, self.position.latitude, self.position.longitude);
    




    Bon il se trouve que j'ai pas gardé l'astuce car j'ai pas pris l'habitude de l'utiliser et qu'il n'est plus trop temps de remplacer toutes mes clés dans mon fichier pour coller à  cette idée.
    Mais je la trouvais sympa pour rendre le code à  la fois plus "sûr" via ce "rappel" et plus lisible car le code continuait à  ressembler à  un "format à  la printf" et on comprenais en lisant le code pourquoi après le nom de la clé on passait des entiers ou des float ou des NSString... faudrait que je la remette au goût du jour tiens.
  • 03:31 modifié #8
    C'est sympa comme système, mais un peu zarb au premier abord :p
  • muqaddarmuqaddar Administrateur
    03:31 modifié #9
    C'est sûr que Ali va très loin.
    Par exemple, je n'ai pas besoin de traduire les pluriels à  la volée.
  • 03:31 modifié #10
    ça peut être chiant des fois aussi, par exemple quand Ali utilise "...cell...", si demain tu ajoutes le même texte ailleurs, tu ne vas pas aller créer une autre chaà®ne (ou alors faut être un peu concon pour se retrouver avec deux trucs identiques à  traduire :p)
  • AliGatorAliGator Membre, Modérateur
    03:31 modifié #11
    Heu c'est toi qui fais le concon là  louka o_O

    Bien sûr je nomme mes clés en conséquence, faut pas être concon comme tu dis. Si c'est un texte générique genre "OK" ou "Annuler" ou un texte qui sera forcément le même à  différents endroits dans l'application bah je vais pas nommer ma clé "feed.nearby.cell.nbnotes" mais "restaurant.nbnotes" plutôt, si c'est générique à  tous les restaurants.

    Mais dans certains cas (pas mal en fait dans mon contexte) je l'ai séparé/dupliqué et j'ai bien fait, parce que en français j'affiche le même texte à  3 endroits dans l'appli... mais en anglais les textes sont différents, parce que le texte en anglais est plus long et que dans le 2e contexte où je l'utilise il ne rentre pas car n'a pas la place donc je vais mettre sa version abrégée. Et ce n'est qu'un exemple.

    Bref faut être cohérent.
    - Bien sûr si tu utilises exactement le même texte à  plusieurs endroits dans l'appli, tu ne vas pas répéter la clé. Mais évidemment faut pas être concon, pour reprendre ton expression : dans ce cas il est évident que tu ne vas pas nommer ta clé un truc comme "feed.nearby.cell.notes" mais "restaurant.notes", bref toujours avec le même principe de notation URL-inversée mais en adaptant le contexte de nommage, ça va de soit.
    - D'ailleurs, dans mes nommages, j'ai un "package" de clés qui commence par "generic." aussi pour tous les textes génériques (OK, Annuler, ...)
    - Mais si tu utilises un texte spécifique dans plusieurs endroits, même si c'est le même texte en français dans les 2 endroits, ce n'est pas toujours judicieux d'utiliser la même clé. D'une part parce que dans une autre langue selon le contexte la formulation ne sera pas pareil (déjà  eu le cas plusieurs fois), d'autre part parce que parfois dans certains contextes un texte en espagnol ou allemand rentre et dans un autre il faut l'abréger, et enfin parce qu'ainsi si tu veux modifier le texte utilisé pour à  cet endroit ça risque pas d'avoir un effet de bord et modifier le texte à  l'autre endroit (ça m'est déjà  arrivé aussi, j'ai voulu changer une traduction de "%d notes" en "%d notes ont été postées", ce qui remplissait mieux l'écran et était plus joli... sauf que j'avais oublié que j'utilisais la même clé ailleurs et que dans cet autre endroit le texte ne rentrais pas...

    Si tu utilises une clé que tu utiliseras aussi ailleurs car elle est générique, tu la nommes en conséquence.
    Si tu utilises une clé spécifique et que tu sais que le texte sera utilisé explicitement à  un endroit précis dans l'app, tu la nommes en conséquence.
    Faut pas être concon
  • muqaddarmuqaddar Administrateur
    03:31 modifié #12
    Et les clés génériques sont en haut dans un MARK Shared ou MARK Generic par exemple.

    // MARK: SHARED<br /><br />&quot;CLOSE&quot; = &quot;Close&quot;;<br />&quot;SAVE&quot; = &quot;Save&quot;;<br />&quot;CANCEL&quot; = &quot;Cancel&quot;;<br />&quot;EDIT&quot; = &quot;Edit&quot;;<br />&quot;DELETE&quot; = &quot;Delete&quot;;
    
  • AliGatorAliGator Membre, Modérateur
    03:31 modifié #13
    Exactement. Bon sauf que moi je les appelle "generic.close", "generic.save", "generic.cancel", etc mais bon ;)
  • 03:31 modifié #14
    T'as pas du comprendre... C'est juste que si feed.nearby.cell.nbrates tu finis par l'utiliser ailleurs plus tard, tu dois donc changer sa "clé", donc faire la modification dans le code... vu que tu vas le passer en générique pour éviter de répéter 2 fois la même valeur pour 2 clés différentes.
    Quand je dis plus tard, c'est dans une version ultérieur, donc tu le sais pas au moment où tu écriras ton feed.nearby.cell.nbrates.
  • AliGatorAliGator Membre, Modérateur
    03:31 modifié #15
    Dans ce cas oui soit je modifie ma clé (vive le refactor ça prend 2s surtout avec ma notation qui m'assure que je risque pas de trouver des doublons ou de foirer mon rechercher/remplacer) soit je crée une autre clé, si le contexte est vraiment différent.

    Ca ne m'est de toute façon jamais arrivé il me semble, malgré toutes les versions et évolutions qu'on a faites depuis le début de FR. Et si ça m'arrivait je préfère avoir tous les avantages de ma méthode et un truc proprement organisé qui ne risque pas de faire des effets de bord (en modifiant le texte d'une clé qui se trouve être utilisée en fait à  plein d'endroits différents*) et occasionnellement avoir à  faire un rechercher/remplacer (oh mon Dieu ça prend 5s) qu'à  l'inverse avoir des clés génériques qui n'ont pas trop de cohérence dans leur nommage et qu'on risque d'utiliser à  mauvais escient dans des contextes séparés là  où ça risque de poser pb* et d'avoir des traductions décontextualisées.


    *Car j'espère que tu es un minimum consciencieux et que si tu modifies la valeur d'une clé tu penses à  vérifier tous les effets de bord, en particulier que dans toutes les langues le nouveau texte ne dépasse pas et tienne toujours dans ta fenêtre... quoique sur Mac tu as peut-être moins le cas avec les fenêtres redimentionnables mais sur iOS surtout sur iPhone ça fait partie des choses qu'il faut vérifier avec la taille d'écran fixe et limitée)
    Or si cette clé est utilisée dans les 150 écrans de ton appli faut penser à  passer partout...! Et vérifier que partout où elle est utilisée elle ne dépasse pas.
    Alors qu'avec des clés spécifiques tu sais tout de suite où tu dois repasser dans tes écrans pour vérifier, et tu n'as pas de risque de dépendances
  • 03:31 modifié #16
    C'est vrai que je me sers très peu du refactoring, j'ai peur que ça foute le dawa.. faudra que je tente quand même un jour..
    En tout cas je tenterai ta méthode dans mon prochain projet, parce que ça me plaà®t beaucoup, même si c'est moins "évident" pour le traducteur.. mais normalement la phrase lui suffit, et si y'a un contexte particulier les commentaires sont là  pour ça :)
  • muqaddarmuqaddar Administrateur
    03:31 modifié #17
    Oui, tout le problème est de faciliter la vie au traducteur également.
  • AliGatorAliGator Membre, Modérateur
    03:31 modifié #18
    Oui et justement cette catégorisation en "namespace" l'aide aussi, le traducteur, puisque c'est organisé en écrans. Tant avec les "MARK:" qui groupent les sections qu'avec le nommage des clés qui permet de voir toutes les clés qui vont ensemble et sont associés à  un écran donné.

    Alors qu'avec "nbnotes" il sait pas trop s'il faut écrire "%d notes" ou "%d notes dans ce restaurant" ou "%d notes sur ce plat" ou quoi, avec "restaurant.nbnotes" il sait que c'est le nombre de notes d'un resto, et si c'est pas une clé générique mais spécifique comme "feed.nearby.restaurant.nbnotes" il sait que c'est un nombre de notes d'un des restaurants affichés dans le feed "Nearby" ("A proximité"), d'autant que ça l'est rappellé autant dans le commentaire "MARK: " que j'ai au dessus que dans le commentaire à  côté de ma clé en fin de la ligne.

    C'est aussi pour ça que j'aime cette notation, elle aide aussi le traducteur à  re-situer le contexte dans lequel est utilisée la chaà®ne (parce que sinon une clé "done" il faut traduire ça par "Terminé", "Valider", "OK" ou "Fini" ?) ce qui donne de meilleures traductions.

    Quand je vois des traductions foireuses dans certains jeux ou applis où tu vois que le mot a été traduit hors contexte et que du coup ç'en est risible (et tu finis par comprendre ce que ça veux dire seulement parce que tu vois à  quel mot en anglais peut correspondre ce mot que tu vois en français, et comprend alors les traductions alternatives et ce qu'ils voulaient vraiment dire), raison de plus pour aider le traducteur en lui donnant encore plus d'indices sur le contexte dans lequel est utilisé ton texte !
  • muqaddarmuqaddar Administrateur
    03:31 modifié #19
    A noter cette évolution de genstrings assez récente:
    https://github.com/Cocoanetics/DTLocalizableStringScanner

    Non testé.
  • AliGatorAliGator Membre, Modérateur
    janvier 2012 modifié #20
    Je ne me sers pas de genstrings mais bon.

    J'aime pas genstrings car d'une part je n'aime pas trop le format de fichier .strings qu'il génère (mais bon encore ça va on peut toujours ajuster après) mais surtout parce que c'est une plaie de faire des mises à  jour de fichiers par ce biais...
    • Autant c'est pratique lors de la première génération du fichier .strings où il scanne le code source pour sortir toutes les clés dans un fichier .strings
    • Autant quand on rajoute plusieurs clés pour la version suivante, c'est 1000x plus rapide de rajouter la nouvelle clé à  la main dans les fichiers .strings au fur et à  mesure qu'on rajoute des textes traduisible dans l'appli plutôt que d'essayer de se dépatouiller avec genstrings pour qu'il fasse un "append" au risque de nous foutre le boxon dans le fichier .strings, ou générer le fichier à  part et tenter une sorte de merge entre les clés existantes qui avaient déjà  été traduit par vos traducteurs et les nouvelles clés à  traduire...


    Et puis bon de toute façon il n'est pas trop compatible avec des usages plus poussés de la traduction comme les miens :
    • ma macro [tt]_T[/tt] (enfin y'aurait que cette macro pas de souci dans les flags de genstrings on peu donner le nom d'une macro à  détecter au lieu de [tt]NSLocalizedString[/tt] mais moi j'utilise plusieurs macros en vrai, [tt]_T[/tt], [tt]_Tf[/tt] qui est une macro de commodité pour [tt][NSString stringWithFormat:_T(x),...][/tt], et [tt]_Tfn[/tt] pour ma gestion des pluriels, etc)
    • Je ne mets jamais les commentaires relatifs à  ma clé de traduction dans le code (comme l'attend [tt]NSLocalizedString[/tt] en 2e paramètre de sa macro), car ça pollue le code et est problématique quand on utilise une même clé à  plusieurs endroits dans le code
    • Ce n'est pas compatible non plus avec mon AutoNibL10n qui traduit à  la volée les chaà®nes de mes fichiers XIB, car genstring ne scrute pas les fichiers XIB à  la recherche de clés et textes à  traduire mais ne sait que fouiller les .m à  la recherche d'une macro.


    Bref, genstring pourrait être une bonne idée à  la base, bien pour débuter au tout début avec un projet simpliste et quand on ne génère les fichiers .string qu'à  la toute fin du projet, mais autrement il n'est vraiment pas adapté. Je connais d'ailleurs personne qui l'utilise, même s'ils connaissent son existence.
  • muqaddarmuqaddar Administrateur
    Lorsque j'envoie des fichiers à  traduire à  mes traducteurs, ils reviennent traduits... mais souvent corrompus.



    Un ";" ou un " qui manque... et patatra Xcode nous signale une erreur mais ne précise JAMAIS la ligne.



    Y-a-til une astuce pour la trouver car ça devient vite un enfer avec un fichier à  500 lignes ?
  • Tu peux utiliser grep pour détecter toutes les lignes se terminant par une double quote suivie de 0 ou plus espaces :


    <br />
    grep -n &#39;&quot;&#092;s*&#036;&#39; Localizable.strings<br />
    
  • muqaddarmuqaddar Administrateur
    'Baarde' a écrit:


    Tu peux utiliser grep pour détecter toutes les lignes se terminant par une double quote suivie de 0 ou plus espaces :


    <br />
    grep -n &#39;&quot;\s*&#036;&#39; Localizable.strings<br />
    





    Merci pour cette astuce !

    Sinon, rien de moins artisanal avec Xcode ?
  • CéroceCéroce Membre, Modérateur
    On peut utiliser les expressions régulières dans les recherches sous Xcode.
  • AliGatorAliGator Membre, Modérateur
    mai 2012 modifié #25
    [font=courier new,courier,monospace]plutil -lint[/font] ?



    Moi c'est comme ça que je fais en tout cas.
  • FKDEVFKDEV Membre
    mai 2012 modifié #26
    'AliGator' a écrit:
    • Autant quand on rajoute plusieurs clés pour la version suivante, c'est 1000x plus rapide de rajouter la nouvelle clé à  la main dans les fichiers .strings au fur et à  mesure qu'on rajoute des textes traduisible dans l'appli plutôt que d'essayer de se dépatouiller avec genstrings pour qu'il fasse un "append" au risque de nous foutre le boxon dans le fichier .strings, ou générer le fichier à  part et tenter une sorte de merge entre les clés existantes qui avaient déjà  été traduit par vos traducteurs et les nouvelles clés à  traduire...





    [font=arial,helvetica,sans-serif] J'ai vraiment été désagréablement surpris quand j'ai voulu faire des mises à  jour la 1ère fois avec genstrings. Du coup je l'utilise pour la première génération, après je fais tout à  la main.



    Ca me parait vraiment intéressant ton système de macros. Utilises-tu "localizedStringForKey:value:table:" dans ta macro ?



    Comment gères-tu les mises à  jours ?

    C'est toujours un problème de trouver un système pratique pour faire traduire juste les mises à  jours sans passer des heures (10aines de minutes) à  fusionner les fichiers après coup.[/font]



    Une autre chose qui est difficile à  faire c'est de donner du contexte aux traducteurs. Les commentaires sont souvent insuffisants si le traducteur ne peut pas utiliser l'app. En plus quand tu es en train d'écrire du code, tu n'as pas la tête à  te lancer dans des explications pour les traducteurs.



    Je cherche toujours le système idéal qui permettrait aux traducteurs de voir les chaà®nes dans le contexte de l'app sans avoir à  utiliser l'app.
  • AliGatorAliGator Membre, Modérateur
    Pour les mises à  jour, un simple "diff" entre la version précédente traduite et la nouvelle version avec les nouvelles clés ajoutées permet de ne sortir que les nouvelles clés à  fournir au traducteur.



    Et pour le contexte, c'est pour ça que j'utilise une notation à  la "URL-inversée" qui aide encore un peu à  comprendre le contexte, même si c'est pas idéal. Mais au moins comme ça quand je fais une version OTA à  mon traducteur, il peut voir clairement où il manque des clés et quel est leur nom complet.
  • tabliertablier Membre
    mai 2012 modifié #28
    Lorsque j'envoie des fichiers à  traduire à  mes traducteurs, ils reviennent traduits... mais souvent corrompus.
    J'ai une solution pour les fichiers .strings que je ne sais pas finaliser.
    NSData *essaye ;

    NSError *bing ;

    NSPropertyListSerialization *berk ;



    bing = nil ;

    essaye = [NSData dataWithContentsOfFile:/leChemin/fichier.strings options:NSDataReadingUncached error:&bing] ;

    berk = [NSPropertyListSerialization propertyListWithData:essaye options:NSPropertyListImmutable format:NULL error:&bing] ;
    Si le fichier .strings a des erreurs, Le dictionnaire _userInfo de bing donne exactement la première erreur trouvée:
    Printing description of _userInfo:

    {type = immutable dict, count = 2,

    entries =>

    0 : {contents = "NSDebugDescription"} = {contents = "Unexpected character / at line 1"}

    1 : {contents = "kCFPropertyListOldStyleParsingError"} = Error Domain=NSCocoaErrorDomain Code=3840 UserInfo=0x38dbd0 "Les donn\u00e9es n\u2019ont pu \u00eatre lues car elles sont corrompues." (Missing ';' on line 45)

    }
    J'ai fabriqué plusieurs types d'erreur et elles ont toujours été trouvées.

    Mais _userInfo est déclarée @private et je ne sais pas y acceder!!! Y a t-il une solution?
  • Runtime?
  • J'ai trouvé et tout le monde connait ça. Il suffit de créer une catégorie sur NSErreur qui renvoie le dictionnaire interne des erreurs.

    Je vais ajouter une vérification des .strings dans mon logiciel Localise.
Connectez-vous ou Inscrivez-vous pour répondre.