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.
- Autoria:
- Redação Token & Tech
- 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].astrorenderiza 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 porAccept-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-defaultapontando 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
getStaticPathse uma coleção comlocale,slugegroupquando 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
- Internationalization (i18n) Routing, Astro Docs. Acesso em 8 de outubro de 2026
- Add i18n features, Astro Docs. Acesso em 8 de outubro de 2026
- Routing, Astro Docs. Acesso em 8 de outubro de 2026
- Content collections, Astro Docs. Acesso em 8 de outubro de 2026
- Managing multi-regional and multilingual sites, Google Search Central. Acesso em 8 de outubro de 2026
- Tell Google about localized versions of your page, Google Search Central. Acesso em 8 de outubro de 2026
- Declaring language in HTML, W3C Internationalization. Acesso em 8 de outubro de 2026
- Indicating the language of a link destination, W3C Internationalization. Acesso em 8 de outubro de 2026