Comme Youtube et Instagram: internationaliser et localiser une application Python

Python est au cƓur d'applications de renommĂ©e mondiale telles que Youtube, Instagram et Pinterest. Pour avancer sur le marchĂ© mondial, une application a besoin de localisation, c'est-Ă -dire d'adaptation aux caractĂ©ristiques d'un pays particulier et d'internationalisation - traduction de contenu. Dans cet article, nous partagerons notre expĂ©rience sur la façon d'accĂ©lĂ©rer l'automatisation de la traduction et de rĂ©soudre certains problĂšmes typiques dans ce domaine.



Présentation


Ceci est un petit guide pour l'internationalisation (i18n) des applications python. Ce guide sera intéressant pour tous les programmeurs ayant une expérience de développement en python. La lecture d'un article prendra 10 à 15 minutes.

Nous utiliserons l'outil gettext bien testé inclus dans le langage python.

Pour commencer, nous comprendrons ce qu'est l'internationalisation:

L'internationalisation (I18N) est le processus d'adaptation d'une application aux langues de différents pays et régions autres que celle dans laquelle elle a été développée.

Mais il existe Ă©galement un concept plus large:

La localisation (L10N) est le processus d'adaptation d'une application internationalisée à une région ou une langue spécifique en ajoutant des composants spécifiques à un environnement local donné et en traduisant du texte.

La localisation signifie la traduction:

  • format de date et d'heure;
  • format numĂ©rique;
  • fuseau horaire
  • un calendrier
  • reprĂ©sentations monĂ©taires;
  • taxes / TVA;
  • tempĂ©rature et autres mesures;
  • codes postaux, tĂ©lĂ©phones;
  • formatage d'adresse;
  • code du rĂšglement.



La localisation va au-delà de la traduction de contenu dans une autre langue. Il existe des paramÚtres culturels et fonctionnels qui nécessitent également une attention particuliÚre. Par exemple, le format de date en Amérique du Nord est MM / JJ / AAAA, mais dans la plupart des pays asiatiques, il est écrit JJ / MM / AAAA.



Un exemple bien connu d'une erreur de traduction d'application

Un autre exemple concerne l'affichage des noms dans les applications. Aux États-Unis, appeler une personne par son nom est acceptable et mĂȘme prĂ©fĂ©rable, le nom du client s'affiche dans l'en-tĂȘte dĂšs que le client se connecte. Cependant, au Japon, le contraire est vrai: appeler quelqu'un par son nom est impoli ou mĂȘme offensant. La localisation doit en tenir compte et Ă©viter d'utiliser des noms pour un public japonais.

Dans cet article, nous ne considérerons que l'internationalisation, mais les mécanismes de localisation sont construits de maniÚre similaire. Les bibliothÚques mentionnées dans cet article prennent en charge la localisation des applications.

Types principaux


L'internationalisation est divisée en:

  1. Traduction de données directement dans des scripts python.
  2. Traduction des données dans les moteurs de modÚles.
  3. Traduction de données stockées dans une base de données.

1. Traduction des données de script python


Pour que notre internationalisation fonctionne, nous devons traiter avec la bibliothÚque babel et la boßte à outils distutils pour gérer l'assemblage du projet à vendre et au-delà.

Préparation de la traduction


Pour commencer, nous devons créer une liste de traductions. Pour commencer, nous installons la bibliothÚque Babel - c'est une bibliothÚque python généralement reconnue pour localiser et convertir des dates, des devises, avec des ajouts pratiques pour construire le projet (discuté ci-dessous).

Python fournit une boßte à outils pour le multilinguisme - gettext. GNU gettext est en fait une solution de localisation universelle qui prend en charge d'autres langages de programmation dans les messages multilingues. Gettext est utilisé non seulement dans de nombreux langages de programmation, mais aussi dans la traduction de systÚmes d'exploitation; c'est un logiciel bien testé et distribué gratuitement disponible sur github .

Pour que les traductions fonctionnent, vous devez importer le module gettext et passer les scripts avec les traductions à l'entrée. Tout d'abord, nous marquons toutes les chaßnes traduites avec la fonction spéciale _ ('some_text'). L'appel à cette fonction dans le projet ressemblera à ceci:

import gettext import os localedir = os.path.join(os.path.abspath('/path/to/locales'), 'locales') translate = gettext.translation('domain_name', localedir, ['ru']) _ = translate.gettext print(_('some_text')) print(_('some_text_2')) 

