Tradução de sites Astro em escala: como automatizar i18n com CSV

Tradução de sites Astro em escala: como automatizar i18n com CSV

14 de maio de 2026

Conclusão: um site Astro multilíngue é um problema estrutural antes de ser um problema de tradução. Configure pastas de conteúdo limpas por localidade, extraia cada trecho traduzível para um único CSV, peça ao AI Glot em uma frase para preencher cada coluna de idioma, usando a tradução de CSV do AI Glot, e então remonte os arquivos programaticamente. Uma vez que o pipeline está pronto, adicionar um novo idioma é uma operação de um único CSV.

Este é o fluxo de trabalho exato que usamos para manter o ai-glot.com em seis idiomas (inglês, francês, alemão, espanhol, italiano, português) sem reescrever um único post de blog manualmente. Vamos percorrer todo o processo, com os prompts que usamos para estruturar cada etapa com um assistente de codificação de IA como o Claude Code ou Cursor.

O objetivo

Ao final deste guia, você terá:

  1. Uma estrutura de roteamento i18n limpa em seu projeto Astro, com uma pasta por idioma.
  2. Um script repetível que extrai cada string traduzível de um arquivo markdown (ou exportação de CMS) para um único CSV.
  3. Um CSV traduzido produzido no AI Glot, preenchido em uma única passagem em todas as colunas de idioma.
  4. Um segundo script que remonta os arquivos markdown traduzidos, com caminhos de imagem e links internos corrigidos para os subdiretórios localizados.
  5. Um arquivo de instruções que você pode entregar ao seu assistente de IA para que cada novo artigo já nasça traduzível por padrão.

Se você já usa Weglot, Crowdin ou um CMS headless que exporta CSV, pode pular o script de extração de markdown e enviar essas exportações diretamente para o AI Glot. A lógica de tradução e remontagem é a mesma para qualquer projeto de tradução de conteúdo de CMS.

Pré-requisitos

Um checklist rápido antes de começar.

  • Um projeto Astro (v4 ou v5) rodando localmente, com coleções de conteúdo configuradas para seu blog.
  • Node 20 ou superior, para que as APIs modernas de CSV e fs/promises funcionem corretamente.
  • Uma conta no AI Glot (o plano gratuito é suficiente para o primeiro lote).
  • Um assistente de IA no seu editor para gerar o código base (Claude Code, Cursor, Copilot, Codeium, você escolhe).

Por que o Astro é ideal para multilíngue em escala

O Astro renderiza HTML estático por padrão, o que significa que cada página localizada é apenas um arquivo no disco. Não há camada de tradução em tempo de execução, nem consulta ao banco de dados para troca de idioma, nem pacote JavaScript para baixar por localidade. O Google indexa cada versão de idioma como uma URL distinta com suas próprias meta tags, e a saída do build é essencialmente um espelho traduzido da sua árvore em inglês.

Isso é exatamente o que você quer para SEO. O SEO multilíngue funciona melhor quando cada idioma tem sua própria página gerada estaticamente e rastreável, com anotações hreflang adequadas e uma estrutura de URL limpa. O Astro oferece isso gratuitamente.

A contrapartida: você é responsável por manter os arquivos de conteúdo sincronizados entre os idiomas. Esse é precisamente o problema que o pipeline de CSV abaixo resolve.

Parte 1: Configure a estrutura de roteamento i18n

O Astro 4 introduziu a configuração de i18n nativa. O padrão mais limpo é declarar seus locales, definir o padrão como sem prefixo (assim, o inglês fica em /blog/my-post e o francês em /fr/blog/my-post) e espelhar suas coleções de conteúdo em uma pasta [lang]/ por idioma.

Aqui está uma estrutura sugerida para um blog com seis idiomas:

src/
  content/
    blog/
      live/                          # English (default locale)
        my-first-post.md
      fr/live/                       # French
        my-first-post.md
      de/live/                       # German
        my-first-post.md
      es/live/                       # Spanish
        my-first-post.md
      it/live/                       # Italian
        my-first-post.md
      pt/live/                       # Portuguese
        my-first-post.md
  pages/
    blog/[slug].astro                # English routes
    [lang]/blog/[slug].astro         # Localized routes
  i18n/
    ui.ts                            # UI string dictionary per locale

