-
-
Notifications
You must be signed in to change notification settings - Fork 19
/
MissingTranslation.ts
158 lines (145 loc) · 4.11 KB
/
MissingTranslation.ts
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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
import { Dict, MissingTranslationStrategy, Scope } from "./typing";
import { getFullScope, inferType } from "./helpers";
import { I18n } from "./I18n";
/**
* Generate a human readable version of the scope as the missing translation.
* To use it, you have to set `i18n.missingBehavior` to `"guess"`.
*
* @type {MissingTranslationStrategy}
*
* @param {I18n} i18n The I18n instance.
*
* @param {Scope} scope The translation scope.
*
* @returns {string} The missing translation string.
*/
export const guessStrategy: MissingTranslationStrategy = function (
i18n,
scope,
) {
if (scope instanceof Array) {
scope = scope.join(i18n.defaultSeparator);
}
// Get only the last portion of the scope.
const message = scope.split(i18n.defaultSeparator).slice(-1)[0];
// Replace underscore with space and camelcase with space and
// lowercase letter.
return (
i18n.missingTranslationPrefix +
message
.replace("_", " ")
.replace(
/([a-z])([A-Z])/g,
(_match: string, p1: string, p2: string) => `${p1} ${p2.toLowerCase()}`,
)
);
};
/**
* Generate the missing translation message, which includes the full scope.
* To use it, you have to set `i18n.missingBehavior` to `"message"`.
* This is the default behavior.
*
* @type {MissingTranslationStrategy}
*
* @param {I18n} i18n The I18n instance.
*
* @param {Scope} scope The translation scope.
*
* @param {Dict} options The translations' options.
*
* @returns {string} The missing translation string.
*/
export const messageStrategy: MissingTranslationStrategy = (
i18n,
scope,
options,
) => {
const fullScope = getFullScope(i18n, scope, options);
const locale = "locale" in options ? options.locale : i18n.locale;
const localeType = inferType(locale);
const fullScopeWithLocale = [
localeType == "string" ? locale : localeType,
fullScope,
].join(i18n.defaultSeparator);
return `[missing "${fullScopeWithLocale}" translation]`;
};
/**
* Throw an error whenever a translation cannot be found. The message will
* includes the full scope.
* To use it, you have to set `i18n.missingBehavior` to `"error"`.
*
* @type {MissingTranslationStrategy}
*
* @param {I18n} i18n The I18n instance.
*
* @param {Scope} scope The translation scope.
*
* @param {Dict} options The translations' options.
*
* @returns {void}
*/
export const errorStrategy: MissingTranslationStrategy = (
i18n,
scope,
options,
) => {
const fullScope = getFullScope(i18n, scope, options);
const fullScopeWithLocale = [i18n.locale, fullScope].join(
i18n.defaultSeparator,
);
throw new Error(`Missing translation: ${fullScopeWithLocale}`);
};
export class MissingTranslation {
private i18n: I18n;
private registry: Dict;
constructor(i18n: I18n) {
this.i18n = i18n;
this.registry = {};
this.register("guess", guessStrategy);
this.register("message", messageStrategy);
this.register("error", errorStrategy);
}
/**
* Registers a new missing translation strategy. This is how messages are
* defined when a translation cannot be found.
*
* The follow example registers a strategy that always return the phrase
* "Oops! Missing translation.".
*
* @example
* ```js
* i18n.missingTranslation.register(
* "oops",
* (i18n, scope, options) => "Oops! Missing translation."
* );
*
* i18n.missingBehavior = "oops";
* ```
*
* @param {string} name The strategy name.
*
* @param {MissingTranslationStrategy} strategy A function that returns a
* string the result of a missing translation scope.
*
* @returns {void}
*/
public register(name: string, strategy: MissingTranslationStrategy): void {
this.registry[name] = strategy;
}
/**
* Return a missing translation message for the given parameters.
*
* @param {Scope} scope The translations' scope.
*
* @param {Dict} options The translations' options.
*
* @returns {string} The missing translation.
*/
public get(scope: Scope, options: Dict): string {
return this.registry[options.missingBehavior ?? this.i18n.missingBehavior](
this.i18n,
scope,
options,
);
}
}