{"id":24,"date":"2010-10-07T12:00:00","date_gmt":"2010-10-07T12:00:00","guid":{"rendered":"http:\/\/www.renaudpradenc.com\/?p=24"},"modified":"2013-07-24T15:52:13","modified_gmt":"2013-07-24T14:52:13","slug":"traduction-des-applications","status":"publish","type":"post","link":"https:\/\/www.renaudpradenc.com\/?p=24","title":{"rendered":"Traduction d&#8217;une appli Cocoa"},"content":{"rendered":"<p>Lorsque l&#8217;on propose son application dans plusieurs langues, comme c&#8217;est mon cas pour <a href=\"http:\/\/www.ceroce.com\/portraimatic\">PortraiMatic<\/a>, la traduction (terme que je pr\u00e9f\u00e8re au vilain anglicisme &#8220;localisation&#8221;) est plus complexe que ce qu&#8217;Apple laisse entendre. En gros, trois aspects doivent \u00eatre g\u00e9r\u00e9s:<\/p>\n<ul>\n<li>Ne pas utiliser de cha\u00eenes de caract\u00e8res &#8221;en dur&#8221;.<\/li>\n<li>Il faut les \u00e9crire dans un fichier .strings et les charger avec une fonction ou m\u00e9thode ad\u00e9quate (NSLocalizedString() ou -NSBundle localizedStringForKey:value:table).<\/li>\n<li>Adapter la saisie et la pr\u00e9sentation des nombres, monnaies et dates (NSNumberFormatters et compagnie)<\/li>\n<li>Traduire les fen\u00eatres, vues et boites de dialogue, habituellement contenus dans des .xib.<\/li>\n<\/ul>\n<p>Comme nous allons le voir, ce dernier aspect est d\u00e9licat.<\/p>\n<h2>Solution 1: Traduire les XIB \u00e0 la mano<\/h2>\n<p>Apple propose un concept simple: le bundle de l&#8217;application contient un dossier par langue (french.lproj, english.lproj, deutsch.lproj, etc.) qui contient les \u00e9l\u00e9ments d&#8217;interface utilisateur sp\u00e9cifiques \u00e0 cette langue. Pour traduire un XIB anglais en fran\u00e7ais, il suffit donc de copier le .xib de english.lproj dans french.lproj et de le traduire. Cette mani\u00e8re fonctionne bien\u2026 la premi\u00e8re fois.<\/p>\n<p>Imaginons que vous vouliez ajouter une case \u00e0 cocher dans votre logiciel:<\/p>\n<ul>\n<li>vous ajoutez la case dans le xib anglais<\/li>\n<li>vous cr\u00e9ez ses connexions (outlets, actions et bindings)<\/li>\n<li>et vous refaites le m\u00eame boulot pour le xib fran\u00e7ais !<\/li>\n<\/ul>\n<p>En dehors des risques d&#8217;erreur sur les connexions, qui ne peuvent \u00eatre d\u00e9tect\u00e9es qu&#8217;\u00e0 l&#8217;ex\u00e9cution, c&#8217;est un travail r\u00e9barbatif, \u00e0 tel point qu&#8217;il vaut mieux repartir des xib anglais et tout retraduire. C&#8217;est ce que je faisais jusqu&#8217;\u00e0 r\u00e9cemment.<\/p>\n<p>Un autre probl\u00e8me est que si vous voulez faire traduire le logiciel, vous ne pouvez pas demander au traducteur d&#8217;utiliser Interface Builder pour modifier le xib lui-m\u00eame. Non seulement, c&#8217;est compliqu\u00e9, mais il risque de casser quelque chose. Vous devez lui fournir un simple fichier texte, ce qui nous am\u00e8ne \u00e0 la\u2026<\/p>\n<h2>Solution 2: Extraire les cha\u00eenes<\/h2>\n<p>C&#8217;est la solution que j&#8217;utilise actuellement pour PortraiMatic. Je me suis inspir\u00e9 de la technique expos\u00e9e par <a href=\"http:\/\/developer.casgrain.com\/?p=94\">Philippe Casgrain<\/a>:<\/p>\n<p>1) un outil en ligne de commande, ibtool, extrait les cha\u00eenes de caract\u00e8res du xib anglais et les \u00e9crit dans un fichier .strings.<\/p>\n<pre>ibtool --export-strings-file English\/MainMenu.strings ..\/English.lproj\/MainMenu.xib<\/pre>\n<p>Voici un extrait du fichier .strings produit:<\/p>\n<pre>\/* Class = \"NSMenuItem\"; title = \"About PortraiMatic\"; ObjectID = \"58\"; *\/ \"58.title\" = \"About PortraiMatic\"; \/* Class = \"NSMenu\"; title = \"File\"; ObjectID = \"81\"; *\/ \"81.title\" = \"File\";<\/pre>\n<p>2) le traducteur traduit le fichier .strings<\/p>\n<pre>\/* Class = \"NSMenuItem\"; title = \"About PortraiMatic\"; ObjectID = \"58\"; *\/ \"58.title\" = \"\u00c0 propos de PortraiMatic\"; \/* Class = \"NSMenu\"; title = \"File\"; ObjectID = \"81\"; *\/ \"81.title\" = \"Fichier\";<\/pre>\n<p>3) ibtool recompose le xib fran\u00e7ais en rempla\u00e7ant les cha\u00eenes traduites:<\/p>\n<pre>ibtool --import-strings-file French\/MainMenu.strings --write ..\/French.lproj\/MainMenu.xib ..\/English.lproj\/MainMenu.xib<\/pre>\n<p>Philippe Casgrain propose d&#8217;extraire les cha\u00eenes \u00e0 chaque build (en ajoutant une &#8221;Script Build Phase&#8221; \u00e0 la &#8221;Target&#8221; sous XCode). C&#8217;est une bonne id\u00e9e car votre logiciel de gestion de version va vous alerter d&#8217;une modification des fichiers .strings, qu&#8217;il faudra renvoyer au traducteur.<\/p>\n<p>Par contre, il sugg\u00e8re aussi de recomposer les XIB traduits \u00e0 chaque build: je ne le fais pas, car le build s&#8217;en trouve allong\u00e9, alors que \u00e7a n&#8217;agit que quand le fichier .strings vient d&#8217;\u00eatre traduit.<\/p>\n<p>Cette technique a l&#8217;avantage d&#8217;\u00eatre simple \u00e0 mettre en \u0153uvre, mais pose encore deux probl\u00e8mes:<\/p>\n<ul>\n<li>les objets de l&#8217;IHM ne sont pas redimensionn\u00e9s; or on sait que l&#8217;Anglais est souvent plus concis que le Fran\u00e7ais ou l&#8217;Allemand. Il faut donc pr\u00e9voir de la marge dans les dimensions lors de l&#8217;\u00e9laboration des XIB anglais. Ce n&#8217;est pas toujours esth\u00e9tique: il reste deux ou trois boutons que je redimensionne \u00e0 chaque livraison.<\/li>\n<li>les XIB sont volumineux, et l&#8217;application prend de l&#8217;embonpoint \u00e0 chaque langue ajout\u00e9e. C&#8217;est particuli\u00e8rement p\u00e9nalisant pour une appli iPhone.<\/li>\n<\/ul>\n<h2>Solution 3: Traduire au chargement du NIB<\/h2>\n<p>Cette solution a \u00e9t\u00e9 expos\u00e9e par Axel P\u00e9ju, l&#8217;auteur de <a href=\"http:\/\/www.squirrelapp.com\">Squirrel<\/a> (et accessoirement gagnant d&#8217;un Apple Design Awards pour la version Mac), lors de <a href=\"http:\/\/www.slideshare.net\/cocoaheadsfr\/slides-de-la-localisation\">sa pr\u00e9sentation<\/a>\u00a0au dernier <a href=\"http:\/\/cocoaheads.fr\">Cocoa Heads<\/a> parisien. L&#8217;id\u00e9e est simple: il suffit de mettre <strong>toutes<\/strong>\u00a0les cha\u00eenes de caract\u00e8res qui apparaissent \u00e0 l&#8217;\u00e9cran dans des fichiers .strings. Quand on charge un NIB, on remplace alors le texte de chaque objet par le code.Pour redimensionner les objets, on appelle leur m\u00e9thode -sizeToFit pour que leur taille s&#8217;adapte au texte affich\u00e9.<\/p>\n<p>Cette technique r\u00e9sout tous les probl\u00e8mes expos\u00e9s pr\u00e9cedemment: il n&#8217;y a plus qu&#8217;un seul XIB, et les cha\u00eenes peuvent \u00eatre fournies directement aux traducteurs. Elle poss\u00e8de aussi un avantage: le traducteur peut essayer lui-m\u00eame l&#8217;application traduite: il n&#8217;a qu&#8217;\u00e0 glisser les fichiers .strings traduits dans le dossier .lproj qui correspond \u00e0 sa langue.<\/p>\n<p>Ceci dit, le travail est important, puisqu&#8217;un objet contr\u00f4leur doit poss\u00e9der des outlets vers <strong>tous<\/strong>\u00a0les objets qui affichent du texte, m\u00eame si ce sont de simple libell\u00e9s, et faire les appels correspondant.<\/p>\n<h2>Solution 4: Traduire (automatiquement) au chargement du NIB<\/h2>\n<p>Voici une solution \u00e0 laquelle j&#8217;avais pens\u00e9; Guillaume Cerquant m&#8217;a dit qu&#8217;il l&#8217;utilisait. L&#8217;id\u00e9e est que chaque \u00e9l\u00e9ment d&#8217;IHM du XIB \u00e0 traduire a son titre qui commence par un ast\u00e9risque (par exemple). Lors du chargement du XIB, on parcourt la hi\u00e9rarchie des vues en entier: si le titre de la vue commence par &#8220;*&#8221;, on va chercher le titre dans un fichier .strings et on remplace le titre. Il me semble que cette technique est un bon compromis: \u00e0 la fois assez l\u00e9g\u00e8re \u00e0 mettre en \u0153uvre, tout en conservant les qualit\u00e9s de la solution 3.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Lorsque l&#8217;on propose son application dans plusieurs langues, comme c&#8217;est mon cas pour PortraiMatic, la traduction (terme que je pr\u00e9f\u00e8re au vilain anglicisme &#8220;localisation&#8221;) est plus complexe que ce qu&#8217;Apple laisse entendre. En gros, trois aspects doivent \u00eatre g\u00e9r\u00e9s: Ne pas utiliser de cha\u00eenes de caract\u00e8res &#8221;en dur&#8221;. Il faut les \u00e9crire dans un fichier [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[36,27,59,58],"class_list":["post-24","post","type-post","status-publish","format-standard","hentry","category-uncategorized","tag-cocoa-mac","tag-cocoa-touch","tag-localization","tag-traduction"],"_links":{"self":[{"href":"https:\/\/www.renaudpradenc.com\/index.php?rest_route=\/wp\/v2\/posts\/24","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.renaudpradenc.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.renaudpradenc.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.renaudpradenc.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.renaudpradenc.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=24"}],"version-history":[{"count":5,"href":"https:\/\/www.renaudpradenc.com\/index.php?rest_route=\/wp\/v2\/posts\/24\/revisions"}],"predecessor-version":[{"id":420,"href":"https:\/\/www.renaudpradenc.com\/index.php?rest_route=\/wp\/v2\/posts\/24\/revisions\/420"}],"wp:attachment":[{"href":"https:\/\/www.renaudpradenc.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=24"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.renaudpradenc.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=24"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.renaudpradenc.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=24"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}