Observe que os arquivos markdown localizados ficam em uma pasta mais profunda que os em inglês. Essa profundidade é importante: caminhos de imagem relativos em markdowns localizados precisam de um ../ extra para chegar em src/assets/. O script de remontagem na Parte 4 resolve isso automaticamente.

Se você preferir não escrever essa estrutura sozinho, envie o seguinte prompt para o seu assistente de codificação por IA. Ele é intencionalmente genérico para funcionar em qualquer projeto Astro e faz as perguntas de esclarecimento corretas antes de gerar o código.

You are helping me scaffold internationalization (i18n) in an existing Astro
project. I want to translate my entire site into multiple languages while
keeping clean URLs, proper hreflang annotations, and per-locale static pages.

Goals:
1. Declare locales in astro.config.mjs using the built-in `i18n` option.
   Default locale should NOT be prefixed (e.g. English lives at `/blog/...`,
   French at `/fr/blog/...`).
2. Create a per-locale folder structure for my content collections so each
   blog post (or CMS item) has a 1:1 file per language. The English files
   stay at their current location; localized files live one folder deeper
   (e.g. `src/content/blog/fr/live/`).
3. Add a typed UI string dictionary at `src/i18n/ui.ts` that exports an object
   keyed by locale, plus a `useTranslations(lang)` helper.
4. Update the content collection schema so the same Zod definition applies to
   every language folder.
5. Generate the dynamic route file `src/pages/[lang]/blog/[slug].astro` that
   reads from the localized content collection and falls back to English when
   a localized version is missing.
6. Add a `<LanguageSwitcher />` component that renders an anchor per locale,
   preserving the current pathname.
7. Inject `<link rel="alternate" hreflang="..." />` tags into the layout for
   every locale that has the page available.

Before you generate any code, ask me:
- The list of target locales (BCP 47 codes) and which one is the default.
- The path to my existing content collection(s) and current route files.
- Whether I want a language switcher in the header, footer, or both.
- Whether I store UI strings in JSON, TS, or somewhere else today.

Then make the changes in small, surgical commits. Do not refactor unrelated
code. Show me the diff and explain any decision where there are two valid
patterns (e.g. prefixed vs. non-prefixed default locale).

Esse padrão é intencional: esclarecer, depois construir. Tentamos a abordagem de “apenas gerar tudo” no início e acabamos com três componentes de alternador de idioma diferentes e um esquema de roteamento que conflitava com nosso esquema de coleção de conteúdo. Pedir para o assistente perguntar primeiro economiza uma hora de limpeza depois.

Parte 2: Extraia seu conteúdo para um CSV de tradução

Script de extração de tradução automatizada no editor Antigravity AI mostrando a lógica de fragmentação

É aqui que a maioria das equipes trava. A tradução em si é a parte fácil. Colocar seu conteúdo dentro e fora de uma ferramenta de tradução sem quebrar a estrutura é a parte difícil.

O truque que faz isso funcionar bem no AI Glot é a extração fragmentada. Você divide cada arquivo markdown nas menores unidades traduzíveis significativas (um campo de frontmatter, um par de FAQ, um único parágrafo), escreve cada fragmento como uma linha em um CSV e adiciona uma coluna por idioma de destino. O resultado fica assim:

path English string fr de es it pt
live/my-first-post.md How to translate an Astro website at scale into many languages
live/my-first-post.md A practical, copy-paste tutorial for shipping a multilingual Astro site.
live/my-first-post.md Astro renders static HTML by default, which means each localized page is just a file on disk.

Matriz CSV multilíngue enriquecida processada pelo AI Glot mostrando múltiplas colunas traduzidas

Por que em blocos? Porque a tradução por IA funciona muito melhor em unidades coerentes do tamanho de um parágrafo do que em documentos inteiros. As regras do glossário são aplicadas de forma consistente, o orçamento de tokens permanece previsível e, se um bloco precisar de correção, você edita uma única célula, não um arquivo de 2.000 palavras.

Opção A: Você armazena o conteúdo como arquivos markdown (blog, docs, landing pages)

Use este prompt para gerar um script de extração adaptado ao seu repositório. Usamos uma versão quase idêntica para o site do AI Glot.

