Search K
Guia de tradução
O SnapOtter já vem com 21 idiomas prontos para uso. O sistema de i18n usa um runtime próprio e leve, com completude de locale garantida pelo TypeScript e code-splitting dinâmico.
Idiomas suportados
| Code | Language | Native Name | Direction |
|---|---|---|---|
en | Inglês | English | LTR |
zh-CN | Chinês (Simplificado) | 简体中文 | LTR |
zh-TW | Chinês (Tradicional) | 繁體中文 | LTR |
ja | Japonês | 日本語 | LTR |
ko | Coreano | 한국어 | LTR |
es | Espanhol | Español | LTR |
fr | Francês | Français | LTR |
it | Italiano | Italiano | LTR |
pt-BR | Português (Brasil) | Português (Brasil) | LTR |
de | Alemão | Deutsch | LTR |
nl | Holandês | Nederlands | LTR |
sv | Sueco | Svenska | LTR |
ru | Russo | Русский | LTR |
pl | Polonês | Polski | LTR |
uk | Ucraniano | Українська | LTR |
ar | Árabe | العربية | RTL |
tr | Turco | Türkçe | LTR |
hi | Híndi | हिन्दी | LTR |
vi | Vietnamita | Tiếng Việt | LTR |
id | Indonésio | Bahasa Indonesia | LTR |
th | Tailandês | ไทย | LTR |
Como funciona a detecção de idioma
O SnapOtter usa uma ordem de resolução em três camadas:
- Preferência do usuário - armazenada em
localStorage("snapotter-locale")e sincronizada com as configurações do usuário quando autenticado - Detecção automática do navegador - percorre o array
navigator.languagescom correspondência de prefixo BCP 47 - Padrão da instância - a variável de ambiente
DEFAULT_LOCALEdo administrador (obtida deGET /api/v1/config/locale) - Fallback para inglês - sempre disponível
Os usuários podem alterar o idioma a partir de:
- O seletor Globo no rodapé (desktop, sempre visível)
- O seletor de idioma da página de login (pré-autenticação)
- A seção Configurações > Geral (preferência por usuário)
- O menu suspenso de idioma da barra lateral móvel
- A seção Configurações > Sistema define o padrão para toda a instância (somente administrador)
Como funcionam as traduções
Todas as strings da interface ficam em packages/shared/src/i18n/. O arquivo de referência é en.ts, que exporta um objeto tipado com todas as strings que o app usa (~1500 chaves). Os demais idiomas são arquivos separados (por exemplo, de.ts, fr.ts) que exportam a mesma estrutura.
O tipo TranslationKeys usa DeepStringRecord para aceitar qualquer valor de string enquanto impõe a estrutura das chaves. O TypeScript detecta chaves faltantes em qualquer arquivo de tradução em tempo de compilação.
Apenas o locale ativo é carregado em runtime via import() dinâmico, mantendo o bundle principal pequeno.
Usando traduções em componentes
tsx
import { useTranslation } from "@/contexts/i18n-context";
import { format, plural } from "@/lib/format";
function MyComponent() {
const { t, locale, setLocale } = useTranslation();
return (
<div>
<h1>{t.common.settings}</h1>
<p>{format(t.settings.people.deleteConfirm, { username: "admin" })}</p>
<p>{plural(count, t.automate.fileCount, t.automate.fileCountPlural)}</p>
</div>
);
}Contribuindo com uma tradução
Aceitamos PRs de tradução diretamente. Você pode aprimorar um locale existente ou adicionar um novo.
Para relatar uma tradução incorreta sem enviar código, abra uma GitHub Issue com o idioma, a string incorreta e a correção sugerida.
TIP
PRs de tradução não exigem aprovação prévia. Faça um fork do repositório, faça suas alterações e abra um PR. Consulte o Guia de Contribuição para o processo completo de PR e o requisito de CLA.
Como criar ou atualizar uma tradução
1. Faça o fork e clone
bash
git clone https://github.com/<your-username>/snapotter.git
cd snapotter
pnpm install2. Copie o arquivo de referência (apenas novo idioma)
Pule esta etapa se você estiver aprimorando uma tradução existente.
bash
cp packages/shared/src/i18n/en.ts packages/shared/src/i18n/XX.ts3. Traduza as strings
Abra seu novo arquivo e traduza cada valor de string. Mantenha a estrutura do objeto e as chaves exatamente iguais.
ts
import type { TranslationKeys } from "./en.js";
export const xx: TranslationKeys = {
common: {
upload: "Your translation here",
// ... translate all entries
},
// ... translate all sections
} as const;Regras:
- Não traduza as chaves do objeto, apenas os valores de string
- Mantenha
as constno final - Importe
TranslationKeysde./en.jse tipe sua exportação - Mantenha os placeholders
{variable}exatamente como estão - Os arrays (
rotatingPhrases,progressMessages) devem ter o mesmo número de entradas - Não traduza: SnapOtter, JPEG, PNG, WebP, EXIF, API e outros termos técnicos
4. Registre o locale (apenas novo idioma)
Adicione seu locale a SUPPORTED_LOCALES em packages/shared/src/i18n/index.ts:
ts
{ code: "xx", name: "Language Name", nativeName: "Native Name", dir: "ltr" },5. Verifique
bash
pnpm typecheck # catches missing or mistyped keys
pnpm lint # formatting check
pnpm dev # manually verify strings appear correctly6. Envie
Abra um PR contra main com um título como feat(i18n): add Swedish translation ou fix(i18n): correct German typos. O bot do CLA pedirá que você assine na sua primeira contribuição.
Adicionando novas chaves de tradução
Ao adicionar um novo recurso que precise de novas strings de interface:
- Adicione as novas chaves a
en.tsprimeiro (o arquivo de referência) - Execute
pnpm typecheck- cada arquivo de locale falhará se estiver sem a nova chave - Adicione a nova chave a todos os arquivos de locale (use o inglês como fallback temporário)
Configuração
Defina o idioma padrão da instância por meio de variável de ambiente:
yaml
DEFAULT_LOCALE: "de" # German as the default for all new usersReferência de arquivos
| File | Purpose |
|---|---|
packages/shared/src/i18n/en.ts | Strings em inglês (locale de referência, ~1500 chaves) |
packages/shared/src/i18n/index.ts | SUPPORTED_LOCALES, loadTranslations(), exportações de tipos |
packages/shared/src/i18n/<locale>.ts | Arquivos de tradução por idioma |
apps/web/src/contexts/i18n-context.tsx | I18nProvider, hook useTranslation() |
apps/web/src/lib/format.ts | Helpers format(), plural(), formatFileSize() |
apps/api/src/routes/config.ts | Endpoint público GET /api/v1/config/locale |
Traduzindo o site, a documentação e a referência da API
O suporte a 21 idiomas descrito acima cobre o app. O site público (snapotter.com), este site de documentação e a referência da API REST também são traduzidos para todos os 21 idiomas, por um pipeline separado protegido por hash que reutiliza os mesmos nomes e descrições de ferramentas de packages/shared/src/i18n, de modo que a terminologia permaneça consistente em toda parte.
Traduzido por máquina por padrão
Toda página que não seja em inglês no site e na documentação é traduzida por máquina na primeira passagem (por uma sessão do Claude Code, não por um serviço de terceiros) e traz um pequeno banner dispensável dizendo isso, com um link de volta para cá. Isso é deliberado: entrega todos os 21 idiomas com rapidez e honestidade e, então, convida a comunidade a refinar as páginas que mais importam. A tradução por máquina transmite o significado; a revisão humana faz o texto soar natural.
Como o pipeline decide o que traduzir
Cada unidade traduzível do texto-fonte em inglês é submetida a hash, e o hash é armazenado ao lado de sua tradução. A cada execução, o pipeline:
- traduz qualquer unidade que ainda não tenha tradução,
- pula qualquer unidade cujo hash armazenado ainda corresponda ao texto-fonte em inglês,
- retraduz uma unidade de máquina quando seu texto-fonte em inglês muda,
- e sinaliza uma unidade refinada por humano como
stale(precisa de revisão) quando seu texto-fonte em inglês muda, em vez de sobrescrever seu trabalho.
Refinando uma tradução da web por PR
Você aprimora uma tradução do site, da documentação ou da referência da API da mesma forma que aprimora um locale do app: editando o arquivo gerado e abrindo um PR.
- Encontre a tradução gerada para o seu idioma:
- strings de interface do site:
apps/landing/src/i18n/<locale>.json - uma página da documentação:
apps/docs/<locale>/**.md - a referência da API:
apps/api/src/openapi.<locale>.yaml
- strings de interface do site:
- Edite o texto. Mantenha código, links,
{placeholders}e quaisquer marcadores⸤I18N…⸥exatamente como estão; o validador do pipeline rejeita uma tradução que remova ou reordene esses elementos. - Abra um PR. Editar uma unidade muda sua proveniência de
machineparahuman, de modo que o pipeline nunca a sobrescreverá em uma execução posterior. Se o texto-fonte em inglês mudar depois, sua unidade é sinalizada comostalepara revisão, em vez de ser substituída silenciosamente.
Para relatar uma tradução incorreta sem enviar código, abra uma GitHub Issue com a URL da página, o idioma, o texto incorreto e sua correção sugerida.
TIP
Os mantenedores executam o pipeline de tradução; você não precisa de uma chave de API para contribuir. Basta editar o arquivo gerado e abrir um PR. Consulte scripts/i18n/README.md para saber como o pipeline funciona.
