O Guia Completo para a Internacionalização de Trilhos (i18n)

Anastasia Blocked Unblock Seguir Seguindo 23 de agosto de 2018

Neste artigo, você aprenderá como traduzir seu aplicativo Rails em vários idiomas, trabalhar com traduções, localizar data e hora e alternar localidades. Vamos ver todos esses aspectos em ação, criando um aplicativo de exemplo e aprimorando-o passo a passo. No final do artigo, você terá todo o conhecimento necessário para começar a implementar esses conceitos em projetos reais.

Preparando seu aplicativo Rails

Então, como eu já disse, vamos ver todos os conceitos em ação, portanto vamos criar um novo aplicativo Rails executando:

 trilhos novo SampleApp 

Para este tutorial, estou usando o Rails 5.2.1 , mas a maioria dos conceitos descritos também se aplica a versões mais antigas.

Agora vamos gerar um StaticPagesController que terá uma ação de index (nossa página principal):

 rails g controller StaticPages índice 

Ajuste a views/static_pages/index.html.erb adicionando algum conteúdo de amostra:

 <h1> Bem-vindo! </ h1> <p> Oferecemos alguns serviços sofisticados para <em> pessoas boas </ em>. </ p> 

Além disso, gostaria de adicionar uma página de comentários em que nossos usuários poderão compartilhar sua opinião (esperançosamente positiva) sobre a empresa. Cada feedback terá o nome de um autor e a mensagem real:

 rails g scaffold Mensagem do autor de feedback 

Estaremos interessados apenas em duas ações: new (que vai renderizar o formulário para postar um review e também listar todas as reviews existentes) e create (para validar e persistir as reviews). É claro que, idealmente, as revisões devem ser pré-moderadas, mas não vamos nos incomodar com isso hoje.

Ajuste a new ação para buscar todos os comentários do banco de dados e solicitá-los por data de criação:

 # feedbacks_controller.rb # ... def new @feedback = Feedback.new @feedbacks = Feedback.ordem created_at:: desc end 

Além disso, gostaria de redirecionar o usuário para a página Feedback quando o formulário for processado e o novo registro for persistido:

 # feedbacks_controller.rb # ... def create @feedback = Feedback.new (feedback_params) se @ feedback.save redirect_to new_feedback_path else @feedbacks = Feedback.ordem created_at:: desc render: novo end end 

Renderize a coleção de feedbacks na new página:

 <! - views / feedbacks / new.html.erb -> <! - outro código vai aqui ... -> <% = render @feedbacks%> 

Por fim, crie uma parcial para um feedback individual:

 <! - views / feedbacks / _feedback.html.erb -> <article> <em> <% = tag.time feedback.created_at, datetime: feedback.created_at%> <br> Postado por <% = feedback.autor %> </ em> <p> <% = feedback.message%> </ p> <hr> </ article> 

Cuide das rotas:

 # config / routes.rb Rails.application.routes.draw faz recursos: feedbacks raiz 'static_pages # index' end 

Por fim, adicione um menu global ao layout:

 <! - views / layouts / application.html.erb -> <! - outro código vai aqui ... -> <nav> <ul> <li> <% = link_to 'Início', root_path%> </ li> <li> <% = link_to "Comentários", new_feedback_path%> </ li> </ ul> </ nav> 

Agora execute migrações e inicialize o servidor:

 trilhos db: migrar trilhos s 

Navegue para o http://locahost:3000 e verifique se tudo está bem. Agora que temos algo para trabalhar, vamos para a parte principal e localizar nosso aplicativo.

Um pouco de configuração

Antes de realizar traduções, precisamos decidir quais idiomas serão suportados. Você pode escolher qualquer um, mas eu vou ficar com russo e inglês, com o último definido como padrão. Reflita isso dentro do config/application.rb :

 # ... config.i18n.available_locales = [: en,: ru] config.i18n.default_locale =: en 

Também conecte uma gem rails-i18n que tenha dados de localidade para diferentes idiomas . Por exemplo, ele traduz nomes de meses, regras de pluralização e outras coisas úteis.

 # Gemfile # ... gem 'rails-i18n' 

Basta instalar esta jóia e você é bom para ir:

 instalação do pacote 

Armazenando Traduções

Agora que tudo está configurado, vamos cuidar da home page e traduzir o texto lá.

A maneira mais simples de fazer isso é utilizando visualizações localizadas . Tudo o que você precisa fazer é criar index.LANG_CODE.html.erb chamados index.LANG_CODE.html.erb , onde o LANG_CODE corresponde a um dos idiomas suportados. Portanto, nesta demonstração, deveríamos criar duas visualizações: index.en.html.erb e index.ru.html.erb . No interior, basta colocar o conteúdo para a versão em inglês e russo do site, e o Rails selecionará automaticamente a visualização apropriada com base na localidade atualmente definida. Conveniente, eh?

Essa abordagem, no entanto, nem sempre é viável. Outra maneira seria armazenar suas strings traduzidas em um arquivo separado e renderizar uma versão apropriada da string com base no idioma escolhido. Por padrão, o Rails emprega arquivos YAML que devem ser armazenados no diretório config/locales . As traduções para diferentes idiomas são armazenadas em arquivos separados e cada arquivo é nomeado após esse idioma.

Abra a pasta config/locales e observe que já existe um arquivo en.yml dentro do qual há alguns dados de amostra:

 pt: ola: "Olá mundo" 

Então, en é uma chave de nível superior representando a linguagem para a qual essas traduções são. Em seguida, há um par de valores-chave aninhado, onde hello é a chave de tradução e Hello world é a sequência traduzida real. Vamos substituir este par pelo seguinte conteúdo:

 pt: bem-vindo: "Bem-vindo!" 

Esta é apenas uma mensagem de boas vindas da nossa página inicial. Agora crie um arquivo ru.yml na pasta config/locales e forneça também uma mensagem de boas-vindas traduzida:

 ru: bem-vindo: "????? ??????????!" 

Acabamos de criar uma tradução para nossa primeira string, o que é ótimo.

Executando Traduções Simples

Agora que preenchemos os arquivos YAML com alguns dados, vamos ver como empregar as sequências traduzidas nas visualizações. Na verdade, é tão simples quanto utilizar o método translate , que é aliado como t . Este método tem um argumento obrigatório: o nome da chave de tradução:

 <! - views / static_pages / index.html.erb -> <h1> <% = t 'bem-vindo'%> </ h1> 

Quando a página é solicitada, o Rails pesquisa a sequência que corresponde à chave fornecida e a renderiza. Se a tradução solicitada não puder ser encontrada, o Rails apenas renderizará a chave na tela (e a transformará em um formato mais legível por humanos).

As chaves de tradução podem ser nomeadas do jeito que você quiser (bem, quase tudo), mas é claro que é aconselhável dar a elas alguns nomes significativos para que você possa entender a que texto elas correspondem.

Vamos cuidar da segunda mensagem:

 pt: bem-vindo: "Bem-vindo!" services_html: "Nós fornecemos alguns serviços sofisticados para <em> pessoas boas </ em>." 
 ru: bem-vindo: "????? ??????????!" services_html: "?? ????????????? ????????? ?????? ??? <em> ??????? ????? </ em>." 

Por que precisamos deste _html postfix? Bem, como você pode ver, nossa string tem alguma marcação HTML e, por padrão, o Rails renderizará a tag em como texto simples. Enquanto não queremos que isso aconteça, marcamos a string como um “HTML seguro”.

Agora é só usar o método t novamente:

 <! - views / static_pages / index.html.erb -> <! - ... ---> <p> <% = t 'services_html'%> </ p> 

Mais sobre chaves de tradução

Nossa homepage agora está localizada, mas paremos por um momento e pensemos no que fizemos. Ao todo, nossas chaves de tradução têm nomes significativos, mas o que acontece se tivermos, digamos, 500 mensagens no aplicativo? Este número não é tão grande e grandes sites podem ter milhares de traduções.

Se todos os nossos pares de valores-chave estiverem armazenados sob a chave en (ou ru ) sem nenhum outro agrupamento, isso leva a dois problemas principais:

  • Precisamos ter certeza de que todas as chaves tenham nomes exclusivos. Isso se torna cada vez mais complexo à medida que seu aplicativo cresce.
  • É difícil localizar todas as traduções relacionadas (por exemplo, traduções para uma única página ou recurso).

Portanto, seria uma boa ideia agrupar ainda mais suas traduções sob chaves arbitrárias. Por exemplo, você pode fazer algo assim:

 pt: main_page: header: welcome: "Mensagem de boas vindas aqui" 

