< BACK Caixas de tipos de impressão tipográfica vintage com fileiras de caracteres metálicos organizados por idioma, luz âmbar quente, profundidade de campo rasa

Astro i18n em 2026: O Setup Multilíngue Estático Que Rankeia

Na primavera passada um cliente me entregou um briefing que me teria feito gemer em 2022. Cinco idiomas. Onze mercados. Um dev. "Já temos WPML configurado", ele disse, com o tipo de confiança que te diz que ele nunca realmente olhou para seus scores de Lighthouse.

Destruí a instalação do WordPress no segundo dia. Reconstruí tudo no Astro. Oito semanas depois, três desses locales estavam na primeira página do Google. O quarto os seguiu dentro do mês. Essa experiência basicamente encerrou meu relacionamento com WordPress para trabalhos multilíngues.

Então. Aqui está exatamente o que faço agora.

Por Que Multilíngue WordPress Continua Falhando em Performance

Olha, eu não gosto de criticar WordPress. Seahawk construiu centenas de sites WP e provavelmente vamos construir centenas mais. Mas a lacuna de performance em projetos multilíngues é genuinamente difícil de defender.

WPML e Polylang funcionam gerando posts traduzidos na camada de banco de dados e roteando através de PHP. Cada requisição atinge o servidor. Mesmo com cache de página completa (WP Rocket, W3TC, ou o que você preferir), você ainda está lutando contra overhead de inicialização PHP, cache misses em páginas de locale com pouco tráfego, e gerenciamento de hreflang que se torna um pesadelo no momento em que você tem mais de dois idiomas.

Cronometrei um site WP com cinco idiomas de um cliente em 1,3 segundos de Time to First Byte na locale italiana. Mesmo conteúdo, reconstruído em Astro como HTML completamente estático: 94ms. Isso não é uma melhoria marginal. Isso é uma categoria diferente de produto.

Core Web Vitals são um fator de ranking confirmado, e arquivos estáticos servidos de um nó edge de CDN simplesmente vencem essa batalha toda vez., and static files served from a CDN edge node simply win that battle every time.

Astro's Built-in i18n Routing (And Why It Changed Everything)

Astro lançou roteamento i18n apropriado na v3 e o refinou significativamente na v4. No momento em que estamos operando em 2026, a API é estável e genuinamente agradável de trabalhar.

Aqui está a configuração básica que uso. Seu astro.config.mjs recebe um bloco i18n:astro.config.mjs gets an i18n block:

`` i18n: { defaultLocale: 'en', locales: ['en', 'de', 'fr', 'it', 'es'], routing: { prefixDefaultLocale: false } } `` i18n: { defaultLocale: 'en', locales: ['en', 'de', 'fr', 'it', 'es'], routing: { prefixDefaultLocale: false } } ``

Essa única configuração diz ao Astro para servir inglês em /, alemão em /de/, francês em /fr/, e assim por diante. Sem plugin. Sem pacote extra. Está embutido./, German at /de/, French at /fr/, and so on. No plugin. No extra package. It's baked in.

O prefixDefaultLocale: false é deliberado. A maioria do público principal dos meus clientes é anglófona, então eu não quero /en/ poluindo a estrutura canônica. Google consegue descobrir o relacionamento via hreflang.prefixDefaultLocale: false is deliberate. Most of my clients' primary audience is English-speaking, so I don't want /en/ polluting the canonical structure. Google can figure out the relationship via hreflang.

The File Structure That Actually Scales

É aqui que a maioria dos tutoriais erra. Eles mostram um pages/[locale]/index.astro simples e acham que terminaram. Tudo bem para um site institucional. Desaba no momento em que você tem 40 rotas.pages/[locale]/index.astro and call it done. Fine for a brochure. Falls apart the moment you have 40 routes.

O que eu faço no lugar disso:

`` src/ pages/ index.astro (English) about.astro (English) [locale]/ index.astro about.astro i18n/ en.json de.json fr.json it.json es.json `` src/ pages/ index.astro (English) about.astro (English) [locale]/ index.astro about.astro i18n/ en.json de.json fr.json it.json es.json ``

