import { Injectable, computed, inject, signal } from '@angular/core'; import { LanguageService } from '../services/language.service'; import { Translations } from './translations'; import { ru } from './ru'; // 'ru' is the platform default language, so it's the only translation pack // bundled eagerly. 'en'/'hy' are code-split and fetched on demand via // preloadLanguage() (invoked from languageGuard, which awaits it before // route activation - components never observe a partially-loaded pack). const translationLoaders: Record Promise> = { en: () => import('./en').then(m => m.en), hy: () => import('./hy').then(m => m.hy), }; @Injectable({ providedIn: 'root', }) export class TranslateService { private langService = inject(LanguageService); private readonly translationCache = signal>({ ru }); readonly translations = computed( () => this.translationCache()[this.langService.currentLanguage()] ?? ru, ); /** * Ensures the given language's translation pack is loaded before it is * activated. Resolves immediately for already-loaded packs (including * the eagerly-bundled 'ru'); dynamically imports otherwise. */ async preloadLanguage(lang: string): Promise { if (this.translationCache()[lang]) { return; } const loader = translationLoaders[lang]; if (!loader) { return; } const pack = await loader(); this.translationCache.update(cache => ({ ...cache, [lang]: pack })); } /** * Translate a dot-separated key with optional interpolation params. * Usage: t('cart.phoneMoreDigits', { count: 3 }) → "Введите ещё 3 цифр" */ t(key: string, params?: Record): string { const parts = key.split('.'); let result: unknown = this.translations(); for (const part of parts) { if (result && typeof result === 'object') { result = (result as Record)[part]; } else { return key; } } if (typeof result !== 'string') { return key; } if (params) { return result.replace(/\{\{(\w+)\}\}/g, (_, k) => params[k] !== undefined ? String(params[k]) : `{{${k}}}`, ); } return result; } }