O nível de aninhamento não é limitado (mas você deve ser razoável sobre isso), e as chaves em grupos diferentes podem ter nomes idênticos.

É benéfico, no entanto, seguir a estrutura de pastas das suas visualizações (daqui a pouco veremos porquê). Portanto, ajuste os arquivos YAML da seguinte maneira:

 pt: static_pages: index: welcome: "Bem-vindo!" services_html: "Nós fornecemos alguns serviços sofisticados para <em> pessoas boas </ em>." 
 ru: static_pages: index: welcome: "????? ??????????!" services_html: "?? ????????????? ????????? ?????? ??? <em> ??????? ????? </ em>." 

Geralmente, você precisa fornecer o caminho completo para a chave de tradução ao referenciá-lo no método t :

 <! - views / static_pages / index.html.erb -> <h1> <% = t 'static_pages.index.welcome'%> </ h1> <p> <% = t 'static_pages.index.services_html' %> </ p> 

No entanto, há também uma pesquisa "preguiçosa" disponível. Se você executar a tradução em uma exibição ou controlador e as chaves de conversão forem nomeadas corretamente seguindo a estrutura de pastas, você poderá omitir os namespaces todos juntos. Desta forma, o código acima se transforma em:

 <! - views / static_pages / index.html.erb -> <h1> <% = t '.welcome'%> </ h1> <p> <% = t '.services_html'%> </ p> 

Note que o ponto principal é necessário aqui.

Vamos também traduzir nosso menu global e namespace as traduções corretamente:

 pt: global: menu: início: "Home" feedback: "Feedback" 
 ru: global: menu: casa: "???????" feedback: "??????" 

Neste caso, não podemos tirar proveito da pesquisa preguiçosa, portanto, forneça o caminho completo:

 <! - views / layouts / application.html.erb -> <! - ... ---> <nav> <ul> <li> <% = link_to t ('global.menu.home') , root_path%> </ li> <li> <% = link_to t ('global.menu.feedback'), caminho_novo_de_backback%> </ li> </ ul> </ nav> 

Traduzindo Modelos

Agora vamos para a página Feedback e cuidar do formulário. A primeira coisa que precisamos traduzir são os rótulos das entradas. Parece que o Rails nos permite fornecer traduções para os atributos do modelo, e eles serão utilizados automaticamente conforme necessário. Tudo o que você precisa fazer é namespace estas traduções corretamente:

 pt: activerecord: atributos: feedback: autor: mensagem "Seu nome": "Mensagem" 
 ru: activerecord: atributos: feedback: autor: "???? ???" message: "?????????" 

Os rótulos serão agora traduzidos automaticamente. Quanto ao botão "enviar", você pode fornecer tradução para o próprio modelo dizendo:

 pt: activerecord: models: feedback: "Feedback" 

Mas honestamente eu não gosto do texto "Criar Feedback" neste botão, então vamos ficar com uma palavra genérica "Enviar":

 pt: global: forms: submit: Enviar 
 ru: global: forms: submit: ????????? 

Agora utilize esta tradução:

 <! - views / feedbacks / _form.html.erb -> <! - ... ---> <% = form.submit t ('global.forms.submit')%> 

Mensagens de erro

Provavelmente não queremos que os visitantes postem mensagens de feedback vazias, portanto, forneça algumas regras de validação simples:

 # models / feedback.rb # ... valida: author, presence: true valida: message, presence: true, length: {minimum: 5} 

Mas e as mensagens de erro correspondentes? Como os traduzimos? Parece que não precisamos fazer nada, pois o rails-i18n gem já sabe como localizar erros comuns. Por exemplo, este arquivo contém mensagens de erro para o idioma russo. Se você realmente quiser ajustar as mensagens de erro padrão, em seguida, verificar o doc oficial que explica como conseguir isso.

Um problema com o formulário, no entanto, é que o subtítulo das mensagens de erro (o que diz que “ N erros impediram que este feedback seja salvo:”) não é traduzido. Vamos consertar isso agora e também falar sobre pluralização.

Regras de Pluralização