Dans un petit morceau de code, créez un objet d'internationalisation qui utilise le répertoire «locales» comme source de phrases traduites. Le répertoire 'locales' n'a pas encore été créé, mais c'est là que l'application recherchera les traductions lors de l'exécution.

Par souci de concision, la fonction translate.gettext sera désignée ci-dessous par _. Underscore est le nom commun de cette fonction, qui est reconnu par la communauté Python.

La fonction _ () marque les lignes Ă  traduire. Le module gettext est accompagnĂ© de l'outil xgettext, qui analyse les marqueurs de chaĂźne _ () par code et forme un modĂšle d'objet portable (fichier-pot). Pour crĂ©er le fichier pot, revenons Ă  la bibliothĂšque Babel installĂ©e, qui possĂšde de nombreuses fonctionnalitĂ©s pour prendre en charge l'internationalisation. Babel Ă©tend le script de construction setup.py, qui peut ĂȘtre Ă©crit en utilisant la bibliothĂšque standard de distythils python ou le paquet setuptools tiers de votre choix. L'assemblage des modules Python dĂ©passe le cadre de notre article; pour plus de dĂ©tails, consultez la documentation . Il suffit de crĂ©er un fichier setup.py avec le contenu suivant:

 from babel.messages import frontend as babel from distutils.core import setup setup(name='foo', version='1.0', cmdclass = {'extract_messages': babel.extract_messages, 'init_catalog': babel.init_catalog, 'update_catalog': babel.update_catalog, 'compile_catalog': babel.compile_catalog,} ) 

Ainsi, nous avons créé des instructions pour la construction du projet et ajouté quatre équipes d'internationalisation de la bibliothÚque babel. Considérez ces commandes plus en détail par ordre d'utilisation.

extract_messages

Cette commande est un wrapper sur l'outil GNU xgettext, qui analyse les balises traduisibles _ () dans un fichier pot. Pour exécuter, vous avez besoin de plusieurs paramÚtres pour l'assemblage. Pour ce faire, dans le répertoire racine, créez le fichier setup.cfg avec le contenu:

 [extract_messages] input_dirs = foobar output_file = foobar/locales/messages.pot 


  • input_dirs - le nom du rĂ©pertoire Ă  partir duquel toutes les Ă©tiquettes du code _ () seront sĂ©lectionnĂ©es pour les traductions.
  • fichier_sortie - chemin du fichier .pot rĂ©sultant

Pour exécuter la commande, exécutez dans la console:

 $ python setup.py extract_messages 


 running extract_messages extracting messages from foobar/__init__.py extracting messages from foobar/core.py ... writing PO template file to foobar/locales/messages.pot 

Dans le fichier pot, les lignes marquées sont collectées dans une liste à partir de laquelle les traducteurs peuvent ensuite créer des traductions pour chacune des langues souhaitées.

 # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR ORGANIZATION # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "POT-Creation-Date: 2018-01-28 16:47+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: pygettext.py 1.5\n" #: src/main.py:5 msgid "some_text" msgstr "" #: src/main.py:6 msgid "some_text_2" msgstr "" 

Ensuite, vous devez créer des traductions pour plusieurs langues. Pour ce faire, utilisez les commandes babel suivantes.

init_catalog

Cette commande est un wrapper sur l'outil GNU msginit, qui crée un nouveau répertoire de traduction basé sur le fichier pot.

 $ python setup.py init_catalog -l en -i foobar/locales/messages.pot \ -o foobar/locales/en/LC_MESSAGES/base.po 

 running init_catalog creating catalog 'foobar/locales/en/LC_MESSAGES/messages.po' based on 'foobar/locales/messages.pot' 

Important! Les fichiers de localisation sont stockés de maniÚre spécifique, conformément à la convention:

locales // LC_MESSAGES / .po

- un rĂ©pertoire avec des traductions dans une langue spĂ©cifique, dans notre cas c'est l'anglais (en). Il peut Ă©galement y avoir un rĂ©pertoire avec des traductions non seulement dans une langue spĂ©cifique, mais prenant Ă©galement en compte des fonctionnalitĂ©s supplĂ©mentaires. Par exemple, une traduction anglaise pour les États-Unis est en_US;

- domaine avec traductions. Si notre application se développe, les traductions seront divisées en domaines afin de ne pas surcharger un fichier.

update_catalog

Cette commande est un wrapper sur l'outil msgmerge GNU, qui met à jour les répertoires de traduction existants pour les fichiers * .po.

