perf: memoize TranslatePipe instead of re-walking translations every CD cycle

pure:false stays (needed so language switches propagate without
touching every | translate template call site), but repeat calls with
unchanged key/params/language now hit a Map lookup instead of
re-splitting the key and re-walking the translation object tree.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
sdarbinyan
2026-08-13 11:03:30 +04:00
parent bad3002006
commit c9a80da7c3

View File

@@ -1,14 +1,42 @@
import { Pipe, PipeTransform, inject } from '@angular/core'; import { Pipe, PipeTransform, inject } from '@angular/core';
import { TranslateService } from './translate.service'; import { TranslateService } from './translate.service';
import { LanguageService } from '../services/language.service';
/**
* Stays impure (must re-run every CD cycle so a live language switch
* updates every binding without touching hundreds of `| translate`
* template call sites to pass the language explicitly as a pure-pipe
* argument). Memoized per-instance instead: repeat calls with the same
* key/params/language - the overwhelming majority of CD cycles, since
* nothing actually changed - hit a Map lookup instead of re-walking the
* translation tree and re-running the interpolation regex.
*/
@Pipe({ @Pipe({
name: 'translate', name: 'translate',
pure: false, pure: false,
}) })
export class TranslatePipe implements PipeTransform { export class TranslatePipe implements PipeTransform {
private translateService = inject(TranslateService); private translateService = inject(TranslateService);
private langService = inject(LanguageService);
private lastLang = '';
private readonly cache = new Map<string, string>();
transform(key: string, params?: Record<string, string | number>): string { transform(key: string, params?: Record<string, string | number>): string {
return this.translateService.t(key, params); const lang = this.langService.currentLanguage();
if (lang !== this.lastLang) {
this.lastLang = lang;
this.cache.clear();
}
const cacheKey = params ? `${key}::${JSON.stringify(params)}` : key;
const cached = this.cache.get(cacheKey);
if (cached !== undefined) {
return cached;
}
const result = this.translateService.t(key, params);
this.cache.set(cacheKey, result);
return result;
} }
} }