Arquitetura / Internacionalização (i18n)
Fonte no GitHub

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), depois navigator.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> em lib/locale-dictionaries.ts. main.tsx começ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 o localStorage.

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

  1. Crie o arquivo do dicionário, por exemplo apps/shared/src/i18n/es.ts.

  2. Copie a estrutura de chaves de pt-BR.ts e traduza todos os valores.

  3. Tipa o arquivo como Messages para garantir compatibilidade estrutural:

    import type { Messages } from './types';
    export const messages: Messages = {
      /* traduções */
    };
  4. Adicione o locale ao tipo Locale em types.ts:

    type Locale = 'pt-BR' | 'en' | 'es';
  5. Conecte o lazy loading: um subpath export ./i18n/es em apps/shared/package.json, um loader em apps/frontend/src/lib/locale-dictionaries.ts, um ramo em detectLocale() e uma entrada noRestrictedImports para o subpath em biome.json. Módulos de shared importam um dicionário do próprio arquivo, nunca um valor em runtime do barrel i18n, ou todos os dicionários vão para o chunk inicial.

  6. Adicione a opção de locale na UI de appearance settings, se fizer sentido.

Adicionando Novas Chaves

  1. Adicione a chave em pt-BR.ts na seção apropriada.
  2. Adicione a tradução em en.ts no mesmo caminho. Se você esquecer esse passo, o build falhará porque en.ts precisa satisfazer o tipo Messages.
  3. Use a chave via t.path.to.key nos 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 erro
  • chat — UI do chat, barra de input, feed, contexto, continuação e eventos de sistema
  • studio — workspace de imagem
  • gallery — página da galeria
  • settings — tabs, contexto, prompts, general, connectors, providers, tools e appearance
  • common — rótulos compartilhados, como salvar, cancelar, deletar e confirmar
  • providers — nomes de exibição e descrições de provedores
  • thinking — rótulos de thinking/reasoning
  • tools — nomes de tools, categorias e labels de parâmetros
  • errors — mensagens de erro
  • api — mensagens de status da API
  • generation — rótulos relacionados à geração