Desenvolvimento e IA Aplicada · Tutorial

Como criar um site internacional com Astro

Um site em vários idiomas começa por decisões de URL, não por tradução. Veja como o Astro organiza idiomas, quando usar o roteamento nativo e quando montar o seu.

Editor responsável:
Daniel Ferreira

Publicado em · 7 min de leitura

Também disponível em: English

Em três pontos

  • Cada idioma precisa de uma URL própria; o Google desaconselha redirecionar automaticamente pelo idioma ou pelo IP do visitante.
  • O roteamento i18n nativo do Astro resolve prefixos e links, mas exige a mesma estrutura de arquivos em todos os idiomas.
  • Para slugs traduzidos e páginas só onde há tradução real, uma rota dinâmica com getStaticPaths dá controle total.

Você tem um site em português e quer publicá-lo também em inglês. A primeira tentação é instalar um tradutor automático e seguir em frente. O problema é que buscadores e leitores precisam encontrar cada versão em um endereço estável, com o idioma bem declarado.

Este tutorial mostra como montar essa base com o Astro, um framework que gera sites a partir de componentes e, por padrão, entrega HTML estático. Os exemplos de código são mínimos e servem para ilustrar. Confira sempre a documentação da versão que você usa.

No fim, descrevemos como o próprio Token & Tech é construído, como um caso documentado. A parte de SEO internacional, com hreflang e x-default, está detalhada no texto irmão internacionalização e hreflang em projetos Astro.

Comece pela estrutura de URLs

Internacionalização (abreviada como i18n, porque há 18 letras entre o “i” e o “n”) é preparar o site para funcionar em vários idiomas. A primeira decisão é onde cada idioma vai morar.

O guia do Google sobre sites multirregionais e multilíngues compara quatro opções:

  • Domínio de país (exemplo.de): sinal geográfico claro, mas custo e infraestrutura maiores.
  • Subdomínio (de.exemplo.com): simples de configurar, mas o leitor pode não saber se “de” indica idioma ou país.
  • Subdiretório (exemplo.com/de/): fácil de manter, tudo em um único servidor.
  • Parâmetro de URL (exemplo.com?loc=de): o Google não recomenda.

Para a maioria dos projetos pequenos e médios, subdiretórios como /en/ e /pt-br/ são o caminho mais simples. É o modelo usado neste tutorial.

O mesmo guia faz duas recomendações que afetam todo o projeto. O Google recomenda usar URLs diferentes para cada versão de idioma, em vez de trocar o conteúdo por cookie ou configuração do navegador. E desaconselha redirecionar o visitante automaticamente com base em um palpite sobre o idioma dele, porque isso pode impedir pessoas e robôs de chegar a todas as versões. O texto lembra que o robô do Google costuma rastrear a partir dos Estados Unidos e sem enviar o cabeçalho Accept-Language, que informa o idioma preferido do navegador. A alternativa é oferecer links visíveis para as outras versões.

Opção 1: o roteamento i18n nativo do Astro

O Astro tem roteamento de internacionalização embutido. Você declara os idiomas e o idioma padrão no arquivo de configuração. Um exemplo:

// astro.config.mjs: exemplo mínimo
import { defineConfig } from "astro/config";

export default defineConfig({
  site: "https://exemplo.com",
  i18n: {
    locales: ["en", "pt-br"],
    defaultLocale: "en",
    routing: {
      prefixDefaultLocale: true,
    },
  },
});

Com prefixDefaultLocale: true, até o idioma padrão ganha prefixo. As páginas ficam em pastas com o mesmo nome dos idiomas:

src/pages/en/index.astro      → /en/
src/pages/en/about.astro      → /en/about/
src/pages/pt-br/index.astro   → /pt-br/
src/pages/pt-br/about.astro   → /pt-br/about/

Segundo a documentação, os nomes das pastas precisam coincidir exatamente com os valores de locales. Sem o prefixo no idioma padrão (o comportamento original), os arquivos desse idioma ficam na raiz de src/pages/.

O módulo astro:i18n oferece funções para montar links sem escrever caminhos à mão. Um exemplo:

---
// exemplo: link para a versão em português da página "about"
import { getRelativeLocaleUrl } from "astro:i18n";
const href = getRelativeLocaleUrl("pt-br", "about");
---
<a href={href} lang="pt-BR">Português (Brasil)</a>

Nas páginas, Astro.currentLocale informa o idioma da URL atual, inclusive em páginas estáticas. Já Astro.preferredLocale, que lê a preferência do navegador, a documentação associa a páginas renderizadas sob demanda, ou seja, geradas no servidor a cada acesso.