Write a Node.js script at `scripts/extract-translations.mjs` that prepares a
CSV for bulk translation in AI Glot.

Inputs:
- A source directory containing English markdown files
  (e.g. `src/content/blog/live/`).
- A list of target locale codes (e.g. `fr de es it pt`).
- An output CSV path (default `multilang-translation.csv`).

For each markdown file:
1. Parse the frontmatter using `gray-matter`.
2. Extract these translatable strings, one per CSV row:
   - `title` (frontmatter)
   - `metaDescription` (frontmatter)
   - For each entry in `faqs`: the `question` and the `answer` as separate rows.
   - Each body paragraph, splitting the markdown body on `\n\n` so paragraphs,
     bullet lists, blockquotes, and headings each become one row.
3. SKIP these chunks (do not include them in the CSV):
   - Pure image lines starting with `![`.
   - Fenced code blocks (between triple backticks).
   - Lines that are only HTML comments or empty after trim.

CSV schema (exact order, UTF-8, RFC 4180 quoting):
`path,English string,<locale1>,<locale2>,...`

Where `path` is the file path relative to the source dir (e.g.
`live/my-first-post.md`), `English string` is the chunk, and every target
locale column is left EMPTY. AI Glot will fill them.

Rules:
- Use `papaparse` for CSV writing so quoting and escaping are correct.
- Preserve the exact whitespace and punctuation of the source chunk; do not
  trim trailing punctuation or collapse internal whitespace.
- Deduplicate rows that have the same `path` + `English string` pair
  (sometimes a paragraph appears twice in a post).
- Print a summary at the end: number of files processed, total rows,
  estimated word count.

Make it a single self-contained file with no dependencies beyond
`gray-matter`. Usage:
`node scripts/extract-translations.mjs <source-file> <output.csv> <locales...>`

Execute-o uma vez e você obterá um único arquivo multilang-translation.csv que captura cada string traduzível do seu blog, pronto para upload.

Opção B: Seu conteúdo reside em um CMS (Webflow, Notion, Sanity, Strapi, Airtable)

Se você usa um headless CMS, não precisará de um script de extração. A maioria das plataformas de CMS modernas já permite exportar um CSV da sua coleção de conteúdo com uma coluna por campo. Você só precisa ajustar a exportação para o formato esperado pelo AI Glot.

Os dois formatos mais comuns que você encontrará:

  • Uma linha por item, uma coluna por campo (a exportação típica do Webflow). Informe ao AI Glot em uma frase quais campos de texto devem ser traduzidos, mantendo IDs, slugs e timestamps intactos. Abordamos exatamente esse padrão em How to translate a Webflow CMS export with AI.
  • Uma linha por string, uma coluna por idioma (uma exportação criada manualmente). Este é o formato que usamos para nosso blog Astro.

Se a exportação do seu CMS estiver no primeiro formato e você quiser o segundo (que é mais rápido quando se deseja traduzir para vários idiomas ao mesmo tempo), este prompt faz a conversão entre eles:

Write a Node.js script `scripts/cms-to-translation-csv.mjs` that reshapes a
flat CMS export into a one-column-per-language CSV ready for AI Glot.

Inputs:
- A source CSV exported from my CMS, with one row per content item and many
  columns per item (e.g. `id, slug, title, metaDescription, body, faq_1_q,
  faq_1_a, faq_2_q, faq_2_a, ...`).
- A list of columns to translate (passed as a CLI arg, comma-separated).
- A list of target locales (passed as a CLI arg).
- An output CSV path.

For each row in the input:
- For each column in the "translate" list that has a non-empty value, emit
  one row in the output CSV with:
    - `id`: the original row id (or slug) for traceability
    - `field`: the original column name
    - `English string`: the cell value
    - one empty column per target locale
- Skip empty cells; preserve all whitespace inside non-empty cells.

Use `papaparse` for both read and write. Make IDs and field names safe to
parse back (no quoting issues). Print a row count and a per-field breakdown.

Usage:
`node scripts/cms-to-translation-csv.mjs in.csv out.csv "title,metaDescription,body" "fr,de,es,it,pt"`

Independentemente da opção escolhida, você terá o mesmo formato de CSV: uma coluna de origem, uma coluna por idioma de destino, pronta para upload no AI Glot.