O diretório i18n/ contém suas strings de tradução como arquivos JSON simples. en.json é a fonte da verdade. Escrevo todas as outras localidades contra ele. Uma chave faltando em de.json? A página em alemão volta para o inglês para aquela string em vez de lançar um erro, porque adiciono uma pequena função utilitária t() que trata o fallback de forma elegante.i18n/ directory holds your translation strings as flat JSON files. en.json is the source of truth. I write every other locale against it. One key missing in de.json ? The German page falls back to English for that string rather than throwing an error, because I add a small t() utility function that handles fallback gracefully.

A Camada de Tradução: Mantenha Simples

Tentei Paraglide JS, i18next e alguns outros. Para builds estáticos do Astro minha recomendação honesta é: não superengenharia isso.

Um arquivo utilitário simples que importa JSON de localidade e retorna uma função getter é suficiente para 90% dos projetos. Paraglide JS do Inlang vale a pena considerar se você precisa de traduções type-safe e seu time é grande o suficiente para se importar com erros em tempo de compilação em chaves faltantes. Para trabalho solo ou pequenas agências, é overhead que você não precisa.Paraglide JS from Inlang is worth considering if you need type-safe translations and your team is large enough to care about compile-time errors on missing keys. For solo work or small agencies, it's overhead you don't need.

O que eu realmente me importo: manter os arquivos de tradução simples em vez de profundamente aninhados. "hero.cta.button.label" como chave soa organizado até você estar procurando por isso às 23h. "heroCTALabel" é feio mas encontrável."hero.cta.button.label" as a key sounds organised until you're searching for it at 11pm. "heroCTALabel" is ugly but findable.

Tratando Plurais e Strings Dinâmicas

A pluralização em alemão vai te humilhar. E o árabe também se você for para lá (uma vez ajudei um cliente a expandir para os Emirados e passei uma tarde inteira só com formatação de números).

Para plurais uso um pequeno helper inline:

`` export function plural(count, one, other) { return count === 1 ? one : other; } `` export function plural(count, one, other) { return count === 1 ? one : other; } ``

Crude? Sim. Mas cobre 95% dos casos para línguas europeias. Se você está indo para árabe, russo ou polonês, você precisa de regras de plural CLDR apropriadas. O projeto Unicode CLDR documenta estas exhaustivamente. Construa seu helper de plural contra essas especificações ou você vai acertar errado.The Unicode CLDR project documents these exhaustively. Build your plural helper against those specs or you will get it wrong.

hreflang: A Parte Que Todo Mundo Erra

Certo. É aqui que o SEO multilíngue realmente vive ou morre.

hreflang diz ao Google qual versão de uma página servir para qual locale. Se você errar e canibalize seus próprios rankings ou serve a versão em alemão para falantes de francês. Já vi ambos acontecerem em sites que herdei.

As regras:

  1. Cada página deve incluir tags hreflang para cada locale, incluindo a si mesma.
  2. As tags devem ser bidirecionais. Se /de/about referencia /fr/about, então /fr/about deve referenciar /de/about de volta./de/about references /fr/about , then /fr/about must reference /de/about back.
  3. Inclua uma tag x-default apontando para sua locale padrão.x-default tag pointing to your default locale.
  4. Use URLs absolutas. Sempre.

No Astro gero essas tags dentro de um componente BaseLayout.astro que toda página importa. Passo o caminho atual, faço loop sobre o array de locales, e gero as tags <link rel="alternate"> automaticamente. Sem manutenção manual.BaseLayout.astro component that every page imports. I pass in the current path, loop over the locale array, and spit out the <link rel="alternate"> tags automatically. No manual maintenance.

Não incluir a tag x-default é o erro mais comum que vejo. A documentação de hreflang do próprio Google é genuinamente clara sobre isso. Leia uma vez direito.x-default tag is the single most common mistake I see. Google's own hreflang documentation is genuinely clear on this. Read it once properly.

Sitemaps Específicos por Locale

Um sitemap com todos os locales misturados funciona. Mas sitemaps separados por locale (submetidos individualmente no Google Search Console) oferecem dados de indexação mais limpos e facilitam muito encontrar erros de crawl em um idioma específico.

