Back to sh0
sh0

i18n desde el día uno: 5 idiomas en 105 sesiones

Por qué construimos sh0 con soporte para 5 idiomas desde la primera sesión del panel, y cómo mantuvimos la ortografía correcta en inglés, francés, español, portugués y suajili a lo largo de 105 sesiones de desarrollo.

Thales & Claude | March 30, 2026 11 min sh0
EN/ FR/ ES
i18ninternationalizationsveltelocalizationafricalanguages

La mayoría de las startups añaden la internacionalización como una ocurrencia tardía. El panel se lanza en inglés. Seis meses después, un cliente en París pide francés. Alguien envuelve cada cadena en una función t(), extrae cientos de claves a un archivo JSON y pasa dos semanas cazando las cadenas que se les pasaron. Literales de plantilla incrustados en la lógica de componentes. Mensajes de error codificados en las respuestas de la API. Tooltips que fueron "solo una cadena rápida" y nunca se extrajeron.

Nosotros lo hicimos al revés. El 12 de marzo de 2026 -- Fase 12, la primera sesión del panel -- cada cadena en la interfaz de sh0 estaba envuelta en una función t(), respaldada por cinco archivos de localización. Inglés, francés, español, portugués y suajili. No porque tuviéramos usuarios en los cinco idiomas desde el primer día. Porque sabíamos que los tendríamos, y el costo de añadir i18n desde el inicio es una fracción del costo de adaptarlo después.

Por qué estos cinco idiomas

La selección de idiomas no fue arbitraria. Se mapea directamente a los mercados objetivo de sh0:

Inglés es el idioma global por defecto. Toda herramienta de desarrollo necesita inglés. Es el idioma de la documentación, la API, el CLI y la interfaz de usuario principal.

Francés es el idioma principal de África Occidental -- Senegal, Costa de Marfil, Camerún, la República Democrática del Congo y una docena más de países. sh0 está construido desde Abiyán. Nuestros primeros usuarios son desarrolladores francófonos. No ofrecer francés sería absurdo.

Español cubre América Latina, un mercado masivo para herramientas de infraestructura asequibles. Los desarrolladores en México, Colombia, Argentina y Chile son sensibles al precio y están desatendidos por las grandes plataformas en la nube. Un PaaS autoalojado en su idioma es inmediatamente más accesible.

Portugués cubre dos regiones distintas: la África lusófona (Mozambique, Angola, Guinea-Bisáu, Cabo Verde, Santo Tomé) y Brasil, la economía tecnológica más grande de Sudamérica. Los desarrolladores de habla portuguesa frecuentemente se ven obligados a usar herramientas solo en inglés. Decidimos que eso no debería ser necesario.

Suajili cubre África Oriental -- Kenia, Tanzania, Uganda y partes de la República Democrática del Congo. Es el idioma africano más hablado por total de hablantes (más de 100 millones). Incluir suajili es una declaración sobre para quién estamos construyendo: no solo los mercados francófonos que mejor conocemos, sino el continente.

Estos cinco idiomas cubren aproximadamente 2.500 millones de personas cuyo idioma primario o secundario es uno de ellos. No son los cinco idiomas más hablados del mundo (esa lista incluiría mandarín, hindi y árabe). Son los cinco idiomas más relevantes para nuestros usuarios.

La implementación

El sistema de i18n es deliberadamente simple. Sin bibliotecas pesadas. Sin formato de mensaje ICU. Sin reglas de pluralización (aún). Solo una función t(), un store de localización y cinco objetos TypeScript.

El store de localización

typescript// stores/locale.ts
import { writable } from 'svelte/store';

const stored = typeof localStorage !== 'undefined'
  ? localStorage.getItem('sh0-locale')
  : null;

export const locale = writable<string>(stored || 'en');

locale.subscribe((value) => {
  if (typeof localStorage !== 'undefined') {
    localStorage.setItem('sh0-locale', value);
  }
});

La localización persiste en localStorage. En la primera visita, por defecto es inglés. El usuario puede cambiar mediante un selector de idioma en la página de configuración. La selección sobrevive a recargas de página y reinicios del navegador.

Los archivos de traducción

Cada localización es un archivo TypeScript que exporta un objeto anidado:

typescript// i18n/en.ts
export const en = {
  nav: {
    home: 'Home',
    stacks: 'Stacks',
    deploy: 'Deploy',
    backups: 'Backups',
    monitoring: 'Monitoring',
    settings: 'Settings',
  },
  dashboard: {
    title: 'Dashboard',
    totalApps: 'Total Apps',
    totalDatabases: 'Total Databases',
    activeDeployments: 'Active Deployments',
    systemStatus: 'System Status',
  },
  deploy: {
    title: 'Deploy Hub',
    searchPlaceholder: 'Search 183 deploy options...',
    selectStack: 'Select a stack',
    chooseStack: 'Choose a stack...',
    createNewStack: 'Create new stack',
    // ... 42 claves en total
  },
  // ... 15+ secciones
};
typescript// i18n/fr.ts
export const fr = {
  nav: {
    home: 'Accueil',
    stacks: 'Stacks',
    deploy: 'Déployer',
    backups: 'Sauvegardes',
    monitoring: 'Surveillance',
    settings: 'Paramètres',
  },
  dashboard: {
    title: 'Tableau de bord',
    totalApps: 'Applications totales',
    totalDatabases: 'Bases de données totales',
    activeDeployments: 'Déploiements actifs',
    systemStatus: 'État du système',
  },
  deploy: {
    title: 'Hub de déploiement',
    searchPlaceholder: 'Rechercher parmi 183 options de déploiement...',
    selectStack: 'Sélectionner un stack',
    chooseStack: 'Choisir un stack...',
    createNewStack: 'Créer un nouveau stack',
    // ...
  },
};

