import * as Sentry from '@sentry/browser'; import type { Config } from '../locale/config.ts'; import type { Translations } from '../locale/translations.ts'; import { deepGet } from './helpers.ts'; import { listMissingTranslations } from './missingTranslations.ts'; export class Translator { translations: Translations; baseTranslations: Translations; config: Config; constructor(translations: Translations, baseTranslations: Translations, config: Config) { this.translations = translations; this.baseTranslations = baseTranslations; this.config = config; } translate( key: string, params: Record = {}, warn: boolean = false, ): NoInfer { return this.applyParams( this.get(key, warn), params, ); } get( key: string, warn: boolean = false, base: boolean = false, useFallback: boolean = true, ): NoInfer { const translations = base ? this.baseTranslations : this.translations; const value = deepGet(translations, key); if (value === undefined) { if (warn) { Sentry.captureMessage(`Cannot find translation: ${key}`, 'warning'); } if (!base && useFallback) { return this.get(key, warn, true) as T; } } return value as T; } has(key: string): boolean { return this.get(key, false, false, false) !== undefined; } hasFallback(key: string): boolean { return this.get(key, false, true, false) !== undefined; } applyParams(value: T, params: Record = {}): T { if (!value) { return value; } for (const [placeholder, placeholderValue] of Object.entries(params)) { value = (Array.isArray(value) ? value.map((v) => v.replaceAll(`%${placeholder}%`, placeholderValue?.toString())) : value.replaceAll(`%${placeholder}%`, placeholderValue?.toString())) as T; } return value; } listMissingTranslations(): string[] { return listMissingTranslations(this.translations, this.baseTranslations, this.config); } }