From 7eb3a08b20a6f1c8174d6beee1f8866cb5a5233c Mon Sep 17 00:00:00 2001 From: Oier Bravo Urtasun Date: Sun, 22 Jun 2025 07:24:36 +0200 Subject: [PATCH] i18n init --- client/I18N_README.md | 240 +++++++++++++++++++ client/src/components/LanguageSwitcher.astro | 49 ++++ client/src/components/Navigation.astro | 22 +- client/src/components/PoleElementsList.astro | 48 ++-- client/src/layouts/Layout.astro | 15 +- client/src/lib/i18n.ts | 147 ++++++++++++ client/src/middleware.ts | 26 ++ client/src/pages/[lang]/elements.astro | 50 ++++ client/src/pages/[lang]/index.astro | 34 +++ client/src/pages/elements.astro | 36 +-- client/src/pages/index.astro | 10 +- 11 files changed, 627 insertions(+), 50 deletions(-) create mode 100644 client/I18N_README.md create mode 100644 client/src/components/LanguageSwitcher.astro create mode 100644 client/src/lib/i18n.ts create mode 100644 client/src/middleware.ts create mode 100644 client/src/pages/[lang]/elements.astro create mode 100644 client/src/pages/[lang]/index.astro diff --git a/client/I18N_README.md b/client/I18N_README.md new file mode 100644 index 00000000..331dd8e3 --- /dev/null +++ b/client/I18N_README.md @@ -0,0 +1,240 @@ +# Internationalization (i18n) Setup + +This project includes a comprehensive internationalization system built with Astro 5.x. The system supports multiple languages with URL-based routing and automatic language detection. + +## Features + +- ✅ URL-based language routing (Spanish: `/`, English: `/en/`) +- ✅ Spanish as default language (no URL prefix) +- ✅ Automatic language detection from URL +- ✅ Language switcher component +- ✅ Type-safe translation keys +- ✅ Fallback to default language +- ✅ Middleware for automatic redirects +- ✅ SEO-friendly URLs + +## Supported Languages + +Currently supported languages: +- **Spanish (es)** - Default language (no URL prefix) +- **English (en)** - Secondary language (with `/en/` prefix) + +## URL Structure + +- **Spanish (default)**: `/`, `/elements`, `/about` +- **English**: `/en/`, `/en/elements`, `/en/about` + +## How to Use + +### 1. Adding New Translations + +Edit the translation dictionaries in `src/lib/i18n.ts`: + +```typescript +const translations = { + es: { + 'your.new.key': 'Texto en español', + // ... more translations + }, + en: { + 'your.new.key': 'English text', + // ... more translations + } +} +``` + +### 2. Using Translations in Components + +```astro +--- +import { t, getLanguageFromPath } from '../lib/i18n'; + +const currentLang = getLanguageFromPath(Astro.url.pathname); +--- + +

{t('your.new.key', currentLang)}

+``` + +### 3. Creating Language-Specific Pages + +For English pages, create them in the `src/pages/[lang]/` directory: + +```astro +--- +// src/pages/[lang]/about.astro (English only) +import { t, isSupportedLanguage, type SupportedLanguage, DEFAULT_LANGUAGE } from '../../lib/i18n'; + +const { lang } = Astro.params; + +if (!lang || !isSupportedLanguage(lang)) { + return Astro.redirect('/about'); +} + +const currentLang = lang as SupportedLanguage; + +// Redirect Spanish to root +if (currentLang === DEFAULT_LANGUAGE) { + return Astro.redirect('/about'); +} +--- + +

{t('about.title', currentLang)}

+``` + +For Spanish pages (default), create them directly in `src/pages/`: + +```astro +--- +// src/pages/about.astro (Spanish default) +import { t, DEFAULT_LANGUAGE } from '../lib/i18n'; + +const currentLang = DEFAULT_LANGUAGE; +--- + +

{t('about.title', currentLang)}

