Search K
Guía de traducción
SnapOtter incluye 21 idiomas de fábrica. El sistema i18n usa un runtime propio y ligero, con integridad de locales reforzada por TypeScript y división dinámica del código.
Idiomas admitidos
| Code | Language | Native Name | Direction |
|---|---|---|---|
en | Inglés | English | LTR |
zh-CN | Chino (simplificado) | 简体中文 | LTR |
zh-TW | Chino (tradicional) | 繁體中文 | LTR |
ja | Japonés | 日本語 | LTR |
ko | Coreano | 한국어 | LTR |
es | Español | Español | LTR |
fr | Francés | Français | LTR |
it | Italiano | Italiano | LTR |
pt-BR | Portugués (Brasil) | Português (Brasil) | LTR |
de | Alemán | Deutsch | LTR |
nl | Neerlandés | Nederlands | LTR |
sv | Sueco | Svenska | LTR |
ru | Ruso | Русский | LTR |
pl | Polaco | Polski | LTR |
uk | Ucraniano | Українська | LTR |
ar | Árabe | العربية | RTL |
tr | Turco | Türkçe | LTR |
hi | Hindi | हिन्दी | LTR |
vi | Vietnamita | Tiếng Việt | LTR |
id | Indonesio | Bahasa Indonesia | LTR |
th | Tailandés | ไทย | LTR |
Cómo funciona la detección de idioma
SnapOtter usa un orden de resolución de tres niveles:
- Preferencia del usuario: almacenada en
localStorage("snapotter-locale")y sincronizada con los ajustes del usuario cuando ha iniciado sesión - Detección automática del navegador: recorre el array
navigator.languagescon coincidencia de prefijos BCP 47 - Predeterminado de la instancia: la variable de entorno
DEFAULT_LOCALEdel administrador (obtenida deGET /api/v1/config/locale) - Respaldo en inglés: siempre disponible
Los usuarios pueden cambiar el idioma desde:
- El selector del globo terráqueo del pie de página (escritorio, siempre visible)
- El selector de idioma de la página de inicio de sesión (previo a la autenticación)
- La sección Ajustes > General (preferencia por usuario)
- El menú desplegable de idioma de la barra lateral móvil
- La sección Ajustes > Sistema, que fija el idioma predeterminado de toda la instancia (solo administradores)
Cómo funcionan las traducciones
Todas las cadenas de la interfaz viven en packages/shared/src/i18n/. El archivo de referencia es en.ts, que exporta un objeto tipado con todas las cadenas que usa la aplicación (~1500 claves). Los demás idiomas son archivos independientes (por ejemplo, de.ts, fr.ts) que exportan la misma forma.
El tipo TranslationKeys usa DeepStringRecord para aceptar cualquier valor de cadena a la vez que refuerza la estructura de claves. TypeScript detecta las claves que falten en cualquier archivo de traducción en tiempo de compilación.
En tiempo de ejecución solo se carga el locale activo mediante import() dinámico, lo que mantiene pequeño el bundle principal.
Uso de traducciones en los 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>
);
}Contribuir con una traducción
Recibimos con gusto PRs de traducción directamente. Puedes mejorar un locale existente o añadir uno nuevo.
Para informar de una traducción incorrecta sin enviar código, abre un GitHub Issue con el idioma, la cadena incorrecta y la corrección sugerida.
TIP
Los PRs de traducción no requieren aprobación previa. Haz un fork del repositorio, realiza tus cambios y abre un PR. Consulta la Guía de contribución para conocer el proceso completo de PR y el requisito del CLA.
Cómo crear o actualizar una traducción
1. Fork y clonado
bash
git clone https://github.com/<your-username>/snapotter.git
cd snapotter
pnpm install2. Copiar el archivo de referencia (solo para un idioma nuevo)
Omite este paso si estás mejorando una traducción existente.
bash
cp packages/shared/src/i18n/en.ts packages/shared/src/i18n/XX.ts3. Traducir las cadenas
Abre tu nuevo archivo y traduce cada valor de cadena. Mantén exactamente igual la estructura del objeto y las claves.
ts
import type { TranslationKeys } from "./en.js";
export const xx: TranslationKeys = {
common: {
upload: "Your translation here",
// ... translate all entries
},
// ... translate all sections
} as const;Reglas:
- No traduzcas las claves del objeto, solo los valores de cadena
- Mantén
as constal final - Importa
TranslationKeysdesde./en.jsy tipa tu exportación - Mantén los marcadores
{variable}exactamente como están - Los arrays (
rotatingPhrases,progressMessages) deben tener el mismo número de entradas - No traduzcas: SnapOtter, JPEG, PNG, WebP, EXIF, API y otros términos técnicos
4. Registrar el locale (solo para un idioma nuevo)
Añade tu locale a SUPPORTED_LOCALES en packages/shared/src/i18n/index.ts:
ts
{ code: "xx", name: "Language Name", nativeName: "Native Name", dir: "ltr" },5. Verificar
bash
pnpm typecheck # catches missing or mistyped keys
pnpm lint # formatting check
pnpm dev # manually verify strings appear correctly6. Enviar
Abre un PR contra main con un título como feat(i18n): add Swedish translation o fix(i18n): correct German typos. El bot del CLA te pedirá que firmes en tu primera contribución.
Añadir nuevas claves de traducción
Al añadir una nueva función que necesita cadenas nuevas en la interfaz:
- Añade primero las nuevas claves a
en.ts(el archivo de referencia) - Ejecuta
pnpm typecheck: cada archivo de locale fallará si le falta la nueva clave - Añade la nueva clave a todos los archivos de locale (usa el inglés como respaldo temporal)
Configuración
Establece el idioma predeterminado de la instancia mediante una variable de entorno:
yaml
DEFAULT_LOCALE: "de" # German as the default for all new usersReferencia de archivos
| File | Purpose |
|---|---|
packages/shared/src/i18n/en.ts | Cadenas en inglés (locale de referencia, ~1500 claves) |
packages/shared/src/i18n/index.ts | SUPPORTED_LOCALES, loadTranslations(), exportaciones de tipos |
packages/shared/src/i18n/<locale>.ts | Archivos de traducción 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 |
Traducir el sitio web, la documentación y la referencia de la API
El soporte de 21 idiomas descrito arriba cubre la aplicación. El sitio web público (snapotter.com), este sitio de documentación y la referencia de la API REST también se traducen a los 21 idiomas, mediante una canalización independiente controlada por hash que reutiliza los mismos nombres y descripciones de herramientas de packages/shared/src/i18n, de modo que la terminología se mantenga coherente en todas partes.
Traducción automática de forma predeterminada
Cada página que no está en inglés del sitio web y de la documentación se traduce automáticamente en la primera pasada (por una sesión de Claude Code, no por un servicio de terceros) y lleva un banner pequeño y descartable que así lo indica, con un enlace de vuelta aquí. Es intencional: así se publican los 21 idiomas de forma rápida y honesta, y luego se invita a la comunidad a refinar las páginas más importantes. La traducción automática transmite el significado; la revisión humana hace que se lea con naturalidad.
Cómo decide la canalización qué traducir
Cada unidad traducible del texto original en inglés se somete a hash, y ese hash se almacena junto a su traducción. En cada ejecución, la canalización:
- traduce cualquier unidad que todavía no tenga traducción,
- omite cualquier unidad cuyo hash almacenado siga coincidiendo con el texto original en inglés,
- vuelve a traducir una unidad automática cuando su texto original en inglés cambia,
- y marca una unidad refinada por un humano como
stale(necesita revisión) cuando su texto original en inglés cambia, en lugar de sobrescribir tu trabajo.
Refinar una traducción del sitio web mediante un PR
Mejoras una traducción del sitio web, de la documentación o de la referencia de la API igual que mejoras un locale de la aplicación: editando el archivo generado y abriendo un PR.
- Encuentra la traducción generada para tu idioma:
- cadenas de la interfaz del sitio web:
apps/landing/src/i18n/<locale>.json - una página de documentación:
apps/docs/<locale>/**.md - la referencia de la API:
apps/api/src/openapi.<locale>.yaml
- cadenas de la interfaz del sitio web:
- Edita el texto. Mantén el código, los enlaces,
{placeholders}y cualquier marcador⸤I18N…⸥exactamente como están; el validador de la canalización rechaza una traducción que los omita o reordene. - Abre un PR. Editar una unidad cambia su procedencia de
machineahuman, de modo que la canalización nunca la sobrescribirá en una ejecución posterior. Si el texto original en inglés cambia después, tu unidad se marca comostalepara revisión en lugar de reemplazarse en silencio.
Para informar de una traducción incorrecta sin enviar código, abre un GitHub Issue con la URL de la página, el idioma, el texto incorrecto y tu corrección sugerida.
TIP
Los mantenedores ejecutan la canalización de traducción; no necesitas una clave de API para contribuir. Solo edita el archivo generado y abre un PR. Consulta scripts/i18n/README.md para saber cómo se ejecuta la canalización.
