French Translation System Implementation
Overview
This document explains the implementation of a French translation system in an Angular application, the problems encountered, and how they were solved.
Initial State
The application already had a comprehensive translation infrastructure:
TranslationServicewith English and French translationsTranslatePipefor template translationsSettingsServicefor user preferences- Settings modal with language selector
 
Problems Encountered
1. Settings Dropdown Not Persisting Selection
Problem: When users selected "Français" in settings, closed the modal, and reopened it, the dropdown still showed "English" selected.
Root Cause: The dropdown was bound to a getter method (settingsService.language) instead of a reactive signal. Angular's change detection wasn't tracking changes to getter methods properly.
Solution:
// Before (problematic)
[value]="settingsService.language"
// After (fixed)
currentLanguage = computed(() => this.settingsService.settings().language);
[value]="currentLanguage()"
2. Translations Not Updating Immediately
Problem: When users changed language, the interface didn't update immediately - they had to refresh the page to see French translations.
Root Cause: The TranslatePipe wasn't reactive to language changes. It was marked as pure: false but had no mechanism to detect when the language signal changed.
Solution: Used Angular's effect() to watch for language signal changes:
// Before (not reactive)
constructor() {
  this.subscription = new Subscription();
}
// After (reactive)
constructor() {
  effect(() => {
    this.translationService.currentLanguage(); // Watch signal
    this.lastKey = ''; // Reset cache
    this.cdr.markForCheck(); // Trigger change detection
  });
}
3. Service Synchronization Issues
Problem: Both SettingsService and TranslationService were trying to manage language persistence independently, causing conflicts.
Root Cause:
TranslationServicehad its own localStorage key ('app-language')SettingsServiceused a different key ('app-settings')- Browser language detection was overriding user preferences
 
Solution: Made SettingsService the single source of truth:
// TranslationService - removed independent storage
setLanguage(language: Language): void {
  this.currentLanguageSignal.set(language);
  // Don't save here - let SettingsService handle persistence
}
// SettingsService - handles both persistence and sync
setLanguage(language: Language): void {
  this.settingsSignal.update(settings => ({ ...settings, language }));
  this.translationService.setLanguage(language); // Sync translation service
  this.saveSettings(); // Persist to localStorage
}
Technical Implementation Details
Angular Signals vs Observables
The solution leveraged Angular's modern signal-based reactivity:
- Signals: Used for state management (
currentLanguageSignal,settingsSignal) - Computed Signals: Used for derived state (
currentLanguage = computed(...)) - Effects: Used for side effects when signals change (
effect(() => {...})) 
Change Detection Strategy
The TranslatePipe uses Angular's change detection mechanism:
effect()watches the language signal- When language changes, it resets the translation cache
 markForCheck()triggers Angular's change detection- All components using the pipe re-render with new translations
 
Data Flow
User selects language in settings
         ↓
SettingsService.setLanguage()
         ↓
Updates settings signal + saves to localStorage
         ↓
Calls TranslationService.setLanguage()
         ↓
Updates currentLanguage signal
         ↓
effect() in TranslatePipe detects change
         ↓
Triggers change detection across all components
         ↓
All translations update immediately
Key Learnings
1. Single Source of Truth
Having multiple services manage the same data leads to synchronization issues. Designate one service as the authoritative source.
2. Angular Signals for Reactivity
Modern Angular signals provide better reactivity than traditional observables for simple state management.
3. Pipe Reactivity
Custom pipes need explicit reactivity mechanisms when depending on external state. Use effect() to watch signals.
4. Change Detection
When updating external state that affects templates, explicitly trigger change detection with markForCheck().
Final Architecture
┌─────────────────┐    ┌──────────────────┐    ┌─────────────────┐
│  SettingsModal  │───▶│  SettingsService │───▶│ TranslationService│
│                 │    │                  │    │                 │
│ - Language      │    │ - Persistence    │    │ - Translations  │
│   Dropdown      │    │ - Single Source  │    │ - Current Lang  │
│                 │    │   of Truth       │    │   Signal        │
└─────────────────┘    └──────────────────┘    └─────────────────┘
         │                                               │
         │                                               │
         ▼                                               ▼
┌─────────────────┐                            ┌─────────────────┐
│   Templates     │◀───────────────────────────│  TranslatePipe  │
│                 │                            │                 │
│ - Reactive      │                            │ - effect()      │
│   Bindings      │                            │ - Change        │
│                 │                            │   Detection     │
└─────────────────┘                            └─────────────────┘
Testing the Solution
To verify the implementation works correctly:
- 
Language Selection Persistence:
- Select French → Close settings → Reopen settings
 - ✅ Should show "Français" selected
 
 - 
Immediate Translation Updates:
- Select French → Interface immediately switches to French
 - ✅ No page refresh required
 
 - 
Cross-Session Persistence:
- Select French → Refresh page → App loads in French
 - ✅ Language preference persists
 
 
Conclusion
The key to solving this translation system was understanding Angular's reactivity model and ensuring proper data flow between services. By using signals, computed values, and effects appropriately, we achieved a seamless, reactive translation experience that updates immediately and persists user preferences correctly.