Pular para o conteúdo

WordPress bilíngue sem plugin: como fiz o prefixo /en com rewrite rules

por Lucas Almeida 6 min de leitura

O DigitalRadar é bilíngue desde o começo, mas até outubro o idioma vinha de um parâmetro na URL: ?lang=en, guardado em cookie. Funcionava para quem navegava — e não funcionava para o Google. Robô não carrega cookie. Para o buscador existia uma URL só, com dois conteúdos possíveis, o que é a definição de conteúdo duplicado.

A correção foi colocar o site em inglês sob o prefixo /en, sem instalar plugin de tradução. Este post é o caminho que segui — incluindo a abordagem que parece óbvia e quebra o site.

O que o prefixo precisa entregar

/                      → home PT        /en/                   → home EN
/slug-do-post/         → post PT        /en/english-slug/      → post EN
/category/noticias/    → arquivo PT     /en/category/noticias/ → arquivo EN

Com três condições inegociáveis: paginação funcionando nos dois lados, canonical apontando para a URL certa, e as URLs antigas redirecionando sem perder o que já estava indexado.

A abordagem que quebra (e é a mais comum)

O atalho natural é interceptar a requisição cedo, cortar o /en da REQUEST_URI e deixar o WordPress processar o resto normalmente:

// NÃO faça isso
$_SERVER['REQUEST_URI'] = preg_replace('#^/en#', '', $_SERVER['REQUEST_URI']);

Funciona nos primeiros cinco minutos e quebra em três lugares depois:

  • Paginação. paginate_links() monta os links a partir da REQUEST_URI. Se você a modificou, a página 2 de um arquivo EN aponta para a URL sem prefixo.
  • $wp->request. Muitos temas e plugins usam essa propriedade para montar o canonical. Sem o prefixo, o canonical da página EN aponta para a PT.
  • redirect_canonical. O WordPress compara a URL pedida com a que ele considera correta e, achando divergência, redireciona — tirando justamente o seu prefixo.

A abordagem que funciona: duplicar as regras

Em vez de mexer na URL que chega, eu ensino o WordPress a entender URLs com prefixo. Cada regra de rewrite ganha uma cópia prefixada que injeta uma query var de idioma:

function dr_rewrite_lang_rules( $rules ) {
    $novas = array();
    foreach ( $rules as $regex => $query ) {
        $prefixada = 'en/' . ltrim( $regex, '^' );
        if ( 0 === strpos( $query, 'index.php?' ) ) {
            $novas[ $prefixada ] = 'index.php?dr_lang=en&'
                . substr( $query, strlen( 'index.php?' ) );
        }
    }
    $novas['en/?$'] = 'index.php?dr_lang=en';   // home do idioma
    return $novas + $rules;                      // prefixadas primeiro
}
add_filter( 'rewrite_rules_array', 'dr_rewrite_lang_rules' );

Três detalhes fazem isso funcionar:

  1. Nenhum grupo de captura novo. Só o prefixo muda no regex, então as referências $matches[1], $matches[2] das queries continuam alinhadas.
  2. A ordem interna é preservada. O foreach percorre as regras na ordem original, então a hierarquia que o WordPress montou continua valendo dentro do bloco prefixado.
  3. $novas + $rules. A união de arrays em PHP mantém as chaves do primeiro, colocando as regras prefixadas — mais específicas — antes das originais.

E a regra da home do idioma (en/?$) vai depois do laço, para ficar por último entre as prefixadas: ela é a mais genérica e não pode capturar /en/qualquer-coisa.

Links: quem segue o conteúdo e quem segue a requisição

Essa distinção é o que evita link quebrado. O permalink de um post segue o idioma do conteúdo, não o da visita — um post inglês tem /en mesmo que você esteja navegando em português:

add_filter( 'post_link', function ( $url, $post ) {
    return 'en' === dr_post_lang( $post ) ? dr_prefix_url( $url ) : $url;
}, 10, 2 );

Já arquivos de categoria e tag existem nos dois idiomas na mesma rota, então seguem o idioma da requisição. O mesmo vale para home_url() — com cuidado, porque ela é usada também pelo REST, pelo login e pelos feeds. No meu filtro, ficam de fora as chamadas de wp-json, wp-admin, wp-login, feeds, arquivos com extensão e — detalhe que custou um tempo até eu notar — URLs com âncora, para os @id do JSON-LD continuarem iguais nos dois idiomas.

Os 301, que é onde o tráfego se perde

Três redirecionamentos garantem uma URL por conteúdo:

  • ?lang=en (esquema antigo) → a URL limpa com /en;
  • post em inglês aberto sem prefixo → /en/english-slug/;
  • post em português aberto sob /en → URL limpa.

Uma armadilha aqui: se a home for uma página estática marcada como português, a regra do segundo item manda /en/ de volta para / e a home inglesa desaparece. O guard é simples e obrigatório:

if ( is_singular() && ! is_front_page() ) { /* ... */ }

E, para o redirect_canonical do WordPress não derrubar o prefixo, cancelo o redirecionamento quando ele tenta remover o /en.

O que o prefixo resolveu de graça

Dois ganhos que eu não tinha planejado. O cache ficou mais simples: antes era preciso configurar o LiteSpeed para variar a cópia por cookie de idioma; agora cada idioma tem a própria URL e o cache funciona sozinho. E o hreflang na home e nos arquivos passou a ser automático — a mesma rota com e sem /en é o par, sem depender de vínculo manual entre posts.

Perguntas frequentes

Dá para fazer site bilíngue no WordPress sem plugin?

Dá, se você aceita manter um post por idioma e ligar os dois por um campo personalizado. O trabalho está nas URLs, no hreflang e no filtro das listagens — não na tradução em si.

Por que não reescrever a REQUEST_URI?

Porque paginação, $wp->request e o canonical do WordPress são montados a partir dela. Alterá-la faz esses três apontarem para a URL sem prefixo.

Preciso regravar as rewrite rules depois de mexer nelas?

Sim. As regras ficam salvas no banco. Ou você abre Configurações → Links Permanentes e salva, ou chama flush_rewrite_rules() uma vez, controlado por uma constante de versão.

O slug do post em inglês pode ser igual ao do português?

Não deve. O prefixo separa as rotas, mas slug próprio em inglês é o que faz a página ranquear para a busca em inglês — que é o motivo de existir a versão traduzida.

No DigitalRadar, o motor bilíngue é código próprio e a gente conta como ele funciona. Fique no radar.

Lucas Almeida
Redação DigitalRadar

Desenvolvedor e fundador da Elion Estúdio, Lucas Almeida é especialista em tecnologias emergentes, games e análise de mercado. Com experiência em SEO e growth marketing, foca em conteúdo aprofundado que conecta o universo da tecnologia com o dia a dia das pessoas.

Apurado a partir das fontes citadas e verificado antes da publicação. Veja a nossa Política Editorial.

Deixe seu comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *