1 | import { Dict, MissingTranslationStrategy, Scope } from "./typing";
|
2 | import { getFullScope, inferType } from "./helpers";
|
3 | import { I18n } from "./I18n";
|
4 |
|
5 | /**
|
6 | * Generate a human readable version of the scope as the missing translation.
|
7 | * To use it, you have to set `i18n.missingBehavior` to `"guess"`.
|
8 | *
|
9 | * @type {MissingTranslationStrategy}
|
10 | *
|
11 | * @param {I18n} i18n The I18n instance.
|
12 | *
|
13 | * @param {Scope} scope The translation scope.
|
14 | *
|
15 | * @returns {string} The missing translation string.
|
16 | */
|
17 | export const guessStrategy: MissingTranslationStrategy = function (
|
18 | i18n,
|
19 | scope,
|
20 | ) {
|
21 | if (scope instanceof Array) {
|
22 | scope = scope.join(i18n.defaultSeparator);
|
23 | }
|
24 |
|
25 | // Get only the last portion of the scope.
|
26 | const message = scope.split(i18n.defaultSeparator).slice(-1)[0];
|
27 |
|
28 | // Replace underscore with space and camelcase with space and
|
29 | // lowercase letter.
|
30 | return (
|
31 | i18n.missingTranslationPrefix +
|
32 | message
|
33 | .replace("_", " ")
|
34 | .replace(
|
35 | /([a-z])([A-Z])/g,
|
36 | (_match: string, p1: string, p2: string) => `${p1} ${p2.toLowerCase()}`,
|
37 | )
|
38 | );
|
39 | };
|
40 |
|
41 | /**
|
42 | * Generate the missing translation message, which includes the full scope.
|
43 | * To use it, you have to set `i18n.missingBehavior` to `"message"`.
|
44 | * This is the default behavior.
|
45 | *
|
46 | * @type {MissingTranslationStrategy}
|
47 | *
|
48 | * @param {I18n} i18n The I18n instance.
|
49 | *
|
50 | * @param {Scope} scope The translation scope.
|
51 | *
|
52 | * @param {Dict} options The translations' options.
|
53 | *
|
54 | * @returns {string} The missing translation string.
|
55 | */
|
56 | export const messageStrategy: MissingTranslationStrategy = (
|
57 | i18n,
|
58 | scope,
|
59 | options,
|
60 | ) => {
|
61 | const fullScope = getFullScope(i18n, scope, options);
|
62 | const locale = "locale" in options ? options.locale : i18n.locale;
|
63 | const localeType = inferType(locale);
|
64 |
|
65 | const fullScopeWithLocale = [
|
66 | localeType == "string" ? locale : localeType,
|
67 | fullScope,
|
68 | ].join(i18n.defaultSeparator);
|
69 |
|
70 | return `[missing "${fullScopeWithLocale}" translation]`;
|
71 | };
|
72 |
|
73 | /**
|
74 | * Throw an error whenever a translation cannot be found. The message will
|
75 | * includes the full scope.
|
76 | * To use it, you have to set `i18n.missingBehavior` to `"error"`.
|
77 | *
|
78 | * @type {MissingTranslationStrategy}
|
79 | *
|
80 | * @param {I18n} i18n The I18n instance.
|
81 | *
|
82 | * @param {Scope} scope The translation scope.
|
83 | *
|
84 | * @param {Dict} options The translations' options.
|
85 | *
|
86 | * @returns {void}
|
87 | */
|
88 | export const errorStrategy: MissingTranslationStrategy = (
|
89 | i18n,
|
90 | scope,
|
91 | options,
|
92 | ) => {
|
93 | const fullScope = getFullScope(i18n, scope, options);
|
94 | const fullScopeWithLocale = [i18n.locale, fullScope].join(
|
95 | i18n.defaultSeparator,
|
96 | );
|
97 |
|
98 | throw new Error(`Missing translation: ${fullScopeWithLocale}`);
|
99 | };
|
100 |
|
101 | export class MissingTranslation {
|
102 | private i18n: I18n;
|
103 | private registry: Dict;
|
104 |
|
105 | constructor(i18n: I18n) {
|
106 | this.i18n = i18n;
|
107 | this.registry = {};
|
108 |
|
109 | this.register("guess", guessStrategy);
|
110 | this.register("message", messageStrategy);
|
111 | this.register("error", errorStrategy);
|
112 | }
|
113 |
|
114 | /**
|
115 | * Registers a new missing translation strategy. This is how messages are
|
116 | * defined when a translation cannot be found.
|
117 | *
|
118 | * The follow example registers a strategy that always return the phrase
|
119 | * "Oops! Missing translation.".
|
120 | *
|
121 | * @example
|
122 | * ```js
|
123 | * i18n.missingTranslation.register(
|
124 | * "oops",
|
125 | * (i18n, scope, options) => "Oops! Missing translation."
|
126 | * );
|
127 | *
|
128 | * i18n.missingBehavior = "oops";
|
129 | * ```
|
130 | *
|
131 | * @param {string} name The strategy name.
|
132 | *
|
133 | * @param {MissingTranslationStrategy} strategy A function that returns a
|
134 | * string the result of a missing translation scope.
|
135 | *
|
136 | * @returns {void}
|
137 | */
|
138 | public register(name: string, strategy: MissingTranslationStrategy): void {
|
139 | this.registry[name] = strategy;
|
140 | }
|
141 |
|
142 | /**
|
143 | * Return a missing translation message for the given parameters.
|
144 | *
|
145 | * @param {Scope} scope The translations' scope.
|
146 | *
|
147 | * @param {Dict} options The translations' options.
|
148 | *
|
149 | * @returns {string} The missing translation.
|
150 | */
|
151 | public get(scope: Scope, options: Dict): string {
|
152 | return this.registry[options.missingBehavior ?? this.i18n.missingBehavior](
|
153 | this.i18n,
|
154 | scope,
|
155 | options,
|
156 | );
|
157 | }
|
158 | }
|