Parte 3: Traduza o CSV com AI Glot

Esta é a parte que leva minutos, não dias. Faça o upload do seu multilang-translation.csv em sua conta AI Glot e deixe a plataforma guiar a configuração.

O fluxo:

  1. Upload do CSV. O AI Glot analisa a estrutura do arquivo: tipos de coluna, idiomas detectados, valores de amostra, contagem total de palavras e estimativa de créditos.
  2. Descrição do trabalho. Diga ao AI Glot, em uma frase, para traduzir a English string para as colunas fr, de, es, it, pt e manter o path intacto.
  3. Revisão do plano. O AI Glot mostra os idiomas, o escopo, a contagem de palavras e o custo antes de gastar qualquer crédito. Se quiser algo diferente, peça a alteração e ele planeja novamente.
  4. Adição de termos ao glossário (opcional, mas recomendado). Nomes de marca, nomes de produtos e vocabulário técnico que você deseja manter consistentes em milhares de linhas. Os glossários são a diferença entre uma tradução que soa como a sua marca e uma que parece ter sido feita por uma ferramenta de tradução genérica. Veja How translation glossaries improve CSV localization para conhecer os padrões que funcionam.
  5. Instruções personalizadas (opcional). Nós dizemos ao AI Glot coisas como: “Use o tom de um fundador confiante escrevendo um guia técnico; mantenha a formatação markdown e os links inline intactos; nunca traduza trechos de código ou URLs.”
  6. Lançamento. O AI Glot processa o arquivo linha por linha, preenche cada coluna de destino e gera um CSV traduzido para download.
Instructions for this batchOptional
Apply
Você descreve o trabalho em inglês simples. O AI Glot o transforma em um plano que você pode ler e corrigir antes de qualquer execução.

Um blog de 30 posts com cinco idiomas de destino geralmente é processado em poucos minutos no modo Standard, e você pode mudar para o modo Pro para obter resultados de maior qualidade nas linhas mais importantes (títulos, meta descriptions, parágrafos de destaque).

Dica: assim que o arquivo for enviado para o AI Glot, você pode excluir a versão local com colunas vazias. Seu workspace passa a ser a fonte da verdade a partir desse momento, e você sempre poderá baixar o arquivo novamente.

AI Glot translation result page showing 100% completion status

Parte 4: Remonte o markdown traduzido de volta no Astro

Agora você tem um multilang-translation_translated.csv com todas as colunas de destino preenchidas. O objetivo é pegar cada trecho traduzido e escrevê-lo de volta no arquivo markdown localizado correto, preservando a estrutura original exatamente como era.

É aqui que a extração em blocos se paga novamente. Como cada linha é vinculada ao trecho original em inglês, você pode simplesmente localizar e substituir os trechos em inglês por suas contrapartes localizadas dentro do arquivo de origem original e salvar o resultado na pasta localizada. Sem parsing de AST, sem conversões complexas de markdown e sem risco de quebrar a formatação.

Duas correções estruturais devem ocorrer durante a remontagem:

  1. Caminhos de imagem. Arquivos em inglês referenciam assets com ../../../assets/.... Arquivos localizados ficam em uma pasta mais profunda, portanto, o caminho relativo torna-se ../../../../assets/....
  2. Links internos. Um link como /blog/glossary-website-translations em inglês precisa se tornar /fr/blog/glossary-website-translations em francês (e assim por diante para cada localidade).

Aqui está o prompt que usamos para gerar o script de remontagem. Ele é genérico o suficiente para ser usado em qualquer projeto Astro que siga a estrutura da Parte 1.

Use the unified assembly script at `scripts/assemble-blog-translations.mjs` that rebuilds
localized markdown files from an AI Glot translated CSV.

Inputs (CLI args):
- Path to the translated CSV (e.g. `multilang-translation_translated.csv`)
  with columns `path, English string, fr, de, es, it, pt`.
- The source directory containing the original English markdown files
  (default `src/content/blog/live/`).
- The destination directory pattern for localized files
  (default `src/content/blog/{lang}/live/`).
- The site origin used for internal links (default `https://ai-glot.com`).