+``` + +### 4. Adding the Language Switcher + +The language switcher is automatically included in the main layout. To add it to other components: + +```astro +--- +import LanguageSwitcher from '../components/LanguageSwitcher.astro'; +--- + + +``` + +### 5. Creating Localized Links + +Use the `getLocalizedPath` function for navigation: + +```astro +--- +import { getLocalizedPath, getLanguageFromPath } from '../lib/i18n'; + +const currentLang = getLanguageFromPath(Astro.url.pathname); +--- + +About +``` + +## File Structure + +``` +src/ +├── lib/ +│ └── i18n.ts # Main i18n utilities and translations +├── components/ +│ ├── LanguageSwitcher.astro # Language selection component +│ └── ... # Other components +├── pages/ +│ ├── [lang]/ # English-specific pages +│ │ ├── index.astro # Redirects Spanish to root +│ │ ├── elements.astro # Redirects Spanish to root +│ │ └── ... +│ ├── index.astro # Spanish default homepage +│ ├── elements.astro # Spanish default elements page +│ └── ... # Other Spanish default pages +└── middleware.ts # Language routing middleware +``` + +## API Reference + +### Translation Functions + +#### `t(key, lang?)` +Get a translation for a given key and language. + +```typescript +t('nav.home', 'es') // Returns: "Inicio" +t('nav.home', 'en') // Returns: "Home" +``` + +#### `getLanguageFromPath(pathname)` +Extract language from URL path. + +```typescript +getLanguageFromPath('/en/elements') // Returns: "en" +getLanguageFromPath('/elements') // Returns: "es" (default) +getLanguageFromPath('/') // Returns: "es" (default) +``` + +#### `getLocalizedPath(path, lang)` +Generate a localized URL path. + +```typescript +getLocalizedPath('/elements', 'es') // Returns: "/elements" +getLocalizedPath('/elements', 'en') // Returns: "/en/elements" +``` + +#### `isSupportedLanguage(lang)` +Check if a language is supported. + +```typescript +isSupportedLanguage('es') // Returns: true +isSupportedLanguage('fr') // Returns: false +``` + +### Constants + +- `SUPPORTED_LANGUAGES` - Array of supported language codes (['es', 'en']) +- `DEFAULT_LANGUAGE` - Default language code ('es') +- `TranslationKey` - TypeScript type for translation keys + +## Adding New Languages + +1. Add the language code to `SUPPORTED_LANGUAGES` in `src/lib/i18n.ts` +2. Add translations for the new language in the `translations` object +3. Update the language switcher component if needed +4. Create language-specific pages in `src/pages/[lang]/` + +Example for French: + +```typescript +export const SUPPORTED_LANGUAGES = ['es', 'en', 'fr'] as const; + +const translations = { + es: { /* Spanish translations */ }, + en: { /* English translations */ }, + fr: { /* French translations */ } +} +``` + +## Best Practices + +1. **Use descriptive keys**: Use namespaced keys like `nav.home`, `elements.title` +2. **Keep translations organized**: Group related translations together +3. **Test all languages**: Ensure all translations are complete +4. **Use TypeScript**: Leverage the `TranslationKey` type for type safety +5. **Handle missing translations**: The system falls back to the default language +6. **Spanish first**: Always put Spanish translations first in the dictionary + +## SEO Considerations + +- Spanish content is served at root URLs (better for SEO) +- English content has `/en/` prefix +- Language is properly set in the HTML `lang` attribute +- Search engines can index content in multiple languages +- Use proper hreflang tags if needed for advanced SEO + +## Troubleshooting + +### Common Issues + +1. **Translation not found**: Check if the key exists in all language dictionaries +2. **Language not detected**: Ensure the URL follows the correct pattern +3. **Redirect loops**: Check middleware configuration +4. **Type errors**: Use the `TranslationKey` type for translation keys + +### Debug Mode + +Enable debug logging by adding console logs in the i18n functions: + +```typescript +export function t(key: TranslationKey, lang: SupportedLanguage = DEFAULT_LANGUAGE): string { + console.log(`Translating: ${key} for language: ${lang}`); + // ... rest of function +} +``` \ No newline at end of file diff --git a/client/src/components/LanguageSwitcher.astro b/client/src/components/LanguageSwitcher.astro new file mode 100644 index 00000000..9cfb90cf --- /dev/null +++ b/client/src/components/LanguageSwitcher.astro @@ -0,0 +1,49 @@ +--- +import { SUPPORTED_LANGUAGES, getLanguageFromPath, getLocalizedPath, type SupportedLanguage } from '../lib/i18n'; + +// Get current language from URL +const currentPath = Astro.url.pathname; +const currentLang = getLanguageFromPath(currentPath); + +// Generate language options +const languageOptions = SUPPORTED_LANGUAGES.map(lang => ({ + code: lang, + name: lang === 'es' ? 'Español' : 'English', + path: getLocalizedPath(currentPath, lang), + isCurrent: lang === currentLang +})); +--- + +
+ +
+ + \ No newline at end of file diff --git a/client/src/components/Navigation.astro b/client/src/components/Navigation.astro index 224882e3..64317ff1 100644 --- a/client/src/components/Navigation.astro +++ b/client/src/components/Navigation.astro @@ -1,5 +1,9 @@ --- // Navigation component for Pole Sport website +import { t, getLanguageFromPath, getLocalizedPath } from "../lib/i18n"; + +// Get current language from URL +const currentLang = getLanguageFromPath(Astro.url.pathname); ---