Meu build no Astro gera cinco arquivos: sitemap-en.xml, sitemap-de.xml, e assim por diante. @astrojs/sitemap suporta isso com a opção i18n nativamente. Use.sitemap-en.xml , sitemap-de.xml , and so on. @astrojs/sitemap supports this with the i18n option out of the box. Use it.

Estratégia de Conteúdo para Sites Estáticos Multilíngues

Aqui está a coisa que a maioria dos desenvolvedores pula completamente: a tradução em si.

Você pode ter um setup Astro tecnicamente perfeito com hreflang impecável e um sitemap que faria um engenheiro do Google chorar de alegria. Se sua cópia em alemão foi passada pelo DeepL e nunca tocada por um humano, o site não vai rankear bem na Alemanha.

Não estou sendo preciosista sobre isso. Uso DeepL constantemente. É um excelente primeiro rascunho. Mas para qualquer página em que você realmente queira rankear, um falante nativo precisa ler. Não necessariamente reescrever. Só ler, corrigir os trechos estranhos, e confirmar que a fraseologia soa como algo que um alemão realmente diria.

A Seahawk teve um projeto fintech ano passado onde o cliente queria sete idiomas e um prazo de quatro semanas. Usamos DeepL como base, depois contratamos sete freelancers via Gengo para revisão em aproximadamente £0,04 por palavra. O orçamento total de tradução ficou abaixo de £800. Esse site agora classifica nos cinco primeiros em quatro desses mercados. A economia funciona.

Metadados Específicos da Localidade

Title tags e meta descriptions também precisam ser localizados. Não apenas traduzidos. "Best accounting software" não mapeia de busca direto para o equivalente em alemão porque os volumes de palavras-chave diferem.

Mantenho metadados específicos da localidade nos arquivos de tradução JSON sob um namespace meta dedicado. O template da página puxa de t('meta.title') e faz fallback para um padrão apenas se a chave está faltando. Isso significa que um especialista em SEO pode editar um arquivo JSON por localidade sem nunca tocar no código.meta namespace. The page template pulls from t('meta.title') and falls back to a default only if the key is missing. This means an SEO can edit one JSON file per locale without ever touching the code.

Deployment: Onde Hospedar um Site Estático com 5 Idiomas

Vercel e Netlify lidam bem com builds estáticos do Astro. Mas especificamente para multilíngue, você quer cache de borda por localidade e a capacidade de definir headers específicos da localidade.

Padronizei em Cloudflare Pages. A camada gratuita cobre a maioria dos orçamentos dos clientes. As requisições são servidas do node de borda mais próximo globalmente, o que importa enormemente quando sua audiência italiana está sendo servida por arquivos de um data centre na Virgínia. A rede Cloudflare tem nodes em Milão, Frankfurt, Amsterdam. A diferença em TTFB para visitantes europeus é mensurável.

Uma coisa a configurar explicitamente: headers Cache-Control. Arquivos estáticos devem ter TTLs de cache longos (max-age=31536000, immutable para assets com hash). Páginas HTML devem ter TTLs mais curtos ou uma política stale-while-revalidate, particularmente se você está fazendo builds incrementais após atualizações de conteúdo.Cache-Control headers. Static files should have long cache TTLs ( max-age=31536000, immutable for hashed assets). HTML pages should have shorter TTLs or a stale-while-revalidate policy, particularly if you're doing incremental builds after content updates.

Debugando Builds i18n Antes de Irem ao Vivo

Algumas coisas que verifico em cada build Astro multilíngue antes de fazer push para produção:

  • Execute astro build localmente e inspecione a saída dist/. Cada pasta de locale deve ter todas as páginas. Se uma rota estiver faltando, a etapa de build é o lugar para detectar.astro build locally and inspect the dist/ output. Every locale folder should have every page. If a route is missing, the build step is the place to catch it.
  • Use Screaming Frog para rastrear o deploy de preview. Filtre por hreflang. Páginas órfãs (páginas não referenciadas por nenhuma tag hreflang) aparecem imediatamente.Screaming Frog to crawl the preview deployment. Filter by hreflang. Any orphaned pages (pages not referenced by any hreflang tag) show up immediately.
  • Verifique o canonical x-default no DevTools do navegador em pelo menos a homepage e uma página interna.x-default canonical in browser DevTools on at least the homepage and one inner page.
  • Visite manualmente uma URL de locale em um navegador configurado para o idioma daquele locale. Parece óbvio. Ainda assim detecta problemas.

