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:

✅ 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:

🚀 Adding a New Language

To add French support:

  1. Update the mappings:
const languageUrlMap = {
  // ... existing languages
  'fr': '/fr'  // Add French URL prefix
};

const languageDirMap = {
  // ... existing languages  
  'fr': 'french'  // Add French directory
};
  1. Create directory structure:
mkdir -p src/assets/docs/french/
  1. Update type definitions (if using):
type SupportedLanguage = 'en' | 'es' | 'fr' | /* ... other languages */;
  1. That's it! The entire system will automatically support French URLs, directory discovery, navigation, and content loading.

⚠️ Migration Checklist

When updating existing code:

🎉 Benefits of This Approach


📞 Support

When implementing language features:

  1. Always use the scalable mapping patterns shown above
  2. Test with multiple languages, not just English/Spanish
  3. Consider future languages in your design decisions
  4. 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! 🌍✨