Scalable Language System Patterns
Essential patterns and best practices for implementing multi-language support that scales beyond English/Spanish
🌐 Scalable Language System Patterns
This guide provides essential patterns for implementing multi-language support that scales beyond the current English/Spanish setup to support any number of languages.
🚨 CRITICAL: The Problem with Hardcoded Language Logic
❌ NEVER do this:
// BAD: Hardcoded language logic - breaks when adding new languages
const langPrefix = currentLang === 'es' ? '/es' : '';
const isSpanish = path.startsWith('/es/');
const languageDir = language === 'es' ? 'spanish' : 'english';
// BAD: Hardcoded language detection
const newLanguage: 'en' | 'es' = decodedUrl.startsWith('/es/') ? 'es' : 'en';
Why this is problematic:
- 🚫 Not scalable: Adding French requires updating every hardcoded check
- 🚫 Error-prone: Easy to miss instances when adding new languages
- 🚫 Maintenance nightmare: Changes require touching multiple files
- 🚫 Inconsistent: Different parts of the system may handle languages differently
✅ The Scalable Solution: Language Mapping Objects
1. Language URL Prefix Mapping
// ✅ CORRECT: Scalable language URL mapping
const languageUrlMap: { [key: string]: string } = {
'en': '', // English has no prefix (default)
'es': '/es', // Spanish URLs start with /es/
'fr': '/fr', // French URLs start with /fr/
'de': '/de', // German URLs start with /de/
'pt': '/pt', // Portuguese URLs start with /pt/
'it': '/it', // Italian URLs start with /it/
'ja': '/ja', // Japanese URLs start with /ja/
'ko': '/ko', // Korean URLs start with /ko/
'zh': '/zh' // Chinese URLs start with /zh/
};
// Usage
const langPrefix = languageUrlMap[currentLang] || '';
2. Language Directory Mapping
// ✅ CORRECT: Scalable language directory mapping
const languageDirMap: { [key: string]: string } = {
'en': 'english',
'es': 'spanish',
'fr': 'french',
'de': 'german',
'pt': 'portuguese',
'it': 'italian',
'ja': 'japanese',
'ko': 'korean',
'zh': 'chinese'
};
// Usage
const languageDir = languageDirMap[currentLang] || 'english';
3. Scalable Language Detection from URLs
// ✅ CORRECT: Scalable language detection
function detectLanguageFromUrl(url: string): string {
const languageUrlMap: { [key: string]: string } = {
'en': '',
'es': '/es',
'fr': '/fr',
'de': '/de',
'pt': '/pt',
'it': '/it',
'ja': '/ja',
'ko': '/ko',
'zh': '/zh'
};
// Check for language prefixes
for (const [lang, prefix] of Object.entries(languageUrlMap)) {
if (prefix && (url === prefix || url.startsWith(prefix + '/'))) {
return lang;
}
}
// Default to English if no prefix found
return 'en';
}
4. Scalable Language Prefix Removal
// ✅ CORRECT: Remove any language prefix from path
function removeLanguagePrefix(path: string): string {
const languageUrlMap: { [key: string]: string } = {
'en': '',
'es': '/es',
'fr': '/fr',
'de': '/de',
'pt': '/pt',
'it': '/it',
'ja': '/ja',
'ko': '/ko',
'zh': '/zh'
};
const knownPrefixes = Object.values(languageUrlMap).filter(prefix => prefix);
for (const prefix of knownPrefixes) {
if (path.startsWith(prefix + '/')) {
return path.substring(prefix.length);
} else if (path === prefix) {
return '/';
}
}
return path;
}
🏗️ Implementation Patterns by Service
AppComponent - Guide Path Generation
// ✅ CORRECT: Scalable guide path generation
private generateGuidePathForSection(section: any): string | null {
const currentLang = this.documentationService.currentLanguage();
// 🌐 SCALABLE LANGUAGE URL PREFIX MAPPING
const languageUrlMap: { [key: string]: string } = {
'en': '',
'es': '/es',
'fr': '/fr',
'de': '/de',
'pt': '/pt',
'it': '/it',
'ja': '/ja',
'ko': '/ko',
'zh': '/zh'
};
const langPrefix = languageUrlMap[currentLang] || '';
// ... rest of the logic
return `${langPrefix}/${basePath}`;
}
NavigationService - URL Handling
// ✅ CORRECT: Scalable navigation
navigateTo(path: string): void {
// 🌐 SCALABLE LANGUAGE URL PREFIX MAPPING
const languageUrlMap: { [key: string]: string } = {
'en': '',
'es': '/es',
'fr': '/fr',
'de': '/de',
'pt': '/pt',
'it': '/it',
'ja': '/ja',
'ko': '/ko',
'zh': '/zh'
};
// Detect and remove language prefix
let detectedLangPrefix = '';
let cleanPath = path;
for (const [lang, prefix] of Object.entries(languageUrlMap)) {
if (prefix && (path === prefix || path.startsWith(prefix + '/'))) {
detectedLangPrefix = prefix;
cleanPath = path.substring(prefix.length);
break;
}
}
// ... rest of navigation logic
}
StructureSyncService - Directory Discovery
// ✅ CORRECT: Scalable structure discovery
async discoverStructure(language: string): Promise<DiscoveredSection[]> {
// 🌐 SCALABLE LANGUAGE DIRECTORY MAPPING
const languageDirMap: { [key: string]: string } = {
'en': 'english',
'es': 'spanish',
'fr': 'french',
'de': 'german',
'pt': 'portuguese',
'it': 'italian',
'ja': 'japanese',
'ko': 'korean',
'zh': 'chinese'
};
const languageDir = languageDirMap[language] || 'english';
// ... rest of discovery logic
}
🎯 Best Practices
1. Centralize Language Mappings
Consider creating a shared utility file:
// utils/language-mappings.ts
export const LANGUAGE_URL_MAP: { [key: string]: string } = {
'en': '',
'es': '/es',
'fr': '/fr',
'de': '/de',
'pt': '/pt',
'it': '/it',
'ja': '/ja',
'ko': '/ko',
'zh': '/zh'
};
export const LANGUAGE_DIR_MAP: { [key: string]: string } = {
'en': 'english',
'es': 'spanish',
'fr': 'french',
'de': 'german',
'pt': 'portuguese',
'it': 'italian',
'ja': 'japanese',
'ko': 'korean',
'zh': 'chinese'
};
export function getLanguagePrefix(language: string): string {
return LANGUAGE_URL_MAP[language] || '';
}
export function getLanguageDirectory(language: string): string {
return LANGUAGE_DIR_MAP[language] || 'english';
}
2. Use Type Safety
// Define supported languages as a type
type SupportedLanguage = 'en' | 'es' | 'fr' | 'de' | 'pt' | 'it' | 'ja' | 'ko' | 'zh';
// Use in function signatures
function detectLanguage(url: string): SupportedLanguage {
// ... implementation
}
3. Consistent Error Handling
// Always provide fallbacks
const langPrefix = languageUrlMap[currentLang] || ''; // Falls back to English
const languageDir = languageDirMap[language] || 'english'; // Falls back to English directory
🔍 Where This Pattern is Applied
The scalable language system is now implemented in:
- ✅
app.component.ts-generateGuidePathForSection()method - ✅
navigation.service.ts- All URL handling and navigation logic - ✅
structure-sync.service.ts- Directory discovery and path building - ✅
markdown.service.ts- Content loading and path resolution - ✅
search.service.ts- Search indexing and filtering - ✅
enhanced-structure.service.ts- Enhanced structure processing - ✅
dynamic-routes.service.ts- Route generation
🚀 Adding a New Language
To add French support:
- Update the mappings:
const languageUrlMap = {
// ... existing languages
'fr': '/fr' // Add French URL prefix
};
const languageDirMap = {
// ... existing languages
'fr': 'french' // Add French directory
};
- Create directory structure:
mkdir -p src/assets/docs/french/
- Update type definitions (if using):
type SupportedLanguage = 'en' | 'es' | 'fr' | /* ... other languages */;
- That's it! The entire system will automatically support French URLs, directory discovery, navigation, and content loading.
⚠️ Migration Checklist
When updating existing code:
- Replace
currentLang === 'es' ? '/es' : ''withlanguageUrlMap[currentLang] || '' - Replace
path.startsWith('/es/')with proper language detection loop - Replace
language === 'es' ? 'spanish' : 'english'withlanguageDirMap[language] || 'english' - Update any hardcoded language type unions to be more flexible
- Test with multiple languages to ensure scalability
🎉 Benefits of This Approach
- ✅ Future-proof: Adding new languages requires minimal changes
- ✅ Maintainable: Single source of truth for language mappings
- ✅ Consistent: Same patterns used across all services
- ✅ Type-safe: Can be combined with TypeScript for better safety
- ✅ Testable: Easy to test with different language configurations
- ✅ Performance: No complex conditional logic, just object lookups
📞 Support
When implementing language features:
- Always use the scalable mapping patterns shown above
- Test with multiple languages, not just English/Spanish
- Consider future languages in your design decisions
- Document any language-specific behavior clearly
The scalable language system ensures our documentation platform can grow to support any number of languages without architectural changes! 🌍✨