Lors de l'ajout de nouvelles traductions, nous exécutons simplement la commande:

 $ python setup.py update_catalog -l en -i foobar/locales/messages.pot \ -o foobar/locales/en/LC_MESSAGES/base.po 

 running update_catalog updating catalog 'foobar/locales/en/LC_MESSAGES/base.po' based on 'foobar/locales/messages.pot' 

Nous pouvons également spécifier la localisation en russe en spécifiant ru au lieu de en.

compile_catalog

La commande finale est un wrapper sur l'outil GNU msgfmt. Il prend les messages traduisibles des fichiers * .po et les compile en fichiers binaires * .mo pour optimiser les performances.

 $ python setup.py compile_catalog --directory foobar/locales --domain base 

 running compile_catalog compiling catalog to foobar/locales/en/LC_MESSAGES/base.mo 

--directory - chemin vers le répertoire avec localisation,
--domain - un indicateur pour spécifier un domaine de traduction, nous le spécifions conformément aux domaines d'application existants.

Les scripts Python ne fonctionnent qu'avec des traductions * .mo optimisées. Par conséquent, avec toute modification, afin qu'elle apparaisse dans l'application, vous devez recompiler les fichiers avec la localisation. Pour travailler avec des fichiers de traduction, vous pouvez utiliser l'application poedit - elle est disponible pour tous les systÚmes d'exploitation et est un logiciel distribué gratuitement.



poedit - application de traduction

Chaque traduction est affichée sur une ligne distincte, ce qui est pratique. Une fois le travail de traduction terminé, lors de l'enregistrement des modifications, un fichier binaire * .mo avec toutes les modifications est automatiquement compilé.

Par conséquent, la structure des catalogues de traduction ressemblera à ceci:

 locales ├── en │ └── LC_MESSAGES │ ├── base.mo │ └── base.po ├── ru │ └── LC_MESSAGES │ ├── base.mo │ └── base.po └── messages.pot 


Convention sur les noms de marqueurs de traduction

Les fichiers po contiennent des traductions de texte et sont logiquement combinĂ©s dans un fichier avec un nom commun. Ces groupes sont appelĂ©s domaines. Dans l'exemple ci-dessus, il n'y a qu'un seul domaine nommĂ© base. Dans les grandes applications, il y aura plus de domaines et les listes de traduction doivent ĂȘtre Ă©crites en tenant compte de la structure de l'application.

La cohĂ©rence des noms des marqueurs de traduction doit ĂȘtre maintenue pour Ă©viter toute confusion supplĂ©mentaire dans les traductions. Par exemple, nous avons un formulaire avec la sauvegarde des donnĂ©es utilisateur sur la page de profil utilisateur:

profile.user_form.component.title: Données utilisateur
profile.user_form.component.save: Enregistrer
profile.user_form.field.username: Nom d'utilisateur
profile.user_form.field.password: mot de passe


DĂ©ploiement d'applications

Pour déployer et déployer l'application dans Docker, vous devez compiler les fichiers de traduction dans des fichiers binaires * .mo à l'aide de la commande suivante:

 $ python setup.py compile_catalog --domain <> 

Nous vous recommandons d'exclure les fichiers * .mo et * .pot dans .gitignore:

# Traductions
* .mo
* .pot

2. Traduction des données dans les moteurs de modÚles


Avec la localisation dans les modÚles, tout est un peu plus facile. Considérez le moteur de modÚle de python le plus populaire - jinja. Pour ce moteur de modÚle, la prise en charge de la localisation de gettext via des modules complémentaires est déjà implémentée. Pour activer le module complémentaire, vous devez spécifier le chemin d'accÚs au module complémentaire dans le constructeur d'environnement. Pour les plateformes multilingues, vous devez télécharger les traductions une fois et ajouter des objets de traduction à l'objet Environnement lors de l'initialisation de l'application:

 translations = get_gettext_translations() env = Environment(extensions=['jinja2.ext.i18n']) env.install_gettext_translations(translations) 

Ensuite, dans les modĂšles, nous utilisons simplement les constructions:

 {{ gettext('some_text') }} {{ gettext('Hello %(name)s!')|format(name='World') }} 

3. Traduction des données stockées dans la base de données


Examinons les options pour travailler avec des traductions dans les bases de donnĂ©es relationnelles les plus courantes. Il convient de noter que la mise en Ɠuvre des traductions et de la localisation pour les bases de donnĂ©es noSQL et newSQL est similaire.