For each unique `path` value in the CSV:
1. Read the original English file from `<sourceDir>/<path>`.
2. For each target locale column (everything after `English string`):
   a. Start from the English file's full text.
   b. For every row that matches this `path`, find the `English string`
      verbatim in the file and replace it with the value in the target
      locale column. Use literal string replace, not regex. Throw if the
      English chunk is not found (it means the source drifted from the CSV;
      this should fail loudly rather than silently skip).
   c. After all replacements, run two structural fixes on the resulting text:
      - Image paths: replace every occurrence of `../../../assets/` with
        `../../../../assets/` (one extra `../` because localized files live
        one folder deeper).
      - Internal links: replace every absolute internal URL on this site
        (e.g. `https://ai-glot.com/blog/...`) and every root-relative path
        (e.g. `/blog/...`, `/sign-up`) with the locale-prefixed equivalent
        (`/fr/blog/...`, `/fr/sign-up`). Skip anchors (`#...`), external URLs,
        and `mailto:` / `tel:` links.
   d. Write the file to `<destDir resolved with this lang>/<path>`. Create
      directories as needed.
3. After processing all paths, print a summary: files written per locale,
   total replacements, any chunks that could not be found.

Robustness:
- Use `papaparse` to read the CSV. Trim BOM if present.
- Preserve the exact frontmatter formatting (delimiters, key order, quoting).
  The simplest way is to do replacements on the raw file string, not on a
  parsed AST.
- Be idempotent: running the script twice on the same inputs produces the
  same outputs.

Make it a single self-contained file. Usage:
`node scripts/assemble-blog-translations.mjs translated.csv`

Execute o script e você verá novos arquivos surgindo em src/content/blog/fr/live/, src/content/blog/de/live/, e assim por diante. Verifique um deles, execute astro dev e suas rotas localizadas devem ser renderizadas instantaneamente.

Arquivo de habilidade de fluxo de trabalho de tradução do blog do AI Glot no Antigravity

Bônus 1: Um arquivo de habilidade para que cada novo artigo seja “traduzível por padrão”

Assim que o pipeline estiver funcionando, você vai querer que todos os artigos futuros sigam o mesmo fluxo. O truque: armazene a convenção em um arquivo de habilidade que seu assistente de IA leia antes de escrever qualquer post novo.

Se você usa Claude Code, Cursor ou qualquer editor de IA que carregue instruções em nível de projeto, coloque este arquivo no seu repositório como skills/blog_translation_workflow.md. Nós usamos exatamente esse padrão no AI Glot.

---
name: blog_translation_workflow
description: How to ship a new blog post so it can be translated in bulk later.
---

# Blog translation workflow

When writing a new blog post or CMS item, follow these conventions so it
flows cleanly through the multilingual extraction pipeline.

## 1. File location and naming
- Drafts live under `src/content/blog/draft/`.
- Slug = filename = the same string used in `slug:` frontmatter.
- Move to `src/content/blog/live/` only after review.

## 2. Frontmatter contract
Every post MUST have:
- `title`: sentence case, no trailing period.
- `metaDescription`: 140 to 160 characters, full sentence.
- `coverImage`: relative path from this file's location.
- `publishedDate`: ISO date.
- `slug`: matches the filename.
- `faqs`: array of `{ question, answer }`. Minimum 3 items.

## 3. Body structure that translates well
- Use sentence-case headings (no Title Case).
- Keep paragraphs to 1 to 4 sentences. Each paragraph becomes one CSV row,
  so shorter paragraphs translate more reliably.
- Never put two images back to back; always separate them with at least one
  paragraph of explanatory text.
- Internal links use root-relative paths (`/blog/...`, `/sign-up`).
- Image paths use `../../../assets/...` (English depth).

## 4. After publishing
Run:
```bash
node scripts/extract-translations.mjs src/content/blog/live/`<slug>`.md \
  multilang-translation.csv fr de es it pt
```
to append this article's translatable chunks to the master CSV. Upload the
CSV to AI Glot and ask it to fill every language column, then run:
```bash
node scripts/assemble-blog-translations.mjs multilang-translation_translated.csv
```
to generate the localized markdown files.

## 5. Anti-patterns
- Do NOT use em-dashes; use commas or colons.
- Do NOT bold entire paragraphs. Bold key phrases only.
- Do NOT skip an FAQ section; FAQs are among the highest-value SEO blocks.
- Do NOT inline raw HTML inside markdown unless absolutely necessary;
  it complicates chunk-based reassembly.