Há outras opções, cada uma com versão mínima indicada na documentação: fallback (mostrar outro idioma quando uma página falta, por redirecionamento ou reescrita), routing: "manual" (desliga o middleware de i18n e deixa a lógica com você) e domains (um domínio por idioma, que exige renderização no servidor e um adaptador).

O limite do modelo nativo

A documentação afirma que a estrutura de arquivos e a de URLs precisam ser iguais em todos os idiomas. Isso significa que /en/about/ e /pt-br/about/ usam o mesmo nome de arquivo. Se você quer /pt-br/sobre/, o roteamento nativo, sozinho, não resolve. O guia não trata de slugs traduzidos.

Outro ponto: a opção fallback pode criar páginas que redirecionam ou mostram o conteúdo de outro idioma. Em um site editorial, isso pode colocar texto em inglês sob uma URL em português. Decida conscientemente se quer esse comportamento.

Opção 2: rotas dinâmicas com slugs traduzidos

A própria documentação tem uma receita, Add i18n features, que não usa o roteamento nativo. Ela combina coleções de conteúdo, dicionários de interface e rotas dinâmicas, e mostra um mapa de rotas traduzidas.

Uma rota dinâmica é um arquivo com colchetes no nome. Segundo o guia de roteamento, um parâmetro de resto, como [...path], aceita caminhos de qualquer profundidade. No modo estático, todas as rotas precisam ser conhecidas no build, e por isso o arquivo exporta getStaticPaths().

Passo 1: guarde cada tradução como um registro

Em uma coleção de conteúdo, cada texto tem idioma, slug próprio, um identificador comum às traduções e um status. Exemplo:

// src/content.config.ts: exemplo mínimo
import { defineCollection } from "astro:content";
import { glob } from "astro/loaders";
import { z } from "astro/zod";

const posts = defineCollection({
  loader: glob({ pattern: "**/*.md", base: "./src/content/posts" }),
  schema: z.object({
    title: z.string(),
    locale: z.enum(["en", "pt-br"]),
    slug: z.string(),      // slug no idioma do texto
    group: z.string(),     // mesmo valor em todas as traduções
    status: z.enum(["draft", "published"]),
  }),
});

export const collections = { posts };

O campo group é o que liga /en/what-is-ai/ a /pt-br/o-que-e-ia/. Você vai precisar dele para o seletor de idioma e para o hreflang.

Passo 2: gere só as páginas que existem

---
// src/pages/[...path].astro: exemplo mínimo
import { getCollection, render } from "astro:content";

export async function getStaticPaths() {
  const posts = await getCollection("posts", (p) => p.data.status === "published");
  return posts.map((post) => ({
    params: { path: `${post.data.locale}/${post.data.slug}` },
    props: { post },
  }));
}

const { post } = Astro.props;
const { Content } = await render(post);
const htmlLang = post.data.locale === "pt-br" ? "pt-BR" : "en";
---
<html lang={htmlLang}>
  <head><title>{post.data.title}</title></head>
  <body><Content /></body>
</html>

O filtro em getCollection deixa rascunhos de fora. Se um texto só existe em português, nenhuma página em inglês é gerada para ele. Não há página vazia nem conteúdo de outro idioma no lugar.

Passo 3: declare o idioma no HTML

O W3C, consórcio que mantém padrões da web, recomenda sempre declarar o idioma no atributo lang da tag html. Trechos em outro idioma recebem seu próprio lang, como <span lang="en">. O mesmo texto desaconselha usar a meta Content-Language para isso e lembra o atributo dir para idiomas escritos da direita para a esquerda, como o árabe.

Passo 4: crie um seletor de idioma honesto

O seletor lista as versões que existem para aquela página, usando o group. Quando não houver tradução, leve o leitor para a página inicial daquele idioma, em vez de um link quebrado.

Escreva o nome de cada idioma por extenso, no próprio idioma. O W3C, em seu texto sobre como indicar o idioma de um link, recomenda normalmente evitar bandeiras, porque bandeiras representam países, e países não correspondem a idiomas.

Passo 5: resolva a raiz do site

O endereço / precisa levar a algum lugar. Em sites estáticos, a receita do Astro usa uma meta refresh para /en/. Se o seu servidor permitir, um redirecionamento fixo no servidor também funciona. Em ambos os casos, o destino deve ser sempre o mesmo, sem adivinhar o idioma do visitante.

Passo 6: hreflang e sitemap

Com o mapa de traduções pronto, cada página pode listar suas versões em outros idiomas no <head> e no sitemap. As regras do Google para isso são rígidas, como a exigência de que as páginas se citem mutuamente. Elas estão explicadas no artigo sobre hreflang. A documentação do Google sobre versões localizadas é a referência primária.

Qual opção escolher

