Internationalization system (i18n)
E-Cashpoint Web uses react-i18next with 3 languages supported: German (default), Spanish and English.
Configuration
The system is initialized in 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;
File structure
src/i18n/
├── index.js
└── locales/
├── de.json (alemán - fuente de verdad)
├── en.json (inglés)
└── es.json (español)
Each file has +1700 keys organized in namespaces:
{
"common": { ... },
"auth": { ... },
"client": { ... },
"comercio": { ... },
"setup": { ... },
"demoMarketing": { ... },
"handelsPanel": { ... },
"servicesPanel": { ... },
"restaurantCatalog": { ... },
...
}
Use in components
Basic hook
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>
);
}
With interpolation
// de.json
{
"user": {
"greeting": "Hallo, {name}!"
}
}
<h1>{t('user.greeting', { name: 'María' })}</h1>
// Result: "Hallo, María!"
With pluralization
{
"cart": {
"items": "{{count}} Artikel",
"items_plural": "{{count}} Artikel"
}
}
<span>{t('cart.items', { count: items.length })}</span>
###Fallback
If a key does not exist in the current language, fallbackLng (German) is used:
t('non.existent.key')
// Devuelve "non.existent.key" si no existe en ningún idioma
// O usa el valor de fallbackLng si está disponible
Language change in 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'
Add new translations
Step 1: Add keys to the 3 files
// de.json
{
"miModulo": {
"saludo": "Willkommen",
"despedida": "Auf Wiedersehen"
}
}
// en.json
{
"miModulo": {
"saludo": "Welcome",
"despedida": "Goodbye"
}
}
// es.json
{
"miModulo": {
"saludo": "Bienvenido",
"despedida": "Adiós"
}
}
Step 2: Use in the component
const { t } = useTranslation();
return <h1>{t('miModulo.saludo')}</h1>;
Important rules
✅ Do
- ✅ Use descriptive namespace:
handelsPanel.modal.titleinstead oftitle1 - ✅ Group by module/component: all keys of a modal go under
modal.* - ✅ Reuse common keys:
common.cancel,common.saveinstead of duplicating - ✅ Keep the 3 files synchronized (same keys in all 3)
- ✅ Use parameters for dynamic values:
t('user.greeting', { name }) - ✅ Camelcase in keys:
myModule.myKey(not snake_case) - ✅ Use fallback in German when a text is very specific
❌ Avoid
- ❌ Hardcode strings in German/Spanish/English in JSX
- ❌ Create keys with special characters (only letters, numbers, periods, hyphens)
- ❌ Mix languages in the same namespace
- ❌ Forget keys in one of the 3 files
- ❌ AI prompt marketing strings on i18n (they are data, not UI)
Pattern for arrays in data
If you have arrays of objects that are rendered, use 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>
))}
Next steps
- Get to know the styles and themes →
- Go back to architecture →