Cada diacrítico importa. "Parametres" sin su acento grave es un error ortográfico. "Donnees" sin su acento agudo es un error ortográfico. En los archivos fuente reales, cada cadena en francés lleva su ortografía completa -- Déployer, Paramètres, données, Déploiements, État, système, déploiement, Sélectionner, Créer -- cada una con el acento agudo, grave o circunflejo correcto. Los logs de sesión confirman esta disciplina en cada fase: "Francés: diacríticos correctos."

Para una herramienta utilizada por desarrolladores en Abiyán, Dakar y Duala, el francés mal escrito no es un problema cosmético -- señala descuido. El mismo estándar se aplica al español (aplicación, compilación, categoría, éxito -- cada uno con su acento) y al portugués (repositório, início, variáveis, conteúdo, serviço, implantação -- cada uno con su acento o cedilla correcta).

La función t()

typescript// i18n/index.ts
import { get } from 'svelte/store';
import { locale } from '../stores/locale';
import { en } from './en';
import { fr } from './fr';
import { es } from './es';
import { pt } from './pt';
import { sw } from './sw';

const translations: Record<string, typeof en> = { en, fr, es, pt, sw };

export function t(key: string): string {
  const lang = get(locale);
  const keys = key.split('.');
  let value: any = translations[lang] || translations.en;

  for (const k of keys) {
    value = value?.[k];
  }

  if (typeof value === 'string') return value;

  // Respaldo a inglés
  value = translations.en;
  for (const k of keys) {
    value = value?.[k];
  }

  return typeof value === 'string' ? value : key;
}

La función t() resuelve una ruta de clave con puntos (ej. 'deploy.selectStack') contra la localización activa. Si la clave falta en el idioma activo, recurre al inglés. Si también falta en inglés, devuelve la clave misma -- lo que hace que las cadenas sin traducir sean inmediatamente visibles en la interfaz como deploy.selectStack en lugar de desaparecer silenciosamente.

Esta cadena de respaldo es importante para la mantenibilidad. Cuando añadimos una nueva funcionalidad, siempre escribimos las traducciones en inglés primero. Los otros cuatro idiomas pueden rezagarse una sesión o dos sin romper la interfaz -- las claves sin traducir simplemente aparecen en inglés hasta que se añaden las traducciones.

La disciplina del mantenimiento

La parte más difícil del i18n no es la configuración inicial. Es mantener cinco archivos de localización a través de 105 sesiones de desarrollo. Cada nuevo componente, cada nueva página, cada nuevo modal significa nuevas claves de traducción en los cinco archivos.

Mantuvimos la disciplina a través de una regla simple: cada pull request que añade cadenas de interfaz debe actualizar los cinco archivos de localización. Sin excepciones. El archivo en inglés es la fuente de verdad. Los otros cuatro se actualizan en la misma sesión, frecuentemente por el mismo agente que escribió el componente.

Los logs de sesión cuentan la historia:

  • Fase 12: Scaffold inicial. 7 secciones en los 5 idiomas.
  • Fase 13: +9 secciones (panel, aplicaciones, despliegues, dominios, logs, env, estado, configuración, pestañas).
  • Fase 14: +4 secciones (bases de datos, respaldos, monitoreo, servidor).
  • Rediseño de stacks: +65 claves (nav, stacks, bienvenida, cómo_funciona, secciones_stack).
  • Deploy Hub: +42 claves (sección deploy + nav.deploy).
  • Terminal + Storage: +22 claves (terminal, storage, pestañas).
  • Explorador de archivos: +26 claves (sección archivos + tabs.files).

Para cuando el panel alcanzó la completitud funcional, cada archivo de localización contenía cientos de claves organizadas en aproximadamente 20 secciones. El esfuerzo total de traducción, distribuido en todas las sesiones, fue quizás 3-4 horas de trabajo. El costo de hacerlo retroactivamente habría sido 3-4 días -- más el riesgo de cadenas faltantes en estados de interfaz oscuros.

El costo de añadir i18n después

Hemos visto la alternativa. En Deblo.ai -- otro producto de ZeroSuite -- el frontend inicial se construyó sin i18n. Cuando se tomó la decisión de soportar francés junto al inglés, cada componente tuvo que ser auditado. Literales de cadena enterrados en operadores ternarios. Mensajes de error concatenados desde variables. Texto de marcador de posición que fue "temporal" durante tres meses.

