Localisation dans Go avec des packages de base

Créer une bonne application n'est pas facile. Quelle que soit l'application unique et utile que vous écrivez, si l'utilisateur ne l'aime pas, alors, comme on dit, vous avez un gros problème. La plupart des gens n'aiment pas et effraient tout ce qu'ils ne comprennent pas. Souvent, l'interface utilisateur et les lettres sont la partie visible de l'iceberg de votre application, selon laquelle l'utilisateur l'évalue. Par conséquent, la localisation de tout ce que l'utilisateur voit est extrêmement importante.


Rappelez-vous qu'il y a dix ans, alors qu'Internet commençait à peine à entrer dans la vie des masses, et que de nombreux géants de l'informatique étaient au stade de start-up nains avec quelques dizaines d'employés, il était dans l'ordre d'envoyer une lettre en anglais à l'utilisateur. Et les utilisateurs étaient sympathiques à cela. Aujourd'hui, lorsque tout est présent sur Internet et que vous n'avez pas besoin d'avoir sept travées dans le front, d'avoir un enseignement supérieur ou de connaître l'anglais pour l'utiliser, il est considéré comme une mauvaise forme de ne pas prendre en charge la localisation dans votre application. Soit dit en passant, dans notre entreprise, la localisation de tous les textes de l'interface utilisateur est déjà effectuée dans 20 langues et la liste des langues prises en charge est en constante augmentation.


Dans Go, comme dans une langue plutôt jeune, toutes les tendances de développement web modernes sont implémentées au niveau des packages de base et ne nécessitent pas de «danse avec un tambourin» supplémentaire. (J'ai commencé à apprendre Go il y a quelques années, mais je me souviens encore du sentiment de «superpuissances découvertes» que j'ai ressenti les premiers jours après avoir appris cette langue. Il semblait que maintenant je peux réaliser n'importe quelle tâche en écrivant seulement quelques lignes.)


Bien sûr, ils n'ont pas non plus ignoré la localisation dans Go. La localisation est disponible presque « prête à l'emploi» en utilisant les packages de base: golang.org/x/text/language , golang.org/x/text/message et golang.org/x/text/feature/plural . Voyons à quel point c'est facile dans Go en seulement une demi-heure, en utilisant ces packages, vous pouvez implémenter une tâche non triviale comme la localisation de lettres.


Pour l'avenir, je dirai que le but de cet article est principalement de montrer la puissance et la beauté de Go et de mettre en évidence les fonctionnalités de base des packages de messages pour travailler avec les localisations. Si vous recherchez une solution pour une application de production, vous souhaiterez peut-être une meilleure bibliothèque standard . Les avantages du go-i18n sont de nombreuses étoiles sur github (parmi eux il y a le mien) et une grande flexibilité. Cependant, il existe des arguments contre son utilisation: vous n'avez peut-être pas besoin de toute cette flexibilité et fonctionnalité; pourquoi utiliser une bibliothèque externe alors que tout est déjà implémenté dans le langage lui-même; si vous avez déjà votre propre système de traduction avec ses propres formats, cette bibliothèque "telle quelle" ne fonctionnera probablement pas et vous devrez quand même la modifier; et, en fin de compte, l'utilisation d'une bibliothèque tierce n'est pas aussi intéressante et informative que la façon de faire quelque chose vous-même.


Nous formulons les exigences de base pour la tâche mise en œuvre. Il existe: a) des étiquettes au format yaml: "nom_étiquette: texte de traduction" ; la langue de traduction est spécifiée dans le nom du fichier, par exemple ru.yml; b) modèles d'email en html. Il est nécessaire, en fonction des paramètres d'entrée: paramètres régionaux et tableau de données, de générer un texte localisé du message.


Et commençons ... Mais d'abord, quelques mots de plus sur le paquet de messages (golang.org/x/text/message). Il est conçu pour formater la sortie des chaînes localisées. Message implémente l'interface du package fmt standard et peut le remplacer. Exemple d'utilisation:

message.SetString(language.Russian, "toxic", "") message.SetString(language.Japanese, "toxic", "毒性") message.NewPrinter(language.Russian).Println(“toxic”) message.NewPrinter(language.Japanese).Println(“toxic”) //: // //毒性 

Pour que l'emballage «voie» l'étiquette, il doit d'abord être déclaré. Dans l'exemple, la fonction SetString est utilisée pour cela. Ensuite, une imprimante est créée pour la langue sélectionnée et une chaîne localisée s'affiche directement.

