@discord/intl 0.9.1 → 0.10.0-canary.d90454b
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 +2 -2
- package/dist/format.js +26 -19
- package/dist/formatters/ast.js +3 -4
- package/dist/formatters/markdown.js +3 -4
- package/dist/formatters/react.js +5 -7
- package/dist/formatters/string.d.ts +1 -1
- package/dist/formatters/string.js +6 -8
- package/dist/intl-manager.js +1 -12
- package/dist/message-loader.d.ts +3 -1
- package/dist/message-loader.js +8 -7
- package/dist/message.d.ts +1 -1
- package/dist/message.js +28 -29
- package/dist/types.d.ts +3 -3
- package/package.json +4 -4
- package/src/format.ts +36 -17
- package/src/formatters/ast.ts +3 -4
- package/src/formatters/markdown.ts +3 -4
- package/src/formatters/react.ts +9 -7
- package/src/formatters/string.ts +6 -8
- package/src/intl-manager.ts +1 -15
- package/src/message-loader.ts +6 -7
- package/src/message.ts +3 -3
- package/src/types.d.ts +3 -3
package/dist/format.d.ts
CHANGED
|
@@ -21,11 +21,11 @@ import { Formatters } from 'intl-messageformat';
|
|
|
21
21
|
import type { FormatConfig } from './format-config';
|
|
22
22
|
import type { RichTextTagNames } from './types';
|
|
23
23
|
export declare abstract class FormatBuilder<Result> {
|
|
24
|
-
abstract pushRichTextTag(tag: RichTextTagNames, children: Result[]): void;
|
|
24
|
+
abstract pushRichTextTag(tag: RichTextTagNames, children: Result[], control: Result[]): void;
|
|
25
25
|
abstract pushLiteralText(text: string): void;
|
|
26
26
|
abstract pushObject(value: object): void;
|
|
27
27
|
abstract finish(): Result[];
|
|
28
28
|
}
|
|
29
29
|
export type FormatBuilderConstructor<Result> = new () => FormatBuilder<Result>;
|
|
30
30
|
export declare function bindFormatValuesWithBuilder<T, Builder extends FormatBuilder<T>>(builder: Builder, nodes: AstNode[], locales: string | string[], formatters: Formatters, formatConfig: FormatConfig, values?: Record<string, string | object>, currentPluralValue?: number, originalMessage?: string): void;
|
|
31
|
-
export declare function bindFormatValues<Result>(Builder: FormatBuilderConstructor<Result>, nodes: AstNode[], locales: string | string[], formatters: Formatters, formatConfig: FormatConfig, values?: Record<string, string | object>, currentPluralValue?: number): Result[];
|
|
31
|
+
export declare function bindFormatValues<Result>(Builder: FormatBuilderConstructor<Result>, nodes: string | AstNode[], locales: string | string[], formatters: Formatters, formatConfig: FormatConfig, values?: Record<string, string | object>, currentPluralValue?: number): Result[];
|
package/dist/format.js
CHANGED
|
@@ -46,7 +46,7 @@ function bindFormatValuesWithBuilder(builder, nodes, locales, formatters, format
|
|
|
46
46
|
builder.pushLiteralText(node);
|
|
47
47
|
continue;
|
|
48
48
|
}
|
|
49
|
-
const nodeType = node[
|
|
49
|
+
const nodeType = node[0 /* AstNodeIndices.Type */];
|
|
50
50
|
switch (nodeType) {
|
|
51
51
|
case intl_ast_1.FormatJsNodeType.Pound:
|
|
52
52
|
// Replace `#` in plural rules with the actual numeric value. Only
|
|
@@ -58,7 +58,7 @@ function bindFormatValuesWithBuilder(builder, nodes, locales, formatters, format
|
|
|
58
58
|
}
|
|
59
59
|
continue;
|
|
60
60
|
}
|
|
61
|
-
const variableName = node[
|
|
61
|
+
const variableName = node[1 /* AstNodeIndices.Value */];
|
|
62
62
|
// Enforce that all required values are provided by the caller, even if the
|
|
63
63
|
// actual value is falsy/undefined.
|
|
64
64
|
if (!(variableName in values) && !isRichTextTag(variableName)) {
|
|
@@ -67,10 +67,7 @@ function bindFormatValuesWithBuilder(builder, nodes, locales, formatters, format
|
|
|
67
67
|
const value = values[variableName];
|
|
68
68
|
switch (nodeType) {
|
|
69
69
|
case intl_ast_1.FormatJsNodeType.Argument:
|
|
70
|
-
|
|
71
|
-
if (variableName == '$_')
|
|
72
|
-
break;
|
|
73
|
-
if (typeof value === 'object') {
|
|
70
|
+
if (typeof value === 'object' || typeof value === 'function') {
|
|
74
71
|
builder.pushObject(value);
|
|
75
72
|
}
|
|
76
73
|
else {
|
|
@@ -80,7 +77,7 @@ function bindFormatValuesWithBuilder(builder, nodes, locales, formatters, format
|
|
|
80
77
|
}
|
|
81
78
|
break;
|
|
82
79
|
case intl_ast_1.FormatJsNodeType.Date: {
|
|
83
|
-
const nodeStyle = node[
|
|
80
|
+
const nodeStyle = node[2 /* AstNodeIndices.Style */];
|
|
84
81
|
// Distinct from FormatJS: We don't currently parse the skeleton ahead of time in the AST,
|
|
85
82
|
// so this manages parsing the skeleton as well before passing it onto the date formatter.
|
|
86
83
|
const style = nodeStyle in formatConfig.date
|
|
@@ -93,7 +90,7 @@ function bindFormatValuesWithBuilder(builder, nodes, locales, formatters, format
|
|
|
93
90
|
break;
|
|
94
91
|
}
|
|
95
92
|
case intl_ast_1.FormatJsNodeType.Time: {
|
|
96
|
-
const nodeStyle = node[
|
|
93
|
+
const nodeStyle = node[2 /* AstNodeIndices.Style */];
|
|
97
94
|
// Distinct from FormatJS: We don't currently parse the skeleton ahead of time in the AST,
|
|
98
95
|
// so this manages parsing the skeleton as well before passing it onto the date formatter.
|
|
99
96
|
const style = nodeStyle in formatConfig.time
|
|
@@ -107,7 +104,7 @@ function bindFormatValuesWithBuilder(builder, nodes, locales, formatters, format
|
|
|
107
104
|
break;
|
|
108
105
|
}
|
|
109
106
|
case intl_ast_1.FormatJsNodeType.Number: {
|
|
110
|
-
const nodeStyle = node[
|
|
107
|
+
const nodeStyle = node[2 /* AstNodeIndices.Style */];
|
|
111
108
|
// Distinct from FormatJS: We don't currently parse the skeleton ahead of time in the AST,
|
|
112
109
|
// so this manages parsing the skeleton as well before passing it onto the date formatter.
|
|
113
110
|
const style = nodeStyle in formatConfig.number
|
|
@@ -122,10 +119,14 @@ function bindFormatValuesWithBuilder(builder, nodes, locales, formatters, format
|
|
|
122
119
|
break;
|
|
123
120
|
}
|
|
124
121
|
case intl_ast_1.FormatJsNodeType.Tag: {
|
|
125
|
-
const children = node[
|
|
122
|
+
const children = node[2 /* AstNodeIndices.Children */];
|
|
123
|
+
const control = node[3 /* AstNodeIndices.Control */];
|
|
126
124
|
const appliedChildren = bindFormatValues(builder.constructor, children, locales, formatters, formatConfig, values, currentPluralValue);
|
|
125
|
+
const appliedControl = control != null
|
|
126
|
+
? bindFormatValues(builder.constructor, control, locales, formatters, formatConfig, values, currentPluralValue)
|
|
127
|
+
: [];
|
|
127
128
|
if (isRichTextTag(variableName)) {
|
|
128
|
-
builder.pushRichTextTag(variableName, appliedChildren);
|
|
129
|
+
builder.pushRichTextTag(variableName, appliedChildren, appliedControl);
|
|
129
130
|
}
|
|
130
131
|
else {
|
|
131
132
|
if (typeof value !== 'function') {
|
|
@@ -145,8 +146,8 @@ function bindFormatValuesWithBuilder(builder, nodes, locales, formatters, format
|
|
|
145
146
|
break;
|
|
146
147
|
}
|
|
147
148
|
case intl_ast_1.FormatJsNodeType.Select: {
|
|
148
|
-
const value = node[
|
|
149
|
-
const options = node[
|
|
149
|
+
const value = node[1 /* AstNodeIndices.Value */];
|
|
150
|
+
const options = node[2 /* AstNodeIndices.Options */];
|
|
150
151
|
const option = value in options ? options[value] : options.other;
|
|
151
152
|
if (option == null) {
|
|
152
153
|
throw `${value} is not a known option for select value ${variableName}. Valid options are ${Object.keys(options).join(', ')}`;
|
|
@@ -155,10 +156,10 @@ function bindFormatValuesWithBuilder(builder, nodes, locales, formatters, format
|
|
|
155
156
|
break;
|
|
156
157
|
}
|
|
157
158
|
case intl_ast_1.FormatJsNodeType.Plural: {
|
|
158
|
-
const value = node[
|
|
159
|
-
const options = node[
|
|
160
|
-
const offset = node[
|
|
161
|
-
const pluralType = node[
|
|
159
|
+
const value = node[1 /* AstNodeIndices.Value */];
|
|
160
|
+
const options = node[2 /* AstNodeIndices.Options */];
|
|
161
|
+
const offset = node[3 /* AstNodeIndices.Offset */];
|
|
162
|
+
const pluralType = node[4 /* AstNodeIndices.PluralType */];
|
|
162
163
|
const option = (() => {
|
|
163
164
|
var _a;
|
|
164
165
|
const exactSelector = `=${value}`;
|
|
@@ -183,6 +184,12 @@ function bindFormatValuesWithBuilder(builder, nodes, locales, formatters, format
|
|
|
183
184
|
}
|
|
184
185
|
function bindFormatValues(Builder, nodes, locales, formatters, formatConfig, values = {}, currentPluralValue) {
|
|
185
186
|
const builder = new Builder();
|
|
186
|
-
|
|
187
|
-
|
|
187
|
+
if (typeof nodes === 'string') {
|
|
188
|
+
builder.pushLiteralText(nodes);
|
|
189
|
+
return builder.finish();
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
bindFormatValuesWithBuilder(builder, nodes, locales, formatters, formatConfig, values, currentPluralValue);
|
|
193
|
+
return builder.finish();
|
|
194
|
+
}
|
|
188
195
|
}
|
package/dist/formatters/ast.js
CHANGED
|
@@ -27,12 +27,11 @@ var RichTextNodeType;
|
|
|
27
27
|
RichTextNodeType["Object"] = "object";
|
|
28
28
|
})(RichTextNodeType || (exports.RichTextNodeType = RichTextNodeType = {}));
|
|
29
29
|
const AST_RICH_TEXT_ELEMENTS = {
|
|
30
|
-
$_: () => ({ type: RichTextNodeType.Text, content: '' }),
|
|
31
30
|
$b: (content) => ({ type: RichTextNodeType.Strong, content }),
|
|
32
31
|
$i: (content) => ({ type: RichTextNodeType.Emphasis, content }),
|
|
33
32
|
$del: (content) => ({ type: RichTextNodeType.Strikethrough, content }),
|
|
34
33
|
$code: (content) => ({ type: RichTextNodeType.Code, content }),
|
|
35
|
-
$link: ([target
|
|
34
|
+
$link: (content, _, [target]) => ({
|
|
36
35
|
type: RichTextNodeType.Link,
|
|
37
36
|
target: target.content,
|
|
38
37
|
content,
|
|
@@ -44,11 +43,11 @@ class AstBuilder extends format_1.FormatBuilder {
|
|
|
44
43
|
super(...arguments);
|
|
45
44
|
this.result = [];
|
|
46
45
|
}
|
|
47
|
-
pushRichTextTag(tag, children) {
|
|
46
|
+
pushRichTextTag(tag, children, control) {
|
|
48
47
|
if (!(tag in AST_RICH_TEXT_ELEMENTS)) {
|
|
49
48
|
throw `${tag} is not a known rich text formatting tag`;
|
|
50
49
|
}
|
|
51
|
-
const result = AST_RICH_TEXT_ELEMENTS[tag](children, '');
|
|
50
|
+
const result = AST_RICH_TEXT_ELEMENTS[tag](children, '', control);
|
|
52
51
|
if (Array.isArray(result)) {
|
|
53
52
|
this.result.push(...result);
|
|
54
53
|
}
|
|
@@ -4,12 +4,11 @@ exports.markdownFormatter = void 0;
|
|
|
4
4
|
exports.formatToMarkdownString = formatToMarkdownString;
|
|
5
5
|
const string_1 = require("./string");
|
|
6
6
|
const MARKDOWN_RICH_TEXT_ELEMENTS = {
|
|
7
|
-
$_: () => '',
|
|
8
7
|
$b: (content) => '**' + content.join('') + '**',
|
|
9
8
|
$i: (content) => '*' + content.join('') + '*',
|
|
10
9
|
$del: (content) => '~~' + content.join('') + '~~',
|
|
11
10
|
$code: (content) => '`' + content.join('') + '`',
|
|
12
|
-
$link: ([target
|
|
11
|
+
$link: (content, _, [target]) => '[' + content.join('') + '](' + target + ')',
|
|
13
12
|
$p: (content) => content.join('') + '\n\n',
|
|
14
13
|
};
|
|
15
14
|
class MarkdownBuilder extends string_1.StringBuilder {
|
|
@@ -17,8 +16,8 @@ class MarkdownBuilder extends string_1.StringBuilder {
|
|
|
17
16
|
super(...arguments);
|
|
18
17
|
this.result = '';
|
|
19
18
|
}
|
|
20
|
-
pushRichTextTag(tag, children) {
|
|
21
|
-
this.result += MARKDOWN_RICH_TEXT_ELEMENTS[tag](children, '');
|
|
19
|
+
pushRichTextTag(tag, children, control) {
|
|
20
|
+
this.result += MARKDOWN_RICH_TEXT_ELEMENTS[tag](children, '', control);
|
|
22
21
|
}
|
|
23
22
|
}
|
|
24
23
|
function formatToMarkdownString(message, values, Builder = MarkdownBuilder) {
|
package/dist/formatters/react.js
CHANGED
|
@@ -7,13 +7,11 @@ const React = require("react");
|
|
|
7
7
|
const format_1 = require("../format");
|
|
8
8
|
const h = React.createElement;
|
|
9
9
|
exports.DEFAULT_REACT_RICH_TEXT_ELEMENTS = {
|
|
10
|
-
$_: () => '',
|
|
11
10
|
$b: (content, key) => h('strong', { key }, content),
|
|
12
11
|
$i: (content, key) => h('em', { key }, content),
|
|
13
12
|
$del: (content, key) => h('del', { key }, content),
|
|
14
13
|
$code: (content, key) => h('code', { key }, content),
|
|
15
|
-
|
|
16
|
-
$link: ([href, ...content], key) => h('a', { href, key }, content),
|
|
14
|
+
$link: (content, key, [href]) => h('a', { href, key }, content),
|
|
17
15
|
$p: (content, key) => h('p', { key }, content),
|
|
18
16
|
};
|
|
19
17
|
/**
|
|
@@ -30,8 +28,8 @@ function createReactBuilder(richTextElements) {
|
|
|
30
28
|
this._nodeKey = 0;
|
|
31
29
|
this.result = [];
|
|
32
30
|
}
|
|
33
|
-
pushRichTextTag(tag, children) {
|
|
34
|
-
this.result.push(richTextElements[tag](children, `${this._nodeKey++}
|
|
31
|
+
pushRichTextTag(tag, children, control) {
|
|
32
|
+
this.result.push(richTextElements[tag](children, `${this._nodeKey++}`, control));
|
|
35
33
|
}
|
|
36
34
|
pushLiteralText(text) {
|
|
37
35
|
const last = this.result[this.result.length - 1];
|
|
@@ -43,8 +41,8 @@ function createReactBuilder(richTextElements) {
|
|
|
43
41
|
}
|
|
44
42
|
}
|
|
45
43
|
pushObject(value) {
|
|
46
|
-
// @ts-expect-error this is technically invalid, but we'll just assume that if a format
|
|
47
|
-
// an object, it'll be acting as some form of ReactNode.
|
|
44
|
+
// @ts-expect-error this is technically invalid, but we'll just assume that if a format
|
|
45
|
+
// returns an object, it'll be acting as some form of ReactNode.
|
|
48
46
|
this.result.push(value);
|
|
49
47
|
}
|
|
50
48
|
finish() {
|
|
@@ -12,7 +12,7 @@ import { IntlManager } from '../intl-manager';
|
|
|
12
12
|
export type StringFunctionTypes = FunctionTypes<string>;
|
|
13
13
|
export declare class StringBuilder extends FormatBuilder<string> {
|
|
14
14
|
result: string;
|
|
15
|
-
pushRichTextTag(
|
|
15
|
+
pushRichTextTag(_tag: RichTextTagNames, children: string[], _control: string[]): void;
|
|
16
16
|
pushLiteralText(text: string): void;
|
|
17
17
|
pushObject(value: object): void;
|
|
18
18
|
finish(): string[];
|
|
@@ -8,16 +8,14 @@ class StringBuilder extends format_1.FormatBuilder {
|
|
|
8
8
|
super(...arguments);
|
|
9
9
|
this.result = '';
|
|
10
10
|
}
|
|
11
|
-
pushRichTextTag(
|
|
11
|
+
pushRichTextTag(_tag, children, _control) {
|
|
12
12
|
// Plain string formatting ignores rich text tags and just takes the
|
|
13
|
-
// visible content from the children. This means the
|
|
14
|
-
//
|
|
15
|
-
// together
|
|
16
|
-
|
|
17
|
-
this.result +=
|
|
18
|
-
return;
|
|
13
|
+
// visible content from the children. This means the control element is not
|
|
14
|
+
// important for string rendering, so the result is always just the
|
|
15
|
+
// children joined together.
|
|
16
|
+
for (const child of children) {
|
|
17
|
+
this.result += child;
|
|
19
18
|
}
|
|
20
|
-
this.result += children.join('');
|
|
21
19
|
}
|
|
22
20
|
pushLiteralText(text) {
|
|
23
21
|
this.result += text;
|
package/dist/intl-manager.js
CHANGED
|
@@ -68,18 +68,7 @@ class IntlManager {
|
|
|
68
68
|
* immediately return the plain string value of the message in the current locale.
|
|
69
69
|
*/
|
|
70
70
|
string(message) {
|
|
71
|
-
|
|
72
|
-
if (resolved == null || resolved.ast.length === 0)
|
|
73
|
-
return '';
|
|
74
|
-
let result = '';
|
|
75
|
-
for (const element of resolved.ast) {
|
|
76
|
-
if (typeof element !== 'string') {
|
|
77
|
-
throw new Error('Attempted to call `string` formatting on a non-literal message: ' +
|
|
78
|
-
JSON.stringify(resolved));
|
|
79
|
-
}
|
|
80
|
-
result += element;
|
|
81
|
-
}
|
|
82
|
-
return result;
|
|
71
|
+
return message(this.currentLocale).reserialize();
|
|
83
72
|
}
|
|
84
73
|
/**
|
|
85
74
|
* Return a raw string representing the syntax of the original message, as
|
package/dist/message-loader.d.ts
CHANGED
|
@@ -48,7 +48,9 @@ export declare class MessageLoader {
|
|
|
48
48
|
/**
|
|
49
49
|
* Map of pre-parsed messages, keyed by the message key and locale
|
|
50
50
|
*/
|
|
51
|
-
_parseCache:
|
|
51
|
+
_parseCache: Record<LocaleId, {
|
|
52
|
+
[name: string]: InternalIntlMessage;
|
|
53
|
+
}>;
|
|
52
54
|
/**
|
|
53
55
|
* List of subscribers listening for changes to the current locale and
|
|
54
56
|
* when it has loaded.
|
package/dist/message-loader.js
CHANGED
|
@@ -22,7 +22,7 @@ class MessageLoader {
|
|
|
22
22
|
this.supportedLocales = Object.keys(localeImportMap);
|
|
23
23
|
this.defaultLocale = defaultLocale;
|
|
24
24
|
this._localeLoadingPromises = {};
|
|
25
|
-
this._parseCache =
|
|
25
|
+
this._parseCache = {};
|
|
26
26
|
this._subscribers = new Set();
|
|
27
27
|
this._loadLocale(this.defaultLocale);
|
|
28
28
|
this.fallbackMessage = new message_1.InternalIntlMessage([], this.defaultLocale);
|
|
@@ -34,7 +34,7 @@ class MessageLoader {
|
|
|
34
34
|
// @ts-expect-error `hot` not defined in types.
|
|
35
35
|
module.hot.accept(file, () => __awaiter(this, void 0, void 0, function* () {
|
|
36
36
|
yield this._loadLocale(locale);
|
|
37
|
-
this._parseCache
|
|
37
|
+
this._parseCache = {};
|
|
38
38
|
}));
|
|
39
39
|
}
|
|
40
40
|
}
|
|
@@ -75,10 +75,11 @@ class MessageLoader {
|
|
|
75
75
|
* returning.
|
|
76
76
|
*/
|
|
77
77
|
getMessageValue(key, locale) {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
78
|
+
var _a, _b;
|
|
79
|
+
var _c;
|
|
80
|
+
const parsed = (_a = this._parseCache[locale]) === null || _a === void 0 ? void 0 : _a[key];
|
|
81
|
+
if (parsed)
|
|
82
|
+
return parsed;
|
|
82
83
|
// Check whether the locale exists in memory. If not, start a request to
|
|
83
84
|
// load the locale if one has not already started. It's impossible for
|
|
84
85
|
// the request to finish within the same tick, so return undefined in any
|
|
@@ -93,7 +94,7 @@ class MessageLoader {
|
|
|
93
94
|
if (key in this.messages[locale]) {
|
|
94
95
|
const content = this.messages[locale][key];
|
|
95
96
|
const message = new message_1.InternalIntlMessage(content, locale);
|
|
96
|
-
this._parseCache
|
|
97
|
+
((_b = (_c = this._parseCache)[locale]) !== null && _b !== void 0 ? _b : (_c[locale] = {}))[key] = message;
|
|
97
98
|
return message;
|
|
98
99
|
}
|
|
99
100
|
// Otherwise just assume it doesn't exist.
|
package/dist/message.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AstNode, FullFormatJsNode } from '@discord/intl-ast';
|
|
2
2
|
export declare class InternalIntlMessage {
|
|
3
3
|
locale: string;
|
|
4
|
-
ast: AstNode[];
|
|
4
|
+
ast: string | AstNode[];
|
|
5
5
|
constructor(messageOrAst: AstNode[] | FullFormatJsNode[], locale: string);
|
|
6
6
|
/**
|
|
7
7
|
* Return a stringified serialization of this message's AST, with no
|
package/dist/message.js
CHANGED
|
@@ -12,6 +12,8 @@ class InternalIntlMessage {
|
|
|
12
12
|
* formatting or values applied.
|
|
13
13
|
*/
|
|
14
14
|
reserialize() {
|
|
15
|
+
if (typeof this.ast === 'string')
|
|
16
|
+
return this.ast;
|
|
15
17
|
const result = { value: '' };
|
|
16
18
|
serializeAst(this.ast, result);
|
|
17
19
|
return result.value;
|
|
@@ -26,41 +28,38 @@ function serializeAst(ast, result) {
|
|
|
26
28
|
result.value += node[0];
|
|
27
29
|
return;
|
|
28
30
|
}
|
|
29
|
-
switch (node[
|
|
31
|
+
switch (node[0 /* AstNodeIndices.Type */]) {
|
|
30
32
|
case intl_ast_1.FormatJsNodeType.Argument:
|
|
31
|
-
|
|
32
|
-
if (result.value === '$_')
|
|
33
|
-
return;
|
|
34
|
-
result.value += '{' + node[intl_ast_1.AstNodeIndices.Value] + '}';
|
|
33
|
+
result.value += '{' + node[1 /* AstNodeIndices.Value */] + '}';
|
|
35
34
|
return;
|
|
36
35
|
case intl_ast_1.FormatJsNodeType.Date:
|
|
37
|
-
result.value += '{' + node[
|
|
38
|
-
if (node[
|
|
39
|
-
result.value += ', ' + node[
|
|
36
|
+
result.value += '{' + node[1 /* AstNodeIndices.Value */] + ', date';
|
|
37
|
+
if (node[2 /* AstNodeIndices.Style */] != null) {
|
|
38
|
+
result.value += ', ' + node[2 /* AstNodeIndices.Style */];
|
|
40
39
|
}
|
|
41
40
|
result.value += '}';
|
|
42
41
|
return;
|
|
43
42
|
case intl_ast_1.FormatJsNodeType.Time:
|
|
44
|
-
result.value += '{' + node[
|
|
45
|
-
if (node[
|
|
46
|
-
result.value += ', ' + node[
|
|
43
|
+
result.value += '{' + node[1 /* AstNodeIndices.Value */] + ', time';
|
|
44
|
+
if (node[2 /* AstNodeIndices.Style */] != null) {
|
|
45
|
+
result.value += ', ' + node[2 /* AstNodeIndices.Style */];
|
|
47
46
|
}
|
|
48
47
|
result.value += '}';
|
|
49
48
|
return;
|
|
50
49
|
case intl_ast_1.FormatJsNodeType.Number:
|
|
51
|
-
result.value += '{' + node[
|
|
52
|
-
if (node[
|
|
53
|
-
result.value += ', ' + node[
|
|
50
|
+
result.value += '{' + node[1 /* AstNodeIndices.Value */] + ', number';
|
|
51
|
+
if (node[2 /* AstNodeIndices.Style */] != null) {
|
|
52
|
+
result.value += ', ' + node[2 /* AstNodeIndices.Style */];
|
|
54
53
|
}
|
|
55
54
|
result.value += '}';
|
|
56
55
|
return;
|
|
57
56
|
case intl_ast_1.FormatJsNodeType.Plural: {
|
|
58
|
-
const pluralType = node[
|
|
59
|
-
result.value += '{' + node[
|
|
60
|
-
if (node[
|
|
61
|
-
result.value += 'offset:' + node[
|
|
57
|
+
const pluralType = node[4 /* AstNodeIndices.PluralType */] == 'ordinal' ? 'selectordinal' : 'plural';
|
|
58
|
+
result.value += '{' + node[1 /* AstNodeIndices.Value */] + ', ' + pluralType + ', ';
|
|
59
|
+
if (node[3 /* AstNodeIndices.Offset */]) {
|
|
60
|
+
result.value += 'offset:' + node[3 /* AstNodeIndices.Offset */];
|
|
62
61
|
}
|
|
63
|
-
for (const [name, arm] of Object.entries(node[
|
|
62
|
+
for (const [name, arm] of Object.entries(node[2 /* AstNodeIndices.Options */])) {
|
|
64
63
|
result.value += ' ' + name + ' {';
|
|
65
64
|
serializeAst(arm, result);
|
|
66
65
|
result.value += '}';
|
|
@@ -71,8 +70,8 @@ function serializeAst(ast, result) {
|
|
|
71
70
|
result.value += '#';
|
|
72
71
|
return;
|
|
73
72
|
case intl_ast_1.FormatJsNodeType.Select: {
|
|
74
|
-
result.value += '{' + node[
|
|
75
|
-
for (const [name, arm] of Object.entries(node[
|
|
73
|
+
result.value += '{' + node[1 /* AstNodeIndices.Value */] + ', select, ';
|
|
74
|
+
for (const [name, arm] of Object.entries(node[2 /* AstNodeIndices.Options */])) {
|
|
76
75
|
result.value += ' ' + name + ' {';
|
|
77
76
|
serializeAst(arm, result);
|
|
78
77
|
result.value += '}';
|
|
@@ -86,30 +85,30 @@ function serializeAst(ast, result) {
|
|
|
86
85
|
}
|
|
87
86
|
}
|
|
88
87
|
function serializeAstTag(node, result) {
|
|
89
|
-
switch (node[
|
|
88
|
+
switch (node[1 /* AstNodeIndices.Value */]) {
|
|
90
89
|
case '$b':
|
|
91
90
|
result.value += '**';
|
|
92
|
-
serializeAst(node[
|
|
91
|
+
serializeAst(node[2 /* AstNodeIndices.Children */], result);
|
|
93
92
|
result.value += '**';
|
|
94
93
|
return;
|
|
95
94
|
case '$i':
|
|
96
95
|
result.value += '*';
|
|
97
|
-
serializeAst(node[
|
|
96
|
+
serializeAst(node[2 /* AstNodeIndices.Children */], result);
|
|
98
97
|
result.value += '*';
|
|
99
98
|
return;
|
|
100
99
|
case '$code':
|
|
101
100
|
result.value += '`';
|
|
102
|
-
serializeAst(node[
|
|
101
|
+
serializeAst(node[2 /* AstNodeIndices.Children */], result);
|
|
103
102
|
result.value += '`';
|
|
104
103
|
return;
|
|
105
104
|
case '$p':
|
|
106
|
-
serializeAst(node[
|
|
105
|
+
serializeAst(node[2 /* AstNodeIndices.Children */], result);
|
|
107
106
|
result.value += '\n\n';
|
|
108
107
|
return;
|
|
109
108
|
case '$link':
|
|
110
109
|
// The target is the first child of the link. We don't have to care if it's a placeholder
|
|
111
110
|
// or not, because the serialization will automatically remove the extra empty.
|
|
112
|
-
const [target, ...children] = node[
|
|
111
|
+
const [target, ...children] = node[2 /* AstNodeIndices.Children */];
|
|
113
112
|
result.value += '[';
|
|
114
113
|
serializeAst(children, result);
|
|
115
114
|
result.value += '](';
|
|
@@ -121,8 +120,8 @@ function serializeAstTag(node, result) {
|
|
|
121
120
|
default:
|
|
122
121
|
// Any other tag name is a hook, which just adds the `$[` on a link tag.
|
|
123
122
|
result.value += '$[';
|
|
124
|
-
serializeAst(node[
|
|
125
|
-
result.value += '](' + node[
|
|
123
|
+
serializeAst(node[2 /* AstNodeIndices.Children */], result);
|
|
124
|
+
result.value += '](' + node[1 /* AstNodeIndices.Value */] + ')';
|
|
126
125
|
return;
|
|
127
126
|
}
|
|
128
127
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -193,8 +193,9 @@ export interface FunctionTypeMap {
|
|
|
193
193
|
}
|
|
194
194
|
|
|
195
195
|
export interface FunctionTypes<Result, HandlerType = () => void> {
|
|
196
|
-
|
|
197
|
-
|
|
196
|
+
// TODO: Figure out a nicer way to type `control` here.
|
|
197
|
+
link: undefined | ((content: Result[], key: string, control: Result[]) => Result | Result[]);
|
|
198
|
+
hook: undefined | ((content: Result[], key: string, control: Result[]) => Result | Result[]);
|
|
198
199
|
handler: undefined | HandlerType;
|
|
199
200
|
}
|
|
200
201
|
|
|
@@ -265,7 +266,6 @@ export type RequiredFormatValues<
|
|
|
265
266
|
>;
|
|
266
267
|
|
|
267
268
|
export interface RichTextFormattingMap<T = any> {
|
|
268
|
-
$_: T;
|
|
269
269
|
$b: T;
|
|
270
270
|
$i: T;
|
|
271
271
|
$p: T;
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@discord/intl",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0-canary.d90454b",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Client runtime for managing messages and translations in a React project.",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"exports": {
|
|
8
8
|
".": {
|
|
9
|
-
"
|
|
10
|
-
"
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
11
|
}
|
|
12
12
|
},
|
|
13
13
|
"files": [
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"@formatjs/intl": "^2.10.1",
|
|
20
20
|
"@intrnl/xxhash64": "^0.1.2",
|
|
21
21
|
"intl-messageformat": "^10.5.11",
|
|
22
|
-
"@discord/intl-ast": "0.
|
|
22
|
+
"@discord/intl-ast": "0.10.0-canary.d90454b"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"@swc/cli": "^0.3.12",
|
package/src/format.ts
CHANGED
|
@@ -37,7 +37,7 @@ function isRichTextTag(name: string) {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
export abstract class FormatBuilder<Result> {
|
|
40
|
-
abstract pushRichTextTag(tag: RichTextTagNames, children: Result[]): void;
|
|
40
|
+
abstract pushRichTextTag(tag: RichTextTagNames, children: Result[], control: Result[]): void;
|
|
41
41
|
abstract pushLiteralText(text: string): void;
|
|
42
42
|
abstract pushObject(value: object): void;
|
|
43
43
|
abstract finish(): Result[];
|
|
@@ -89,10 +89,7 @@ export function bindFormatValuesWithBuilder<T, Builder extends FormatBuilder<T>>
|
|
|
89
89
|
const value = values[variableName];
|
|
90
90
|
switch (nodeType) {
|
|
91
91
|
case FormatJsNodeType.Argument:
|
|
92
|
-
|
|
93
|
-
if (variableName == '$_') break;
|
|
94
|
-
|
|
95
|
-
if (typeof value === 'object') {
|
|
92
|
+
if (typeof value === 'object' || typeof value === 'function') {
|
|
96
93
|
builder.pushObject(value);
|
|
97
94
|
} else {
|
|
98
95
|
// Taken from FormatJS: non-objects (strings, numbers, and falsy
|
|
@@ -149,6 +146,7 @@ export function bindFormatValuesWithBuilder<T, Builder extends FormatBuilder<T>>
|
|
|
149
146
|
|
|
150
147
|
case FormatJsNodeType.Tag: {
|
|
151
148
|
const children = node[AstNodeIndices.Children];
|
|
149
|
+
const control = node[AstNodeIndices.Control];
|
|
152
150
|
const appliedChildren = bindFormatValues(
|
|
153
151
|
builder.constructor as FormatBuilderConstructor<T>,
|
|
154
152
|
children,
|
|
@@ -158,8 +156,24 @@ export function bindFormatValuesWithBuilder<T, Builder extends FormatBuilder<T>>
|
|
|
158
156
|
values,
|
|
159
157
|
currentPluralValue,
|
|
160
158
|
);
|
|
159
|
+
const appliedControl =
|
|
160
|
+
control != null
|
|
161
|
+
? bindFormatValues(
|
|
162
|
+
builder.constructor as FormatBuilderConstructor<T>,
|
|
163
|
+
control,
|
|
164
|
+
locales,
|
|
165
|
+
formatters,
|
|
166
|
+
formatConfig,
|
|
167
|
+
values,
|
|
168
|
+
currentPluralValue,
|
|
169
|
+
)
|
|
170
|
+
: [];
|
|
161
171
|
if (isRichTextTag(variableName)) {
|
|
162
|
-
builder.pushRichTextTag(
|
|
172
|
+
builder.pushRichTextTag(
|
|
173
|
+
variableName as RichTextTagNames,
|
|
174
|
+
appliedChildren,
|
|
175
|
+
appliedControl,
|
|
176
|
+
);
|
|
163
177
|
} else {
|
|
164
178
|
if (typeof value !== 'function') {
|
|
165
179
|
throw `expected a function type for a Tag formatting value, ${variableName}. got ${typeof value}: ${value}`;
|
|
@@ -224,7 +238,7 @@ export function bindFormatValuesWithBuilder<T, Builder extends FormatBuilder<T>>
|
|
|
224
238
|
|
|
225
239
|
export function bindFormatValues<Result>(
|
|
226
240
|
Builder: FormatBuilderConstructor<Result>,
|
|
227
|
-
nodes: AstNode[],
|
|
241
|
+
nodes: string | AstNode[],
|
|
228
242
|
locales: string | string[],
|
|
229
243
|
formatters: Formatters,
|
|
230
244
|
formatConfig: FormatConfig,
|
|
@@ -232,14 +246,19 @@ export function bindFormatValues<Result>(
|
|
|
232
246
|
currentPluralValue?: number,
|
|
233
247
|
): Result[] {
|
|
234
248
|
const builder = new Builder();
|
|
235
|
-
|
|
236
|
-
builder
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
249
|
+
if (typeof nodes === 'string') {
|
|
250
|
+
builder.pushLiteralText(nodes);
|
|
251
|
+
return builder.finish();
|
|
252
|
+
} else {
|
|
253
|
+
bindFormatValuesWithBuilder(
|
|
254
|
+
builder,
|
|
255
|
+
nodes,
|
|
256
|
+
locales,
|
|
257
|
+
formatters,
|
|
258
|
+
formatConfig,
|
|
259
|
+
values,
|
|
260
|
+
currentPluralValue,
|
|
261
|
+
);
|
|
262
|
+
return builder.finish();
|
|
263
|
+
}
|
|
245
264
|
}
|
package/src/formatters/ast.ts
CHANGED
|
@@ -78,12 +78,11 @@ export type RichTextNode =
|
|
|
78
78
|
| RichTextObjectNode;
|
|
79
79
|
|
|
80
80
|
const AST_RICH_TEXT_ELEMENTS: RichTextFormattingMap<AstFunctionTypes['hook']> = {
|
|
81
|
-
$_: () => ({ type: RichTextNodeType.Text, content: '' }),
|
|
82
81
|
$b: (content) => ({ type: RichTextNodeType.Strong, content }),
|
|
83
82
|
$i: (content) => ({ type: RichTextNodeType.Emphasis, content }),
|
|
84
83
|
$del: (content) => ({ type: RichTextNodeType.Strikethrough, content }),
|
|
85
84
|
$code: (content) => ({ type: RichTextNodeType.Code, content }),
|
|
86
|
-
$link: ([target
|
|
85
|
+
$link: (content, _, [target]) => ({
|
|
87
86
|
type: RichTextNodeType.Link,
|
|
88
87
|
target: target.content as string,
|
|
89
88
|
content,
|
|
@@ -94,11 +93,11 @@ const AST_RICH_TEXT_ELEMENTS: RichTextFormattingMap<AstFunctionTypes['hook']> =
|
|
|
94
93
|
class AstBuilder extends FormatBuilder<RichTextNode> {
|
|
95
94
|
result: RichTextNode[] = [];
|
|
96
95
|
|
|
97
|
-
pushRichTextTag(tag: RichTextTagNames, children: RichTextNode[]) {
|
|
96
|
+
pushRichTextTag(tag: RichTextTagNames, children: RichTextNode[], control: RichTextNode[]) {
|
|
98
97
|
if (!(tag in AST_RICH_TEXT_ELEMENTS)) {
|
|
99
98
|
throw `${tag} is not a known rich text formatting tag`;
|
|
100
99
|
}
|
|
101
|
-
const result = AST_RICH_TEXT_ELEMENTS[tag](children, '');
|
|
100
|
+
const result = AST_RICH_TEXT_ELEMENTS[tag](children, '', control);
|
|
102
101
|
if (Array.isArray(result)) {
|
|
103
102
|
this.result.push(...result);
|
|
104
103
|
} else {
|
|
@@ -19,20 +19,19 @@ import { StringBuilder } from './string';
|
|
|
19
19
|
export type MarkdownFunctionTypes = FunctionTypes<string>;
|
|
20
20
|
|
|
21
21
|
const MARKDOWN_RICH_TEXT_ELEMENTS: RichTextFormattingMap<MarkdownFunctionTypes['hook']> = {
|
|
22
|
-
$_: () => '',
|
|
23
22
|
$b: (content) => '**' + content.join('') + '**',
|
|
24
23
|
$i: (content) => '*' + content.join('') + '*',
|
|
25
24
|
$del: (content) => '~~' + content.join('') + '~~',
|
|
26
25
|
$code: (content) => '`' + content.join('') + '`',
|
|
27
|
-
$link: ([target
|
|
26
|
+
$link: (content, _, [target]) => '[' + content.join('') + '](' + target + ')',
|
|
28
27
|
$p: (content) => content.join('') + '\n\n',
|
|
29
28
|
};
|
|
30
29
|
|
|
31
30
|
class MarkdownBuilder extends StringBuilder {
|
|
32
31
|
result: string = '';
|
|
33
32
|
|
|
34
|
-
pushRichTextTag(tag: RichTextTagNames, children: string[]) {
|
|
35
|
-
this.result += MARKDOWN_RICH_TEXT_ELEMENTS[tag](children, '');
|
|
33
|
+
pushRichTextTag(tag: RichTextTagNames, children: string[], control: string[]) {
|
|
34
|
+
this.result += MARKDOWN_RICH_TEXT_ELEMENTS[tag](children, '', control);
|
|
36
35
|
}
|
|
37
36
|
}
|
|
38
37
|
|
package/src/formatters/react.ts
CHANGED
|
@@ -27,13 +27,11 @@ export type ReactFunctionTypes = FunctionTypes<
|
|
|
27
27
|
|
|
28
28
|
const h = React.createElement;
|
|
29
29
|
export const DEFAULT_REACT_RICH_TEXT_ELEMENTS: RichTextFormattingMap<ReactFunctionTypes['hook']> = {
|
|
30
|
-
$_: () => '',
|
|
31
30
|
$b: (content, key) => h('strong', { key }, content),
|
|
32
31
|
$i: (content, key) => h('em', { key }, content),
|
|
33
32
|
$del: (content, key) => h('del', { key }, content),
|
|
34
33
|
$code: (content, key) => h('code', { key }, content),
|
|
35
|
-
|
|
36
|
-
$link: ([href, ...content], key) => h('a', { href, key }, content),
|
|
34
|
+
$link: (content, key, [href]) => h('a', { href, key }, content),
|
|
37
35
|
$p: (content, key) => h('p', { key }, content),
|
|
38
36
|
};
|
|
39
37
|
|
|
@@ -51,8 +49,12 @@ function createReactBuilder(richTextElements: RichTextFormattingMap<ReactFunctio
|
|
|
51
49
|
_nodeKey: number = 0;
|
|
52
50
|
result: React.ReactNode[] = [];
|
|
53
51
|
|
|
54
|
-
pushRichTextTag(
|
|
55
|
-
|
|
52
|
+
pushRichTextTag(
|
|
53
|
+
tag: RichTextTagNames,
|
|
54
|
+
children: React.ReactNode[],
|
|
55
|
+
control: React.ReactNode[],
|
|
56
|
+
) {
|
|
57
|
+
this.result.push(richTextElements[tag](children, `${this._nodeKey++}`, control));
|
|
56
58
|
}
|
|
57
59
|
|
|
58
60
|
pushLiteralText(text: string) {
|
|
@@ -65,8 +67,8 @@ function createReactBuilder(richTextElements: RichTextFormattingMap<ReactFunctio
|
|
|
65
67
|
}
|
|
66
68
|
|
|
67
69
|
pushObject(value: object) {
|
|
68
|
-
// @ts-expect-error this is technically invalid, but we'll just assume that if a format
|
|
69
|
-
// an object, it'll be acting as some form of ReactNode.
|
|
70
|
+
// @ts-expect-error this is technically invalid, but we'll just assume that if a format
|
|
71
|
+
// returns an object, it'll be acting as some form of ReactNode.
|
|
70
72
|
this.result.push(value);
|
|
71
73
|
}
|
|
72
74
|
|
package/src/formatters/string.ts
CHANGED
|
@@ -15,16 +15,14 @@ export type StringFunctionTypes = FunctionTypes<string>;
|
|
|
15
15
|
export class StringBuilder extends FormatBuilder<string> {
|
|
16
16
|
result: string = '';
|
|
17
17
|
|
|
18
|
-
pushRichTextTag(
|
|
18
|
+
pushRichTextTag(_tag: RichTextTagNames, children: string[], _control: string[]) {
|
|
19
19
|
// Plain string formatting ignores rich text tags and just takes the
|
|
20
|
-
// visible content from the children. This means the
|
|
21
|
-
//
|
|
22
|
-
// together
|
|
23
|
-
|
|
24
|
-
this.result +=
|
|
25
|
-
return;
|
|
20
|
+
// visible content from the children. This means the control element is not
|
|
21
|
+
// important for string rendering, so the result is always just the
|
|
22
|
+
// children joined together.
|
|
23
|
+
for (const child of children) {
|
|
24
|
+
this.result += child;
|
|
26
25
|
}
|
|
27
|
-
this.result += children.join('');
|
|
28
26
|
}
|
|
29
27
|
|
|
30
28
|
pushLiteralText(text: string) {
|
package/src/intl-manager.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { LiteralNode } from '@discord/intl-ast';
|
|
2
1
|
import { createIntl, IntlShape } from '@formatjs/intl';
|
|
3
2
|
import { Formatters } from 'intl-messageformat';
|
|
4
3
|
|
|
@@ -117,20 +116,7 @@ export class IntlManager {
|
|
|
117
116
|
* immediately return the plain string value of the message in the current locale.
|
|
118
117
|
*/
|
|
119
118
|
string<T extends TypedIntlMessageGetter<{}>>(message: T): string {
|
|
120
|
-
|
|
121
|
-
if (resolved == null || resolved.ast.length === 0) return '';
|
|
122
|
-
|
|
123
|
-
let result = '';
|
|
124
|
-
for (const element of resolved.ast) {
|
|
125
|
-
if (typeof element !== 'string') {
|
|
126
|
-
throw new Error(
|
|
127
|
-
'Attempted to call `string` formatting on a non-literal message: ' +
|
|
128
|
-
JSON.stringify(resolved),
|
|
129
|
-
);
|
|
130
|
-
}
|
|
131
|
-
result += element;
|
|
132
|
-
}
|
|
133
|
-
return result;
|
|
119
|
+
return message(this.currentLocale).reserialize();
|
|
134
120
|
}
|
|
135
121
|
|
|
136
122
|
/**
|
package/src/message-loader.ts
CHANGED
|
@@ -54,7 +54,7 @@ export class MessageLoader {
|
|
|
54
54
|
/**
|
|
55
55
|
* Map of pre-parsed messages, keyed by the message key and locale
|
|
56
56
|
*/
|
|
57
|
-
_parseCache:
|
|
57
|
+
_parseCache: Record<LocaleId, { [name: string]: InternalIntlMessage }>;
|
|
58
58
|
|
|
59
59
|
/**
|
|
60
60
|
* List of subscribers listening for changes to the current locale and
|
|
@@ -98,7 +98,7 @@ export class MessageLoader {
|
|
|
98
98
|
this.defaultLocale = defaultLocale;
|
|
99
99
|
|
|
100
100
|
this._localeLoadingPromises = {};
|
|
101
|
-
this._parseCache =
|
|
101
|
+
this._parseCache = {};
|
|
102
102
|
this._subscribers = new Set();
|
|
103
103
|
|
|
104
104
|
this._loadLocale(this.defaultLocale);
|
|
@@ -112,7 +112,7 @@ export class MessageLoader {
|
|
|
112
112
|
// @ts-expect-error `hot` not defined in types.
|
|
113
113
|
module.hot.accept(file, async () => {
|
|
114
114
|
await this._loadLocale(locale);
|
|
115
|
-
this._parseCache
|
|
115
|
+
this._parseCache = {};
|
|
116
116
|
});
|
|
117
117
|
}
|
|
118
118
|
}
|
|
@@ -160,9 +160,8 @@ export class MessageLoader {
|
|
|
160
160
|
* returning.
|
|
161
161
|
*/
|
|
162
162
|
getMessageValue(key: string, locale: LocaleId): InternalIntlMessage | undefined {
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
if (cachedValue != null) return cachedValue;
|
|
163
|
+
const parsed = this._parseCache[locale]?.[key];
|
|
164
|
+
if (parsed) return parsed;
|
|
166
165
|
|
|
167
166
|
// Check whether the locale exists in memory. If not, start a request to
|
|
168
167
|
// load the locale if one has not already started. It's impossible for
|
|
@@ -179,7 +178,7 @@ export class MessageLoader {
|
|
|
179
178
|
if (key in this.messages[locale]) {
|
|
180
179
|
const content = this.messages[locale][key];
|
|
181
180
|
const message = new InternalIntlMessage(content, locale);
|
|
182
|
-
this._parseCache
|
|
181
|
+
(this._parseCache[locale] ??= {})[key] = message;
|
|
183
182
|
return message;
|
|
184
183
|
}
|
|
185
184
|
|
package/src/message.ts
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
|
|
11
11
|
export class InternalIntlMessage {
|
|
12
12
|
locale: string;
|
|
13
|
-
ast: AstNode[];
|
|
13
|
+
ast: string | AstNode[];
|
|
14
14
|
|
|
15
15
|
constructor(messageOrAst: AstNode[] | FullFormatJsNode[], locale: string) {
|
|
16
16
|
this.locale = locale;
|
|
@@ -22,6 +22,8 @@ export class InternalIntlMessage {
|
|
|
22
22
|
* formatting or values applied.
|
|
23
23
|
*/
|
|
24
24
|
reserialize(): string {
|
|
25
|
+
if (typeof this.ast === 'string') return this.ast;
|
|
26
|
+
|
|
25
27
|
const result = { value: '' };
|
|
26
28
|
serializeAst(this.ast, result);
|
|
27
29
|
return result.value;
|
|
@@ -39,8 +41,6 @@ function serializeAst(ast: AstNode[], result: { value: string }) {
|
|
|
39
41
|
|
|
40
42
|
switch (node[AstNodeIndices.Type]) {
|
|
41
43
|
case FormatJsNodeType.Argument:
|
|
42
|
-
// Empties are an artifact of our parsing strategy, not necessary here.
|
|
43
|
-
if (result.value === '$_') return;
|
|
44
44
|
result.value += '{' + node[AstNodeIndices.Value] + '}';
|
|
45
45
|
return;
|
|
46
46
|
case FormatJsNodeType.Date:
|
package/src/types.d.ts
CHANGED
|
@@ -193,8 +193,9 @@ export interface FunctionTypeMap {
|
|
|
193
193
|
}
|
|
194
194
|
|
|
195
195
|
export interface FunctionTypes<Result, HandlerType = () => void> {
|
|
196
|
-
|
|
197
|
-
|
|
196
|
+
// TODO: Figure out a nicer way to type `control` here.
|
|
197
|
+
link: undefined | ((content: Result[], key: string, control: Result[]) => Result | Result[]);
|
|
198
|
+
hook: undefined | ((content: Result[], key: string, control: Result[]) => Result | Result[]);
|
|
198
199
|
handler: undefined | HandlerType;
|
|
199
200
|
}
|
|
200
201
|
|
|
@@ -265,7 +266,6 @@ export type RequiredFormatValues<
|
|
|
265
266
|
>;
|
|
266
267
|
|
|
267
268
|
export interface RichTextFormattingMap<T = any> {
|
|
268
|
-
$_: T;
|
|
269
269
|
$b: T;
|
|
270
270
|
$i: T;
|
|
271
271
|
$p: T;
|