O problema mais comum que enfrento em tempo de build é esquecer de adicionar um novo locale ao array locales no astro.config.mjs depois de adicionar o arquivo JSON. O build não gera erro. Apenas ignora silenciosamente o novo locale. Frustrante.astro.config.mjs locales array after adding the JSON file. The build doesn't error. It just silently ignores the new locale. Annoying.

FAQ

O i18n do Astro funciona com server-side rendering (SSR) ou apenas com saída estática?

Ambos. O roteamento i18n do Astro funciona também em modo SSR. As funções auxiliares getRelativeLocaleUrl() e getAbsoluteLocaleUrl() funcionam de forma idêntica independentemente do modo de saída. Dito isso, se você for totalmente estático, os benefícios de performance são significativamente maiores porque você está servindo HTML pré-construído sem custo de runtime.getRelativeLocaleUrl() and getAbsoluteLocaleUrl() helper functions work identically regardless of output mode. That said, if you're going fully static, the performance benefits are significantly greater because you're serving pre-built HTML with no runtime cost.

Como faço para lidar com detecção de locale e redirecionamentos para visitantes novos?

Para sites estáticos faço isso na camada CDN/edge, não dentro do Astro. Cloudflare Workers pode ler o header Accept-Language e redirecionar para o caminho de locale apropriado. Não faça isso dentro da sua app Astro porque adiciona um round trip e torna a saída estática não-determinística.Accept-Language header and redirect to the appropriate locale path. Don't do this inside your Astro app because it adds a round trip and makes the static output non-deterministic.

Qual é a melhor forma de gerenciar arquivos de tradução conforme o site cresce?

Mantenha-os em controle de versão junto com o código, não em um CMS separado ou em um sistema de gerenciamento de tradução de terceiros, até que o projeto realmente justifique. No momento em que você tiver mais de três idiomas e uma equipe editorial ativa, considere Tolgee ou Localazy. Ambos se integram com arquivos JSON planos e têm sincronização com GitHub. Abaixo desse limite, um documento Notion compartilhado e atualizações JSON manuais funciona perfeitamente.Tolgee or Localazy. Both integrate with flat JSON files and have GitHub sync. Below that threshold, a shared Notion doc and manual JSON updates is honestly fine.

Posso usar Astro Content Collections com i18n?

Sim, e é minha abordagem preferida para sites focados em blog. Estruture sua coleção de conteúdo com o idioma como um segmento de caminho: src/content/blog/en/, src/content/blog/de/. Depois filtre por idioma em sua função getStaticPaths(). Cada idioma recebe seu próprio HTML estático no momento do build. Limpo, rápido, e a equipe editorial pode gerenciar arquivos markdown sem tocar em nenhum componente.src/content/blog/en/, src/content/blog/de/. Then filter by locale in your getStaticPaths() function. Each locale gets its own static HTML at build time. Clean, fast, and the editorial team can manage markdown files without touching any components.

Ter múltiplas páginas de idiomas prejudica o SEO através de conteúdo duplicado?

Não, desde que sua implementação de hreflang esteja correta. Google trata variantes de idioma corretamente anotadas como páginas relacionadas mas distintas, não como duplicatas. O risco vem de uma implementação incorreta de hreflang, particularmente anotações recíprocas ausentes ou tratamento incorreto de x-default. Acerte essas duas coisas e conteúdo duplicado não é uma preocupação.incorrectly implementing hreflang, particularly missing reciprocal annotations or incorrect x-default handling. Nail those two things and duplicate content is not a concern.

---

A transição de sites multilíngues renderizados dinamicamente em PHP para sites pré-construídos estaticamente não é realmente sobre Astro especificamente. É sobre aceitar que a maioria do conteúdo de tradução não muda hora a hora. Construa uma vez, cache em todos os lugares, deixe o CDN fazer o trabalho pesado. Astro apenas torna esse fluxo genuinamente agradável em vez de uma luta contra o framework.

Cinco idiomas, um desenvolvedor, oito semanas. As ferramentas estão aí. Use-as.

< BACK