Enquanto potencialmente pode haver uma ou mais mensagens de erro, a palavra "erro" na legenda deve ser pluralizada de acordo. Em inglês, as palavras geralmente são pluralizadas adicionando-se um postfix “s”, mas para o russo as regras são um pouco mais complexas.

Eu já mencionei que o gem rails-i18n contém regras de pluralização para todos os idiomas suportados, então não precisamos nos incomodar em escrevê-los do zero. Tudo o que você precisa fazer é fornecer a chave apropriada para cada caso possível. Então, para o inglês existem apenas dois casos possíveis: um erro ou muitos erros (claro, não pode haver erros, mas neste caso a mensagem não será exibida).

 pt: global: formulários: enviar: enviar mensagens: erros: um: "Um erro impediu que este comentário fosse salvo" outro: "% {count} erros impediram que este feedback fosse salvo" 

A %{count} aqui é interpolação – pegamos o valor passado e o colocamos na string.

Agora cuide da localidade russa que tem mais casos possíveis:

 ru: globais: formas: enviar: mensagens ?????????: erros: um: "?? ??????? ????????? ????? ??????? ???? ??????:" alguns: "?? ??????? ????????? ????? ???????% {count} ??????:" muitos: "?? ??????? ????????? ?????! ???????% {count} ??????: "outro:" ?? ??????? ????????? ?????! ???????% {contagem} ??????: " 

Tendo isso em prática, basta utilizar esta tradução:

 <! - views / feedbacks / _form.html.erb -> <! - ... ---> <% = form_with (model: feedback, local: true) do | %> <% se feedback.errors.any %> <div id = "error_explanation"> <h2> <% = t 'global.forms.messages.errors', contagem: feedback.errors.count%> </ h2> <! - errors ... - > </ ul> </ div> <% end%> <! - campos de formulário -> <% end%> 

Note que neste caso nós passamos a chave de tradução assim como o valor da variável de count . Rails terá a variante de tradução adequada com base nesse número. Além disso, o valor da count será interpolado em cada espaço reservado %{count} .

Nossa próxima parada é a parcial de _feedback.html.erb . Aqui, precisamos localizar duas strings: “Posted by…” e datetime ( created_at field). Quanto a "Postado por …", vamos apenas utilizar a interpolação novamente:

 pt: global: feedback: posted_by: "Postado por% {author}" 
 ru: global: feedback: posted_by: "?????:% {author}" 
 <! - views / feedbacks / _feedback.html.erb -> <article> <em> <% = tag.time feedback.created_at, datetime: feedback.created_at%> <br> <% = t 'global.feedback .posted_by ', autor: feedback.author%> </ em> <p> <% = feedback.message%> </ p> <hr> </ article> 

Mas e quanto ao created_at ? Para cuidar disso, podemos aproveitar o método de localize alias como apenas l . É muito parecido com o de Ruby strftime , mas produz uma versão traduzida da data (especificamente, os nomes dos meses são traduzidas corretamente). Vamos usar um formato prédefinido chamado :long :

 <! - views / feedbacks / _feedback.html.erb -> <article> <em> <% = tag.time l (feedback.created_at, formato:: long), datetime: feedback.created_at%> <br> <% = t 'global.feedback.posted_by', autor: feedback.author%> </ em> <! --... -> </ article> 

Se você quiser adicionar seu próprio formato, também é possível, conforme explicado aqui .

Alternando entre localidades

Então, nosso aplicativo agora está totalmente traduzido… mas há uma coisa muito pequena: não podemos mudar o local! Venha para pensar sobre isso, este é realmente um grande problema, então vamos consertar isso agora.

várias maneiras possíveis de definir e persistir o local escolhido nas solicitações. Nós vamos ficar com a seguinte abordagem:

  • Nossas URLs terão um parâmetro opcional :locale , e elas parecerão http://localhost:3000/en/some_page
  • Se este parâmetro for definido e o local especificado for suportado, traduzimos o aplicativo no idioma correspondente
  • Se este parâmetro não estiver configurado ou a localidade não for suportada, defina uma localidade padrão

Soa simples? Então vamos mergulhar no código!

Primeiro de tudo, ajuste as routes.rb incluindo um scope :

 # config / routes.rb scope "(: locale)", locale: /#{I18n.available_locales.join("|")}/ faça # suas rotas aqui ... end 

Aqui estamos validando o parâmetro especificado usando um RegEx para garantir que a localidade seja suportada (observe que os caracteres âncora, como A não são permitidos aqui).

