Se o seu app Next.js está traduzido, mas a aba do navegador, a página 404, os erros de formulário e a prévia do link continuam em inglês, a causa quase sempre é a mesma: essas strings são produzidas fora de um componente, então uma chamada useTranslations nunca chegou a elas. A globalize.now é uma infraestrutura de localização com IA, e essa é a camada que vemos ficar pela metade quando um agente de IA adiciona i18n a um projeto com App Router: a página visível é convertida, o resto da superfície não.
A seguir, onde cada uma dessas strings mora e a chamada do next-intl que cuida dela. Todos os exemplos seguem a documentação atual do next-intl, consultada em 28/10/2026.
Onde as strings não traduzidas se escondem em um app com App Router?
Elas se escondem em seis lugares que não são componentes React renderizando texto. Um agente de código que adiciona i18n percorrendo seus componentes vai converter a UI visível e passar direto por estes:
- Títulos e descrições de página em
generateMetadata. - Erros de validação e mensagens de resultado em Server Actions.
not-found.tsx, a página 404.error.tsx, a página de erro em runtime.- Arquivos gerados, como
opengraph-image.tsx,manifest.tsesitemap.ts. - Route Handlers que retornam texto.
O ponto em comum é que nenhum deles é renderizado dentro da sua árvore de layout [locale] da forma normal, então o locale precisa ser entregue a eles ou lido de outro jeito. O post sobre roteamento no Next.js 16 cobre a configuração do roteamento; este aqui trata do que essa configuração não alcança.
Como traduzir títulos e descrições de página?
Chame getTranslations de next-intl/server dentro de generateMetadata. A função pode ser exportada de uma página ou de um layout e é assíncrona, então a API com await é a indicada:
// app/[locale]/page.tsx
import {getTranslations} from 'next-intl/server';
export async function generateMetadata() {
const t = await getTranslations('Metadata');
return {title: t('title'), description: t('description')};
}
A armadilha é a forma estática. Agentes costumam escrever export const metadata = {title: 'Dashboard'} porque é mais curto, e uma constante não consegue chamar uma função de tradução. Converta para a forma de função e adicione as mesmas chaves ao catálogo de todos os locales. Esse também é o texto que aparece nas buscas, e é por isso que traduzir o app sem traduzir os metadados pouco ajuda no ranqueamento.
Como traduzir mensagens de Server Actions e de validação?
Passe o locale para a action e chame getTranslations com um namespace e esse locale. Uma Server Action não sabe em que idioma a página está, então a documentação do next-intl pede que você o forneça, por exemplo como argumento:
'use server';
import {getTranslations} from 'next-intl/server';
export async function subscribe(locale: string, formData: FormData) {
const t = await getTranslations({locale, namespace: 'Subscribe'});
// ...validate, then return t('success') or t('invalidEmail')
}
Um alerta da mesma documentação: pense no que acontece se o visitante puder trocar de idioma enquanto a mensagem de uma action está na tela. Com um segmento [locale] na URL, isso é tratado automaticamente, porque a rota muda. Sem ele, talvez você queira limpar a mensagem por conta própria, por exemplo dando ao componente key={locale} para que o estado seja resetado.
Para validação de formulários com Zod, a abordagem documentada é um error map. O Zod aceita um por chamada de safeParse, e o mapa pode chamar getTranslations. Coloque isso em um único helper assíncrono e passe o resultado como a opção error em cada parse. Assim seus schemas guardam regras, não frases em inglês, e formulários gerados por IA deixam de entregar "Invalid email" em uma página em letão.
Como traduzir as páginas 404 e de erro?
Coloque not-found.tsx dentro de app/[locale]/ e adicione um catch-all para que ele realmente seja disparado. O Next.js renderiza esse arquivo quando um segmento chama notFound(), e uma URL desconhecida não o chama sozinha:
// app/[locale]/[...rest]/page.tsx
import {notFound} from 'next/navigation';
export default function CatchAllPage() {
notFound();
}
Requisições que o middleware do next-intl não captura não carregam locale algum, então o 404 de [locale] nunca se aplica a elas. A resposta da documentação é app/global-not-found.tsx, que o Next.js documenta como experimental e que renderiza seus próprios html e body. Para um valor de [locale] desconhecido, a documentação diz para chamar notFound(), normalmente em i18n/request.ts.
error.tsx é diferente. O Next.js exige que seja um Client Component, e ele é renderizado sob o seu layout, então precisa de um NextIntlClientProvider com pelo menos o namespace Error. Passe só essas mensagens, em vez do catálogo inteiro:
// app/[locale]/layout.tsx (excerpt)
const messages = await getMessages();
// ...
<NextIntlClientProvider locale={locale} messages={pick(messages, 'Error')}>
Assim, useTranslations('Error') funciona dentro de error.tsx. A página não cobre global-error, então confira esse arquivo manualmente se você tiver um.
E as imagens de compartilhamento, o manifest e o sitemap?
Todos precisam receber o locale, porque ficam fora da árvore de layout. Em opengraph-image.tsx, leia locale de params e chame getTranslations({locale, namespace}). Com um localePrefix personalizado, a documentação alerta que a rota gerada pode ficar inacessível, porque o Next.js não enxerga os rewrites do proxy (middleware); a correção é fazer o matcher ignorar caminhos que terminam em opengraph-image.
app/manifest.ts fica fora de [locale], então o next-intl não consegue inferir o locale a partir do caminho. Escolha um representativo, geralmente o padrão, e passe-o explicitamente. Em sitemap.ts, adicione entradas alternates.languages e monte cada URL com o getPathname({locale, href}) assíncrono do seu módulo de navegação, prefixado com o host. O next-intl também envia por padrão um header de resposta link que anuncia as versões de idioma; o sitemap é a opção quando você precisa de controle sobre slugs vindos de um CMS.
Como você lida com Route Handlers?
Pegue o locale de um search param, de um segmento ou do header accept-language, valide-o com hasLocale(routing.locales, locale) e retorne 400 se falhar. Depois chame getTranslations({locale, namespace}). Pular a validação significa que uma string arbitrária chega à busca no seu catálogo.
Como você encontra o inglês que seu agente deixou para trás?
Rode estes comandos na raiz do projeto. Eles são imprecisos de propósito; a ideia é gerar uma lista para ler, não uma pontuação.
# 1. Static metadata that cannot be translated
grep -rn "export const metadata" app
# 2. Titles and descriptions already in generateMetadata
grep -rn "title:" app --include=page.tsx --include=layout.tsx
# 3. Server Actions with literal return messages
grep -rln "use server" app src 2>/dev/null | xargs grep -n "message:\|error:"
# 4. Zod schemas with inline messages
grep -rn "message:\s*['\"]" app src 2>/dev/null
# 5. 404 and error files, and whether a catch-all exists
ls app/\[locale\]/not-found.tsx app/\[locale\]/error.tsx "app/[locale]/[...rest]/page.tsx" 2>&1
Qualquer coisa nos quatro primeiros resultados que seja um literal simples em inglês pertence ao catálogo. Por que os agentes continuam adicionando strings hardcoded explica por que isso se repete: o agente segue o padrão visível, e o padrão visível é o componente. Se você usa o Copilot, os limites dos arquivos de instrução do Copilot também valem aqui.
O que isso muda no seu catálogo?
Ele adiciona chaves, e é nas chaves que o drift começa. Cada item acima traz novas entradas para o catálogo, como Metadata, Error, Subscribe e as mensagens de validação, e cada uma precisa de tradução em todos os locales que você publica. Um projeto que traduz seus componentes uma vez e depois adiciona uma Server Action ou um novo título de página é o mesmo projeto descrito em por que os arquivos de tradução saem de sincronia.
O next-intl serve as traduções em runtime e nós não o substituímos. A globalize.now fica uma camada acima: você conecta o repositório no app, a base de código é convertida uma vez em um catálogo, e os jobs de push passam a traduzir as novas unidades do catálogo conforme elas aparecem, entregues como alterações para você revisar. A integração com Next.js descreve a configuração, e o que tocamos e o que não tocamos em uma configuração de i18n existente cobre o caso em que o next-intl já foi configurado manualmente.
Por onde começar
Rode os cinco greps, converta qualquer export estático de metadata, adicione a rota catch-all e dê a error.tsx o seu provider. Depois confira no catálogo os novos namespaces em todos os locales. Se você publicou um app feito com IA e quer que a conversão e a tradução sejam resolvidas em vez de mantidas por você, a página para vibe coders traz a visão geral, a referência para desenvolvedores cobre as configurações de agentes, e os preços ficam em uma página própria.
O globalize.now transforma textos fixos do app em arquivos de locale prontos para tradução e os mantém atualizados a cada novo lançamento.
Experimente o globalize.now grátis