Pour résoudre notre problème, nous pourrions générer un fichier go avec toutes les balises, mais ce n'est pas très pratique, car lorsque vous ajoutez de nouvelles balises, vous devrez régénérer ce fichier à chaque fois et reconstruire l'application. Une autre façon de transmettre un message sur nos balises est d'utiliser des dictionnaires. Un dictionnaire est une structure qui implémente l'interface de recherche d'étiquettes Lookup (chaîne de clés) (chaîne de données, ok bool) .

L'option dictionnaires nous convient. Tout d'abord, nous définissons la structure du dictionnaire et implémentons l'interface de recherche pour celui-ci:

 type dictionary struct { Data map[string]string } func (d *dictionary) Lookup(key string) (data string, ok bool) { if value, ok := d.Data[key]; ok { return "\x02" + value, true } return "", false } 

Nous analysons toutes les balises des fichiers yaml dans une collection de dictionnaires, qui est une carte au format dictionnaire map [lang] * , où lang est une balise de langue au format BCP47 .

 func parseYAMLDict() (map[string]catalog.Dictionary, error) { dir := "./translations" files, err := ioutil.ReadDir(dir) if err != nil { return nil, err } translations := map[string]catalog.Dictionary{} for _, f := range files { yamlFile, err := ioutil.ReadFile(dir + "/" + f.Name()) if err != nil { return nil, err } data := map[string]string{} err = yaml.Unmarshal(yamlFile, &data) if err != nil { return nil, err } lang := strings.Split(f.Name(), ".")[0] translations[lang] = &dictionary{Data: data} } return translations, nil } 

Nous installons la collection de dictionnaires dans la fonction init afin que les dictionnaires puissent être utilisés par le package de messages au démarrage de l'application.

 func init() { dict, err := parseYAMLDict() if err != nil { panic(err) } cat, err := catalog.NewFromMap(dict) if err != nil { panic(err) } message.DefaultCatalog = cat } 

Donc, pour le moment, nous avons atteint la disponibilité de la localisation des étiquettes à partir de nos fichiers n'importe où dans le programme:

 message.NewPrinter(language.Russian).Println(“label_name”) 

Il est temps de passer à la deuxième partie de la tâche et de remplacer nos étiquettes localisées dans les modèles de courrier électronique. Par exemple, considérez un message simple - une lettre de bienvenue lors de l'inscription d'un utilisateur:
Bonjour, Bill Smith!


Pour l'analyse, nous utilisons un autre package standard - html / template . Lors de l'analyse des modèles dans le modèle, vous pouvez définir vos fonctions via .Funcs () :

 template.New(tplName).Funcs(fmap).ParseFiles(tplName) 

Ajoutez une fonction au modèle qui traduira les étiquettes et y substituera des variables, et appelez-la traduire . Code d'analyse du modèle:

 //  lang:=language.Russian //  tplName:=”./templates/hello.html” //   data := &struct { Name string LastName string }{Name: "Bill", LastName: "Smith"} fmap := template.FuncMap{ //   "translate": message.NewPrinter(lang).Sprintf, } t, err := template.New(tplName).Funcs(fmap).ParseFiles(tplName) if err != nil { panic(err) } buf := bytes.NewBuffer([]byte{}) if err := t.Execute(buf, data); err != nil { panic(err) } fmt.Println(buf.String()) 

Le modèle de lettre résultant ./templates/hello.html:

 <!DOCTYPE html> <head> <title>{{translate "hello_subject"}}</title> </head> <body> {{translate "hello_msg" .Name .LastName}} </body> </html> 

Puisque dans translate nous utilisons la fonction Sprintf pour la localisation, les variables dans le texte de l'étiquette seront cousues en utilisant la syntaxe de cette fonction. Par exemple, % s est une chaîne, % d est un entier.
Fichiers balisés
en.yml

 hello_subject: Greeting mail hello_msg: Hello, %s %s! 

ru.yml

 hello_subject:   hello_msg: , %s %s! 

Sur cela, en principe, c’est tout, la localisation des lettres est prête! Ayant écrit seulement quelques dizaines de lignes de code, nous avons obtenu de puissantes fonctionnalités qui permettent de localiser des lettres de toute complexité dans des dizaines de langues.


Si vous avez aimé cet exemple, vous pouvez continuer et implémenter la pluralisation vous-même, en utilisant des noms de variables pour les variables dans les étiquettes au lieu de % s et en utilisant des fonctions dans les étiquettes. Je n'ai délibérément pas fait cela pour laisser de la place à votre imagination.


Le code dans les exemples est écrit spécifiquement pour démontrer les capacités du package de message et ne prétend pas être idéal; une liste complète du code est disponible sur github .

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


All Articles