Internationalization
The frontend uses an enterprise-grade i18next setup via react-i18next with optimized translation loading and CDN support.
Architecture Overview
This project uses a modern, scalable i18n architecture that:
- Eliminates dev server issues: No file copying during development
- Supports CDN delivery: Configurable CDN URLs for production deployment
- Lazy loads translations: Namespaces are loaded on-demand for better performance
- Environment-aware: Different configurations for development and production
- Hot-reloadable: Translation updates are reflected immediately in development
Development vs Production
Development
- Translations served dynamically via custom Vite plugin
- No file copying or build-time operations
- Immediate hot-reload when translation files change
- Debug mode enabled for detailed logging
Production
- Translations bundled as static assets during build
- Optimized caching headers and lazy loading
- Preloading of critical namespaces for better UX
Translation Files
You can add or edit translations in apps/frontend/locales/en. The project follows a namespace-based approach for organizing translations.
Structure
apps/frontend/locales/
├── en/
│ ├── auth.json # Authentication pages (login, register, etc.)
│ ├── common.json # Common/shared translations
│ ├── header.json # Header component
│ ├── footer.json # Footer component
│ ├── landing_page.json # Landing page
│ ├── messages.json # Message form and actions
│ ├── second_page.json # Second page
│ └── color_mode_toggle.json # Theme toggle
├── de/ # German translations
│ └── ...
├── es/ # Spanish translations
│ └── ...
└── fr/ # French translations
└── ...LLM Prompt
Here is a prompt you can use to create translations from a single input:
For the attached English files, can you add translations for German, Spanish, and French? Please provide a single zip file with each language's files in its own directory (de, es, fr).You can unzip the file and extract the new translations to apps/frontend/locales.
Available Namespaces
The project currently includes the following translation namespaces:
auth- Authentication pages (login, register, verify email, dashboard)common- Common/shared translationsheader- Header component translationsfooter- Footer component translationslanding_page- Landing page contentmessages- Message form and actionssecond_page- Second page contentcolor_mode_toggle- Theme toggle component
Usage in Components
Basic Usage
tsx
import { useTranslation } from 'react-i18next';
function MyComponent() {
const { t } = useTranslation('common');
return <h1>{t('welcome')}</h1>;
}Authentication Pages Example
tsx
import { useTranslation } from 'react-i18next';
function LoginPage() {
const { t } = useTranslation('auth');
return (
<div>
<h1>{t('login.title')}</h1>
<p>{t('login.subtitle')}</p>
<button>{t('login.submit_button')}</button>
</div>
);
}With Multiple Namespaces
tsx
import { useTranslation } from 'react-i18next';
function MyComponent() {
const { t } = useTranslation(['common', 'forms']);
return (
<div>
<h1>{t('common:welcome')}</h1>
<p>{t('forms:submit')}</p>
</div>
);
}Advanced Features
Interpolation
tsx
// In your translation file: "welcome": "Hello, {{name}}!"
const { t } = useTranslation('common');
return <p>{t('welcome', { name: 'John' })}</p>;Pluralization
tsx
// In your translation file:
// "items": "{{count}} item",
// "items_plural": "{{count}} items"
const { t } = useTranslation('common');
return <p>{t('items', { count: 5 })}</p>;Adding New Languages
- Create a new directory in
apps/frontend/locales/with the language code (e.g.,frfor French) - Copy the structure from the
endirectory - Translate the content in each JSON file
- Update the language configuration in
apps/frontend/src/app.config.ts(add toLANGUAGESarray)
Note: New languages are automatically detected and served - no build configuration needed.
Namespace Organization Best Practices
When to Create a New Namespace
Create a new namespace when:
- You have a distinct feature or page with many translations (e.g.,
authfor all authentication pages) - Translations are logically grouped and used together (e.g.,
headerfor header-specific content) - The namespace will be loaded on-demand for better performance
Namespace Naming Conventions
- Use lowercase with underscores for multi-word namespaces:
landing_page,color_mode_toggle - Name namespaces after the feature/component they serve:
auth,header,footer - Keep namespace names concise but descriptive
Example: Auth Namespace Structure
The auth.json namespace demonstrates good organization:
json
{
"login": {
"title": "Sign In",
"subtitle": "Welcome back!",
"email_label": "Email",
"submit_button": "Sign In"
},
"register": {
"title": "Create Account",
"email_label": "Email",
"submit_button": "Create Account"
},
"common": {
"loading": "Loading..."
}
}Benefits:
- Related translations grouped by page/feature
- Shared translations in
commonsection - Clear, hierarchical structure
- Easy to maintain and translate
Best Practices
- Use translation keys that are descriptive and follow a consistent naming convention
- Group related translations in the same namespace (e.g., all auth pages in
auth.json) - Use interpolation for dynamic values:
t('hello', { name: 'John' }) - Consider using pluralization for countable items:
t('items', { count: 5 }) - Keep translation keys flat when possible to avoid deep nesting (max 2-3 levels)
- Use comments in your translation files to provide context for translators
- Organize by feature/component rather than by type (e.g.,
auth.jsonnotbuttons.json) - Share common translations within a namespace using a
commonsection
Troubleshooting
Development Issues
- Translations not loading: Check that namespace exists in
apps/frontend/locales/{lang}/{namespace}.json - 404 errors: Verify the Vite dev server is running and the i18n plugin is configured correctly
- Hot reload not working: Translation files are watched automatically - check browser dev tools for errors
Production Issues
- Slow loading: Check network tab - translations should be cached after first load
- Missing translations: Ensure build process includes all language files via the
copyTranslationsPlugin
General
- Enable debug mode: Set
NODE_ENV=developmentto see detailed i18next logs - Check browser network: Look for failed HTTP requests to translation endpoints
- Namespace auto-discovery: Namespaces are automatically discovered - no manual registration needed
- Missing translations: If translations appear as keys (e.g., "auth.login.title"), check that:
- The namespace file exists in
locales/{lang}/{namespace}.json - The translation key path is correct
- The namespace is being loaded via
useTranslation('{namespace}')
- The namespace file exists in
Performance Optimization
- Critical namespaces: Update
preloadNamespacesini18n.tsfor immediate loading - Bundle analysis: Monitor bundle size - only English fallbacks should be bundled
- CDN caching: Configure appropriate cache headers (default: 1 hour TTL)