如何创建基于Rest Call的Angular 5 i18n自定义指令
Let's walk through creating a custom i18n directive that fetches translation JSON via a REST call and stores the data in a dedicated service. I'll use Angular for this example (since directives are a core feature there), but the core concepts can adapt to other frameworks like Vue or React with minor tweaks.
1. First: Create the Translation Service
This service will be our single source of truth for translation data—handling API calls, storing translations, and providing methods to retrieve strings.
import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { BehaviorSubject, Observable } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class TranslationService { // Holds the current translations (key-value pairs) private translations: Record<string, string> = {}; // Subject to notify when language/translations change private languageChanged = new BehaviorSubject<string>('en'); constructor(private http: HttpClient) {} // Fetch translations from REST API and store them fetchTranslations(language: string = 'en'): Observable<Record<string, string>> { // Replace with your actual API endpoint return this.http.get<Record<string, string>>(`/api/translations/${language}`).pipe( (response) => { this.translations = response; this.languageChanged.next(language); return response; } ); } // Get a translation by key, fall back to key if not found getTranslation(key: string): string { return this.translations[key] || `[${key}]`; } // Subscribe to language change events onLanguageChange(): Observable<string> { return this.languageChanged.asObservable(); } }
Key points here:
- We use a
BehaviorSubjectto broadcast language changes, so our directive can react dynamically. - The
fetchTranslationsmethod caches the response directly in the service—no need to refetch unless the language changes. - A fallback (
[${key}]) ensures we don't show empty content if a translation is missing.
2. Build the Custom i18n Directive
This directive will attach to DOM elements, pull the translation key from an attribute, and replace the element's content with the translated string. It'll also listen for language changes to update content automatically.
import { Directive, ElementRef, Input, OnInit, OnDestroy } from '@angular/core'; import { TranslationService } from './translation.service'; import { Subscription } from 'rxjs'; @Directive({ selector: '[appI18n]' // Use this attribute in your templates }) export class I18nDirective implements OnInit, OnDestroy { @Input('appI18n') translationKey!: string; private languageSub?: Subscription; constructor( private el: ElementRef, private translationService: TranslationService ) {} ngOnInit(): void { // Set initial translation this.updateTranslation(); // Subscribe to language changes to update content this.languageSub = this.translationService.onLanguageChange().subscribe(() => { this.updateTranslation(); }); } private updateTranslation(): void { const translatedText = this.translationService.getTranslation(this.translationKey); this.el.nativeElement.textContent = translatedText; } ngOnDestroy(): void { // Clean up subscription to avoid memory leaks this.languageSub?.unsubscribe(); } }
Key details:
- The
@Inputgrabs the translation key from the template attribute (e.g.,[appI18n]="'welcome.greeting'"). updateTranslationhandles replacing the element's text content—you could extend this to handle attributes likeplaceholderortitletoo.- We clean up the subscription in
ngOnDestroyto prevent memory leaks, which is crucial for long-lived apps.
3. Initialize and Use the Directive
Step 3.1: Load Initial Translations
In your root component (e.g., AppComponent), call the fetchTranslations method to load the default language's data when the app starts:
import { Component, OnInit } from '@angular/core'; import { TranslationService } from './translation.service'; @Component({ selector: 'app-root', template: ` <h1 [appI18n]="'home.title'"></h1> <p [appI18n]="'home.description'"></p> <button (click)="switchToSpanish()">Switch to Spanish</button> ` }) export class AppComponent implements OnInit { constructor(private translationService: TranslationService) {} ngOnInit(): void { // Load English translations on app start this.translationService.fetchTranslations('en').subscribe(); } switchToSpanish(): void { // Switch to Spanish and fetch new translations this.translationService.fetchTranslations('es').subscribe(); } }
Step 3.2: Use the Directive in Templates
Just add the [appI18n] attribute to any element, passing the translation key as the value:
<h2 [appI18n]="'dashboard.welcome'"></h2> <button [appI18n]="'actions.save'"></button>
Bonus Enhancements
- Add Loading States: Modify the service to track loading status, then update the directive to show a spinner or placeholder while translations are fetching.
- Support Attribute Translations: Extend the directive to handle attributes like
placeholderortitle(e.g.,[appI18nPlaceholder]="'form.email.placeholder'"). - Cache Translations: Store fetched translations in
localStorageso users don't re-download them on subsequent visits.
内容的提问来源于stack exchange,提问作者Vijayabaskaran M