Remarque: nous ne considérerons pas le cas lorsque la traduction de chaque langue est stockée dans une colonne distincte. Une telle implémentation implique des limitations de mise à l'échelle et d'autres risques avec un support d'application supplémentaire.

1) Lignes distinctes pour chaque langue


Avec cette approche, pour chaque langue, la traduction dans une langue spécifique dans les lignes est basée sur la valeur de la colonne, par exemple code_langue. Si la valeur en est dans cette colonne, toutes les valeurs traduites doivent se référer au pays et à la région donnés.



Pour le schéma décrit, les données du tableau doivent ressembler à ceci:



Avantages:

  • ImplĂ©mentation simple et efficace.
  • RequĂȘtes simples lors de l'utilisation d'un code de langue spĂ©cifique.


Inconvénient:
  • Manque de centralisation

Les traductions dans diffĂ©rentes langues peuvent ĂȘtre stockĂ©es dans diffĂ©rentes tables. Ainsi, vous ne savez pas combien de langues votre application est entiĂšrement traduite.

Cette solution convient aux applications qui ne nécessitent pas initialement une internationalisation complÚte de toutes les données. Mais il est possible d'ajouter des traductions pour de nouvelles régions à mesure que l'entreprise se développe.

La demande de données sera la suivante:

 SELECT p.product_name, p.price, p.description FROM product p WHERE p.language_code = @language_code; 

2) Tableaux séparés avec traductions


Dans cette approche, pour chaque table nécessitant une localisation, nous créons des tables avec des traductions.



Avantages:

  • Il n'est pas nĂ©cessaire de joindre des tables pour les donnĂ©es non traduites.
  • Les requĂȘtes deviennent faciles car il existe des tables sĂ©parĂ©es pour la traduction.
  • Il n'y a aucun Ă©cart dans les donnĂ©es.
  • En plus des traductions, il est possible de localiser efficacement le reste des donnĂ©es dans la table des langues.

Inconvénient:

  • Dans les grandes applications, la table de traduction est gonflĂ©e et ralentit. Lors de l'optimisation de l'application, il sera nĂ©cessaire d'implĂ©menter la migration des donnĂ©es sur des tables distinctes.

La demande de données sera la suivante:

 SELECT tp.text, p.price, tc.text, c.contact_name FROM order_line o, product p, customer c, translation tp, translation tc, language l WHERE o.product_id = p.id AND o.customer_id = c.id AND p.name_translation_id = tp.id AND c.name_translation_id = tc.id AND tp.language_id = l.id AND tc.language_id = l.id AND l.name = @language_code AND o.id = ***; 

3) Création d'entités pour les champs traduits et non traduits


Dans cette solution, les tables d'entité qui contiennent un ou plusieurs champs traduits développent les données avec des champs non traduits.



Avantages:

  • Il n'est pas nĂ©cessaire de combiner des tables de traduction avec des tables contenant des donnĂ©es qui ne nĂ©cessitent pas de traduction. Par consĂ©quent, l'Ă©chantillonnage de ces donnĂ©es aura de meilleures performances,
  • Il est facile d'Ă©crire des requĂȘtes ORM,
  • Une simple requĂȘte SQL pour obtenir le texte traduit,
  • Il est facile de prendre en charge la traduction de certaines donnĂ©es dans toutes les langues disponibles.

Inconvénient:

  • La relative complexitĂ© de la mise en Ɠuvre.

Voici un exemple de requĂȘte qui rĂ©cupĂ©rera le texte traduit:

 SELECT pt.product_name, pt.description, p.price FROM order_line o, product p, product_translation pt, language l WHERE o.product_id = p.id AND AND p.id = pt.product_non_trans_id AND pt.language_id = l.id AND l.name = @language_code; 

Conclusions


Lors de la localisation et de l'internationalisation d'applications pour le marchĂ© international, diffĂ©rentes mĂ©thodes peuvent ĂȘtre utilisĂ©es, chacune ayant certaines caractĂ©ristiques et limitations.

Dans cet article, nous avons examiné les types d'internationalisation suivants:

  • dans le code: nous utilisons des traductions lors de la crĂ©ation d'un service ou d'une application avec gui;
  • dans les modĂšles: nous utilisons lors du dĂ©veloppement d'une application web sans interface dynamique;
  • dans la base de donnĂ©es: Ă  utiliser lors du stockage de donnĂ©es utilisateur ou gĂ©nĂ©rĂ©es dynamiquement.

Nous espérons que notre article vous aidera à choisir la méthode la plus adaptée à votre projet.

Source: https://habr.com/ru/post/fr479738/


All Articles