Internacionalização (i18n)
O MangoStudio usa um sistema de i18n em TypeScript puro com verificação de traduções em tempo de compilação.
Arquitetura
apps/shared/src/i18n/
pt-BR.ts → Fonte de verdade (todas as chaves definidas aqui)
en.ts → Traduções em inglês (tipadas contra `Messages`)
types.ts → Tipo `Messages` + união `Locale`
apps/frontend/src/lib/
locale-dictionaries.ts → Inglês no bundle inicial; cada outro locale em um chunk próprio
apps/frontend/src/hooks/
use-i18n.tsx → Hook de React Context sobre o dicionário carregado
Convenção Das Chaves
As chaves usam notação com pontos organizada por escopo de feature:
auth.loginTitle
chat.input.placeholder
settings.providers.caching.title
tools.generate_image.title
O tipo Messages é inferido a partir do dicionário pt-BR.ts usando as const. Adicionar uma chave em pt-BR sem traduzi-la em en.ts gera erro de compilação porque en.ts é tipado como Messages.
Como Funciona
Sistema De Tipos
types.ts define:
type DeepStringRecord<T> = {
[K in keyof T]: T[K] extends string
? string
: T[K] extends object
? DeepStringRecord<T[K]>
: never;
};
type Messages = DeepStringRecord<typeof ptBR>;
type Locale = 'pt-BR' | 'en';
Isso garante que en.ts tenha todas as mesmas chaves de pt-BR.ts, embora os valores possam ser strings diferentes.
Hook React
useI18n() retorna { t, locale, setLocale }:
import { useI18n } from '@/hooks/use-i18n';
function MyComponent() {
const { t } = useI18n();
return <h1>{t.auth.loginTitle}</h1>;
}
- Detecção de locale: verifica primeiro
localStorage(mangostudio:locale), depoisnavigator.language; o padrão éen. - Lazy loading: o inglês vai no chunk inicial e é o fallback. Cada outro locale é um chunk
próprio, importado pelo subpath
@mangostudio/shared/i18n/<locale>emlib/locale-dictionaries.ts.main.tsxcomeça a carregar o locale inicial antes do primeiro render, junto com a requisição de sessão. Uma troca em runtime mantém o texto atual até o próximo dicionário chegar; uma falha de carregamento renderiza inglês e mantém a escolha salva para a próxima inicialização. - Persistência:
setLocale()atualiza o estado React e olocalStorage.
Provider
I18nProvider envolve a raiz da aplicação e fornece o contexto de i18n:
<I18nProvider>
<App />
</I18nProvider>
Usa a API use() do React 19 para consumo do contexto. Lança erro se useI18n() for chamado fora do provider.
Adicionando Um Novo Idioma
-
Crie o arquivo do dicionário, por exemplo
apps/shared/src/i18n/es.ts. -
Copie a estrutura de chaves de
pt-BR.tse traduza todos os valores. -
Tipa o arquivo como
Messagespara garantir compatibilidade estrutural:import type { Messages } from './types'; export const messages: Messages = { /* traduções */ }; -
Adicione o locale ao tipo
Localeemtypes.ts:type Locale = 'pt-BR' | 'en' | 'es'; -
Conecte o lazy loading: um subpath export
./i18n/esemapps/shared/package.json, um loader emapps/frontend/src/lib/locale-dictionaries.ts, um ramo emdetectLocale()e uma entradanoRestrictedImportspara o subpath embiome.json. Módulos de shared importam um dicionário do próprio arquivo, nunca um valor em runtime do barreli18n, ou todos os dicionários vão para o chunk inicial. -
Adicione a opção de locale na UI de appearance settings, se fizer sentido.
Adicionando Novas Chaves
- Adicione a chave em
pt-BR.tsna seção apropriada. - Adicione a tradução em
en.tsno mesmo caminho. Se você esquecer esse passo, o build falhará porqueen.tsprecisa satisfazer o tipoMessages. - Use a chave via
t.path.to.keynos componentes.
Traduções Atuais
| Locale | Cobertura | Arquivo |
|---|---|---|
pt-BR |
Completa (fonte de verdade) | pt-BR.ts (~444 linhas) |
en |
Completa | en.ts (~440 linhas) |
Seções Das Chaves
O dicionário é organizado nestas seções de topo:
auth— login, signup e mensagens de errochat— UI do chat, barra de input, feed, contexto, continuação e eventos de sistemastudio— workspace de imagemgallery— página da galeriasettings— tabs, contexto, prompts, general, connectors, providers, tools e appearancecommon— rótulos compartilhados, como salvar, cancelar, deletar e confirmarproviders— nomes de exibição e descrições de provedoresthinking— rótulos de thinking/reasoningtools— nomes de tools, categorias e labels de parâmetroserrors— mensagens de erroapi— mensagens de status da APIgeneration— rótulos relacionados à geração