Em seguida, defina um before_action no ApplicationController para verificar e definir a localidade em cada solicitação:

 # application_controller.rb # ... before_action: set_locale def privado set_locale I18n.locale = extract_locale || I18n.default_locale end def extract_locale parsed_locale = parâmetros [: locale] I18n.available_locales.map (&: to_s) .include? (Parsed_locale)? parsed_locale: nil end 

Além disso, para persistir o código de idioma escolhido nas solicitações, defina as default_url_options :

 # application_controller.rb # ... private def default_url_options {locale: I18n.locale} end 

O vai incluir o parâmetro locale em cada link gerado com os helpers do Rails.

O último passo é apresentar dois links para alternar entre as localidades:

 <! - views / layouts / application.html.erb -> <! - ... -> <nav> <ul> <li> <% = link_to t ('global.menu.home'), root_path%> </ li> <li> <% = link_to t ('global.menu.feedback'), new_feedback_path%> </ li> </ ul> <ul> <li> <% = link_to 'português', root_path (locale:: en)%> </ li> <li> <% = link_to '???????', raiz_caminho (localidade:: ru)%> </ li> </ ul> </ nav> 

Como exercício, você pode tornar esses links mais sofisticados e, por exemplo, redirecionar o usuário de volta à página que ele estava navegando.

Simplifique sua vida com Lokalise

Até agora você provavelmente está pensando que o suporte a vários idiomas em um grande site é provavelmente uma dor. E, honestamente, você está certo. É claro que as traduções podem ser classificadas em um namespace e até divididas em vários arquivos YAML, se necessário, mas você ainda deve se certificar de que todas as chaves sejam traduzidas para cada localidade.

Felizmente, existe uma solução para este problema: a plataforma Lokalise, que torna o trabalho com os arquivos de localização muito mais simples . Deixe-me guiá-lo através da configuração inicial que não é realmente complexa.

  • Para começar, aproveite sua avaliação gratuita
  • Instale o Lokalise CLI que será usado para fazer upload e download de arquivos de tradução
  • Abra sua página de perfil pessoal , navegue até a seção "Tokens de API" e gere um token de leitura / gravação
  • Crie um novo projeto, dê um nome a ele e defina o inglês como um idioma base
  • Na página do projeto, clique no botão "Mais" e escolha "Configurações". Nesta página você deve ver o ID do projeto
  • Agora, a partir da linha de comando, execute lokalise --token <token> import <project_id> --lang_iso en --file config/locales/en.yml enquanto fornece seu token gerado e ID do projeto (no Windows você também pode precisar fornecer o caminho completo para o arquivo). Isso deve carregar a tradução em inglês para o Lokalise. Execute o mesmo comando para o idioma russo.
  • Volte para a página de visão geral do projeto. Você deve ver todas as suas chaves de tradução e valores lá. Claro, é possível editá-los, apagá-los e adicionar novos. Aqui você também pode filtrar as chaves e, por exemplo, encontrar as não estilizadas, o que é realmente conveniente.
  • Depois que você terminar de editar as traduções, faça o download delas executando lokalise --token <token> export <project_id> --type yaml --bundle_structure %LANG_ISO%.yml --unzip_to E:/Supreme/docs/work/lokalise/rails/SampleApp/config/locales/ . Ótimo!

O Lokalise tem muito mais recursos, incluindo suporte a dezenas de plataformas e formatos, capacidade de solicitar traduções de profissionais e até mesmo a possibilidade de fazer upload de capturas de tela para ler textos deles. Então, fique com o Lokalise e facilite a sua vida!

Conclusão

Neste artigo, discutimos exaustivamente como introduzir o suporte à internacionalização em aplicativos Rails e implementamos ele mesmo. Você aprendeu como e onde armazenar traduções, como pesquisá-las, quais são as visualizações localizadas, como traduzir mensagens de erro e coisas relacionadas ao ActiveRecord, além de como alternar entre as localidades e persistir a localidade escolhida entre a solicitação. Nada mal para hoje, né?

É claro que é impossível cobrir todos os detalhes do Rails I18n em um artigo e, portanto, recomendo verificar o guia oficial que fornece algumas informações mais detalhadas sobre o tópico e fornece exemplos úteis.