@discord/intl 0.1.2 → 0.2.0-canary.641ab2f
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/format.d.ts +30 -0
- package/dist/format.js +214 -0
- package/dist/formatters/ast.d.ts +67 -0
- package/dist/formatters/ast.js +148 -0
- package/dist/formatters/index.d.ts +4 -0
- package/dist/formatters/index.js +14 -0
- package/dist/formatters/markdown.d.ts +13 -0
- package/dist/formatters/markdown.js +80 -0
- package/dist/formatters/react.d.ts +28 -0
- package/dist/formatters/react.js +111 -0
- package/dist/formatters/string.d.ts +21 -0
- package/dist/formatters/string.js +90 -0
- package/dist/index.d.ts +7 -6
- package/dist/index.js +19 -4
- package/dist/intl-manager.d.ts +25 -63
- package/dist/intl-manager.js +61 -89
- package/dist/keyless-json.d.ts +8 -1
- package/dist/message-loader.d.ts +38 -6
- package/dist/message-loader.js +117 -22
- package/dist/message.d.ts +3 -38
- package/dist/message.js +1 -132
- package/dist/types.d.ts +41 -45
- package/package.json +1 -2
- package/src/format.ts +224 -0
- package/src/formatters/ast.ts +155 -0
- package/src/formatters/index.ts +9 -0
- package/src/formatters/markdown.ts +54 -0
- package/src/formatters/react.ts +100 -0
- package/src/formatters/string.ts +62 -0
- package/src/index.ts +7 -7
- package/src/intl-manager.ts +73 -161
- package/src/keyless-json.ts +7 -0
- package/src/message-loader.ts +91 -24
- package/src/message.ts +4 -96
- package/src/types.d.ts +41 -45
- package/dist/react.d.ts +0 -38
- package/dist/react.js +0 -80
- package/src/react.ts +0 -80
package/dist/message-loader.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
+
import { FormatJsNode } from './keyless-json';
|
|
1
2
|
import { InternalIntlMessage } from './message';
|
|
2
|
-
|
|
3
|
-
type AnyRawMessage = string | MessageFormatElement[];
|
|
4
|
-
type MessagesData = Record<string, AnyRawMessage>;
|
|
3
|
+
type MessagesData = Record<string, FormatJsNode[]>;
|
|
5
4
|
export interface IntlMessageGetterAdditions {
|
|
6
5
|
onChange(callback: () => void): () => void;
|
|
7
6
|
}
|
|
@@ -29,9 +28,17 @@ export declare class MessageLoader {
|
|
|
29
28
|
* then a request has already been made to load the locale and it is waiting
|
|
30
29
|
* to resolve.
|
|
31
30
|
*/
|
|
32
|
-
_localeLoadingPromises: Record<LocaleId,
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
_localeLoadingPromises: Record<LocaleId, {
|
|
32
|
+
/**`true` if this locale has been loaded at least once. */
|
|
33
|
+
initialized: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* If the locale is currently being loaded, this Promise will exist and
|
|
36
|
+
* represent that loading status. Otherwise, this value is undefined.
|
|
37
|
+
*/
|
|
38
|
+
current?: Promise<{
|
|
39
|
+
default: MessagesData;
|
|
40
|
+
}> | undefined;
|
|
41
|
+
}>;
|
|
35
42
|
/**
|
|
36
43
|
* Map of pre-parsed messages, keyed by the message key and locale
|
|
37
44
|
*/
|
|
@@ -100,5 +107,30 @@ export declare class MessageLoader {
|
|
|
100
107
|
* Subscribe to events about when a locale has loaded.
|
|
101
108
|
*/
|
|
102
109
|
onChange(callback: () => void): () => void;
|
|
110
|
+
isLocaleLoaded(locale: LocaleId, requireCurrent?: boolean): boolean;
|
|
111
|
+
waitForLocaleLoaded(locale: LocaleId, requireCurrent?: boolean): Promise<void>;
|
|
112
|
+
/**
|
|
113
|
+
* Returns true if this loader's default locale has finished loading. When
|
|
114
|
+
* true, all messages managed by this loader are guaranteed to have _a_
|
|
115
|
+
* render-able value, even if one does not exist in the current locale.
|
|
116
|
+
*/
|
|
117
|
+
waitForDefaultLocale(requireCurrent?: boolean): Promise<void>;
|
|
103
118
|
}
|
|
119
|
+
/**
|
|
120
|
+
* Returns a new Promise that resolves after all currently-registered message
|
|
121
|
+
* loaders have finished successfully loading their default locale content.
|
|
122
|
+
* Once this Promise has resolved, all messages that currently exist in the
|
|
123
|
+
* application (i.e., within all modules that have been imported or required in
|
|
124
|
+
* the current session) can be guaranteed to have _a_ render-able value.
|
|
125
|
+
*
|
|
126
|
+
* This function can be called at any point in an applications life cycle and
|
|
127
|
+
* will always ensure that _all_ loaders that currently exist are initialized.
|
|
128
|
+
*/
|
|
129
|
+
export declare function waitForAllDefaultIntlMessagesLoaded(): Promise<void>;
|
|
130
|
+
/**
|
|
131
|
+
* Create a new MessageLoader, which handles lazily loading messages for
|
|
132
|
+
* different locales and sanity checks as needed to provide accessors for each
|
|
133
|
+
* message defined in `messageKeys`.
|
|
134
|
+
*/
|
|
135
|
+
export declare function createLoader(messageKeys: string[], localeImportMap: LocaleImportMap, defaultLocale: LocaleId): MessageLoader;
|
|
104
136
|
export {};
|
package/dist/message-loader.js
CHANGED
|
@@ -64,6 +64,8 @@ var __read = (this && this.__read) || function (o, n) {
|
|
|
64
64
|
};
|
|
65
65
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
66
66
|
exports.MessageLoader = void 0;
|
|
67
|
+
exports.waitForAllDefaultIntlMessagesLoaded = waitForAllDefaultIntlMessagesLoaded;
|
|
68
|
+
exports.createLoader = createLoader;
|
|
67
69
|
var message_1 = require("./message");
|
|
68
70
|
var MessageLoader = /** @class */ (function () {
|
|
69
71
|
function MessageLoader(messageKeys, localeImportMap, defaultLocale) {
|
|
@@ -78,17 +80,24 @@ var MessageLoader = /** @class */ (function () {
|
|
|
78
80
|
this._parseCache = new Map();
|
|
79
81
|
this._subscribers = new Set();
|
|
80
82
|
this._loadLocale(this.defaultLocale);
|
|
81
|
-
this.fallbackMessage = new message_1.InternalIntlMessage(
|
|
83
|
+
this.fallbackMessage = new message_1.InternalIntlMessage([], this.defaultLocale);
|
|
82
84
|
// In cases where hot module replacement is available, set up cache clearing whenever the
|
|
83
85
|
// targets change so that values are always replaced.
|
|
84
86
|
// @ts-expect-error `hot` not defined in types.
|
|
85
87
|
if (module.hot) {
|
|
86
88
|
var _loop_1 = function (locale, file) {
|
|
87
89
|
// @ts-expect-error `hot` not defined in types.
|
|
88
|
-
module.hot.accept(file, function () {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
90
|
+
module.hot.accept(file, function () { return __awaiter(_this, void 0, void 0, function () {
|
|
91
|
+
return __generator(this, function (_a) {
|
|
92
|
+
switch (_a.label) {
|
|
93
|
+
case 0: return [4 /*yield*/, this._loadLocale(locale)];
|
|
94
|
+
case 1:
|
|
95
|
+
_a.sent();
|
|
96
|
+
this._parseCache.clear();
|
|
97
|
+
return [2 /*return*/];
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
}); });
|
|
92
101
|
};
|
|
93
102
|
try {
|
|
94
103
|
for (var _b = __values(Object.entries(localeImportMap)), _c = _b.next(); !_c.done; _c = _b.next()) {
|
|
@@ -119,9 +128,8 @@ var MessageLoader = /** @class */ (function () {
|
|
|
119
128
|
MessageLoader.prototype.get = function (key, locale) {
|
|
120
129
|
var _a;
|
|
121
130
|
var value = (_a = this.getMessageValue(key, locale)) !== null && _a !== void 0 ? _a : this.getMessageValue(key, this.defaultLocale);
|
|
122
|
-
if (value != null)
|
|
131
|
+
if (value != null)
|
|
123
132
|
return value;
|
|
124
|
-
}
|
|
125
133
|
// If the message couldn't be found in either the requested nor the default locale, then
|
|
126
134
|
// nothing can be done.
|
|
127
135
|
var errorKey = this._debugKeyMap != null ? "\"".concat(this._debugKeyMap[key], "\" (").concat(key, ")") : undefined;
|
|
@@ -146,13 +154,11 @@ var MessageLoader = /** @class */ (function () {
|
|
|
146
154
|
var cachedValue = this._parseCache.get(cacheKey);
|
|
147
155
|
if (cachedValue != null)
|
|
148
156
|
return cachedValue;
|
|
149
|
-
//
|
|
150
|
-
if
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
// If not, check whether it's been loaded and trigger a load if not.
|
|
157
|
+
// Check whether the locale exists in memory. If not, start a request to
|
|
158
|
+
// load the locale if one has not already started. It's impossible for
|
|
159
|
+
// the request to finish within the same tick, so return undefined in any
|
|
160
|
+
// case then.
|
|
154
161
|
if (this.messages[locale] == null) {
|
|
155
|
-
// Ensure the locale is loaded, if it is supported
|
|
156
162
|
if (this.supportedLocales.includes(locale)) {
|
|
157
163
|
this._loadLocale(locale);
|
|
158
164
|
}
|
|
@@ -186,12 +192,13 @@ var MessageLoader = /** @class */ (function () {
|
|
|
186
192
|
};
|
|
187
193
|
MessageLoader.prototype._loadLocale = function (locale) {
|
|
188
194
|
return __awaiter(this, void 0, void 0, function () {
|
|
189
|
-
var
|
|
190
|
-
|
|
191
|
-
|
|
195
|
+
var current, _a, _b;
|
|
196
|
+
var _c;
|
|
197
|
+
return __generator(this, function (_d) {
|
|
198
|
+
switch (_d.label) {
|
|
192
199
|
case 0:
|
|
193
200
|
// Don't re-load a locale that's already in progress.
|
|
194
|
-
if (this._localeLoadingPromises[locale] != null) {
|
|
201
|
+
if (((_c = this._localeLoadingPromises[locale]) === null || _c === void 0 ? void 0 : _c.current) != null) {
|
|
195
202
|
return [2 /*return*/];
|
|
196
203
|
}
|
|
197
204
|
// Safety check in case the locale map doesn't include a require for the
|
|
@@ -203,14 +210,14 @@ var MessageLoader = /** @class */ (function () {
|
|
|
203
210
|
// If the locale is already set in `messages`, then it doesn't need to be loaded again.
|
|
204
211
|
if (this.messages[locale] != null)
|
|
205
212
|
return [2 /*return*/];
|
|
206
|
-
|
|
207
|
-
this._localeLoadingPromises[locale] =
|
|
213
|
+
current = this.localeImportMap[locale]();
|
|
214
|
+
this._localeLoadingPromises[locale] = { initialized: false, current: current };
|
|
208
215
|
_a = this.messages;
|
|
209
216
|
_b = locale;
|
|
210
|
-
return [4 /*yield*/,
|
|
217
|
+
return [4 /*yield*/, current];
|
|
211
218
|
case 1:
|
|
212
|
-
_a[_b] = (
|
|
213
|
-
|
|
219
|
+
_a[_b] = (_d.sent()).default;
|
|
220
|
+
this._localeLoadingPromises[locale] = { initialized: true, current: undefined };
|
|
214
221
|
this.emitChange();
|
|
215
222
|
return [2 /*return*/];
|
|
216
223
|
}
|
|
@@ -245,6 +252,94 @@ var MessageLoader = /** @class */ (function () {
|
|
|
245
252
|
this._subscribers.add(callback);
|
|
246
253
|
return function () { return _this._subscribers.delete(callback); };
|
|
247
254
|
};
|
|
255
|
+
MessageLoader.prototype.isLocaleLoaded = function (locale, requireCurrent) {
|
|
256
|
+
if (requireCurrent === void 0) { requireCurrent = false; }
|
|
257
|
+
var ref = this._localeLoadingPromises[locale];
|
|
258
|
+
// Ensure the locale at least exists and is initialized.
|
|
259
|
+
if (ref == null || ref.initialized == false)
|
|
260
|
+
return false;
|
|
261
|
+
// `current` will be deleted once the locale has loaded, so if it is null,
|
|
262
|
+
// then the locale data is current.
|
|
263
|
+
if (requireCurrent)
|
|
264
|
+
return ref.current == null;
|
|
265
|
+
// Otherwise, initialization is enough.
|
|
266
|
+
return true;
|
|
267
|
+
};
|
|
268
|
+
MessageLoader.prototype.waitForLocaleLoaded = function (locale_1) {
|
|
269
|
+
return __awaiter(this, arguments, void 0, function (locale, requireCurrent) {
|
|
270
|
+
var ref;
|
|
271
|
+
if (requireCurrent === void 0) { requireCurrent = false; }
|
|
272
|
+
return __generator(this, function (_a) {
|
|
273
|
+
switch (_a.label) {
|
|
274
|
+
case 0:
|
|
275
|
+
ref = this._localeLoadingPromises[locale];
|
|
276
|
+
// If the locale hasn't been loaded at all, kick off the load and return
|
|
277
|
+
// that Promise directly.
|
|
278
|
+
if (ref == null)
|
|
279
|
+
return [2 /*return*/, this._loadLocale(locale)];
|
|
280
|
+
// If initialization is enough for this request, just resolve immediately.
|
|
281
|
+
if (ref.initialized && !requireCurrent)
|
|
282
|
+
return [2 /*return*/];
|
|
283
|
+
// Otherwise, check the `current` loading state on the object, and if that
|
|
284
|
+
// doesn't exist, then the locale data is already finished loading, and the
|
|
285
|
+
// containing Promise will just resolve with `undefined`.
|
|
286
|
+
return [4 /*yield*/, ref.current];
|
|
287
|
+
case 1:
|
|
288
|
+
// Otherwise, check the `current` loading state on the object, and if that
|
|
289
|
+
// doesn't exist, then the locale data is already finished loading, and the
|
|
290
|
+
// containing Promise will just resolve with `undefined`.
|
|
291
|
+
_a.sent();
|
|
292
|
+
return [2 /*return*/];
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
});
|
|
296
|
+
};
|
|
297
|
+
/**
|
|
298
|
+
* Returns true if this loader's default locale has finished loading. When
|
|
299
|
+
* true, all messages managed by this loader are guaranteed to have _a_
|
|
300
|
+
* render-able value, even if one does not exist in the current locale.
|
|
301
|
+
*/
|
|
302
|
+
MessageLoader.prototype.waitForDefaultLocale = function () {
|
|
303
|
+
return __awaiter(this, arguments, void 0, function (requireCurrent) {
|
|
304
|
+
if (requireCurrent === void 0) { requireCurrent = false; }
|
|
305
|
+
return __generator(this, function (_a) {
|
|
306
|
+
return [2 /*return*/, this.waitForLocaleLoaded(this.defaultLocale, requireCurrent)];
|
|
307
|
+
});
|
|
308
|
+
});
|
|
309
|
+
};
|
|
248
310
|
return MessageLoader;
|
|
249
311
|
}());
|
|
250
312
|
exports.MessageLoader = MessageLoader;
|
|
313
|
+
var LOADER_REGISTRY = [];
|
|
314
|
+
/**
|
|
315
|
+
* Returns a new Promise that resolves after all currently-registered message
|
|
316
|
+
* loaders have finished successfully loading their default locale content.
|
|
317
|
+
* Once this Promise has resolved, all messages that currently exist in the
|
|
318
|
+
* application (i.e., within all modules that have been imported or required in
|
|
319
|
+
* the current session) can be guaranteed to have _a_ render-able value.
|
|
320
|
+
*
|
|
321
|
+
* This function can be called at any point in an applications life cycle and
|
|
322
|
+
* will always ensure that _all_ loaders that currently exist are initialized.
|
|
323
|
+
*/
|
|
324
|
+
function waitForAllDefaultIntlMessagesLoaded() {
|
|
325
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
326
|
+
return __generator(this, function (_a) {
|
|
327
|
+
switch (_a.label) {
|
|
328
|
+
case 0: return [4 /*yield*/, Promise.all(LOADER_REGISTRY.map(function (loader) { return loader.waitForDefaultLocale(); }))];
|
|
329
|
+
case 1:
|
|
330
|
+
_a.sent();
|
|
331
|
+
return [2 /*return*/];
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Create a new MessageLoader, which handles lazily loading messages for
|
|
338
|
+
* different locales and sanity checks as needed to provide accessors for each
|
|
339
|
+
* message defined in `messageKeys`.
|
|
340
|
+
*/
|
|
341
|
+
function createLoader(messageKeys, localeImportMap, defaultLocale) {
|
|
342
|
+
var loader = new MessageLoader(messageKeys, localeImportMap, defaultLocale);
|
|
343
|
+
LOADER_REGISTRY.push(loader);
|
|
344
|
+
return loader;
|
|
345
|
+
}
|
package/dist/message.d.ts
CHANGED
|
@@ -1,41 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type { MessageFormatElement } from '@formatjs/icu-messageformat-parser';
|
|
1
|
+
import { FormatJsNode } from './keyless-json';
|
|
3
2
|
export declare class InternalIntlMessage {
|
|
4
3
|
locale: string;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* A stripped-down representation of the message with no rich formatting
|
|
9
|
-
* elements preserved. This will be lazily created the first time it is
|
|
10
|
-
* requested (e.g., by calling `formatToPlainString`).
|
|
11
|
-
*/
|
|
12
|
-
plainAst?: MessageFormatElement[];
|
|
13
|
-
constructor(messageOrAst: string | MessageFormatElement[], locale: string);
|
|
14
|
-
/**
|
|
15
|
-
* Format this message with the given locale into an AST of chunks that can
|
|
16
|
-
* then be used or rendered into strings, React elements, or anything else as
|
|
17
|
-
* needed.
|
|
18
|
-
*/
|
|
19
|
-
formatToParts<T>(formatters: Formatters, formats: Formats, values?: Record<string, any>): Array<MessageFormatPart<T>>;
|
|
20
|
-
/**
|
|
21
|
-
* Formats this message just like `formatToParts`, but strips all stylistic
|
|
22
|
-
* elements and tags and returns a plain string with only the literal text of
|
|
23
|
-
* the message remaining.
|
|
24
|
-
*
|
|
25
|
-
* This method works by quickly scanning the string and removing any tag
|
|
26
|
-
* elements, formatting, then concatenating the pieces into a single result.
|
|
27
|
-
*
|
|
28
|
-
* Note that this function does _not_ handle serializing values other than
|
|
29
|
-
* plain/primitive values into strings. All values are simply converted using
|
|
30
|
-
* the `String()` constructor. So, for example, passing a function as a value
|
|
31
|
-
* will directly serialize the function definition into a string, rather than
|
|
32
|
-
* calling the function or evaluating the result.
|
|
33
|
-
*/
|
|
34
|
-
formatToPlainString(formatters: Formatters, formats: Formats, values?: Record<string, any>): string;
|
|
35
|
-
/**
|
|
36
|
-
* Returns the same element with any formatting tags removed. If the element
|
|
37
|
-
* itself is a tag, the children will be hoisted and returned, otherwise the
|
|
38
|
-
* element is returned as an array of itself.
|
|
39
|
-
*/
|
|
40
|
-
_removeRichTags(element: MessageFormatElement): MessageFormatElement[];
|
|
4
|
+
ast: FormatJsNode[];
|
|
5
|
+
constructor(messageOrAst: FormatJsNode[], locale: string);
|
|
41
6
|
}
|
package/dist/message.js
CHANGED
|
@@ -1,142 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __values = (this && this.__values) || function(o) {
|
|
3
|
-
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
|
|
4
|
-
if (m) return m.call(o);
|
|
5
|
-
if (o && typeof o.length === "number") return {
|
|
6
|
-
next: function () {
|
|
7
|
-
if (o && i >= o.length) o = void 0;
|
|
8
|
-
return { value: o && o[i++], done: !o };
|
|
9
|
-
}
|
|
10
|
-
};
|
|
11
|
-
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
|
|
12
|
-
};
|
|
13
|
-
var __read = (this && this.__read) || function (o, n) {
|
|
14
|
-
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
|
15
|
-
if (!m) return o;
|
|
16
|
-
var i = m.call(o), r, ar = [], e;
|
|
17
|
-
try {
|
|
18
|
-
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
|
19
|
-
}
|
|
20
|
-
catch (error) { e = { error: error }; }
|
|
21
|
-
finally {
|
|
22
|
-
try {
|
|
23
|
-
if (r && !r.done && (m = i["return"])) m.call(i);
|
|
24
|
-
}
|
|
25
|
-
finally { if (e) throw e.error; }
|
|
26
|
-
}
|
|
27
|
-
return ar;
|
|
28
|
-
};
|
|
29
|
-
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
30
|
-
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
31
|
-
if (ar || !(i in from)) {
|
|
32
|
-
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
33
|
-
ar[i] = from[i];
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
return to.concat(ar || Array.prototype.slice.call(from));
|
|
37
|
-
};
|
|
38
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
3
|
exports.InternalIntlMessage = void 0;
|
|
40
|
-
var icu_messageformat_parser_1 = require("@formatjs/icu-messageformat-parser");
|
|
41
|
-
var intl_messageformat_1 = require("intl-messageformat");
|
|
42
4
|
var InternalIntlMessage = /** @class */ (function () {
|
|
43
5
|
function InternalIntlMessage(messageOrAst, locale) {
|
|
44
6
|
this.locale = locale;
|
|
45
|
-
|
|
46
|
-
this.message = messageOrAst;
|
|
47
|
-
this.ast = (0, icu_messageformat_parser_1.parse)(messageOrAst);
|
|
48
|
-
}
|
|
49
|
-
else {
|
|
50
|
-
this.ast = messageOrAst;
|
|
51
|
-
}
|
|
52
|
-
this.plainAst = undefined;
|
|
7
|
+
this.ast = messageOrAst;
|
|
53
8
|
}
|
|
54
|
-
/**
|
|
55
|
-
* Format this message with the given locale into an AST of chunks that can
|
|
56
|
-
* then be used or rendered into strings, React elements, or anything else as
|
|
57
|
-
* needed.
|
|
58
|
-
*/
|
|
59
|
-
InternalIntlMessage.prototype.formatToParts = function (formatters, formats, values) {
|
|
60
|
-
return (0, intl_messageformat_1.formatToParts)(this.ast, this.locale, formatters, formats, values);
|
|
61
|
-
};
|
|
62
|
-
/**
|
|
63
|
-
* Formats this message just like `formatToParts`, but strips all stylistic
|
|
64
|
-
* elements and tags and returns a plain string with only the literal text of
|
|
65
|
-
* the message remaining.
|
|
66
|
-
*
|
|
67
|
-
* This method works by quickly scanning the string and removing any tag
|
|
68
|
-
* elements, formatting, then concatenating the pieces into a single result.
|
|
69
|
-
*
|
|
70
|
-
* Note that this function does _not_ handle serializing values other than
|
|
71
|
-
* plain/primitive values into strings. All values are simply converted using
|
|
72
|
-
* the `String()` constructor. So, for example, passing a function as a value
|
|
73
|
-
* will directly serialize the function definition into a string, rather than
|
|
74
|
-
* calling the function or evaluating the result.
|
|
75
|
-
*/
|
|
76
|
-
InternalIntlMessage.prototype.formatToPlainString = function (formatters, formats, values) {
|
|
77
|
-
var e_1, _a, _b, e_2, _c;
|
|
78
|
-
// Lazily create the reduced AST if it hasn't already been done.
|
|
79
|
-
if (this.plainAst == null) {
|
|
80
|
-
this.plainAst = [];
|
|
81
|
-
try {
|
|
82
|
-
for (var _d = __values(this.ast), _e = _d.next(); !_e.done; _e = _d.next()) {
|
|
83
|
-
var part = _e.value;
|
|
84
|
-
(_b = this.plainAst).push.apply(_b, __spreadArray([], __read(this._removeRichTags(part)), false));
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
88
|
-
finally {
|
|
89
|
-
try {
|
|
90
|
-
if (_e && !_e.done && (_a = _d.return)) _a.call(_d);
|
|
91
|
-
}
|
|
92
|
-
finally { if (e_1) throw e_1.error; }
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
var parts = (0, intl_messageformat_1.formatToParts)(this.plainAst, this.locale, formatters, formats, values);
|
|
96
|
-
var result = '';
|
|
97
|
-
try {
|
|
98
|
-
for (var parts_1 = __values(parts), parts_1_1 = parts_1.next(); !parts_1_1.done; parts_1_1 = parts_1.next()) {
|
|
99
|
-
var part = parts_1_1.value;
|
|
100
|
-
result += String(part.value);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
|
104
|
-
finally {
|
|
105
|
-
try {
|
|
106
|
-
if (parts_1_1 && !parts_1_1.done && (_c = parts_1.return)) _c.call(parts_1);
|
|
107
|
-
}
|
|
108
|
-
finally { if (e_2) throw e_2.error; }
|
|
109
|
-
}
|
|
110
|
-
return result;
|
|
111
|
-
};
|
|
112
|
-
/**
|
|
113
|
-
* Returns the same element with any formatting tags removed. If the element
|
|
114
|
-
* itself is a tag, the children will be hoisted and returned, otherwise the
|
|
115
|
-
* element is returned as an array of itself.
|
|
116
|
-
*/
|
|
117
|
-
InternalIntlMessage.prototype._removeRichTags = function (element) {
|
|
118
|
-
var e_3, _a;
|
|
119
|
-
if (element.type === icu_messageformat_parser_1.TYPE.tag) {
|
|
120
|
-
var childContent = [];
|
|
121
|
-
try {
|
|
122
|
-
for (var _b = __values(element.children), _c = _b.next(); !_c.done; _c = _b.next()) {
|
|
123
|
-
var child = _c.value;
|
|
124
|
-
childContent.push.apply(childContent, __spreadArray([], __read(this._removeRichTags(child)), false));
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
catch (e_3_1) { e_3 = { error: e_3_1 }; }
|
|
128
|
-
finally {
|
|
129
|
-
try {
|
|
130
|
-
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
|
|
131
|
-
}
|
|
132
|
-
finally { if (e_3) throw e_3.error; }
|
|
133
|
-
}
|
|
134
|
-
return childContent;
|
|
135
|
-
}
|
|
136
|
-
else {
|
|
137
|
-
return [element];
|
|
138
|
-
}
|
|
139
|
-
};
|
|
140
9
|
return InternalIntlMessage;
|
|
141
10
|
}());
|
|
142
11
|
exports.InternalIntlMessage = InternalIntlMessage;
|
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { InternalIntlMessage } from './message';
|
|
2
2
|
import type { IntlMessageGetterAdditions } from './message-loader';
|
|
3
|
+
import { FormatBuilderConstructor } from './format';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Use this function to define messages as part of the `@discord/intl`
|
|
@@ -11,14 +12,6 @@ import type { IntlMessageGetterAdditions } from './message-loader';
|
|
|
11
12
|
*/
|
|
12
13
|
export declare function defineMessages(messages: object): object;
|
|
13
14
|
|
|
14
|
-
/**
|
|
15
|
-
* A map of tag names to render functions to apply when the tags are
|
|
16
|
-
* encountered while formatting a message. Each `IntlManager` can be
|
|
17
|
-
* constructed with a different map of these elements, for example to allow
|
|
18
|
-
* one manager to render React output while another renders accessibility text.
|
|
19
|
-
*/
|
|
20
|
-
export type RichTextElementMap = Record<string, (chunks: any[]) => any>;
|
|
21
|
-
|
|
22
15
|
/**
|
|
23
16
|
* Placeholder type representing that this property is a Hook to be rendered
|
|
24
17
|
* while formatting.
|
|
@@ -46,19 +39,8 @@ export type IntlTime = string | number | Date | null | undefined;
|
|
|
46
39
|
* strings, and are able to skip multiple formatting steps to work more
|
|
47
40
|
* efficiently. This type represents those messages distinctly.
|
|
48
41
|
*/
|
|
49
|
-
type PlainIntlMessage = string;
|
|
50
|
-
|
|
51
|
-
* A richly-formatted message that injects dynamic values while formatting,
|
|
52
|
-
* with the types of those values defined by the `FormatValues` object. All
|
|
53
|
-
* messages of this type must have at least one value in the object, otherwise
|
|
54
|
-
* the message should be typed as a `PlainIntlMessage`.
|
|
55
|
-
*/
|
|
56
|
-
type TypedIntlMessage<FormatValues extends object> = InternalIntlMessage & {
|
|
57
|
-
__values: FormatValues;
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
type AnyIntlMessage<FormatValues extends object | undefined = undefined> =
|
|
61
|
-
FormatValues extends undefined ? PlainIntlMessage : TypedIntlMessage<FormatValues>;
|
|
42
|
+
export type PlainIntlMessage = string;
|
|
43
|
+
export type AnyIntlMessage = PlainIntlMessage | InternalIntlMessage;
|
|
62
44
|
|
|
63
45
|
export interface IntlMessageGetter extends IntlMessageGetterAdditions {
|
|
64
46
|
(locale: string): InternalIntlMessage | PlainIntlMessage;
|
|
@@ -85,8 +67,8 @@ export interface TypedIntlMessageGetter<FormatValues extends object | undefined>
|
|
|
85
67
|
// from the declared type. The latter of these is definitely semantically
|
|
86
68
|
// correct, but the former is more just an annoying part of the type system
|
|
87
69
|
// because of this returned type being nested within this interface and the
|
|
88
|
-
// strict
|
|
89
|
-
(locale: string): InternalIntlMessage
|
|
70
|
+
// strict subtyping that TypeScript uses.
|
|
71
|
+
(locale: string): InternalIntlMessage;
|
|
90
72
|
|
|
91
73
|
/**
|
|
92
74
|
* A phantom type (not part of the actual typing of the object) that ensures
|
|
@@ -183,7 +165,7 @@ type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
|
|
|
183
165
|
* checked against the provided message types, and error messages will be
|
|
184
166
|
* accurate as a result.
|
|
185
167
|
*/
|
|
186
|
-
type FormatValuesFor<T> =
|
|
168
|
+
export type FormatValuesFor<T> =
|
|
187
169
|
// `[]` syntax forces TypeScript to not distribute the possible union of
|
|
188
170
|
// types, meaning this represents "_all_ values of T are an extension of
|
|
189
171
|
// the parent type", which in this case represents messages with no
|
|
@@ -209,12 +191,18 @@ type FormatValuesFor<T> =
|
|
|
209
191
|
* the caller to inject React elements within a string, while an AST formatter
|
|
210
192
|
* might require a specific object shape, or allow any plain object.
|
|
211
193
|
*/
|
|
212
|
-
interface FunctionTypeMap {
|
|
194
|
+
export interface FunctionTypeMap {
|
|
213
195
|
link: any;
|
|
214
196
|
hook: any;
|
|
215
197
|
handler: any;
|
|
216
198
|
}
|
|
217
199
|
|
|
200
|
+
export interface FunctionTypes<Result, HandlerType = (content: Result[]) => Result | Result[]> {
|
|
201
|
+
link: undefined | ((content: Result[]) => Result | Result[]);
|
|
202
|
+
hook: undefined | ((content: Result[]) => Result | Result[]);
|
|
203
|
+
handler: undefined | HandlerType;
|
|
204
|
+
}
|
|
205
|
+
|
|
218
206
|
type ReplaceKeepingNullability<T, P> = P | Exclude<T, NonNullable<T>>;
|
|
219
207
|
|
|
220
208
|
/**
|
|
@@ -226,6 +214,8 @@ type ReplaceKeepingNullability<T, P> = P | Exclude<T, NonNullable<T>>;
|
|
|
226
214
|
type MapFunctionTypes<T, FunctionTypes extends FunctionTypeMap> = {
|
|
227
215
|
[K in keyof T]: ReplaceKeepingNullability<
|
|
228
216
|
T[K],
|
|
217
|
+
// TODO: replace these types with string literal types in the generated
|
|
218
|
+
// file, turning this whole chain into `FunctionTypes[T[K]] ?? T[K]`.
|
|
229
219
|
NonNullable<T[K]> extends LinkFunction
|
|
230
220
|
? FunctionTypes['link']
|
|
231
221
|
: NonNullable<T[K]> extends HookFunction
|
|
@@ -251,7 +241,7 @@ type CheckStrictAny<Check, Yes, No> = (Check extends never ? true : false) exten
|
|
|
251
241
|
* // Result is `{name: string}`
|
|
252
242
|
* ```
|
|
253
243
|
*/
|
|
254
|
-
type IntlMessageGetterInnerType<T extends IntlMessageGetter> =
|
|
244
|
+
export type IntlMessageGetterInnerType<T extends IntlMessageGetter> =
|
|
255
245
|
T extends TypedIntlMessageGetter<infer U> ? U : never;
|
|
256
246
|
|
|
257
247
|
/**
|
|
@@ -260,13 +250,12 @@ type IntlMessageGetterInnerType<T extends IntlMessageGetter> =
|
|
|
260
250
|
* types from `FunctionTypes`. The result is the set of required values
|
|
261
251
|
* necessary to format the message of type `T` with the calling formatter.
|
|
262
252
|
*/
|
|
263
|
-
type RequiredFormatValues<
|
|
253
|
+
export type RequiredFormatValues<
|
|
264
254
|
Values extends IntlMessageGetter,
|
|
265
|
-
DefaultElements,
|
|
266
255
|
FunctionTypes extends FunctionTypeMap,
|
|
267
256
|
> =
|
|
268
257
|
IntlMessageGetterInnerType<Values> extends undefined
|
|
269
|
-
?
|
|
258
|
+
? {}
|
|
270
259
|
: CheckStrictAny<
|
|
271
260
|
IntlMessageGetterInnerType<Values>,
|
|
272
261
|
// If `T` is explicitly `any`, then individual properties can't be typed and
|
|
@@ -277,23 +266,30 @@ type RequiredFormatValues<
|
|
|
277
266
|
// value and turn every property into the mapped `LinkFunction` type, which
|
|
278
267
|
// is almost always going to be wrong.
|
|
279
268
|
any,
|
|
280
|
-
MapFunctionTypes<
|
|
281
|
-
Omit<FormatValuesFor<IntlMessageGetterInnerType<Values>>, keyof DefaultElements>,
|
|
282
|
-
FunctionTypes
|
|
283
|
-
>
|
|
269
|
+
MapFunctionTypes<FormatValuesFor<IntlMessageGetterInnerType<Values>>, FunctionTypes>
|
|
284
270
|
>;
|
|
285
271
|
|
|
286
|
-
export interface
|
|
287
|
-
$_:
|
|
288
|
-
$b:
|
|
289
|
-
$i:
|
|
290
|
-
$p:
|
|
291
|
-
$link:
|
|
292
|
-
$code:
|
|
272
|
+
export interface RichTextFormattingMap<T = any> {
|
|
273
|
+
$_: T;
|
|
274
|
+
$b: T;
|
|
275
|
+
$i: T;
|
|
276
|
+
$p: T;
|
|
277
|
+
$link: T;
|
|
278
|
+
$code: T;
|
|
293
279
|
}
|
|
294
280
|
|
|
295
|
-
export type
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
FunctionTypeMap
|
|
299
|
-
|
|
281
|
+
export type RichTextTagNames = keyof RichTextFormattingMap;
|
|
282
|
+
|
|
283
|
+
export interface FormatterImplementation<
|
|
284
|
+
FunctionTypes extends FunctionTypeMap,
|
|
285
|
+
Result,
|
|
286
|
+
BuilderResult = Result,
|
|
287
|
+
> {
|
|
288
|
+
format: (
|
|
289
|
+
message: AnyIntlMessage,
|
|
290
|
+
values: object,
|
|
291
|
+
builder?: FormatBuilderConstructor<BuilderResult>,
|
|
292
|
+
) => Result;
|
|
293
|
+
builder: FormatBuilderConstructor<BuilderResult extends (infer T)[] ? T : BuilderResult>;
|
|
294
|
+
__$functionTypes?: FunctionTypes;
|
|
295
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@discord/intl",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0-canary.641ab2f",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Client runtime for managing messages and translations in a React project.",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -15,7 +15,6 @@
|
|
|
15
15
|
"src"
|
|
16
16
|
],
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@formatjs/icu-messageformat-parser": "^2.7.6",
|
|
19
18
|
"@formatjs/intl": "^2.10.1",
|
|
20
19
|
"intl-messageformat": "^10.5.11"
|
|
21
20
|
},
|