Traduções no Magento 2: CSV i18n, language packs e strings JS
O Magento 2 tem um sistema de tradução completo, mas as strings vêm de várias fontes com uma ordem de prioridade, e as strings de JavaScript seguem um caminho diferente das de PHP. Quando uma frase continua em inglês numa loja traduzida, o motivo quase sempre é uma de poucas causas conhecidas.
Este artigo explica onde ficam as traduções no Magento 2.4.8, como elas se sobrepõem, como traduzir strings em PHP, templates, JavaScript e templates Knockout, e como depurar as que se recusam a mudar.
Como o locale é escolhido
Cada store view tem um locale, definido em Stores, Configuration, General, General, Locale Options. O config path é general/locale/code, com valores como en_US, pt_BR ou de_DE. O painel admin usa o locale do usuário admin, definido nas configurações da conta dele.
O Magento então monta um dicionário para esse locale juntando as fontes de tradução. Lojas com vários idiomas normalmente rodam uma store view por idioma. O artigo sobre hreflang e SEO multilíngue cobre o lado de SEO desse setup.
Onde ficam as traduções
São quatro fontes, da menor para a maior prioridade:
- Dicionários de módulo em
app/code/Vendor/Module/i18n/pt_BR.csvou na mesma pasta de um módulo do Composer. - Language packages, como
app/i18n/Vendor/pt_br/ou um pacote do Composer do tipomagento2-language. - Dicionários de theme em
app/design/frontend/Vendor/theme/i18n/pt_BR.csv, incluindo os herdados de themes pai. - Traduções no banco, na tabela
translation, gravadas pela inline translation.
Uma frase definida em mais de um lugar usa o valor da fonte de maior prioridade. Por isso o theme é o lugar certo para textos específicos de uma storefront, e um language package é o lugar certo para uma tradução completa compartilhada entre projetos.
O formato do CSV
Os dicionários são arquivos CSV com a frase original na primeira coluna e a tradução na segunda:
"Add to Cart","Adicionar ao carrinho"
"%1 item(s) in your cart","%1 item(s) no seu carrinho"
"Shipping & Handling","Frete e manuseio"
A correspondência é exata e diferencia maiúsculas de minúsculas, incluindo pontuação e espaços. Placeholders como %1 precisam continuar na tradução. Coloque aspas em todos os campos e escape aspas duplas dentro de um campo dobrando-as. Salve o arquivo em UTF-8.
Language packages
Um language package é um componente pequeno com um language.xml e um registration.php:
<?xml version="1.0"?>
<language xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:App/Language/package.xsd">
<code>pt_BR</code>
<vendor>vendor</vendor>
<package>pt_br</package>
</language>
<?php
declare(strict_types=1);
use Magento\Framework\Component\ComponentRegistrar;
ComponentRegistrar::register(
ComponentRegistrar::LANGUAGE,
'vendor_pt_br',
__DIR__
);
O pacote então guarda um pt_BR.csv com todas as frases. Packs de tradução da comunidade para vários idiomas são distribuídos assim, e você pode instalar um com Composer e sobrescrever no theme só o que precisar.
Traduzindo strings no código
PHP e templates
Envolva toda string visível ao usuário em __(), que retorna um Magento\Framework\Phrase traduzido quando convertido para string:
$message = __('You added %1 to your shopping cart.', $productName);
Nos templates, traduza e escape juntos:
<h2><?= $escaper->escapeHtml(__('Delivery information')) ?></h2>
Passe valores dinâmicos como argumentos, nunca concatenando na frase. __('Hello ' . $name) gera uma frase diferente para cada nome, então nenhuma entrada do dicionário consegue casar com ela.
Layout XML
Argumentos string no layout XML podem ser traduzidos com translate="true", e muitos atributos de label em system.xml, widget.xml e no XML de UI components aceitam o mesmo atributo.
JavaScript
Em módulos RequireJS, use mage/translate:
define([
'jquery',
'mage/translate'
], function ($, $t) {
'use strict';
return function () {
alert($t('Please select a delivery date.'));
};
});
$.mage.__() faz a mesma coisa e é comum em código mais antigo.
Templates Knockout
Nos templates .html de UI components, use o binding i18n ou o atributo translate:
<span data-bind="i18n: 'Order Summary'"></span>
<span translate="'Order Summary'"></span>
Os dois precisam da frase como string literal dentro do binding.
Como as traduções de JavaScript chegam ao navegador
As strings de PHP são traduzidas no servidor na hora de renderizar. As strings de JavaScript são traduzidas no navegador, usando um arquivo de dicionário, o js-translation.json, gerado por theme e locale durante o static content deploy. O Magento o monta varrendo os arquivos JavaScript e os templates HTML atrás de chamadas de tradução com strings literais, e inclui só as frases que encontra e que também têm tradução nos dicionários.
Isso explica a maioria dos problemas de "minha string de JavaScript não está traduzida":
- A frase é montada a partir de variáveis, então o scanner não consegue encontrá-la.
- O static content não foi publicado de novo para aquele locale depois de adicionar a tradução.
- O navegador ainda tem um
js-translation.jsonantigo em cache no local storage. Limpe o local storage do site e recarregue.
A estratégia é definida por dev/js/translate_strategy, com dictionary como padrão e embedded como alternativa, que grava as traduções direto nos arquivos JavaScript publicados. A estratégia dictionary é a escolha de costume.
Coletando as frases
Para gerar um CSV com todas as frases do seu código que precisam de tradução:
bin/magento i18n:collect-phrases -o app/code/Vendor/Module/i18n/en_US.csv app/code/Vendor/Module
Isso varre PHP, templates, layout, JavaScript e templates HTML daquela pasta. Entregue o arquivo aos tradutores e depois salve a versão deles como pt_BR.csv ou o locale correspondente. Para coletar as frases da instalação inteira e montar um language package completo, use a flag --magento com o caminho raiz. Gerar um pack completo com i18n:pack serve mais para quem mantém language packages.
Inline translation
A inline translation deixa um usuário admin editar frases direto na storefront. Ative em Stores, Configuration, Advanced, Developer, Translate Inline, que só aparece em developer mode. As alterações são gravadas na tabela translation e vencem qualquer CSV.
É útil para um conserto rápido num staging, mas espalha traduções no banco, onde code review e controle de versão não enxergam. Não ative em produção. Se alguém ativou, exporte a tabela translation, mova as entradas para o CSV do theme e limpe a tabela.
Aplicando as mudanças
bin/magento cache:clean translate full_page block_html
bin/magento setup:static-content:deploy pt_BR en_US -f
Traduções do lado PHP só precisam da limpeza do cache de tradução. Traduções de JavaScript também precisam do static content deploy do locale. Em production mode, rode o static content deploy de todos os locales em uso como parte do build normal. O artigo sobre estratégias de static content deploy mostra como manter isso rápido com vários locales.
Depurando uma frase que não traduz
- Confirme que o locale da store view é o que você espera.
- Ache a frase de origem exata no código, com maiúsculas, espaços e pontuação, e compare com o seu CSV.
- Verifique se o código realmente chama
__()ou$t(). Alguns templates de terceiros imprimem inglês fixo no código. - Procure uma fonte de maior prioridade com valor diferente, principalmente a tabela
translation. - Para JavaScript, procure a frase no
js-translation.jsonpublicado. - Limpe o cache de tradução e, no caso de JavaScript, faça o static content deploy e limpe o local storage.
Para dicionários no nível do theme e como os themes os herdam, veja o guia de child theme.
FAQ
As traduções devem ficar no módulo ou no theme?
Entregue as traduções padrão junto com o módulo dono das strings. Coloque os textos específicos da loja no theme, onde eles sobrescrevem as traduções do módulo e do language package.
Por que uma frase é traduzida em PHP mas não em JavaScript?
O JavaScript usa o js-translation.json, que é gerado durante o static content deploy. Faça o static content deploy do locale de novo e limpe o local storage do navegador.
A mesma frase pode ter traduções diferentes em módulos diferentes?
Na prática, não. O dicionário de um locale é compartilhado, então uma frase de origem acaba com uma única tradução na storefront. Use uma frase de origem diferente se precisar de textos diferentes.
Inline translation é seguro em produção?
Não. Ele grava traduções no banco fora do controle de versão e adiciona marcação de edição às páginas. Use só em ambientes de desenvolvimento ou staging.
Atendo como freelancer em Magento e Adobe Commerce, incluindo setups de lojas com vários idiomas. Se você precisa de ajuda com isso, fale comigo.
Artigos relacionados
Core Web Vitals no Magento 2: como corrigir LCP, INP e CLS
Onde lojas Magento 2 com Luma perdem nos Core Web Vitals e como corrigir LCP, INP e CLS: resposta do servidor, CSS, fontes, imagens, galeria e scripts de terceiros.
Static content deploy no Magento 2: estratégias e opções
Acelere o setup:static-content:deploy no Magento 2: estratégias standard, quick e compact, jobs em paralelo, escopo de temas e locales e builds sem banco.
Layout XML do Magento 2: guia prático para desenvolvedores
Como o layout XML do Magento 2 funciona: handles, containers, blocks, move e remove, arguments, view models e page layouts, com exemplos que valem no 2.4.8.
Plano de migração do Luma para o Hyvä: auditoria, fases e lançamento
Plano de migração do Luma para o Hyvä no Magento 2: auditoria de extensões e tema, decisão de checkout, reconstrução página a página, paridade de SEO, testes e rollback.