La adaptación tomó una semana completa y aún así pasó por alto casos especiales que surgieron meses después: un tooltip aquí, un diálogo de confirmación allá, un mensaje de error que solo aparece cuando una llamada API específica falla.

El desglose de costos es asimétrico:

EnfoqueCosto inicialCosto continuo por funcionalidadCosto de adaptación
i18n desde el día uno~2 horas~30 segundos por cadena0
i18n como ocurrencia tardía003-5 días + faltas continuas

El enfoque de "i18n desde el día uno" tiene un costo inicial mayor (escribir la función t(), configurar archivos de localización, envolver cadenas). Pero el costo continuo es insignificante -- teclear t('deploy.title') en lugar de 'Deploy Hub' añade quizás cinco segundos por cadena. El enfoque de adaptación tiene costo cero hasta el día que tiene un costo masivo, más una cola continua de cadenas faltantes.

Traducciones de muestra

Para ilustrar el cuidado que va en cada idioma, aquí está la misma sección en las cinco localizaciones:

ClaveInglésFrancésEspañolPortuguésSuajili
nav.homeHomeAccueilInicioInícioNyumbani
nav.deployDeployDéployerDesplegarImplantarSambaza
nav.backupsBackupsSauvegardesCopias de seguridadBackupsNakala rudufu
nav.monitoringMonitoringSurveillanceMonitoreoMonitoramentoUfuatiliaji
nav.settingsSettingsParamètresConfiguraciónConfiguraçõesMipangilio
dashboard.systemStatusSystem StatusÉtat du systèmeEstado del sistemaEstado do sistemaHali ya mfumo

Cada traducción no es solo una sustitución palabra por palabra. "Backups" en portugués se mantiene como "Backups" porque ese es el término que realmente usan los desarrolladores de habla portuguesa. "Sauvegardes" en francés se prefiere sobre el anglicismo "backups" porque los desarrolladores francófonos en África Occidental usan el término francés. "Nakala rudufu" en suajili significa literalmente "copias duplicadas" -- la formulación más natural para el concepto.

Lo que no construimos (todavía)

El sistema de i18n actual es deliberadamente mínimo. Varias funcionalidades están en la hoja de ruta pero no se necesitaban para el lanzamiento:

Pluralización. Los plurales en inglés son simples ("1 app" / "2 apps"), pero el francés, español y portugués tienen sustantivos con género y reglas de concordancia. El suajili tiene clases nominales que afectan la pluralización de forma completamente diferente. Por ahora, usamos construcciones que evitan problemas de pluralización: "Total Apps: 5" en lugar de "You have 5 app(s)."

Formato de fecha y número. Las fechas actualmente se muestran en formato ISO o relativo ("hace 2 horas"). El formato específico por localización (DD/MM/YYYY para francés, MM/DD/YYYY para inglés) aún no está implementado.

Soporte RTL. Ninguno de nuestros cinco idiomas de lanzamiento es de derecha a izquierda. Si añadimos árabe (una opción natural para el norte de África), el diseño necesitará soporte RTL.

Traducciones del lado del servidor. Los mensajes de error de la API están actualmente en inglés. Traducirlos requeriría pasar la preferencia de localización del usuario al backend, lo que añade complejidad a cada respuesta de error.

Estas son brechas reales, pero son brechas que podemos llenar incrementalmente. La base -- la función t(), el store de localización, los cinco archivos de traducción y la disciplina de mantenerlos -- está en su lugar.

La declaración cultural

Hay una razón por la que incluimos suajili cuando habría sido más fácil detenernos en cuatro idiomas. sh0 está construido desde Abiyán por un equipo que cree que los desarrolladores africanos merecen herramientas en sus idiomas. No como una ocurrencia tardía de traducción. No como una contribución comunitaria seis meses después del lanzamiento. Desde el día uno.

La mayoría de las herramientas de desarrollo son solo en inglés o inglés más algunos idiomas europeos. La suposición es que los desarrolladores leen inglés, así que ¿para qué molestarse? La suposición es incorrecta. Un desarrollador en Dar es-Salam puede leer documentación en inglés, pero piensa más rápido en suajili. Un desarrollador en Duala escribe código en inglés pero discute arquitectura en francés. El idioma no es solo comprensión -- es comodidad, velocidad y pertenencia.

Al distribuir cinco idiomas desde la primera versión del panel, sh0 hace una declaración: esta herramienta es para ti. No eventualmente. Ahora.

Esa declaración nos costó aproximadamente tres a cuatro horas de trabajo de traducción distribuidas en 105 sesiones. Es, por cualquier medida, la inversión de mayor retorno que hicimos en todo el producto.


Esto concluye la serie del panel. Siguiente: Construir un CLI que se sienta como en casa -- cómo construimos el CLI sh0 con comandos deploy, logs, env y SSH que hacen del terminal un ciudadano de primera clase junto al panel.

Share this article:

Responses

Write a response
0/2000
Loading responses...

Related Articles