Internacionalização
Sirva sua documentação em vários idiomas com roteamento sensível ao locale, navegação por idioma, interface traduzida e SEO — tudo baseado em convenções.
O Blume serve um projeto em muitos idiomas. Coloque os arquivos traduzidos no lugar certo e o Blume cuida do roteamento, do seletor de idiomas, da navegação por locale e do SEO para você — não há uma camada de roteamento separada para manter. É opcional: sem um bloco i18n, seu site continua em um único idioma exatamente como antes. Também se combina com o versionamento — um snapshot congelado mantém suas traduções, e o fallback de locale funciona dentro de cada versão.
Ative o recurso
Adicione um bloco i18n listando seus locales e qual deles é o padrão:
i18n: {
defaultLocale: "en",
locales: [
{ code: "en", label: "English" },
{ code: "fr", label: "Français" },
{ code: "ar", label: "العربية", dir: "rtl" },
],
}
Cada locale tem um code (usado nas URLs), um label (exibido no seletor de idiomas) e um dir opcional para escritas da direita para a esquerda ("ltr" por padrão). Um style opcional dá ao blume translate orientações livres para o locale — registro, dialeto, terminologia, por exemplo "Brazilian Portuguese, informal você" — de modo que a escolha fica fixada desde a primeira tradução, em vez de ser decidida pelo agente.
Organize o conteúdo traduzido
O locale padrão fica na raiz do seu conteúdo. Todos os outros locales são pastas de nível superior nomeadas pelo respectivo code, espelhando a estrutura padrão:
docs/
index.mdx -> /
guides/quickstart.mdx -> /guides/quickstart
fr/
index.mdx -> /fr
guides/quickstart.mdx -> /fr/guides/quickstart
ar/
index.mdx -> /ar
| Arquivo | Rota |
|---|---|
docs/index.mdx |
/ |
docs/guides/quickstart.mdx |
/guides/quickstart |
docs/fr/guides/quickstart.mdx |
/fr/guides/quickstart |
Não dê ao locale padrão uma pasta própria: apenas os outros códigos são pastas de locale, então docs/en/ com en como padrão é conteúdo comum, publicado em /en/… (e em /fr/en/… como fallback em francês). O Blume emite um aviso quando encontra uma pasta assim.
Você traduz apenas os arquivos que quiser — todo o resto recorre automaticamente ao fallback (veja Fallbacks).
Sufixos de nome de arquivo
Prefere manter as traduções ao lado do original? Defina parser: "dot" e nomeie os arquivos com um sufixo de locale em vez de usar pastas:
docs/
guides/quickstart.mdx -> /guides/quickstart (default)
guides/quickstart.fr.mdx -> /fr/guides/quickstart (French)
Bom para traduções esparsas — coloque as poucas páginas que você traduziu junto às originais sem espelhar a árvore inteira.
Arquivos compartilhados
Para conteúdo que é igual em todos os idiomas — um changelog, uma página de status — adicione um marcador $ para que um único arquivo sirva a todos os locales sem duplicação:
docs/changelog.$.mdx -> /changelog and /fr/changelog (same content)
docs/guides/meta.$.ts (folder meta applied to every locale)
Um meta.ts específico de um locale ainda substitui o compartilhado para aquele idioma.
URLs do locale padrão
Por padrão, o locale padrão não tem prefixo de URL (/, /guides/quickstart), enquanto os outros locales são prefixados (/fr/…). Isso mantém limpas as URLs do seu idioma principal. Para prefixar todos os locales, incluindo o padrão:
i18n: {
// …
hideDefaultLocalePrefix: false, // /en/…, /fr/…
}
Navegação por locale
Cada idioma ganha sua própria barra lateral, construída a partir dos arquivos daquele locale — então as traduções podem divergir em estrutura, ordenação ou rótulos. Os arquivos meta.ts de pasta também são resolvidos por locale: com o parser dir padrão, coloque um meta.ts em fr/guides/ para ordenar o grupo em francês de forma independente. Até que um locale tenha o seu (ou um meta.$.ts compartilhado), o grupo dele espelha o do locale de fallback — o meta.ts daquela pasta e o sidebar.display definido pela página de índice dela —, de modo que as páginas que recorrem ao fallback mantêm os mesmos títulos, a mesma ordem e os mesmos grupos recolhíveis. Com o parser dot, as traduções ficam ao lado das originais, portanto o meta.ts de uma pasta se aplica a todos os locales. Todo o resto da navegação funciona da mesma forma, por idioma.
As abas do cabeçalho, os links do cabeçalho e o rodapé são configurados, e não derivados do conteúdo, então seus rótulos são localizados em blume.config.ts. Todo rótulo configurado aceita um mapa por locale ({ en: "Docs", fr: "Documentation" }) além da forma de string simples, recorrendo à entrada do locale padrão para os locales que você não preencheu: rótulos de abas e de menus suspensos (Abas), links em destaque, ações do cabeçalho e a chamada para ação, links do rodapé, o texto do banner e do link dele, e os clientes personalizados do menu Conectar ao MCP.
navigation: {
cta: {
href: "https://acme.dev/signup",
label: { en: "Start free", fr: "Essai gratuit" },
},
},
footer: {
links: [
{ label: { en: "Pricing", fr: "Tarifs" }, href: "https://acme.dev/pricing" },
],
},
Os caminhos das abas, os links do cabeçalho e do rodapé e o link do logo do cabeçalho também passam para o locale do leitor sempre que esse locale serve a rota, de modo que os elementos da interface permanecem dentro de um único idioma. Uma rota servida apenas pelo locale padrão — uma página personalizada ou o índice gerado do changelog — mantém o próprio caminho em vez de apontar para uma URL localizada que daria 404.
Fallbacks
Quando uma página ainda não foi traduzida, o Blume renderiza o conteúdo do locale de fallback na URL localizada — assim o link funciona, a página é totalmente pré-renderizada e os motores de busca não são levados a um beco sem saída. O fallback usa por padrão o seu defaultLocale:
i18n: {
// …
fallbackLocale: "en", // default; set to null to 404 instead
}
Páginas de fallback apontam o link canônico para a página que copiam, são excluídas do índice de busca, do llms.txt e das listas de páginas do MCP e da API JSON, e não são anunciadas como traduções reais no hreflang, de modo que conteúdo não traduzido não concorre por posicionamento. Elas ainda aparecem na barra lateral daquele locale, então a navegação permanece completa — um leitor consegue chegar a qualquer página em qualquer idioma.
As notas de versão de uma fonte githubReleases() são a exceção: elas são publicadas em um único idioma, então nunca são copiadas para as URLs de outros locales e não exibem seletor de idiomas.
Links entre locales
Escreva os links internos como você faria no locale padrão — [Setup](/guides/setup), <Card href="/guides/setup"> — em todos os idiomas, inclusive nas páginas traduzidas. Quando uma página é renderizada sob um prefixo de locale, o Blume move cada link de página relativo à raiz para aquele locale (/fr/guides/setup), desde que a rota seja servida ali, como uma tradução real ou como página de fallback. Um link sem variante por locale — uma página personalizada, uma rota gerada ou uma tradução ausente em um site com fallbacks desativados — mantém o destino que você escreveu em vez de apontar para um 404, e um link que já carrega um prefixo de locale (/de/guides/setup) fica intocado, de modo que os links entre locales permanecem explícitos.
As âncoras acompanham o link, então os ids dos títulos precisam coincidir entre os idiomas. O blume translate cuida disso: cada título traduzido é fixado ao id do título de origem com um marcador [#id] no final. Em uma tradução que você escreve à mão, fixe os títulos você mesmo com o mesmo marcador [#custom-id] — caso contrário, #ordering não vai corresponder ao #ordre gerado automaticamente na página em francês, e o blume validate reporta a divergência na página traduzida em que o leitor de fato cai.
Traduzindo com um agente
Você não precisa preencher os locales manualmente. O blume translate encontra todas as páginas que estão faltando ou desatualizadas em cada locale e as traduz com uma CLI de agente local (Codex ou Claude Code):
blume translate --codex
O Blume valida a estrutura de cada resultado — frontmatter, blocos de código, links — e escreve os arquivos ele mesmo; o agente apenas traduz o texto. Um registro versionado (blume.translations.json) rastreia de qual revisão de origem cada tradução veio, então as reexecuções tocam apenas o que mudou, e as traduções que você escreveu à mão são adotadas como estão, nunca sobrescritas. Em CI, blume translate --check falha quando uma página de origem avançou além de suas traduções.
O seletor de idiomas
Quando a i18n está ativada, um seletor de idiomas aparece automaticamente no cabeçalho, gerado a partir dos seus locales. Para cada página, ele aponta para a tradução correspondente em todos os idiomas; onde falta uma tradução, ele aponta para a página de fallback e a marca como não traduzida. Com os fallbacks desativados (fallbackLocale: null), não há página para a qual apontar, então o seletor deixa esse idioma de fora. Não há nada para configurar. Páginas de uma fonte GitHub Releases são a exceção: as notas de versão existem em um único idioma, então essas páginas são renderizadas sem seletor.
Roteamento pelo idioma do navegador
Para enviar os visitantes para o próprio idioma deles, ative o routeByBrowserLanguage:
i18n: {
defaultLocale: "en",
locales: [
{ code: "en", label: "English" },
{ code: "fr", label: "Français" },
],
routeByBrowserLanguage: true,
}
Um visitante que chega de fora do site à página inicial do idioma padrão é então levado à página inicial no idioma preferido do navegador dele, quando o site tem esse idioma. O Blume tenta cada idioma que o navegador prefere, em ordem, buscando primeiro uma correspondência exata de código e depois o idioma base (fr-CA encontra fr). Se o idioma padrão vier primeiro, o visitante permanece onde está. Depois que um leitor escolhe um idioma com o seletor, o Blume lembra a escolha e deixa de redirecioná-lo. Só a página inicial é redirecionada; um link para qualquer outra página abre exatamente como foi escrito.
O redirecionamento acontece no navegador antes de a página ser exibida, então funciona em qualquer host, inclusive em builds estáticos. Ele vem desativado por padrão porque os motores de busca rastreiam em um único idioma: com ele ativado, mantenha as alternativas hreflang que o Blume emite para que as páginas de cada idioma continuem sendo indexadas.
Interface traduzida
O Blume inclui traduções nativas para os elementos da própria interface — “Nesta página”, “Buscar”, “Editar no GitHub” e o restante — de modo que um locale com um pacote nativo já vem com a interface traduzida. Você traduz apenas o seu conteúdo.
Há pacotes para mais de 30 idiomas — árabe, bengali, búlgaro, catalão, chinês (simplificado e tradicional), croata, tcheco, dinamarquês, holandês, finlandês, francês, alemão, grego, hebraico, híndi, húngaro, indonésio, italiano, japonês, coreano, norueguês, persa, polonês, português (e português brasileiro), romeno, russo, sérvio, eslovaco, espanhol, sueco, tailandês, turco, ucraniano e vietnamita. Eles são mantidos pela comunidade — abra um PR para adicionar um locale ou aprimorar uma tradução.
Os pacotes também cobrem a referência da API: os títulos, as tabelas de schema e o painel Try it de uma página de operação seguem o idioma da página. Os rótulos que a referência adiciona à barra lateral — a página de visão geral dela e os grupos que ela mesma nomeia (Operations, Webhooks e os tipos do GraphQL) — usam as palavras do locale padrão, já que a referência é publicada nesse locale. Só os rótulos mudam, então as URLs da referência permanecem as mesmas em todos os idiomas.
Strings ausentes ou não incluídas recorrem ao locale padrão e, depois, ao inglês. Para substituir uma string ou fornecer seu próprio idioma, defina i18n.ui, com chaves por locale:
i18n: {
// …
ui: {
fr: {
search: { button: "Rechercher", placeholder: "Rechercher…" },
page: { previous: "Précédent", next: "Suivant" },
reference: { tryIt: "Essayer" },
},
},
}
SEO
O SEO localizado é cuidado para você — não há metadados por página para escrever:
<html lang>edirsão definidos a partir do locale ativo.- As alternativas
hreflangapontam para todas as traduções reais de uma página que os motores de busca podem indexar (uma tradução comnoindex, oculta ou com canonical apontando para outro lugar fica de fora), mais umx-defaultapontando para o locale padrão. - As URLs canônicas são corretas por locale, e o JSON-LD carrega
inLanguage.
Defina deployment.site para que isso possa ser emitido como URLs absolutas.
Busca
A busca é limitada ao idioma ativo: em uma página /fr/…, o diálogo retorna resultados em francês, com uma opção Todos os idiomas para buscar em todos os locales de uma vez. Os índices padrão (Orama) e FlexSearch filtram no navegador, o Pagefind mantém um índice por idioma, e o Algolia, o Orama Cloud e o Typesense carregam um campo locale em cada registro. O Mixedbread não consegue limitar a busca por idioma, então com ele toda busca abrange todos os locales e o diálogo não exibe essa opção. Veja Busca para saber como cada adaptador lida com isso.
Da direita para a esquerda
Defina dir: "rtl" em um locale e o Blume espelha toda a interface — a barra lateral, o cabeçalho, o índice da página, a paginação, a busca e os menus — e define <html dir> de acordo. Duas coisas permanecem deliberadamente da esquerda para a direita: blocos de código (código se lê da esquerda para a direita em qualquer idioma) e conteúdo de fallback — uma página não traduzida mantém a direção do idioma em que ela realmente está escrita, então o inglês exibido sob um locale RTL continua legível enquanto os elementos ao redor são espelhados.