This major feature introduces complete internationalization architecture with Spanish (Mexico) as the first additional language: ## New Features: - **I18n Architecture**: Complete client-side and server-side internationalization system - **Spanish (Mexico) Support**: Full es-MX translations for all user-facing text - **Language Selector**: Dynamic language switching UI component in header - **API Endpoints**: RESTful endpoints for serving translations (/api/i18n) - **Progressive Enhancement**: Language detection via Accept-Language header and cookies ## Technical Implementation: - **Frontend**: Client-side i18n.js with automatic DOM translation and language selector - **Backend**: Server-side i18n service with locale detection middleware - **Build Process**: Automated copying of translation files to dist/ directory - **Responsive Design**: Language selector integrated into header controls layout ## Files Added: - public/i18n.js - Client-side internationalization library - src/i18n/index.ts - Server-side i18n service - src/i18n/locales/en.json - English translations - src/i18n/locales/es-MX.json - Spanish (Mexico) translations - src/routes/i18n.ts - API endpoints for translations ## Files Modified: - package.json - Updated build process to include i18n files - public/index.html - Added i18n attributes and language selector - public/app.js - Integrated dynamic translation updates - src/server.ts - Added locale detection middleware - src/scss/pages/_index.scss - Language selector styling This implementation supports easy addition of future languages and maintains backward compatibility while providing a seamless multilingual experience. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
76 lines
No EOL
1.9 KiB
TypeScript
76 lines
No EOL
1.9 KiB
TypeScript
import { Router, Request, Response } from 'express';
|
|
import { i18nService } from '../i18n';
|
|
|
|
export function createI18nRoutes(): Router {
|
|
const router = Router();
|
|
|
|
/**
|
|
* Get translations for a specific locale
|
|
* GET /api/i18n/:locale
|
|
*/
|
|
router.get('/:locale', (req: Request, res: Response): void => {
|
|
const { locale } = req.params;
|
|
|
|
// Validate locale
|
|
if (!i18nService.isLocaleSupported(locale)) {
|
|
res.status(400).json({
|
|
error: 'Unsupported locale',
|
|
supportedLocales: i18nService.getAvailableLocales()
|
|
});
|
|
return;
|
|
}
|
|
|
|
// Get translations for the locale
|
|
const translations = i18nService.getTranslations(locale);
|
|
|
|
if (!translations) {
|
|
res.status(404).json({
|
|
error: 'Translations not found for locale',
|
|
locale
|
|
});
|
|
return;
|
|
}
|
|
|
|
// Set appropriate headers for caching
|
|
res.set({
|
|
'Cache-Control': 'public, max-age=3600', // Cache for 1 hour
|
|
'Content-Type': 'application/json'
|
|
});
|
|
|
|
res.json(translations);
|
|
});
|
|
|
|
/**
|
|
* Get available locales and their display names
|
|
* GET /api/i18n
|
|
*/
|
|
router.get('/', (req: Request, res: Response): void => {
|
|
const locales = i18nService.getAvailableLocales().map(locale => ({
|
|
code: locale,
|
|
name: i18nService.getLocaleDisplayName(locale)
|
|
}));
|
|
|
|
res.json({
|
|
default: i18nService.getDefaultLocale(),
|
|
available: locales
|
|
});
|
|
});
|
|
|
|
/**
|
|
* Detect user's preferred locale based on Accept-Language header
|
|
* GET /api/i18n/detect
|
|
*/
|
|
router.get('/detect', (req: Request, res: Response): void => {
|
|
const acceptLanguage = req.get('Accept-Language');
|
|
const detectedLocale = i18nService.detectLocale(acceptLanguage);
|
|
|
|
res.json({
|
|
detected: detectedLocale,
|
|
displayName: i18nService.getLocaleDisplayName(detectedLocale)
|
|
});
|
|
});
|
|
|
|
return router;
|
|
}
|
|
|
|
export default createI18nRoutes; |