@discord/intl 0.0.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Discord, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,3 @@
1
+ export { IntlManager, createLoader, DEFAULT_LOCALE } from './intl-manager';
2
+ export { IntlManagerReact } from './react';
3
+ export type * from './types.d.ts';
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.IntlManagerReact = exports.DEFAULT_LOCALE = exports.createLoader = exports.IntlManager = void 0;
4
+ var intl_manager_1 = require("./intl-manager");
5
+ Object.defineProperty(exports, "IntlManager", { enumerable: true, get: function () { return intl_manager_1.IntlManager; } });
6
+ Object.defineProperty(exports, "createLoader", { enumerable: true, get: function () { return intl_manager_1.createLoader; } });
7
+ Object.defineProperty(exports, "DEFAULT_LOCALE", { enumerable: true, get: function () { return intl_manager_1.DEFAULT_LOCALE; } });
8
+ var react_1 = require("./react");
9
+ Object.defineProperty(exports, "IntlManagerReact", { enumerable: true, get: function () { return react_1.IntlManagerReact; } });
@@ -0,0 +1,59 @@
1
+ import { IntlShape } from '@formatjs/intl';
2
+ import { LocaleImportMap, MessageLoader } from './message-loader';
3
+ import type { TypedIntlMessageGetter, FormatValuesFor, RichTextElementMap } from './types';
4
+ /**
5
+ * Fallback locale used for all internationalization when an operation in the
6
+ * requested locale is not possible.
7
+ */
8
+ export declare const DEFAULT_LOCALE: string;
9
+ export declare class IntlManager<DefaultElements extends RichTextElementMap, DefaultValues extends keyof DefaultElements> {
10
+ defaultLocale: string;
11
+ currentLocale: string;
12
+ intl: IntlShape;
13
+ /**
14
+ * When formatting tag elements like `<b>value</b>`, these functions will
15
+ * automatically be used by all messages, unless they are explicitly removed
16
+ * for a specific call, either by overriding the value or by calling a
17
+ * dedicated method like `formatToPlainString` which will remove all
18
+ * rich elements from the string and return only the plain text result.
19
+ */
20
+ defaultRichTextElements: DefaultElements;
21
+ _localeSubscriptions: Set<(locale: string) => void>;
22
+ constructor(defaultLocale: string, defaultRichTextElements: DefaultElements);
23
+ /**
24
+ * Set the locale for this package to use. This is a global change, and all
25
+ * methods on this object will use this locale for formatting.
26
+ */
27
+ setLocale(locale: string): void;
28
+ emitLocaleChange(locale: string): void;
29
+ /**
30
+ * Subscribe to changes in the current locale for this manager.
31
+ */
32
+ onLocaleChange: (callback: (locale: string) => void) => () => boolean;
33
+ /**
34
+ * Format the given message in the current locale with the provided values.
35
+ * The returned values is _always_ an Array of parts, even if the message is
36
+ * a simple string value.
37
+ *
38
+ * This function is the basis of how messages are normally formatted, and can
39
+ * be used anywhere. However, it is not reactive and only functions on the
40
+ * data that is currently loaded and known. For a reactive function that
41
+ * automatically updates when the locale changes or when new data is loaded,
42
+ * use `format`, which will wrap the formatting in a React component that
43
+ * subscribes to the current locale and state of loaded messages.
44
+ */
45
+ formatToParts<T extends TypedIntlMessageGetter<object | undefined>>(message: T): Array<string | any>;
46
+ formatToParts<T extends TypedIntlMessageGetter<object | undefined>>(message: T, values: Omit<FormatValuesFor<T>, DefaultValues>): Array<string | any>;
47
+ /**
48
+ * Format the given message with the provided values, removing any styling
49
+ * and non-textual content from the message, returning a plain string.
50
+ */
51
+ formatToPlainString<T extends TypedIntlMessageGetter<object | undefined>>(message: T): string;
52
+ formatToPlainString<T extends TypedIntlMessageGetter<object | undefined>>(message: T, values: Omit<FormatValuesFor<T>, DefaultValues>): string;
53
+ }
54
+ /**
55
+ * Create a new MessageLoader, which handles lazily loading messages for
56
+ * different locales and sanity checks as needed to provide accessors for each
57
+ * message defined in `messageKeys`.
58
+ */
59
+ export declare function createLoader(messageKeys: string[], localeImportMap: LocaleImportMap): MessageLoader;
@@ -0,0 +1,138 @@
1
+ "use strict";
2
+ var __assign = (this && this.__assign) || function () {
3
+ __assign = Object.assign || function(t) {
4
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
5
+ s = arguments[i];
6
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
+ t[p] = s[p];
8
+ }
9
+ return t;
10
+ };
11
+ return __assign.apply(this, arguments);
12
+ };
13
+ var __values = (this && this.__values) || function(o) {
14
+ var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
15
+ if (m) return m.call(o);
16
+ if (o && typeof o.length === "number") return {
17
+ next: function () {
18
+ if (o && i >= o.length) o = void 0;
19
+ return { value: o && o[i++], done: !o };
20
+ }
21
+ };
22
+ throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
23
+ };
24
+ Object.defineProperty(exports, "__esModule", { value: true });
25
+ exports.IntlManager = exports.DEFAULT_LOCALE = void 0;
26
+ exports.createLoader = createLoader;
27
+ var intl_1 = require("@formatjs/intl");
28
+ var intl_messageformat_1 = require("intl-messageformat");
29
+ var message_loader_1 = require("./message-loader");
30
+ /**
31
+ * Fallback locale used for all internationalization when an operation in the
32
+ * requested locale is not possible.
33
+ */
34
+ exports.DEFAULT_LOCALE = 'en-US';
35
+ var IntlManager = /** @class */ (function () {
36
+ function IntlManager(defaultLocale, defaultRichTextElements) {
37
+ if (defaultLocale === void 0) { defaultLocale = exports.DEFAULT_LOCALE; }
38
+ var _this = this;
39
+ /**
40
+ * Subscribe to changes in the current locale for this manager.
41
+ */
42
+ this.onLocaleChange = function (callback) {
43
+ _this._localeSubscriptions.add(callback);
44
+ return function () { return _this._localeSubscriptions.delete(callback); };
45
+ };
46
+ this.defaultLocale = defaultLocale;
47
+ this.currentLocale = defaultLocale;
48
+ this.intl = (0, intl_1.createIntl)({
49
+ formats: intl_messageformat_1.IntlMessageFormat.formats,
50
+ defaultLocale: defaultLocale,
51
+ locale: defaultLocale,
52
+ });
53
+ this.defaultRichTextElements = defaultRichTextElements;
54
+ this._localeSubscriptions = new Set();
55
+ }
56
+ /**
57
+ * Set the locale for this package to use. This is a global change, and all
58
+ * methods on this object will use this locale for formatting.
59
+ */
60
+ IntlManager.prototype.setLocale = function (locale) {
61
+ this.currentLocale = locale;
62
+ this.intl = (0, intl_1.createIntl)({ defaultLocale: this.defaultLocale, locale: locale });
63
+ this.emitLocaleChange(locale);
64
+ };
65
+ IntlManager.prototype.emitLocaleChange = function (locale) {
66
+ var e_1, _a;
67
+ try {
68
+ for (var _b = __values(this._localeSubscriptions), _c = _b.next(); !_c.done; _c = _b.next()) {
69
+ var callback = _c.value;
70
+ callback(locale);
71
+ }
72
+ }
73
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
74
+ finally {
75
+ try {
76
+ if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
77
+ }
78
+ finally { if (e_1) throw e_1.error; }
79
+ }
80
+ };
81
+ IntlManager.prototype.formatToParts = function (message, values) {
82
+ var e_2, _a;
83
+ if (typeof message === 'string')
84
+ return [message];
85
+ var resolvedMessage = typeof message === 'function' ? message(this.currentLocale) : message;
86
+ if (typeof resolvedMessage === 'string')
87
+ return [resolvedMessage];
88
+ var resolvedValues = values != null
89
+ ? __assign(__assign({}, this.defaultRichTextElements), values) : this.defaultRichTextElements;
90
+ var parts = resolvedMessage.formatToParts(this.intl.formatters, this.intl.formats, resolvedValues);
91
+ var result = [];
92
+ var inLiteral = false;
93
+ try {
94
+ for (var parts_1 = __values(parts), parts_1_1 = parts_1.next(); !parts_1_1.done; parts_1_1 = parts_1.next()) {
95
+ var part = parts_1_1.value;
96
+ // This condition merges consecutive literal elements (static strings)
97
+ // into single parts to reduce the number of nodes in the result. This
98
+ // condition will never be true on the first loop, ensuring that the
99
+ // array must at least have one entry before it attempts to concatenate
100
+ // onto that value again.
101
+ if (inLiteral && (inLiteral = part.type === intl_messageformat_1.PART_TYPE.literal)) {
102
+ result[result.length - 1] += part.value;
103
+ continue;
104
+ }
105
+ inLiteral = part.type === intl_messageformat_1.PART_TYPE.literal;
106
+ result.push(part.value);
107
+ }
108
+ }
109
+ catch (e_2_1) { e_2 = { error: e_2_1 }; }
110
+ finally {
111
+ try {
112
+ if (parts_1_1 && !parts_1_1.done && (_a = parts_1.return)) _a.call(parts_1);
113
+ }
114
+ finally { if (e_2) throw e_2.error; }
115
+ }
116
+ return result;
117
+ };
118
+ IntlManager.prototype.formatToPlainString = function (message, values) {
119
+ if (typeof message === 'string')
120
+ return message;
121
+ var resolvedMessage = message(this.currentLocale);
122
+ if (typeof resolvedMessage === 'string')
123
+ return resolvedMessage;
124
+ // No need to pass in `defaultRichTextElements`, since the stylistic tags
125
+ // will be removed from the string anyway.
126
+ return resolvedMessage.formatToPlainString(this.intl.formatters, this.intl.formats, values);
127
+ };
128
+ return IntlManager;
129
+ }());
130
+ exports.IntlManager = IntlManager;
131
+ /**
132
+ * Create a new MessageLoader, which handles lazily loading messages for
133
+ * different locales and sanity checks as needed to provide accessors for each
134
+ * message defined in `messageKeys`.
135
+ */
136
+ function createLoader(messageKeys, localeImportMap) {
137
+ return new message_loader_1.MessageLoader(messageKeys, localeImportMap);
138
+ }
@@ -0,0 +1,61 @@
1
+ export declare enum FormatJsNodeType {
2
+ Literal = 0,
3
+ Argument = 1,
4
+ Number = 2,
5
+ Date = 3,
6
+ Time = 4,
7
+ Select = 5,
8
+ Plural = 6,
9
+ Pound = 7,
10
+ Tag = 8
11
+ }
12
+ export interface FormatJsLiteral {
13
+ type: FormatJsNodeType.Literal;
14
+ value: string;
15
+ }
16
+ export interface FormatJsArgument {
17
+ type: FormatJsNodeType.Argument;
18
+ value: string;
19
+ }
20
+ export interface FormatJsNumber {
21
+ type: FormatJsNodeType.Number;
22
+ value: string;
23
+ style?: string;
24
+ }
25
+ export interface FormatJsDate {
26
+ type: FormatJsNodeType.Date;
27
+ value: string;
28
+ style?: string;
29
+ }
30
+ export interface FormatJsTime {
31
+ type: FormatJsNodeType.Time;
32
+ value: string;
33
+ style?: string;
34
+ }
35
+ export type FormatJsPluralType = 'cardinal' | 'ordinal';
36
+ export interface FormatJsPlural {
37
+ type: FormatJsNodeType.Plural;
38
+ value: string;
39
+ options: Record<string, {
40
+ value: FormatJsNode[];
41
+ }>;
42
+ offset: number;
43
+ pluralType: FormatJsPluralType;
44
+ }
45
+ export interface FormatJsPound {
46
+ type: FormatJsNodeType.Pound;
47
+ }
48
+ export interface FormatJsTag {
49
+ type: FormatJsNodeType.Tag;
50
+ value: string;
51
+ children: FormatJsNode[];
52
+ }
53
+ export type FormatJsNode = FormatJsLiteral | FormatJsArgument | FormatJsNumber | FormatJsDate | FormatJsTime | FormatJsPlural | FormatJsPound | FormatJsTag;
54
+ export declare const FORMAT_JS_POUND: FormatJsPound;
55
+ /**
56
+ * Hydrate the given keyless JSON into a FormatJS-compatible AST structure.
57
+ * The given object is reused as much as possible, meaning it cannot be safely
58
+ * used again after hydrating.
59
+ */
60
+ export declare function hydrateFormatJsAst(keyless: Array<Array<any>>): FormatJsNode[];
61
+ export declare function hydrateMessages(messages: Record<string, any>): Record<string, any>;
@@ -0,0 +1,107 @@
1
+ "use strict";
2
+ var __read = (this && this.__read) || function (o, n) {
3
+ var m = typeof Symbol === "function" && o[Symbol.iterator];
4
+ if (!m) return o;
5
+ var i = m.call(o), r, ar = [], e;
6
+ try {
7
+ while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
8
+ }
9
+ catch (error) { e = { error: error }; }
10
+ finally {
11
+ try {
12
+ if (r && !r.done && (m = i["return"])) m.call(i);
13
+ }
14
+ finally { if (e) throw e.error; }
15
+ }
16
+ return ar;
17
+ };
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ exports.FORMAT_JS_POUND = exports.FormatJsNodeType = void 0;
20
+ exports.hydrateFormatJsAst = hydrateFormatJsAst;
21
+ exports.hydrateMessages = hydrateMessages;
22
+ var FormatJsNodeType;
23
+ (function (FormatJsNodeType) {
24
+ FormatJsNodeType[FormatJsNodeType["Literal"] = 0] = "Literal";
25
+ FormatJsNodeType[FormatJsNodeType["Argument"] = 1] = "Argument";
26
+ FormatJsNodeType[FormatJsNodeType["Number"] = 2] = "Number";
27
+ FormatJsNodeType[FormatJsNodeType["Date"] = 3] = "Date";
28
+ FormatJsNodeType[FormatJsNodeType["Time"] = 4] = "Time";
29
+ FormatJsNodeType[FormatJsNodeType["Select"] = 5] = "Select";
30
+ FormatJsNodeType[FormatJsNodeType["Plural"] = 6] = "Plural";
31
+ FormatJsNodeType[FormatJsNodeType["Pound"] = 7] = "Pound";
32
+ FormatJsNodeType[FormatJsNodeType["Tag"] = 8] = "Tag";
33
+ })(FormatJsNodeType || (exports.FormatJsNodeType = FormatJsNodeType = {}));
34
+ // Using a static object here ensures there's only one allocation for it, which
35
+ // will likely appear in many, many messages. Freezing ensures that the shared
36
+ // object can't be accidentally modified if a consumer tries to transform the
37
+ // AST.
38
+ exports.FORMAT_JS_POUND = Object.freeze({ type: 7 });
39
+ function hydrateArray(elements) {
40
+ for (var i = 0; i < elements.length; i++) {
41
+ elements[i] = hydrateFormatJsAst(elements[i]);
42
+ }
43
+ }
44
+ function hydratePlural(keyless) {
45
+ var _a = __read(keyless, 5), type = _a[0], value = _a[1], options = _a[2], offset = _a[3], pluralType = _a[4];
46
+ // This tries to be efficient about updating the value for each option
47
+ // with a parsed version, reusing the same receiving object, and even
48
+ // the inner key within it, just replacing the end value with the
49
+ // parsed version.
50
+ // This saves multiple allocations compared to building a new object
51
+ // from scratch, either for the whole options object or for each value.
52
+ for (var key in options) {
53
+ hydrateArray(options[key].value);
54
+ }
55
+ // `pluralType` is technically only valid on `Plural` nodes, even
56
+ // though the structure is identical to `Select`.
57
+ if (type === FormatJsNodeType.Plural) {
58
+ return { type: type, value: value, options: options, offset: offset, pluralType: pluralType };
59
+ }
60
+ else {
61
+ return { type: type, value: value, options: options, offset: offset };
62
+ }
63
+ }
64
+ function hydrateSingle(keyless) {
65
+ var _a = __read(keyless, 1), type = _a[0];
66
+ switch (type) {
67
+ case FormatJsNodeType.Literal:
68
+ case FormatJsNodeType.Argument:
69
+ return { type: type, value: keyless[1] };
70
+ case FormatJsNodeType.Number:
71
+ case FormatJsNodeType.Date:
72
+ case FormatJsNodeType.Time:
73
+ return { type: type, value: keyless[1], style: keyless[2] };
74
+ case FormatJsNodeType.Select:
75
+ case FormatJsNodeType.Plural:
76
+ return hydratePlural(keyless);
77
+ case FormatJsNodeType.Pound:
78
+ return exports.FORMAT_JS_POUND;
79
+ case FormatJsNodeType.Tag: {
80
+ var _b = __read(keyless, 3), type_1 = _b[0], value = _b[1], children = _b[2];
81
+ hydrateArray(children);
82
+ return { type: type_1, value: value, children: children };
83
+ }
84
+ default:
85
+ throw new Error("FormatJS keyless JSON encountered an unknown type: ".concat(type));
86
+ }
87
+ }
88
+ function hydrateFormatJsAst(keyless) {
89
+ // If the first element of the array is itself an array, then we have a list
90
+ // of elements to parse rather than a single object.
91
+ if (Array.isArray(keyless[0])) {
92
+ hydrateArray(keyless);
93
+ return keyless;
94
+ }
95
+ else if (keyless.length === 0) {
96
+ // Some entries can be empty arrays, like an empty set of children, and those can
97
+ // be preserved.
98
+ return keyless;
99
+ }
100
+ return hydrateSingle(keyless);
101
+ }
102
+ function hydrateMessages(messages) {
103
+ for (var key in messages) {
104
+ messages[key] = hydrateFormatJsAst(messages[key]);
105
+ }
106
+ return messages;
107
+ }
@@ -0,0 +1,60 @@
1
+ import { InternalIntlMessage } from './message';
2
+ import type { MessageFormatElement } from '@formatjs/icu-messageformat-parser';
3
+ type AnyRawMessage = string | MessageFormatElement[];
4
+ type MessagesData = Record<string, AnyRawMessage>;
5
+ export interface IntlMessageGetterAdditions {
6
+ isLocaleLoaded(locale: LocaleId): boolean;
7
+ onChange(callback: () => void): () => void;
8
+ }
9
+ export interface IntlMessageGetter extends IntlMessageGetterAdditions {
10
+ (locale: LocaleId): InternalIntlMessage;
11
+ }
12
+ type LocaleId = string;
13
+ export type LocaleImportMap = Record<LocaleId, () => Promise<{
14
+ default: MessagesData;
15
+ }>>;
16
+ export declare class MessageLoader {
17
+ messageKeys: string[];
18
+ /** Map of locale to string key to message content. */
19
+ messages: Record<LocaleId, MessagesData>;
20
+ localeImportMap: LocaleImportMap;
21
+ supportedLocales: LocaleId[];
22
+ /**
23
+ * Promise representing the current locale being loaded. If this is non-null,
24
+ * then a request has already been made to load the locale and it is waiting
25
+ * to resolve.
26
+ */
27
+ _localeLoadingPromises: Record<LocaleId, Promise<{
28
+ default: MessagesData;
29
+ }> | undefined>;
30
+ /**
31
+ * Map of pre-parsed messages, keyed by the message key and locale
32
+ */
33
+ _parseCache: Map<string, InternalIntlMessage>;
34
+ /**
35
+ * List of subscribers listening for changes to the current locale and
36
+ * when it has loaded.
37
+ */
38
+ _subscribers: Set<() => void>;
39
+ fallbackMessage: InternalIntlMessage;
40
+ constructor(messageKeys: string[], localeImportMap: LocaleImportMap);
41
+ get(key: string, locale: LocaleId): InternalIntlMessage;
42
+ /**
43
+ * Returns a record mapping the keys this object manages to bound functions
44
+ * for `get` with the that key as the first argument, allowing consumers to
45
+ * just call the function with a locale to retrieve the translated message
46
+ * for that key.
47
+ */
48
+ getBinds(): Record<string, IntlMessageGetter>;
49
+ _loadLocale(locale: LocaleId): Promise<void>;
50
+ /**
51
+ * Inform subscribers that the loader state has changed and they should
52
+ * potentially update to get new values for messages.
53
+ */
54
+ emitChange(): void;
55
+ /**
56
+ * Subscribe to events about when a locale has loaded.
57
+ */
58
+ onChange(callback: () => void): () => void;
59
+ }
60
+ export {};
@@ -0,0 +1,168 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __generator = (this && this.__generator) || function (thisArg, body) {
12
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
13
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
+ function verb(n) { return function (v) { return step([n, v]); }; }
15
+ function step(op) {
16
+ if (f) throw new TypeError("Generator is already executing.");
17
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
+ if (y = 0, t) op = [op[0] & 2, t.value];
20
+ switch (op[0]) {
21
+ case 0: case 1: t = op; break;
22
+ case 4: _.label++; return { value: op[1], done: false };
23
+ case 5: _.label++; y = op[1]; op = [0]; continue;
24
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
+ default:
26
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
+ if (t[2]) _.ops.pop();
31
+ _.trys.pop(); continue;
32
+ }
33
+ op = body.call(thisArg, _);
34
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
+ }
37
+ };
38
+ var __values = (this && this.__values) || function(o) {
39
+ var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
40
+ if (m) return m.call(o);
41
+ if (o && typeof o.length === "number") return {
42
+ next: function () {
43
+ if (o && i >= o.length) o = void 0;
44
+ return { value: o && o[i++], done: !o };
45
+ }
46
+ };
47
+ throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
48
+ };
49
+ Object.defineProperty(exports, "__esModule", { value: true });
50
+ exports.MessageLoader = void 0;
51
+ var message_1 = require("./message");
52
+ var MessageLoader = /** @class */ (function () {
53
+ function MessageLoader(messageKeys, localeImportMap) {
54
+ this.messageKeys = messageKeys;
55
+ this.messages = {};
56
+ this.localeImportMap = localeImportMap;
57
+ this.supportedLocales = Object.keys(localeImportMap);
58
+ this._localeLoadingPromises = {};
59
+ this._parseCache = new Map();
60
+ this._subscribers = new Set();
61
+ this.fallbackMessage = new message_1.InternalIntlMessage('THIS MESSAGE FAILED TO LOAD', 'en-US');
62
+ }
63
+ MessageLoader.prototype.get = function (key, locale) {
64
+ var cacheKey = key + '@' + locale;
65
+ var cachedValue = this._parseCache.get(cacheKey);
66
+ if (cachedValue != null)
67
+ return cachedValue;
68
+ if (this.messages[locale] == null) {
69
+ // Ensure the locale is loaded, if it is supported
70
+ if (this.supportedLocales.includes(locale)) {
71
+ this._loadLocale(locale);
72
+ }
73
+ // Otherwise, fallback to just the key value in the meantime.
74
+ return this.fallbackMessage;
75
+ }
76
+ // Then try to return the loaded message
77
+ if (key in this.messages[locale]) {
78
+ var content = this.messages[locale][key];
79
+ var message = new message_1.InternalIntlMessage(content, locale);
80
+ this._parseCache.set(cacheKey, message);
81
+ return message;
82
+ }
83
+ // And finally throw if there's no match whatsoever
84
+ throw new Error("Requested message ".concat(key, " does not have a value in the requested locale nor the default locale"));
85
+ };
86
+ /**
87
+ * Returns a record mapping the keys this object manages to bound functions
88
+ * for `get` with the that key as the first argument, allowing consumers to
89
+ * just call the function with a locale to retrieve the translated message
90
+ * for that key.
91
+ */
92
+ MessageLoader.prototype.getBinds = function () {
93
+ var _this = this;
94
+ var onChange = this.onChange.bind(this);
95
+ return this.messageKeys.reduce(function (acc, key) {
96
+ var bound = _this.get.bind(_this, key);
97
+ bound.onChange = onChange;
98
+ acc[key] = bound;
99
+ return acc;
100
+ }, {});
101
+ };
102
+ MessageLoader.prototype._loadLocale = function (locale) {
103
+ return __awaiter(this, void 0, void 0, function () {
104
+ var loadStart, loadingPromise, _a, _b, loadEnd;
105
+ return __generator(this, function (_c) {
106
+ switch (_c.label) {
107
+ case 0:
108
+ // Don't re-load a locale that's already in progress.
109
+ if (this._localeLoadingPromises[locale] != null) {
110
+ return [2 /*return*/];
111
+ }
112
+ // Safety check in case the locale map doesn't include a require for the
113
+ // requested locale. Shouldn't happen, but throwing here is much more
114
+ // contextual than whatever error would result otherwise.
115
+ if (this.localeImportMap[locale] == null) {
116
+ throw new Error("Requested to load locale ".concat(locale, ", which should be supported, but no source for translation data was provided."));
117
+ }
118
+ // If the locale is already set in `messages`, then it doesn't need to be loaded again.
119
+ if (this.messages[locale] != null)
120
+ return [2 /*return*/];
121
+ loadStart = performance.now();
122
+ loadingPromise = this.localeImportMap[locale]();
123
+ this._localeLoadingPromises[locale] = loadingPromise;
124
+ _a = this.messages;
125
+ _b = locale;
126
+ return [4 /*yield*/, loadingPromise];
127
+ case 1:
128
+ _a[_b] = (_c.sent()).default;
129
+ delete this._localeLoadingPromises[locale];
130
+ loadEnd = performance.now();
131
+ console.warn("[INTL] Took ".concat(loadEnd - loadStart, "ms to load ").concat(locale, " with ").concat(Object.keys(this.messages[locale]).length, " keys"));
132
+ this.emitChange();
133
+ return [2 /*return*/];
134
+ }
135
+ });
136
+ });
137
+ };
138
+ /**
139
+ * Inform subscribers that the loader state has changed and they should
140
+ * potentially update to get new values for messages.
141
+ */
142
+ MessageLoader.prototype.emitChange = function () {
143
+ var e_1, _a;
144
+ try {
145
+ for (var _b = __values(this._subscribers.values()), _c = _b.next(); !_c.done; _c = _b.next()) {
146
+ var callback = _c.value;
147
+ callback();
148
+ }
149
+ }
150
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
151
+ finally {
152
+ try {
153
+ if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
154
+ }
155
+ finally { if (e_1) throw e_1.error; }
156
+ }
157
+ };
158
+ /**
159
+ * Subscribe to events about when a locale has loaded.
160
+ */
161
+ MessageLoader.prototype.onChange = function (callback) {
162
+ var _this = this;
163
+ this._subscribers.add(callback);
164
+ return function () { return _this._subscribers.delete(callback); };
165
+ };
166
+ return MessageLoader;
167
+ }());
168
+ exports.MessageLoader = MessageLoader;
@@ -0,0 +1,41 @@
1
+ import { Formats, Formatters, MessageFormatPart } from 'intl-messageformat';
2
+ import type { MessageFormatElement } from '@formatjs/icu-messageformat-parser';
3
+ export declare class InternalIntlMessage {
4
+ locale: string;
5
+ message?: string;
6
+ ast: MessageFormatElement[];
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[];
41
+ }