Skip to main content

Sistema de internacionalización (i18n)

E-Cashpoint Web usa react-i18next con 3 idiomas soportados: alemán (default), español e inglés.

Configuración

El sistema se inicializa en src/i18n/index.js:

import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import es from './locales/es.json';
import de from './locales/de.json';
import en from './locales/en.json';

const resources = {
es: { translation: es },
de: { translation: de },
en: { translation: en },
};

i18n
.use(initReactI18next)
.init({
resources,
lng: 'de', // idioma por defecto: alemán
fallbackLng: 'de',
interpolation: { escapeValue: false },
});

export default i18n;

Estructura de archivos

src/i18n/
├── index.js
└── locales/
├── de.json (alemán - fuente de verdad)
├── en.json (inglés)
└── es.json (español)

Cada archivo tiene +1700 keys organizadas en namespaces:

{
"common": { ... },
"auth": { ... },
"client": { ... },
"comercio": { ... },
"setup": { ... },
"demoMarketing": { ... },
"handelsPanel": { ... },
"servicesPanel": { ... },
"restaurantCatalog": { ... },
...
}

Uso en componentes

Hook básico

import { useTranslation } from 'react-i18next';

function MiComponente() {
const { t, i18n } = useTranslation();

return (
<div>
<h1>{t('common.welcome')}</h1>
<p>Idioma actual: {i18n.language}</p>
<button onClick={() => i18n.changeLanguage('es')}>
Español
</button>
</div>
);
}

Con interpolación

// de.json
{
"user": {
"greeting": "Hallo, {name}!"
}
}
<h1>{t('user.greeting', { name: 'María' })}</h1>
// Result: "Hallo, María!"

Con pluralización

{
"cart": {
"items": "{{count}} Artikel",
"items_plural": "{{count}} Artikel"
}
}
<span>{t('cart.items', { count: items.length })}</span>

Fallback

Si una key no existe en el idioma actual, se usa el fallbackLng (alemán):

t('non.existent.key')
// Devuelve "non.existent.key" si no existe en ningún idioma
// O usa el valor de fallbackLng si está disponible

Cambio de idioma en runtime

import { useTranslation } from 'react-i18next';

const { i18n } = useTranslation();

// Cambiar idioma
i18n.changeLanguage('es'); // a español
i18n.changeLanguage('en'); // a inglés
i18n.changeLanguage('de'); // a alemán

// Idioma actual
console.log(i18n.language); // 'es'

Agregar nuevas traducciones

Paso 1: Agregar keys a los 3 archivos

// de.json
{
"miModulo": {
"saludo": "Willkommen",
"despedida": "Auf Wiedersehen"
}
}

// en.json
{
"miModulo": {
"saludo": "Welcome",
"despedida": "Goodbye"
}
}

// es.json
{
"miModulo": {
"saludo": "Bienvenido",
"despedida": "Adiós"
}
}

Paso 2: Usar en el componente

const { t } = useTranslation();
return <h1>{t('miModulo.saludo')}</h1>;

Reglas importantes

✅ Hacer

  • ✅ Usar namespace descriptivo: handelsPanel.modal.title en vez de title1
  • Agrupar por módulo/componente: todas las keys de un modal van bajo modal.*
  • Reutilizar keys comunes: common.cancel, common.save en vez de duplicar
  • Mantener los 3 archivos sincronizados (mismas keys en los 3)
  • ✅ Usar parámetros para valores dinámicos: t('user.greeting', { name })
  • Camelcase en keys: myModule.myKey (no snake_case)
  • ✅ Usar fallback en alemán cuando un texto es muy específico

❌ Evitar

  • ❌ Hardcodear strings en alemán/español/inglés en JSX
  • ❌ Crear keys con caracteres especiales (solo letras, números, puntos, guiones)
  • ❌ Mezclar idiomas en un mismo namespace
  • ❌ Olvidar keys en uno de los 3 archivos
  • ❌ Strings de marketing de prompts de IA en i18n (son data, no UI)

Patrón para arrays en data

Si tienes arrays de objetos que se renderizan, usa labelKey:

// En constants.js
export const MY_OPTIONS = [
{ value: 'a', labelKey: 'options.a' },
{ value: 'b', labelKey: 'options.b' },
];

// En el componente
{MY_OPTIONS.map(opt => (
<option key={opt.value} value={opt.value}>
{t(opt.labelKey)}
</option>
))}

Próximos pasos