@discord/intl 0.2.0 → 0.2.1-canary.11837d1
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.js +23 -19
- package/dist/formatters/ast.d.ts +6 -2
- package/dist/formatters/ast.js +4 -10
- package/dist/formatters/markdown.js +3 -2
- package/dist/formatters/react.d.ts +6 -3
- package/dist/formatters/react.js +9 -7
- package/dist/formatters/string.js +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +7 -3
- package/dist/intl-manager.d.ts +11 -8
- package/dist/intl-manager.js +19 -12
- package/dist/message-loader.d.ts +42 -3
- package/dist/message-loader.js +135 -22
- package/dist/message.d.ts +4 -13
- package/dist/message.js +144 -51
- package/dist/types.d.ts +4 -3
- package/package.json +2 -1
- package/src/format.ts +26 -16
- package/src/formatters/ast.ts +9 -12
- package/src/formatters/markdown.ts +3 -2
- package/src/formatters/react.ts +17 -10
- package/src/formatters/string.ts +1 -1
- package/src/index.ts +8 -2
- package/src/intl-manager.ts +22 -19
- package/src/message-loader.ts +98 -20
- package/src/message.ts +114 -29
- package/src/types.d.ts +4 -3
package/dist/message-loader.js
CHANGED
|
@@ -64,6 +64,9 @@ 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.loadAllMessagesInLocale = loadAllMessagesInLocale;
|
|
68
|
+
exports.waitForAllDefaultIntlMessagesLoaded = waitForAllDefaultIntlMessagesLoaded;
|
|
69
|
+
exports.createLoader = createLoader;
|
|
67
70
|
var message_1 = require("./message");
|
|
68
71
|
var MessageLoader = /** @class */ (function () {
|
|
69
72
|
function MessageLoader(messageKeys, localeImportMap, defaultLocale) {
|
|
@@ -78,17 +81,24 @@ var MessageLoader = /** @class */ (function () {
|
|
|
78
81
|
this._parseCache = new Map();
|
|
79
82
|
this._subscribers = new Set();
|
|
80
83
|
this._loadLocale(this.defaultLocale);
|
|
81
|
-
this.fallbackMessage = new message_1.InternalIntlMessage(
|
|
84
|
+
this.fallbackMessage = new message_1.InternalIntlMessage([], this.defaultLocale);
|
|
82
85
|
// In cases where hot module replacement is available, set up cache clearing whenever the
|
|
83
86
|
// targets change so that values are always replaced.
|
|
84
87
|
// @ts-expect-error `hot` not defined in types.
|
|
85
88
|
if (module.hot) {
|
|
86
89
|
var _loop_1 = function (locale, file) {
|
|
87
90
|
// @ts-expect-error `hot` not defined in types.
|
|
88
|
-
module.hot.accept(file, function () {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
91
|
+
module.hot.accept(file, function () { return __awaiter(_this, void 0, void 0, function () {
|
|
92
|
+
return __generator(this, function (_a) {
|
|
93
|
+
switch (_a.label) {
|
|
94
|
+
case 0: return [4 /*yield*/, this._loadLocale(locale)];
|
|
95
|
+
case 1:
|
|
96
|
+
_a.sent();
|
|
97
|
+
this._parseCache.clear();
|
|
98
|
+
return [2 /*return*/];
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}); });
|
|
92
102
|
};
|
|
93
103
|
try {
|
|
94
104
|
for (var _b = __values(Object.entries(localeImportMap)), _c = _b.next(); !_c.done; _c = _b.next()) {
|
|
@@ -119,9 +129,8 @@ var MessageLoader = /** @class */ (function () {
|
|
|
119
129
|
MessageLoader.prototype.get = function (key, locale) {
|
|
120
130
|
var _a;
|
|
121
131
|
var value = (_a = this.getMessageValue(key, locale)) !== null && _a !== void 0 ? _a : this.getMessageValue(key, this.defaultLocale);
|
|
122
|
-
if (value != null)
|
|
132
|
+
if (value != null)
|
|
123
133
|
return value;
|
|
124
|
-
}
|
|
125
134
|
// If the message couldn't be found in either the requested nor the default locale, then
|
|
126
135
|
// nothing can be done.
|
|
127
136
|
var errorKey = this._debugKeyMap != null ? "\"".concat(this._debugKeyMap[key], "\" (").concat(key, ")") : undefined;
|
|
@@ -146,13 +155,11 @@ var MessageLoader = /** @class */ (function () {
|
|
|
146
155
|
var cachedValue = this._parseCache.get(cacheKey);
|
|
147
156
|
if (cachedValue != null)
|
|
148
157
|
return cachedValue;
|
|
149
|
-
//
|
|
150
|
-
if
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
// If not, check whether it's been loaded and trigger a load if not.
|
|
158
|
+
// Check whether the locale exists in memory. If not, start a request to
|
|
159
|
+
// load the locale if one has not already started. It's impossible for
|
|
160
|
+
// the request to finish within the same tick, so return undefined in any
|
|
161
|
+
// case then.
|
|
154
162
|
if (this.messages[locale] == null) {
|
|
155
|
-
// Ensure the locale is loaded, if it is supported
|
|
156
163
|
if (this.supportedLocales.includes(locale)) {
|
|
157
164
|
this._loadLocale(locale);
|
|
158
165
|
}
|
|
@@ -186,12 +193,13 @@ var MessageLoader = /** @class */ (function () {
|
|
|
186
193
|
};
|
|
187
194
|
MessageLoader.prototype._loadLocale = function (locale) {
|
|
188
195
|
return __awaiter(this, void 0, void 0, function () {
|
|
189
|
-
var
|
|
190
|
-
|
|
191
|
-
|
|
196
|
+
var current, _a, _b;
|
|
197
|
+
var _c;
|
|
198
|
+
return __generator(this, function (_d) {
|
|
199
|
+
switch (_d.label) {
|
|
192
200
|
case 0:
|
|
193
201
|
// Don't re-load a locale that's already in progress.
|
|
194
|
-
if (this._localeLoadingPromises[locale] != null) {
|
|
202
|
+
if (((_c = this._localeLoadingPromises[locale]) === null || _c === void 0 ? void 0 : _c.current) != null) {
|
|
195
203
|
return [2 /*return*/];
|
|
196
204
|
}
|
|
197
205
|
// Safety check in case the locale map doesn't include a require for the
|
|
@@ -203,14 +211,14 @@ var MessageLoader = /** @class */ (function () {
|
|
|
203
211
|
// If the locale is already set in `messages`, then it doesn't need to be loaded again.
|
|
204
212
|
if (this.messages[locale] != null)
|
|
205
213
|
return [2 /*return*/];
|
|
206
|
-
|
|
207
|
-
this._localeLoadingPromises[locale] =
|
|
214
|
+
current = this.localeImportMap[locale]();
|
|
215
|
+
this._localeLoadingPromises[locale] = { initialized: false, current: current };
|
|
208
216
|
_a = this.messages;
|
|
209
217
|
_b = locale;
|
|
210
|
-
return [4 /*yield*/,
|
|
218
|
+
return [4 /*yield*/, current];
|
|
211
219
|
case 1:
|
|
212
|
-
_a[_b] = (
|
|
213
|
-
|
|
220
|
+
_a[_b] = (_d.sent()).default;
|
|
221
|
+
this._localeLoadingPromises[locale] = { initialized: true, current: undefined };
|
|
214
222
|
this.emitChange();
|
|
215
223
|
return [2 /*return*/];
|
|
216
224
|
}
|
|
@@ -245,6 +253,111 @@ var MessageLoader = /** @class */ (function () {
|
|
|
245
253
|
this._subscribers.add(callback);
|
|
246
254
|
return function () { return _this._subscribers.delete(callback); };
|
|
247
255
|
};
|
|
256
|
+
MessageLoader.prototype.isLocaleLoaded = function (locale, requireCurrent) {
|
|
257
|
+
if (requireCurrent === void 0) { requireCurrent = false; }
|
|
258
|
+
var ref = this._localeLoadingPromises[locale];
|
|
259
|
+
// Ensure the locale at least exists and is initialized.
|
|
260
|
+
if (ref == null || ref.initialized == false)
|
|
261
|
+
return false;
|
|
262
|
+
// `current` will be deleted once the locale has loaded, so if it is null,
|
|
263
|
+
// then the locale data is current.
|
|
264
|
+
if (requireCurrent)
|
|
265
|
+
return ref.current == null;
|
|
266
|
+
// Otherwise, initialization is enough.
|
|
267
|
+
return true;
|
|
268
|
+
};
|
|
269
|
+
MessageLoader.prototype.waitForLocaleLoaded = function (locale_1) {
|
|
270
|
+
return __awaiter(this, arguments, void 0, function (locale, requireCurrent) {
|
|
271
|
+
var ref;
|
|
272
|
+
if (requireCurrent === void 0) { requireCurrent = false; }
|
|
273
|
+
return __generator(this, function (_a) {
|
|
274
|
+
switch (_a.label) {
|
|
275
|
+
case 0:
|
|
276
|
+
ref = this._localeLoadingPromises[locale];
|
|
277
|
+
// If the locale hasn't been loaded at all, kick off the load and return
|
|
278
|
+
// that Promise directly.
|
|
279
|
+
if (ref == null)
|
|
280
|
+
return [2 /*return*/, this._loadLocale(locale)];
|
|
281
|
+
// If initialization is enough for this request, just resolve immediately.
|
|
282
|
+
if (ref.initialized && !requireCurrent)
|
|
283
|
+
return [2 /*return*/];
|
|
284
|
+
// Otherwise, check the `current` loading state on the object, and if that
|
|
285
|
+
// doesn't exist, then the locale data is already finished loading, and the
|
|
286
|
+
// containing Promise will just resolve with `undefined`.
|
|
287
|
+
return [4 /*yield*/, ref.current];
|
|
288
|
+
case 1:
|
|
289
|
+
// Otherwise, check the `current` loading state on the object, and if that
|
|
290
|
+
// doesn't exist, then the locale data is already finished loading, and the
|
|
291
|
+
// containing Promise will just resolve with `undefined`.
|
|
292
|
+
_a.sent();
|
|
293
|
+
return [2 /*return*/];
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
});
|
|
297
|
+
};
|
|
298
|
+
/**
|
|
299
|
+
* Returns true if this loader's default locale has finished loading. When
|
|
300
|
+
* true, all messages managed by this loader are guaranteed to have _a_
|
|
301
|
+
* render-able value, even if one does not exist in the current locale.
|
|
302
|
+
*/
|
|
303
|
+
MessageLoader.prototype.waitForDefaultLocale = function () {
|
|
304
|
+
return __awaiter(this, arguments, void 0, function (requireCurrent) {
|
|
305
|
+
if (requireCurrent === void 0) { requireCurrent = false; }
|
|
306
|
+
return __generator(this, function (_a) {
|
|
307
|
+
return [2 /*return*/, this.waitForLocaleLoaded(this.defaultLocale, requireCurrent)];
|
|
308
|
+
});
|
|
309
|
+
});
|
|
310
|
+
};
|
|
248
311
|
return MessageLoader;
|
|
249
312
|
}());
|
|
250
313
|
exports.MessageLoader = MessageLoader;
|
|
314
|
+
var LOADER_REGISTRY = [];
|
|
315
|
+
/**
|
|
316
|
+
* Kick off a load request for the given locale across all currently-registered
|
|
317
|
+
* message loaders, returning a Promise that resolves when all loaders have
|
|
318
|
+
* finished loading that locale.
|
|
319
|
+
*/
|
|
320
|
+
function loadAllMessagesInLocale(locale) {
|
|
321
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
322
|
+
return __generator(this, function (_a) {
|
|
323
|
+
switch (_a.label) {
|
|
324
|
+
case 0: return [4 /*yield*/, Promise.all(LOADER_REGISTRY.map(function (loader) { return loader._loadLocale(locale); }))];
|
|
325
|
+
case 1:
|
|
326
|
+
_a.sent();
|
|
327
|
+
return [2 /*return*/];
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Returns a new Promise that resolves after all currently-registered message
|
|
334
|
+
* loaders have finished successfully loading their default locale content.
|
|
335
|
+
* Once this Promise has resolved, all messages that currently exist in the
|
|
336
|
+
* application (i.e., within all modules that have been imported or required in
|
|
337
|
+
* the current session) can be guaranteed to have _a_ render-able value.
|
|
338
|
+
*
|
|
339
|
+
* This function can be called at any point in an applications life cycle and
|
|
340
|
+
* will always ensure that _all_ loaders that currently exist are initialized.
|
|
341
|
+
*/
|
|
342
|
+
function waitForAllDefaultIntlMessagesLoaded() {
|
|
343
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
344
|
+
return __generator(this, function (_a) {
|
|
345
|
+
switch (_a.label) {
|
|
346
|
+
case 0: return [4 /*yield*/, Promise.all(LOADER_REGISTRY.map(function (loader) { return loader.waitForDefaultLocale(); }))];
|
|
347
|
+
case 1:
|
|
348
|
+
_a.sent();
|
|
349
|
+
return [2 /*return*/];
|
|
350
|
+
}
|
|
351
|
+
});
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Create a new MessageLoader, which handles lazily loading messages for
|
|
356
|
+
* different locales and sanity checks as needed to provide accessors for each
|
|
357
|
+
* message defined in `messageKeys`.
|
|
358
|
+
*/
|
|
359
|
+
function createLoader(messageKeys, localeImportMap, defaultLocale) {
|
|
360
|
+
var loader = new MessageLoader(messageKeys, localeImportMap, defaultLocale);
|
|
361
|
+
LOADER_REGISTRY.push(loader);
|
|
362
|
+
return loader;
|
|
363
|
+
}
|
package/dist/message.d.ts
CHANGED
|
@@ -1,20 +1,11 @@
|
|
|
1
1
|
import { FormatJsNode } from './keyless-json';
|
|
2
2
|
export declare class InternalIntlMessage {
|
|
3
3
|
locale: string;
|
|
4
|
-
message?: string;
|
|
5
4
|
ast: FormatJsNode[];
|
|
5
|
+
constructor(messageOrAst: FormatJsNode[], locale: string);
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* requested (e.g., by calling `formatToPlainString`).
|
|
7
|
+
* Return a stringified serialization of this message's AST, with no
|
|
8
|
+
* formatting or values applied.
|
|
10
9
|
*/
|
|
11
|
-
|
|
12
|
-
_plainAst: FormatJsNode[];
|
|
13
|
-
constructor(messageOrAst: string | FormatJsNode[], locale: string);
|
|
14
|
-
/**
|
|
15
|
-
* Returns the same element with any formatting tags removed. If the element
|
|
16
|
-
* itself is a tag, the children will be hoisted and returned, otherwise the
|
|
17
|
-
* element is returned as an array of itself.
|
|
18
|
-
*/
|
|
19
|
-
private _removeRichTags;
|
|
10
|
+
reserialize(): string;
|
|
20
11
|
}
|
package/dist/message.js
CHANGED
|
@@ -26,68 +26,161 @@ var __read = (this && this.__read) || function (o, n) {
|
|
|
26
26
|
}
|
|
27
27
|
return ar;
|
|
28
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
29
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
30
|
exports.InternalIntlMessage = void 0;
|
|
40
31
|
var keyless_json_1 = require("./keyless-json");
|
|
41
32
|
var InternalIntlMessage = /** @class */ (function () {
|
|
42
33
|
function InternalIntlMessage(messageOrAst, locale) {
|
|
43
34
|
this.locale = locale;
|
|
44
|
-
|
|
45
|
-
this.message = messageOrAst;
|
|
46
|
-
}
|
|
47
|
-
else {
|
|
48
|
-
this.ast = messageOrAst;
|
|
49
|
-
}
|
|
35
|
+
this.ast = messageOrAst;
|
|
50
36
|
}
|
|
51
|
-
Object.defineProperty(InternalIntlMessage.prototype, "plainAst", {
|
|
52
|
-
/**
|
|
53
|
-
* A stripped-down representation of the message with no rich formatting
|
|
54
|
-
* elements preserved. This will be lazily created the first time it is
|
|
55
|
-
* requested (e.g., by calling `formatToPlainString`).
|
|
56
|
-
*/
|
|
57
|
-
get: function () {
|
|
58
|
-
return (this._plainAst = this._removeRichTags(this.ast));
|
|
59
|
-
},
|
|
60
|
-
enumerable: false,
|
|
61
|
-
configurable: true
|
|
62
|
-
});
|
|
63
37
|
/**
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
* element is returned as an array of itself.
|
|
38
|
+
* Return a stringified serialization of this message's AST, with no
|
|
39
|
+
* formatting or values applied.
|
|
67
40
|
*/
|
|
68
|
-
InternalIntlMessage.prototype.
|
|
69
|
-
var
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
41
|
+
InternalIntlMessage.prototype.reserialize = function () {
|
|
42
|
+
var value = '';
|
|
43
|
+
serializeFormatJsAst(this.ast, { value: value });
|
|
44
|
+
return value;
|
|
45
|
+
};
|
|
46
|
+
return InternalIntlMessage;
|
|
47
|
+
}());
|
|
48
|
+
exports.InternalIntlMessage = InternalIntlMessage;
|
|
49
|
+
// Accepting an object as the `result` parameter lets the same string get passed around and
|
|
50
|
+
// appended, rather than creating a bunch of intermediate strings.
|
|
51
|
+
function serializeFormatJsAst(ast, result) {
|
|
52
|
+
var e_1, _a, e_2, _b, e_3, _c;
|
|
53
|
+
try {
|
|
54
|
+
for (var ast_1 = __values(ast), ast_1_1 = ast_1.next(); !ast_1_1.done; ast_1_1 = ast_1.next()) {
|
|
55
|
+
var node = ast_1_1.value;
|
|
56
|
+
switch (node.type) {
|
|
57
|
+
case keyless_json_1.FormatJsNodeType.Literal:
|
|
58
|
+
result.value += node.value;
|
|
59
|
+
return;
|
|
60
|
+
case keyless_json_1.FormatJsNodeType.Argument:
|
|
61
|
+
// Empties are an artifact of our parsing strategy, not necessary here.
|
|
62
|
+
if (result.value === '$_')
|
|
63
|
+
return;
|
|
64
|
+
result.value += '{' + node.value + '}';
|
|
65
|
+
return;
|
|
66
|
+
case keyless_json_1.FormatJsNodeType.Date:
|
|
67
|
+
result.value += '{' + node.value + ', date';
|
|
68
|
+
if (node.style != null) {
|
|
69
|
+
result.value += ', ' + node.style;
|
|
70
|
+
}
|
|
71
|
+
result.value += '}';
|
|
72
|
+
return;
|
|
73
|
+
case keyless_json_1.FormatJsNodeType.Time:
|
|
74
|
+
result.value += '{' + node.value + ', time';
|
|
75
|
+
if (node.style != null) {
|
|
76
|
+
result.value += ', ' + node.style;
|
|
77
|
+
}
|
|
78
|
+
result.value += '}';
|
|
79
|
+
return;
|
|
80
|
+
case keyless_json_1.FormatJsNodeType.Number:
|
|
81
|
+
result.value += '{' + node.value + ', number';
|
|
82
|
+
if (node.style != null) {
|
|
83
|
+
result.value += ', ' + node.style;
|
|
84
|
+
}
|
|
85
|
+
result.value += '}';
|
|
86
|
+
return;
|
|
87
|
+
case keyless_json_1.FormatJsNodeType.Plural: {
|
|
88
|
+
var pluralType = node.pluralType == 'ordinal' ? 'selectordinal' : 'plural';
|
|
89
|
+
result.value += '{' + node.value + ', ' + pluralType + ', ';
|
|
90
|
+
if (node.offset) {
|
|
91
|
+
result.value += 'offset:' + node.offset;
|
|
92
|
+
}
|
|
93
|
+
try {
|
|
94
|
+
for (var _d = (e_2 = void 0, __values(Object.entries(node.options))), _e = _d.next(); !_e.done; _e = _d.next()) {
|
|
95
|
+
var _f = __read(_e.value, 2), name = _f[0], arm = _f[1];
|
|
96
|
+
result.value += ' ' + name + ' {';
|
|
97
|
+
serializeFormatJsAst(arm.value, result);
|
|
98
|
+
result.value += '}';
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
|
102
|
+
finally {
|
|
103
|
+
try {
|
|
104
|
+
if (_e && !_e.done && (_b = _d.return)) _b.call(_d);
|
|
105
|
+
}
|
|
106
|
+
finally { if (e_2) throw e_2.error; }
|
|
107
|
+
}
|
|
108
|
+
return;
|
|
76
109
|
}
|
|
77
|
-
|
|
78
|
-
result.
|
|
110
|
+
case keyless_json_1.FormatJsNodeType.Pound:
|
|
111
|
+
result.value += '#';
|
|
112
|
+
return;
|
|
113
|
+
case keyless_json_1.FormatJsNodeType.Select: {
|
|
114
|
+
result.value += '{' + node.value + ', select, ';
|
|
115
|
+
try {
|
|
116
|
+
for (var _g = (e_3 = void 0, __values(Object.entries(node.options))), _h = _g.next(); !_h.done; _h = _g.next()) {
|
|
117
|
+
var _j = __read(_h.value, 2), name = _j[0], arm = _j[1];
|
|
118
|
+
result.value += ' ' + name + ' {';
|
|
119
|
+
serializeFormatJsAst(arm.value, result);
|
|
120
|
+
result.value += '}';
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
catch (e_3_1) { e_3 = { error: e_3_1 }; }
|
|
124
|
+
finally {
|
|
125
|
+
try {
|
|
126
|
+
if (_h && !_h.done && (_c = _g.return)) _c.call(_g);
|
|
127
|
+
}
|
|
128
|
+
finally { if (e_3) throw e_3.error; }
|
|
129
|
+
}
|
|
130
|
+
return;
|
|
79
131
|
}
|
|
132
|
+
case keyless_json_1.FormatJsNodeType.Tag:
|
|
133
|
+
serializeFormatJsTag(node, result);
|
|
134
|
+
return;
|
|
80
135
|
}
|
|
81
136
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
finally { if (e_1) throw e_1.error; }
|
|
137
|
+
}
|
|
138
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
139
|
+
finally {
|
|
140
|
+
try {
|
|
141
|
+
if (ast_1_1 && !ast_1_1.done && (_a = ast_1.return)) _a.call(ast_1);
|
|
88
142
|
}
|
|
89
|
-
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
143
|
+
finally { if (e_1) throw e_1.error; }
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
function serializeFormatJsTag(node, result) {
|
|
147
|
+
switch (node.value) {
|
|
148
|
+
case '$b':
|
|
149
|
+
result.value += '**';
|
|
150
|
+
serializeFormatJsAst(node.children, result);
|
|
151
|
+
result.value += '**';
|
|
152
|
+
return;
|
|
153
|
+
case '$i':
|
|
154
|
+
result.value += '*';
|
|
155
|
+
serializeFormatJsAst(node.children, result);
|
|
156
|
+
result.value += '*';
|
|
157
|
+
return;
|
|
158
|
+
case '$code':
|
|
159
|
+
result.value += '`';
|
|
160
|
+
serializeFormatJsAst(node.children, result);
|
|
161
|
+
result.value += '`';
|
|
162
|
+
return;
|
|
163
|
+
case '$p':
|
|
164
|
+
serializeFormatJsAst(node.children, result);
|
|
165
|
+
result.value += '\n\n';
|
|
166
|
+
return;
|
|
167
|
+
case '$link':
|
|
168
|
+
// The target is the first child of the link. We don't have to care if it's a placeholder
|
|
169
|
+
// or not, because the serialization will automatically remove the extra empty.
|
|
170
|
+
var _a = __read(node.children), target = _a[0], children = _a.slice(1);
|
|
171
|
+
result.value += '[';
|
|
172
|
+
serializeFormatJsAst(children, result);
|
|
173
|
+
result.value += '](';
|
|
174
|
+
if (target != null) {
|
|
175
|
+
serializeFormatJsAst([target], result);
|
|
176
|
+
}
|
|
177
|
+
result.value += ')';
|
|
178
|
+
return;
|
|
179
|
+
default:
|
|
180
|
+
// Any other tag name is a hook, which just adds the `$[` on a link tag.
|
|
181
|
+
result.value += '$[';
|
|
182
|
+
serializeFormatJsAst(node.children, result);
|
|
183
|
+
result.value += '](' + node.value + ')';
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -197,9 +197,9 @@ export interface FunctionTypeMap {
|
|
|
197
197
|
handler: any;
|
|
198
198
|
}
|
|
199
199
|
|
|
200
|
-
export interface FunctionTypes<Result, HandlerType = (
|
|
201
|
-
link: undefined | ((content: Result[]) => Result | Result[]);
|
|
202
|
-
hook: undefined | ((content: Result[]) => Result | Result[]);
|
|
200
|
+
export interface FunctionTypes<Result, HandlerType = () => void> {
|
|
201
|
+
link: undefined | ((content: Result[], key: string) => Result | Result[]);
|
|
202
|
+
hook: undefined | ((content: Result[], key: string) => Result | Result[]);
|
|
203
203
|
handler: undefined | HandlerType;
|
|
204
204
|
}
|
|
205
205
|
|
|
@@ -276,6 +276,7 @@ export interface RichTextFormattingMap<T = any> {
|
|
|
276
276
|
$p: T;
|
|
277
277
|
$link: T;
|
|
278
278
|
$code: T;
|
|
279
|
+
$del: T;
|
|
279
280
|
}
|
|
280
281
|
|
|
281
282
|
export type RichTextTagNames = keyof RichTextFormattingMap;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@discord/intl",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1-canary.11837d1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Client runtime for managing messages and translations in a React project.",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
],
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"@formatjs/intl": "^2.10.1",
|
|
19
|
+
"@formatjs/icu-skeleton-parser": "1.8.2",
|
|
19
20
|
"intl-messageformat": "^10.5.11"
|
|
20
21
|
},
|
|
21
22
|
"devDependencies": {
|
package/src/format.ts
CHANGED
|
@@ -20,6 +20,11 @@
|
|
|
20
20
|
import { FormatJsNode, FormatJsNodeType } from './keyless-json';
|
|
21
21
|
import { Formats, Formatters, MissingValueError } from 'intl-messageformat';
|
|
22
22
|
import { RichTextTagNames } from './types';
|
|
23
|
+
import {
|
|
24
|
+
parseDateTimeSkeleton,
|
|
25
|
+
parseNumberSkeleton,
|
|
26
|
+
parseNumberSkeletonFromString,
|
|
27
|
+
} from '@formatjs/icu-skeleton-parser';
|
|
23
28
|
|
|
24
29
|
/**
|
|
25
30
|
* Returns true if the tag name should be considered a rich text tag that
|
|
@@ -96,10 +101,12 @@ export function bindFormatValuesWithBuilder<T, Builder extends FormatBuilder<T>>
|
|
|
96
101
|
case FormatJsNodeType.Date: {
|
|
97
102
|
// Distinct from FormatJS: We don't currently parse the skeleton ahead of time in the AST,
|
|
98
103
|
// so this manages parsing the skeleton as well before passing it onto the date formatter.
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
104
|
+
const style =
|
|
105
|
+
node.style in formats.date
|
|
106
|
+
? formats.date[node.style]
|
|
107
|
+
: node.style != null
|
|
108
|
+
? parseDateTimeSkeleton(node.style)
|
|
109
|
+
: undefined;
|
|
103
110
|
// @ts-expect-error Cast string values to dates properly.
|
|
104
111
|
builder.pushLiteralText(formatters.getDateTimeFormat(locales, style).format(value));
|
|
105
112
|
break;
|
|
@@ -107,10 +114,12 @@ export function bindFormatValuesWithBuilder<T, Builder extends FormatBuilder<T>>
|
|
|
107
114
|
case FormatJsNodeType.Time: {
|
|
108
115
|
// Distinct from FormatJS: We don't currently parse the skeleton ahead of time in the AST,
|
|
109
116
|
// so this manages parsing the skeleton as well before passing it onto the date formatter.
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
117
|
+
const style =
|
|
118
|
+
node.style in formats.time
|
|
119
|
+
? formats.time[node.style]
|
|
120
|
+
: node.style != null
|
|
121
|
+
? parseDateTimeSkeleton(node.style)
|
|
122
|
+
: undefined; // TODO: parseSkeleton();
|
|
114
123
|
builder.pushLiteralText(
|
|
115
124
|
// @ts-expect-error Cast string values to dates properly.
|
|
116
125
|
formatters.getDateTimeFormat(locales, style).format(value),
|
|
@@ -120,14 +129,15 @@ export function bindFormatValuesWithBuilder<T, Builder extends FormatBuilder<T>>
|
|
|
120
129
|
case FormatJsNodeType.Number: {
|
|
121
130
|
// Distinct from FormatJS: We don't currently parse the skeleton ahead of time in the AST,
|
|
122
131
|
// so this manages parsing the skeleton as well before passing it onto the date formatter.
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
const scaledValue =
|
|
130
|
-
|
|
132
|
+
const style =
|
|
133
|
+
node.style in formats.number
|
|
134
|
+
? formats.number[node.style]
|
|
135
|
+
: node.style != null
|
|
136
|
+
? parseNumberSkeleton(parseNumberSkeletonFromString(node.style))
|
|
137
|
+
: undefined;
|
|
138
|
+
const scaledValue =
|
|
139
|
+
// @ts-expect-error This is a weird cast that's not accurate, but works in the short term.
|
|
140
|
+
typeof value !== 'number' ? (value as number) : ((value as number) * style?.scale ?? 1);
|
|
131
141
|
builder.pushLiteralText(formatters.getNumberFormat(locales, style).format(scaledValue));
|
|
132
142
|
break;
|
|
133
143
|
}
|
package/src/formatters/ast.ts
CHANGED
|
@@ -12,7 +12,7 @@ import { FormatBuilder } from '../format';
|
|
|
12
12
|
* Types for formatting functions when calling `formatToParts`, ensuring the
|
|
13
13
|
* functions yield value AST nodes.
|
|
14
14
|
*/
|
|
15
|
-
export type AstFunctionTypes = FunctionTypes<RichTextNode>;
|
|
15
|
+
export type AstFunctionTypes = FunctionTypes<RichTextNode, object>;
|
|
16
16
|
|
|
17
17
|
// This structure aims to match `simple-markdown`'s AST, but that type is very
|
|
18
18
|
// loosely defined as just "type" and "arbitrary map of content". This is more
|
|
@@ -21,6 +21,7 @@ export enum RichTextNodeType {
|
|
|
21
21
|
Text = 'text',
|
|
22
22
|
Strong = 'strong',
|
|
23
23
|
Emphasis = 'emphasis',
|
|
24
|
+
Strikethrough = 'strikethrough',
|
|
24
25
|
Code = 'code',
|
|
25
26
|
Link = 'link',
|
|
26
27
|
Paragraph = 'paragraph',
|
|
@@ -36,15 +37,6 @@ export enum RichTextNodeType {
|
|
|
36
37
|
Object = 'object',
|
|
37
38
|
}
|
|
38
39
|
|
|
39
|
-
const RICH_TEXT_TAG_TYPES: Record<RichTextTagNames, RichTextNodeType> = {
|
|
40
|
-
$_: RichTextNodeType.Text,
|
|
41
|
-
$b: RichTextNodeType.Strong,
|
|
42
|
-
$i: RichTextNodeType.Emphasis,
|
|
43
|
-
$code: RichTextNodeType.Code,
|
|
44
|
-
$link: RichTextNodeType.Link,
|
|
45
|
-
$p: RichTextNodeType.Paragraph,
|
|
46
|
-
};
|
|
47
|
-
|
|
48
40
|
interface RichTextNodeBase<ContentType> {
|
|
49
41
|
type: RichTextNodeType;
|
|
50
42
|
content: ContentType;
|
|
@@ -58,6 +50,9 @@ interface RichTextStrongNode extends RichTextNodeBase<RichTextNode[]> {
|
|
|
58
50
|
interface RichTextEmphasisNode extends RichTextNodeBase<RichTextNode[]> {
|
|
59
51
|
type: RichTextNodeType.Emphasis;
|
|
60
52
|
}
|
|
53
|
+
interface RichTextStrikethroughNode extends RichTextNodeBase<RichTextNode[]> {
|
|
54
|
+
type: RichTextNodeType.Strikethrough;
|
|
55
|
+
}
|
|
61
56
|
interface RichTextCodeNode extends RichTextNodeBase<RichTextNode[]> {
|
|
62
57
|
type: RichTextNodeType.Code;
|
|
63
58
|
}
|
|
@@ -76,6 +71,7 @@ export type RichTextNode =
|
|
|
76
71
|
| RichTextTextNode
|
|
77
72
|
| RichTextStrongNode
|
|
78
73
|
| RichTextEmphasisNode
|
|
74
|
+
| RichTextStrikethroughNode
|
|
79
75
|
| RichTextCodeNode
|
|
80
76
|
| RichTextParagraphNode
|
|
81
77
|
| RichTextLinkNode
|
|
@@ -84,8 +80,9 @@ export type RichTextNode =
|
|
|
84
80
|
const AST_RICH_TEXT_ELEMENTS: RichTextFormattingMap<AstFunctionTypes['hook']> = {
|
|
85
81
|
$_: () => ({ type: RichTextNodeType.Text, content: '' }),
|
|
86
82
|
$b: (content) => ({ type: RichTextNodeType.Strong, content }),
|
|
87
|
-
$code: (content) => ({ type: RichTextNodeType.Code, content }),
|
|
88
83
|
$i: (content) => ({ type: RichTextNodeType.Emphasis, content }),
|
|
84
|
+
$del: (content) => ({ type: RichTextNodeType.Strikethrough, content }),
|
|
85
|
+
$code: (content) => ({ type: RichTextNodeType.Code, content }),
|
|
89
86
|
$link: ([target, ...content]) => ({
|
|
90
87
|
type: RichTextNodeType.Link,
|
|
91
88
|
target: target.content as string,
|
|
@@ -101,7 +98,7 @@ class AstBuilder extends FormatBuilder<RichTextNode> {
|
|
|
101
98
|
if (!(tag in AST_RICH_TEXT_ELEMENTS)) {
|
|
102
99
|
throw `${tag} is not a known rich text formatting tag`;
|
|
103
100
|
}
|
|
104
|
-
const result = AST_RICH_TEXT_ELEMENTS[tag](children);
|
|
101
|
+
const result = AST_RICH_TEXT_ELEMENTS[tag](children, '');
|
|
105
102
|
if (Array.isArray(result)) {
|
|
106
103
|
this.result.push(...result);
|
|
107
104
|
} else {
|
|
@@ -21,8 +21,9 @@ export type MarkdownFunctionTypes = FunctionTypes<string>;
|
|
|
21
21
|
const MARKDOWN_RICH_TEXT_ELEMENTS: RichTextFormattingMap<MarkdownFunctionTypes['hook']> = {
|
|
22
22
|
$_: () => '',
|
|
23
23
|
$b: (content) => '**' + content.join('') + '**',
|
|
24
|
-
$code: (content) => '`' + content.join('') + '`',
|
|
25
24
|
$i: (content) => '*' + content.join('') + '*',
|
|
25
|
+
$del: (content) => '~~' + content.join('') + '~~',
|
|
26
|
+
$code: (content) => '`' + content.join('') + '`',
|
|
26
27
|
$link: ([target, ...content]) => '[' + content.join('') + '](' + target + ')',
|
|
27
28
|
$p: (content) => content.join('') + '\n\n',
|
|
28
29
|
};
|
|
@@ -31,7 +32,7 @@ class MarkdownBuilder extends StringBuilder {
|
|
|
31
32
|
result: string = '';
|
|
32
33
|
|
|
33
34
|
pushRichTextTag(tag: RichTextTagNames, children: string[]) {
|
|
34
|
-
this.result += MARKDOWN_RICH_TEXT_ELEMENTS[tag](children);
|
|
35
|
+
this.result += MARKDOWN_RICH_TEXT_ELEMENTS[tag](children, '');
|
|
35
36
|
}
|
|
36
37
|
}
|
|
37
38
|
|