Na próxima vez que você (ou qualquer pessoa da sua equipe) escrever um novo artigo, o assistente lerá este arquivo primeiro e produzirá um post que se encaixe no pipeline sem a necessidade de limpeza manual.

Bônus 2: Adicionando um novo idioma a todo o seu blog em um único lote

Esta é a parte que mais surpreende as pessoas. Uma vez que o pipeline existe, adicionar um novo idioma é uma operação de um único CSV para todo o arquivo do seu blog.

Digamos que você tenha 80 artigos traduzidos para francês, alemão, espanhol, italiano e português, e decida adicionar o holandês.

O fluxo de trabalho:

  1. Execute novamente seu script de extração com nl adicionado à lista de locais. Como o script lê da pasta de origem em inglês, todos os artigos (antigos e novos) são incluídos.
  2. Abra o CSV. A coluna English string é idêntica à última vez. As colunas fr, de, es, it, pt ainda estão vazias (o script não olha para as traduções existentes, ele apenas constrói a matriz vazia). A coluna nl é nova e está vazia.
  3. Remova as colunas que você não precisa retraduzir. Exclua fr, de, es, it, pt da planilha, mantendo path, English string, nl.
  4. Faça o upload para o AI Glot e peça para traduzir English string apenas para nl.
  5. Execute o script de remontagem com o novo CSV. Ele gravará cada artigo em src/content/blog/nl/live/.

Você passou de “precisamos adicionar holandês” para “holandês publicado” em um único upload. Para 80 artigos. É assim que fica quando o fluxo de trabalho faz o serviço, não a equipe.

O mesmo padrão funciona para alterações pontuais: edite um único parágrafo em inglês, re-exporte apenas os trechos afetados, traduza-os e reconstrua. As atualizações continuam baratas porque o pipeline é estrutural, não baseado em documentos.

Checklist de verificação

Antes de publicar em produção, faça um teste de sanidade em cada local:

  • npm run build (ou astro build) é concluído sem erros de ImageNotFound.
  • Passe o mouse sobre o seletor de idiomas em um post localizado e confirme se a URL permanece limpa.
  • Abra um post em francês e faça uma verificação rápida: título, meta descrição, três primeiros parágrafos e uma resposta de FAQ. Compare com a origem.
  • Clique em um link interno dentro do post em francês. Ele deve levar a outra página em francês, não de volta para o inglês.
  • Inspecione o <head> para verificar as anotações hreflang corretas em todos os locais.
  • Execute uma auditoria do Lighthouse em uma URL localizada; a pontuação de SEO deve ser a mesma do equivalente em inglês.

Se algum desses falhar, os culpados mais comuns são: profundidade de caminho de imagem incorreta, um link interno que escapou do regex ou um trecho que se perdeu entre a extração e a remontagem. Os avisos de “could not find chunk” do script de remontagem indicam exatamente onde procurar.

A conclusão

Tratar um site Astro multilíngue como um pipeline de conteúdo, em vez de um projeto de tradução único, é o que o torna sustentável. Acerte o pipeline (roteamento de locale, extração de CSV em blocos, tradução com AI Glot, remontagem determinística) e traduzir um novo artigo, corrigir um parágrafo ou adicionar um novo idioma se torna uma operação rotineira, em vez de uma iniciativa trimestral.

As peças que fazem isso funcionar:

  • i18n nativo do Astro para páginas estáticas limpas por local.
  • Um pequeno script de extração que transforma markdown em um CSV traduzível.
  • Tradução de CSV do AI Glot, instruído em uma frase a preencher cada coluna de idioma em uma única passagem, com controle de glossário e instruções.
  • Um script de remontagem que lida com a profundidade das imagens e a localização de links internos.
  • Um arquivo de habilidade para que cada novo artigo se encaixe no pipeline por padrão.

Pronto para expandir globalmente? Cadastre-se no AI Glot, envie seu primeiro CSV de extração e veja seu blog entrar no ar em seis idiomas até o final da tarde.

Ganhe 10.000 palavras grátis ao se cadastrar

Pronto para traduzir seus arquivos grandes?