Localização no Go com pacotes básicos

Criar um bom aplicativo não é fácil. Qualquer que seja o aplicativo exclusivo e útil que você escreve, se o usuário não gostar, então, como eles dizem, você tem um grande problema. A maioria das pessoas não gosta e espanta tudo o que não entende. Freqüentemente, a interface do usuário e as letras são a ponta visível do iceberg do seu aplicativo, segundo a qual o usuário o avalia. Portanto, a localização de tudo o que o usuário vê é extremamente importante.


Lembre-se de há dez anos, quando a Internet estava apenas começando a entrar na vida das massas, e muitos dos gigantes de TI de hoje estavam no estágio de anões iniciantes com duas dúzias de funcionários, era normal enviar uma carta ao usuário em inglês. E os usuários foram simpáticos a isso. Hoje, quando tudo está presente na Internet e você não precisa ter sete extensões na testa, ter um ensino superior ou saber inglês para usá-lo, é considerado uma forma ruim não oferecer suporte à localização em seu aplicativo. A propósito, em nossa empresa, a localização de todos os textos da interface do usuário já é realizada em 20 idiomas e a lista de idiomas suportados está em constante crescimento.


No Go, como em uma linguagem bastante jovem, todas as tendências modernas de desenvolvimento da web são implementadas no nível de pacotes básicos e não exigem "dançar com um pandeiro" adicional. (Comecei a aprender o Go há alguns anos, mas ainda me lembro do sentimento de “superpotências descobertas” que experimentei nos primeiros dias após o aprendizado desse idioma. Parecia que agora posso realizar qualquer tarefa escrevendo apenas algumas linhas.)


Obviamente, eles também não ignoraram a localização no Go. A localização está disponível praticamente “fora da caixa” usando os pacotes básicos: golang.org/x/text/language , golang.org/x/text/message e golang.org/x/text/feature/plural . Vamos ver como é fácil no Go em apenas meia hora, usando esses pacotes, você pode implementar uma tarefa não trivial como localização de mensagens.


No futuro, direi que o objetivo deste artigo é principalmente mostrar o poder e a beleza do Go e destacar os recursos básicos dos pacotes de mensagens para trabalhar com localizações. Se você está procurando uma solução para um aplicativo de produção, pode querer uma biblioteca pronta para uso . As vantagens do go-i18n são muitas estrelas no github (entre elas a minha) e grande flexibilidade. No entanto, existem argumentos contra seu uso: talvez você não precise de toda essa flexibilidade e funcionalidade; por que usar uma biblioteca externa quando tudo já está implementado na própria linguagem; se você já possui seu próprio sistema de tradução com seus próprios formatos, essa biblioteca "como está" provavelmente não funcionará e você terá que modificá-lo de qualquer maneira; e, no final, o uso de uma biblioteca de terceiros não é tão interessante e informativo quanto a maneira de fazer alguma coisa.


Formulamos os requisitos básicos para a tarefa que está sendo implementada. Existem: a) etiquetas no formato yaml: “label_name: translation text” ; o idioma da tradução é especificado no nome do arquivo, por exemplo ru.yml; b) modelos de email em html. É necessário, com base nos parâmetros de entrada: locale e matriz de dados, gerar texto localizado da mensagem.


E vamos começar ... Mas primeiro, mais algumas palavras sobre o pacote de mensagens (golang.org/x/text/message). Ele foi projetado para formatar a saída de seqüências localizadas. A mensagem implementa a interface do pacote fmt padrão e pode substituí-lo. Exemplo de uso:

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

Para que a embalagem “veja” o rótulo, ela deve primeiro ser declarada. No exemplo, a função SetString é usada para isso. Em seguida, uma impressora é criada para o idioma selecionado e uma sequência localizada é exibida diretamente.

Para resolver nosso problema, poderíamos gerar um arquivo go com todas as tags, mas isso não é muito conveniente, porque quando você adiciona novas tags, você precisa regenerar esse arquivo toda vez e criar o aplicativo novamente. Outra maneira de dizer a mensagem sobre nossas tags é usar dicionários. Um dicionário é uma estrutura que implementa a interface de pesquisa de etiquetas Lookup (string de chaves) (string de dados, ok bool) .

A opção de dicionários nos convém. Primeiro, definimos a estrutura do dicionário e implementamos a interface de pesquisa para ele:

 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 } 

Analisamos todas as tags dos arquivos yaml em uma coleção de dicionários, que é um mapa [lang] * no formato de dicionário , onde lang é uma tag de idioma no formato 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 } 

Instalamos a coleção de dicionários na função init, para que os dicionários possam ser usados ​​pelo pacote de mensagens quando o aplicativo for iniciado.

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

Portanto, no momento, alcançamos a disponibilidade de localização de rótulos de nossos arquivos em qualquer lugar do programa:

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

É hora de passar para a segunda parte da tarefa e substituir nossos marcadores localizados nos modelos de email. Por exemplo, considere uma mensagem simples - uma carta de boas-vindas ao registrar um usuário:
Olá, Bill Smith!


Para análise, usamos outro pacote padrão - html / template . Ao analisar modelos no modelo, você pode definir suas funções através de .Funcs () :

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

Adicione uma função ao modelo que traduza rótulos e substitua variáveis ​​neles, e chame-o de traduzir . Código de análise de modelo:

 //  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()) 

O modelo de carta resultante ./templates/hello.html:

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

Como no translate usamos a função Sprintf para localização, as variáveis ​​no texto do rótulo serão costuradas usando a sintaxe dessa função. Por exemplo, % s é uma sequência, % d é um número inteiro.
Arquivos marcados
en.yml

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

ru.yml

 hello_subject:   hello_msg: , %s %s! 

Por isso, em princípio, é tudo, a localização das letras está pronta! Depois de escrever apenas algumas dezenas de linhas de código, obtivemos uma funcionalidade poderosa que permite localizar letras de qualquer complexidade em dezenas de idiomas.


Se você gostou deste exemplo, pode seguir em frente e implementar a pluralização, usando nomes de variáveis ​​para variáveis ​​em rótulos em vez de % s e funções em rótulos. Eu deliberadamente não fiz isso para deixar espaço para sua imaginação.


O código nos exemplos foi escrito especificamente para demonstrar os recursos do pacote de mensagens e não afirma ser o ideal; uma lista completa do código está disponível no github .

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


All Articles