Rodei o bin/magento i18n:collect-phrases -o var/i18n/pt_BR.csv -m app/code/Roger e vieram duas mil linhas esperando tradução. A regra é uma só: a coluna 1 é a chave e não pode mudar nem um espaço. Mexeu nela, o Magento devolve o inglês na tela.
Por isso eu não abro esse arquivo em planilha. O Excel troca o separador, come aspas e estraga o %1 dos placeholders. Ler com fgetcsv, mexer só na coluna 2 e gravar num arquivo novo, nunca por cima do original.
O script que preserva a coluna de origem
<?php
function grava($out, array $lote): void {
// sua rotina de tradução, 50 frases por chamada
$traduzidas = traduzir(array_column($lote, 1));
foreach ($lote as $i => $linha) {
$linha[1] = $traduzidas[$i]; // só a coluna 2 muda
fputcsv($out, $linha, ',', '"', '');
}
}
$in = fopen('var/i18n/pt_BR.csv', 'r');
$out = fopen('var/i18n/pt_BR.novo.csv', 'w');
$lote = [];
while (($linha = fgetcsv($in, null, ',', '"', '')) !== false) {
if (count($linha) < 2) { continue; }
$lote[] = $linha;
if (count($lote) === 50) { grava($out, $lote); $lote = []; }
}
if ($lote) { grava($out, $lote); }
fclose($in); fclose($out);Repare que eu jogo a $linha inteira no fputcsv, não um array novo de dois campos. Com o -m o collect-phrases escreve terceira e quarta coluna com o tipo e o nome do módulo, e remontar a linha na mão faz elas sumirem. O %1 tem que continuar na tradução, na mesma ordem, senão o Magento lança exceção de argumento faltando.
O quinto argumento vazio não é enfeite
Aquele '' no fim do fgetcsv e do fputcsv desliga o caractere de escape. O padrão do PHP é a barra invertida, que não existe no CSV de verdade, e uma frase terminada em barra sai com as aspas desalinhadas. No PHP 8.4 omitir esse argumento já gera deprecated, então passe sempre.
cp var/i18n/pt_BR.novo.csv app/design/frontend/Roger/tema/i18n/pt_BR.csv
bin/magento cache:clean translate
bin/magento setup:static-content:deploy pt_BR -fCSV dentro do tema o Magento lê direto. Como language pack em app/i18n/Roger/pt_br/, o diretório ainda precisa de composer.json, registration.php e language.xml, senão ele nem abre o arquivo. Frase que continuar em inglês: compare a chave byte a byte com o texto dentro do __() no template, quase sempre é espaço duplo sobrando.