summaryrefslogtreecommitdiff
path: root/shared/localization/src/i18n.ts
blob: bcd5e28c2241a3fc8fea08677f442d1dc01993b7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
import Translator from './translator';
import type {
    Locale,
    InterpolationOptions,
    ILocaleJSON,
    ITranslator,
} from './types';
import type { Logger } from '@amp/web-apps-logger';

/** @internal */
const formatOptions = (
    options: InterpolationOptions | number,
): InterpolationOptions =>
    typeof options === 'number' ? { count: options } : options;

/**
 *
 * Adapter class to expose expected LOC interface
 * @category Localization
 */
export class I18N {
    private readonly log: Logger;
    private readonly locale: Locale;
    private readonly translator: ITranslator;
    private readonly keys: ILocaleJSON;
    private readonly alwaysShowScreamers: boolean;

    /**
     * builds a new I18N class
     * @param locale - the locale to use default:`'en-us'`
     * @param translation - translation object default: `{}`
     * @param alwaysShowScreamers - optional boolean that is set upstream
     * by a FeatureKit feature flag. This makes it so the LOC keys themselves are
     * printed to the DOM, rather than their translations, which is helpful for QA testing
     */
    constructor(
        log: Logger,
        locale: Locale = 'en-us',
        translation: ILocaleJSON = {},
        alwaysShowScreamers: boolean = false,
    ) {
        this.log = log;
        this.locale = locale;
        this.translator = new Translator(locale, translation, {
            onMissingKeyFn: (key: string): string => {
                log.warn('key missing:', key);
                return `**${key}**`;
            },
            onMissingInterpolationFn: (key: string, interpolation: string) => {
                log.warn(`key ${key} missing interpolation:`, interpolation);
            },
        });
        this.keys = translation;
        this.alwaysShowScreamers = alwaysShowScreamers;
    }

    get currentLocale(): Locale {
        return this.locale;
    }

    get currentKeys(): ILocaleJSON {
        return this.keys;
    }

    /**
     * Gets non-interpolated string.
     * @category Localization
     * @param key key to lookup in the translation.json
     * @returns an uninterpolated string value
     */
    getUninterpolatedString(key: string): string {
        if (this.alwaysShowScreamers) {
            return key;
        } else {
            return this.translator.getUninterpolatedString(key);
        }
    }

    /**
     * Method for fetching translation based on key.
     *
     * If alwaysShowScreamers is true, return the key itself for QA testing purposes
     * (our app tends to call into this method within Svelte templates)
     *
     * @category Localization
     * @param key key to lookup in the translation.json
     * @param options options for translations
     * @returns interpolated string
     */
    t = (key: string, options: number | InterpolationOptions = {}): string => {
        if (this.alwaysShowScreamers) {
            return key;
        }

        let internalOptions: InterpolationOptions = formatOptions(options);
        if (typeof key !== 'string') {
            this.log.warn('received non-string key:', key);
            return '';
        }
        return this.translator.translate(key, internalOptions);
    };
}

export default I18N;