Pela avaliação da redação, a escolha depende de duas perguntas:

  • Os slugs precisam ser traduzidos? Se /pt-br/about/ é aceitável, o roteamento nativo é mais simples e já vem com funções prontas.
  • Todas as páginas existem em todos os idiomas? Se não, prefira gerar páginas só onde há tradução, em vez de depender de fallback.

Nada impede misturar as abordagens. A receita do Astro, por exemplo, usa pastas por idioma junto com rotas dinâmicas.

Caso documentado: como o Token & Tech é estruturado

O site do Token & Tech, a publicação que você está lendo, é feito com Astro e segue a opção 2. A descrição a seguir vem da documentação técnica do próprio projeto.

  • Saída estática. O site é gerado como HTML no build, com barra final em todas as URLs.
  • Roteador central. Um único módulo monta a lista de todas as páginas. Cada página tem caminho, idioma, um identificador de grupo comum às traduções e a indicação se deve ser indexada. O arquivo src/pages/[...path].astro renderiza essa lista. Seletor de idioma, hreflang e sitemap leem a mesma lista, por isso não podem divergir.
  • Sem o roteador i18n nativo. A configuração do projeto registra que essa escolha foi feita para permitir slugs traduzidos por idioma e gerar páginas apenas onde existe tradução real.
  • Português na raiz. O português, idioma original, fica na raiz, sem prefixo; os outros idiomas usam prefixo, como /en/. Segmentos e slugs são traduzidos: a seção de desenvolvimento é /desenvolvimento-e-ia-aplicada/ em português e /en/building-with-ai/ em inglês. Os endereços antigos com /pt-br/ redirecionam de forma permanente para a raiz.
  • Nada de idioma emprestado. Um artigo só aparece em um idioma quando a tradução daquele idioma está publicada. Se não houver versão equivalente, o seletor leva à página inicial do idioma escolhido.
  • Raiz fixa, sem redirecionamento por idioma. O endereço / abre sempre a edição em português, sem decisão por IP ou por Accept-Language. Quando o navegador do visitante está em outro idioma publicado, uma barra discreta oferece a troca, e a escolha fica salva no aparelho.
  • hreflang recíproco. Cada página indexável lista todas as suas traduções indexáveis e um x-default apontando para a versão em português, no <head> e no sitemap.

Em outubro de 2026, inglês e português do Brasil estão no ar. Outros seis idiomas têm textos de interface e URLs preparados, mas desligados até revisão por falantes nativos.

O que levar deste texto

  • Decida a estrutura de URLs antes de traduzir. Subdiretórios por idioma são a opção mais simples de manter.
  • Use o roteamento i18n nativo do Astro quando os nomes de arquivo podem ser iguais em todos os idiomas.
  • Use getStaticPaths e uma coleção com locale, slug e group quando precisar de slugs traduzidos e de páginas só onde há tradução.
  • Declare o idioma com lang, ofereça links visíveis entre versões e não redirecione pelo idioma presumido do visitante.

Divulgação: O site do Token & Tech é construído com Astro e usa a arquitetura multilíngue descrita aqui.

Fontes

  1. Internationalization (i18n) Routing, Astro Docs. Acesso em 8 de outubro de 2026
  2. Add i18n features, Astro Docs. Acesso em 8 de outubro de 2026
  3. Routing, Astro Docs. Acesso em 8 de outubro de 2026
  4. Content collections, Astro Docs. Acesso em 8 de outubro de 2026
  5. Managing multi-regional and multilingual sites, Google Search Central. Acesso em 8 de outubro de 2026
  6. Tell Google about localized versions of your page, Google Search Central. Acesso em 8 de outubro de 2026
  7. Declaring language in HTML, W3C Internationalization. Acesso em 8 de outubro de 2026
  8. Indicating the language of a link destination, W3C Internationalization. Acesso em 8 de outubro de 2026

Leia também

Desenvolvimento e IA Aplicada · Explicador

Content Collections do Astro: conteúdo em escala

Quando um site passa de algumas páginas para centenas de textos, o problema deixa de ser escrever e passa a ser manter tudo consistente. Veja como o Astro resolve isso com coleções tipadas.

Desenvolvimento e IA Aplicada · Explicador

Astro: por que usar em sites com milhares de páginas

Quando um site tem milhares de páginas de conteúdo, cada decisão de arquitetura se multiplica. Veja, pela documentação oficial e por dados de campo datados, o que o Astro faz bem nesse cenário e onde estão os limites.

Desenvolvimento e IA Aplicada · Análise

Astro ou Next.js: arquitetura e desempenho comparados

Astro e Next.js geram HTML no servidor, mas partem de premissas diferentes: um nasceu para sites de conteúdo, o outro para aplicações React. Veja, com documentação oficial e dados de campo datados, o que isso muda na prática.