Framework
React
O pacote oficial de React 18/19 para Verbaly. Hooks sobre o mesmo core reativo, um <Trans> ciente do compilador para rich text, e chaves + params totalmente tipados.
O que você pode fazer:
- Traduza com hooks (useT, useLocale) que re-renderizam exatamente quando o idioma muda
- Escreva rich text no lugar: o compilador extrai os children de
<Trans>como qualquert`…` - Mantenha chaves e params totalmente tipados, com autocompletar das suas próprias mensagens
- Concurrent-safe por construção: as assinaturas usam
useSyncExternalStore
Instalação
Instale o pacote com o seu gerenciador de pacotes preferido.
pnpm add @verbaly/reactSetup e hooks
import { VerbalyProvider, useT, useLocale } from '@verbaly/react';
import { verbaly } from 'virtual:verbaly';
<VerbalyProvider instance={verbaly}>
<App />
</VerbalyProvider>
function Inbox() {
const t = useT();
const [locale, setLocale] = useLocale();
return <p>{t('inbox', { count: 3 })}</p>;
}- Re-renderiza via
useSyncExternalStore: concurrent-safe, amigável com SSR. useVerbaly()expõe a instância completa quando você precisa.
Rich text com <Trans>
Escreva o texto fonte no lugar, e desde 0.6.0 o compilador o extrai, exatamente como t`…`:
import { Trans } from '@verbaly/react';
// you write:
<Trans>Read the <a href='/terms'>terms</a> before continuing</Trans>
// the compiler extracts "Read the <a>terms</a> before continuing" and rewrites to:
<Trans id='x7Ka9q2f' components={{ a: <a href='/terms' /> }} />- Os atributos dos filhos são preservados; os params (
{count}) viramvalues; o aninhamento funciona; os tags desconhecidos caem para o texto interno. - As tags da whitelist renderizam de verdade (
0.23.0):<em>,<code>e companhia numa mensagem viram elementos reais, a mesma whitelist segura do interpretador de DOM.richTagssubstitui a lista; uma lista vazia restaura o comportamento anterior de achatar para texto. - Chaves legíveis (
0.8.0): escreva<Trans id="inbox.title">…</Trans>com children e ele extrai sob esse id em vez de um hash. - Runtime-first ainda funciona: passe
idsem children (ou com seus própriosvalues/components) e o compilador não toca no elemento. - Valem as regras de whitespace do JSX: uma quebra de linha entre um elemento e texto não renderiza espaço; use o idiom
{' '}, a extração é fiel ao que o React renderiza. - Links nomeados (
0.11.0): a proplinksmapeia nomes de tag para hrefs:<docs>…</docs>renderiza como um<a>real sem componente custom.componentsganha delinksno mesmo nome; hrefsjavascript:são bloqueados. - Sem provider? Passe a prop
instance(0.23.0) e<Trans>renderiza direto dali, re-renderizando a cada troca de idioma.
Next.js
Os Server Components renderizam já traduzidos no idioma do visitante, e os Client Components hidratam com o mesmo locale e o mesmo catálogo: sem flash de texto sem tradução nem mismatch de hidratação.
- Cada página é renderizada no idioma do visitante, escolhido a cada request a partir do cookie ou do header
Accept-Language - Sem flash de texto sem tradução: o HTML que o servidor envia e a página já hidratada são idênticos, catálogo incluído
- Cada request tem sua própria instância, então dois visitantes simultâneos nunca veem o idioma um do outro
- Turbopack e webpack, ambos de primeira: o compilador de
troda como loader em qualquer um dos dois bundlers
Next.js precisa da integração (@verbaly/next) junto com os bindings de React:
pnpm add verbaly @verbaly/next @verbaly/reactConecte tudo
Envolva sua config com withVerbaly. Ele conecta a extração ao vivo em dev, o compilador de mensagens no Turbopack e no webpack, e bloqueia next build se faltam traduções:
import { withVerbaly } from '@verbaly/next';
export default withVerbaly({ /* your Next config */ });
// locales live in your verbaly.config (npx verbaly init), or inline:
export default withVerbaly({}, { locales: ['en', 'es', 'pt'] });No layout raiz, getVerbalyProps serializa o locale negociado e seu catálogo, e VerbalyProvider semeia o cliente a partir deles:
import { getRequestLocale, getVerbalyProps } from '@verbaly/next/server';
import { VerbalyProvider } from '@verbaly/next/client';
export default async function RootLayout({ children }) {
const locale = await getRequestLocale();
const props = await getVerbalyProps();
return (
<html lang={locale}>
<body>
<VerbalyProvider {...props}>{children}</VerbalyProvider>
</body>
</html>
);
}Server e Client Components
Os Server Components obtêm um t por request com await getT(), negociado uma vez por request (cookie, depois Accept-Language, depois seu fallback):
import { getT } from '@verbaly/next/server';
export default async function Page() {
const t = await getT();
return <h1>{t`Welcome back`}</h1>;
}Os Client Components usam os bindings de React, re-exportados de @verbaly/next/client:
'use client';
import { useT } from '@verbaly/next/client';
export function Counter() {
const t = useT();
return <p>{t`You have new messages`}</p>;
}Trocar de idioma
useSwitchLocale troca o cliente ao vivo, escreve o cookie que o servidor lê, e re-renderiza os Server Components via router.refresh():
'use client';
import { useSwitchLocale } from '@verbaly/next/client';
export function LangPicker() {
const switchLocale = useSwitchLocale();
return <button onClick={() => switchLocale('es')}>Español</button>;
}- O cookie padrão é
verbaly-locale(a mesma identidade da storage key do core);cookie: falsenas opções o desativa, paraAccept-Languagepuro. fallbacké por padrão seu locale fonte; a negociação entende q-values e estreita regiões (es-PEcasa comes).next buildbloqueia se faltam traduções, com as keys exatas a preencher;failOnMissing: falsedesativa isso.- A negociação lê headers do request, então as páginas negociadas renderizam dinamicamente. Site totalmente estático? Prefira saída pré-traduzida por locale com verbaly render.