saykit 0.0.0 → 0.1.0
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/README.md +37 -0
- package/dist/runtime.d.mts +207 -0
- package/dist/runtime.mjs +254 -0
- package/package.json +42 -1
package/README.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# saykit
|
|
2
|
+
|
|
3
|
+
> Type-safe i18n library with compile-time macro transforms.
|
|
4
|
+
|
|
5
|
+
The core runtime for [SayKit](https://saykit.js.org). Exports the `Say` class, which stores your locales, loads message catalogues, and formats messages using ICU MessageFormat.
|
|
6
|
+
|
|
7
|
+
You author messages with the `` say`...` `` tagged template (and `say.plural`, `say.ordinal`, `say.select`); a SayKit build-tool plugin rewrites them at build time into small runtime calls.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
pnpm add saykit
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
You will normally also want [`@saykit/config`](https://github.com/k0d13/saykit/tree/main/packages/config) and a build-tool plugin ([`unplugin-saykit`](https://github.com/k0d13/saykit/tree/main/packages/plugin-unplugin) or [`babel-plugin-saykit`](https://github.com/k0d13/saykit/tree/main/packages/plugin-babel)).
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import { Say } from 'saykit';
|
|
21
|
+
import en from './locales/en.po';
|
|
22
|
+
import fr from './locales/fr.po';
|
|
23
|
+
|
|
24
|
+
const say = new Say({
|
|
25
|
+
locales: ['en', 'fr'],
|
|
26
|
+
messages: { en, fr },
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
say.activate('en');
|
|
30
|
+
|
|
31
|
+
say`Hello, ${name}!`;
|
|
32
|
+
say.plural(count, { one: '1 item', other: '# items' });
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Documentation
|
|
36
|
+
|
|
37
|
+
Full guide at [saykit.js.org](https://saykit.js.org).
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
//#region src/types.d.ts
|
|
2
|
+
type Tuple = [any, ...any[]];
|
|
3
|
+
type Disallow<T, K extends PropertyKey> = T & Partial<Record<K, never>>;
|
|
4
|
+
type Awaitable<T> = T | PromiseLike<T>;
|
|
5
|
+
interface NumeralOptions extends Omit<Partial<Record<Intl.LDMLPluralRule, string>>, 'other'> {
|
|
6
|
+
other: string;
|
|
7
|
+
[digit: number]: string;
|
|
8
|
+
}
|
|
9
|
+
interface SelectOptions {
|
|
10
|
+
other: string;
|
|
11
|
+
[match: string | number]: string;
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
14
|
+
//#region src/runtime.d.ts
|
|
15
|
+
declare namespace Say {
|
|
16
|
+
type Messages = {
|
|
17
|
+
[key: string]: string;
|
|
18
|
+
};
|
|
19
|
+
type Loader<Locale extends string> = (locale: Locale) => Messages | Promise<Messages>;
|
|
20
|
+
type Options<Locale extends string, Loader extends Say.Loader<Locale> | undefined> = {
|
|
21
|
+
locales: Locale[];
|
|
22
|
+
} & ({
|
|
23
|
+
messages: Record<Locale, Messages>;
|
|
24
|
+
loader?: Loader;
|
|
25
|
+
} | {
|
|
26
|
+
messages?: Partial<Record<Locale, Messages>>;
|
|
27
|
+
loader: Loader;
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
interface Say {
|
|
31
|
+
/**
|
|
32
|
+
* Define a message.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```ts
|
|
36
|
+
* say`Hello, ${name}!`
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* @remark This is a macro and must be used with the relevant saykit plugin
|
|
40
|
+
*/
|
|
41
|
+
(strings: TemplateStringsArray, ...placeholders: unknown[]): string;
|
|
42
|
+
/**
|
|
43
|
+
* Provide a custom id or context for the message, the latter used to disambiguate
|
|
44
|
+
* identical strings that have different meanings depending on usage.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```ts
|
|
48
|
+
* say({ context: 'direction' })`Right`
|
|
49
|
+
* say({ context: 'correctness' })`Right`
|
|
50
|
+
* ```
|
|
51
|
+
*
|
|
52
|
+
* @param descriptor Object containing optional `id` and `context` properties
|
|
53
|
+
* @remark This is a macro and must be used with the relevant saykit plugin
|
|
54
|
+
*/
|
|
55
|
+
(descriptor: {
|
|
56
|
+
id?: string;
|
|
57
|
+
context?: string;
|
|
58
|
+
}): Say;
|
|
59
|
+
}
|
|
60
|
+
type ReadonlySay<Locale extends string = string, Loader extends Say.Loader<Locale> | undefined = Say.Loader<Locale> | undefined> = Say<Locale, Loader> & {
|
|
61
|
+
activate: never;
|
|
62
|
+
load: never;
|
|
63
|
+
assign: never;
|
|
64
|
+
};
|
|
65
|
+
declare class Say<Locale extends string = string, Loader extends Say.Loader<Locale> | undefined = Say.Loader<Locale> | undefined> {
|
|
66
|
+
#private;
|
|
67
|
+
constructor(options: Say.Options<Locale, Loader>);
|
|
68
|
+
/**
|
|
69
|
+
* The currently active locale.
|
|
70
|
+
*
|
|
71
|
+
* @throws If no locale is active
|
|
72
|
+
*/
|
|
73
|
+
get locale(): Locale;
|
|
74
|
+
/**
|
|
75
|
+
* All available messages mapped by locale.
|
|
76
|
+
*
|
|
77
|
+
* @throws If no locale is active
|
|
78
|
+
* @throws If no messages are available for the active locale
|
|
79
|
+
*/
|
|
80
|
+
get messages(): Say.Messages;
|
|
81
|
+
/**
|
|
82
|
+
* All available locales.
|
|
83
|
+
*/
|
|
84
|
+
get locales(): Locale[];
|
|
85
|
+
/**
|
|
86
|
+
* Loads messages for the given locales.
|
|
87
|
+
* If no locales are provided, all available locales are loaded.
|
|
88
|
+
* Requires a {@link Say.Loader} to be provided.
|
|
89
|
+
* If `loader` returns a promise, so will this method.
|
|
90
|
+
*
|
|
91
|
+
* @param locales Locales to load messages for, defaults to {@link Say.locales}
|
|
92
|
+
* @returns This
|
|
93
|
+
*/
|
|
94
|
+
load(...locales: Locale[]): this | Promise<this>;
|
|
95
|
+
/**
|
|
96
|
+
* Manually bulk assign messages.
|
|
97
|
+
*
|
|
98
|
+
* @param messages Messages map to assign
|
|
99
|
+
*/
|
|
100
|
+
assign(messages: Partial<Record<Locale, Say.Messages>>): this;
|
|
101
|
+
/**
|
|
102
|
+
* Manually assign messages to a locale.
|
|
103
|
+
*
|
|
104
|
+
* @param locale Locale to assign messages to
|
|
105
|
+
* @param messages Messages to assign
|
|
106
|
+
* @returns This
|
|
107
|
+
*/
|
|
108
|
+
assign(locale: Locale, messages: Say.Messages): this;
|
|
109
|
+
/**
|
|
110
|
+
* Set the active locale.
|
|
111
|
+
*
|
|
112
|
+
* @param locale Locale to set
|
|
113
|
+
* @returns This
|
|
114
|
+
* @throws If locale is not available
|
|
115
|
+
*/
|
|
116
|
+
activate(locale: Locale): this;
|
|
117
|
+
/**
|
|
118
|
+
* Creates a clone of the Say instance, with the same locales and messages.
|
|
119
|
+
*
|
|
120
|
+
* @returns A clone of the Say instance
|
|
121
|
+
*/
|
|
122
|
+
clone(): this;
|
|
123
|
+
/**
|
|
124
|
+
* Make this `Say` instance immutable.
|
|
125
|
+
*/
|
|
126
|
+
freeze(): ReadonlySay<Locale, Loader>;
|
|
127
|
+
[Symbol.iterator](): Generator<[ReadonlySay<Locale, Loader>, Locale], void, unknown>;
|
|
128
|
+
/**
|
|
129
|
+
* Matches the best locale from a list of guesses.
|
|
130
|
+
*
|
|
131
|
+
* @param guesses List of locale guesses
|
|
132
|
+
*
|
|
133
|
+
* @returns The best matching locale, or the first locale if no matches are found
|
|
134
|
+
*/
|
|
135
|
+
match(...guesses: (string | string[])[]): Locale;
|
|
136
|
+
/**
|
|
137
|
+
* Get the translation for a descriptor.
|
|
138
|
+
*
|
|
139
|
+
* @param descriptor Descriptor to get the translation for
|
|
140
|
+
* @returns The translation string for the descriptor
|
|
141
|
+
* @throws If no locale is active
|
|
142
|
+
* @throws If no messages are available for the active locale
|
|
143
|
+
* @throws If descriptor id is not found
|
|
144
|
+
*/
|
|
145
|
+
call(descriptor: {
|
|
146
|
+
id: string;
|
|
147
|
+
[match: string | number]: unknown;
|
|
148
|
+
}): string;
|
|
149
|
+
/**
|
|
150
|
+
* Define a pluralised message.
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* ```ts
|
|
154
|
+
* say.plural(count, {
|
|
155
|
+
* one: 'You have 1 item',
|
|
156
|
+
* other: 'You have # items',
|
|
157
|
+
* })
|
|
158
|
+
* ```
|
|
159
|
+
*
|
|
160
|
+
* The `#` symbol inside options is replaced with the numeric value.
|
|
161
|
+
* @param _ Number to determine the plural form of
|
|
162
|
+
* @param options Pluralisation rules keyed by CLDR categories or specific numbers
|
|
163
|
+
* @returns The plural form of the number
|
|
164
|
+
* @remark This is a macro and must be used with the relevant saykit plugin
|
|
165
|
+
*/
|
|
166
|
+
plural(_: number, options: Disallow<NumeralOptions, 'id' | 'context'>): string;
|
|
167
|
+
/**
|
|
168
|
+
* Define an ordinal message (e.g. "1st", "2nd", "3rd").
|
|
169
|
+
* The `#` symbol inside options is replaced with the numeric value.
|
|
170
|
+
*
|
|
171
|
+
* @example
|
|
172
|
+
* ```ts
|
|
173
|
+
* say.ordinal(position, {
|
|
174
|
+
* 1: '#st',
|
|
175
|
+
* 2: '#nd',
|
|
176
|
+
* 3: '#rd',
|
|
177
|
+
* other: '#th',
|
|
178
|
+
* })
|
|
179
|
+
* ```
|
|
180
|
+
*
|
|
181
|
+
* @param _ Number to determine the ordinal form of
|
|
182
|
+
* @param options Ordinal rules keyed by CLDR categories or specific numbers
|
|
183
|
+
* @returns The ordinal form of the number
|
|
184
|
+
* @remark This is a macro and must be used with the relevant saykit plugin
|
|
185
|
+
*/
|
|
186
|
+
ordinal(_: number, options: Disallow<NumeralOptions, 'id' | 'context'>): string;
|
|
187
|
+
/**
|
|
188
|
+
* Define a select message, useful for handling gender, status, or other categories.
|
|
189
|
+
*
|
|
190
|
+
* @example
|
|
191
|
+
* ```ts
|
|
192
|
+
* say.select(gender, {
|
|
193
|
+
* male: 'He',
|
|
194
|
+
* female: 'She',
|
|
195
|
+
* other: 'They',
|
|
196
|
+
* })
|
|
197
|
+
* ```
|
|
198
|
+
*
|
|
199
|
+
* @param _ Selector value to determine which option is chosen
|
|
200
|
+
* @param options A mapping of possible selector values to message strings
|
|
201
|
+
* @returns The select form of the value
|
|
202
|
+
* @remark This is a macro and must be used with the relevant saykit plugin
|
|
203
|
+
*/
|
|
204
|
+
select(_: string, options: Disallow<SelectOptions, 'id' | 'context'>): string;
|
|
205
|
+
}
|
|
206
|
+
//#endregion
|
|
207
|
+
export { Awaitable, Disallow, NumeralOptions, ReadonlySay, Say, SelectOptions, Tuple };
|
package/dist/runtime.mjs
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { mf1ToMessage } from "@messageformat/icu-messageformat-1";
|
|
2
|
+
//#region \0@oxc-project+runtime@0.127.0/helpers/checkPrivateRedeclaration.js
|
|
3
|
+
function _checkPrivateRedeclaration(e, t) {
|
|
4
|
+
if (t.has(e)) throw new TypeError("Cannot initialize the same private elements twice on an object");
|
|
5
|
+
}
|
|
6
|
+
//#endregion
|
|
7
|
+
//#region \0@oxc-project+runtime@0.127.0/helpers/classPrivateMethodInitSpec.js
|
|
8
|
+
function _classPrivateMethodInitSpec(e, a) {
|
|
9
|
+
_checkPrivateRedeclaration(e, a), a.add(e);
|
|
10
|
+
}
|
|
11
|
+
//#endregion
|
|
12
|
+
//#region \0@oxc-project+runtime@0.127.0/helpers/classPrivateFieldInitSpec.js
|
|
13
|
+
function _classPrivateFieldInitSpec(e, t, a) {
|
|
14
|
+
_checkPrivateRedeclaration(e, t), t.set(e, a);
|
|
15
|
+
}
|
|
16
|
+
//#endregion
|
|
17
|
+
//#region \0@oxc-project+runtime@0.127.0/helpers/assertClassBrand.js
|
|
18
|
+
function _assertClassBrand(e, t, n) {
|
|
19
|
+
if ("function" == typeof e ? e === t : e.has(t)) return arguments.length < 3 ? t : n;
|
|
20
|
+
throw new TypeError("Private element is not present on this object");
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region \0@oxc-project+runtime@0.127.0/helpers/classPrivateFieldSet2.js
|
|
24
|
+
function _classPrivateFieldSet2(s, a, r) {
|
|
25
|
+
return s.set(_assertClassBrand(s, a), r), r;
|
|
26
|
+
}
|
|
27
|
+
//#endregion
|
|
28
|
+
//#region \0@oxc-project+runtime@0.127.0/helpers/classPrivateFieldGet2.js
|
|
29
|
+
function _classPrivateFieldGet2(s, a) {
|
|
30
|
+
return s.get(_assertClassBrand(s, a));
|
|
31
|
+
}
|
|
32
|
+
//#endregion
|
|
33
|
+
//#region src/runtime.ts
|
|
34
|
+
let _Symbol$iterator, _Symbol$for;
|
|
35
|
+
var _locales = /* @__PURE__ */ new WeakMap();
|
|
36
|
+
var _loader = /* @__PURE__ */ new WeakMap();
|
|
37
|
+
var _messages = /* @__PURE__ */ new WeakMap();
|
|
38
|
+
var _formats = /* @__PURE__ */ new WeakMap();
|
|
39
|
+
var _active = /* @__PURE__ */ new WeakMap();
|
|
40
|
+
var _Say_brand = /* @__PURE__ */ new WeakSet();
|
|
41
|
+
_Symbol$iterator = Symbol.iterator;
|
|
42
|
+
_Symbol$for = Symbol.for("nodejs.util.inspect.custom");
|
|
43
|
+
var Say = class Say {
|
|
44
|
+
constructor(options) {
|
|
45
|
+
_classPrivateMethodInitSpec(this, _Say_brand);
|
|
46
|
+
_classPrivateFieldInitSpec(this, _locales, void 0);
|
|
47
|
+
_classPrivateFieldInitSpec(this, _loader, void 0);
|
|
48
|
+
_classPrivateFieldInitSpec(this, _messages, void 0);
|
|
49
|
+
_classPrivateFieldInitSpec(this, _formats, void 0);
|
|
50
|
+
_classPrivateFieldInitSpec(this, _active, void 0);
|
|
51
|
+
_classPrivateFieldSet2(_locales, this, options.locales);
|
|
52
|
+
_classPrivateFieldSet2(_loader, this, options.loader);
|
|
53
|
+
_classPrivateFieldSet2(_messages, this, /* @__PURE__ */ new Map());
|
|
54
|
+
_classPrivateFieldSet2(_formats, this, /* @__PURE__ */ new Map());
|
|
55
|
+
if (options.messages) this.assign(options.messages);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* The currently active locale.
|
|
59
|
+
*
|
|
60
|
+
* @throws If no locale is active
|
|
61
|
+
*/
|
|
62
|
+
get locale() {
|
|
63
|
+
if (!_classPrivateFieldGet2(_active, this)) throw new Error("No active locale");
|
|
64
|
+
return _classPrivateFieldGet2(_active, this);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* All available messages mapped by locale.
|
|
68
|
+
*
|
|
69
|
+
* @throws If no locale is active
|
|
70
|
+
* @throws If no messages are available for the active locale
|
|
71
|
+
*/
|
|
72
|
+
get messages() {
|
|
73
|
+
if (!_classPrivateFieldGet2(_messages, this).has(this.locale)) throw new Error("No messages loaded for locale");
|
|
74
|
+
return _classPrivateFieldGet2(_messages, this).get(this.locale);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* All available locales.
|
|
78
|
+
*/
|
|
79
|
+
get locales() {
|
|
80
|
+
return _classPrivateFieldGet2(_locales, this);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Loads messages for the given locales.
|
|
84
|
+
* If no locales are provided, all available locales are loaded.
|
|
85
|
+
* Requires a {@link Say.Loader} to be provided.
|
|
86
|
+
* If `loader` returns a promise, so will this method.
|
|
87
|
+
*
|
|
88
|
+
* @param locales Locales to load messages for, defaults to {@link Say.locales}
|
|
89
|
+
* @returns This
|
|
90
|
+
*/
|
|
91
|
+
load(...locales) {
|
|
92
|
+
if (Object.isFrozen(this)) throw new Error("Cannot load messages on a frozen Say");
|
|
93
|
+
if (locales.length === 0) locales = _classPrivateFieldGet2(_locales, this);
|
|
94
|
+
const tasks = [];
|
|
95
|
+
for (const locale of locales) {
|
|
96
|
+
if (_classPrivateFieldGet2(_messages, this).has(locale)) continue;
|
|
97
|
+
if (!_classPrivateFieldGet2(_loader, this)) throw new Error("No loader provided, cannot load messages");
|
|
98
|
+
const result = _classPrivateFieldGet2(_loader, this).call(this, locale);
|
|
99
|
+
if (result instanceof Promise) {
|
|
100
|
+
const task = result.then((m) => this.assign(locale, m));
|
|
101
|
+
tasks.push(task);
|
|
102
|
+
} else this.assign(locale, result);
|
|
103
|
+
}
|
|
104
|
+
return tasks.length > 0 ? Promise.all(tasks).then(() => this) : this;
|
|
105
|
+
}
|
|
106
|
+
assign(localeOrMessages, maybeMessages) {
|
|
107
|
+
if (Object.isFrozen(this)) throw new Error("Cannot assign messages on a frozen Say");
|
|
108
|
+
if (typeof localeOrMessages === "string") _classPrivateFieldGet2(_messages, this).set(localeOrMessages, maybeMessages);
|
|
109
|
+
else for (const locale in localeOrMessages) _classPrivateFieldGet2(_messages, this).set(locale, localeOrMessages[locale]);
|
|
110
|
+
return this;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Set the active locale.
|
|
114
|
+
*
|
|
115
|
+
* @param locale Locale to set
|
|
116
|
+
* @returns This
|
|
117
|
+
* @throws If locale is not available
|
|
118
|
+
*/
|
|
119
|
+
activate(locale) {
|
|
120
|
+
if (Object.isFrozen(this)) throw new Error("Cannot activate locale on a frozen Say");
|
|
121
|
+
if (!_classPrivateFieldGet2(_messages, this).has(locale)) throw new Error("No messages loaded for locale");
|
|
122
|
+
_classPrivateFieldSet2(_active, this, locale);
|
|
123
|
+
return this;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Creates a clone of the Say instance, with the same locales and messages.
|
|
127
|
+
*
|
|
128
|
+
* @returns A clone of the Say instance
|
|
129
|
+
*/
|
|
130
|
+
clone() {
|
|
131
|
+
const copy = new Say({
|
|
132
|
+
locales: _classPrivateFieldGet2(_locales, this),
|
|
133
|
+
messages: Object.fromEntries(_classPrivateFieldGet2(_messages, this)),
|
|
134
|
+
loader: _classPrivateFieldGet2(_loader, this)
|
|
135
|
+
});
|
|
136
|
+
_classPrivateFieldSet2(_active, copy, _classPrivateFieldGet2(_active, this));
|
|
137
|
+
return copy;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Make this `Say` instance immutable.
|
|
141
|
+
*/
|
|
142
|
+
freeze() {
|
|
143
|
+
return Object.freeze(this);
|
|
144
|
+
}
|
|
145
|
+
*[_Symbol$iterator]() {
|
|
146
|
+
for (const l of _classPrivateFieldGet2(_locales, this)) yield [this.clone().activate(l).freeze(), l];
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Matches the best locale from a list of guesses.
|
|
150
|
+
*
|
|
151
|
+
* @param guesses List of locale guesses
|
|
152
|
+
*
|
|
153
|
+
* @returns The best matching locale, or the first locale if no matches are found
|
|
154
|
+
*/
|
|
155
|
+
match(...guesses) {
|
|
156
|
+
const flat = guesses.flat();
|
|
157
|
+
if (flat.length === 0) return _classPrivateFieldGet2(_locales, this)[0];
|
|
158
|
+
for (const guess of flat) {
|
|
159
|
+
if (_classPrivateFieldGet2(_locales, this).includes(guess)) return guess;
|
|
160
|
+
const prefix = guess.split("-")[0];
|
|
161
|
+
if (!prefix) continue;
|
|
162
|
+
const match = _classPrivateFieldGet2(_locales, this).find((l) => l.startsWith(prefix));
|
|
163
|
+
if (match) return match;
|
|
164
|
+
}
|
|
165
|
+
return _classPrivateFieldGet2(_locales, this)[0];
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Get the translation for a descriptor.
|
|
169
|
+
*
|
|
170
|
+
* @param descriptor Descriptor to get the translation for
|
|
171
|
+
* @returns The translation string for the descriptor
|
|
172
|
+
* @throws If no locale is active
|
|
173
|
+
* @throws If no messages are available for the active locale
|
|
174
|
+
* @throws If descriptor id is not found
|
|
175
|
+
*/
|
|
176
|
+
call(descriptor) {
|
|
177
|
+
return _assertClassBrand(_Say_brand, this, _call).call(this, this.locale, this.messages, descriptor);
|
|
178
|
+
}
|
|
179
|
+
[_Symbol$for](_depth, context, inspect) {
|
|
180
|
+
if (_classPrivateFieldGet2(_active, this)) return `${this.constructor.name}<${inspect(_classPrivateFieldGet2(_active, this), context)}> {}`;
|
|
181
|
+
else return `${this.constructor.name} {}`;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Define a pluralised message.
|
|
185
|
+
*
|
|
186
|
+
* @example
|
|
187
|
+
* ```ts
|
|
188
|
+
* say.plural(count, {
|
|
189
|
+
* one: 'You have 1 item',
|
|
190
|
+
* other: 'You have # items',
|
|
191
|
+
* })
|
|
192
|
+
* ```
|
|
193
|
+
*
|
|
194
|
+
* The `#` symbol inside options is replaced with the numeric value.
|
|
195
|
+
* @param _ Number to determine the plural form of
|
|
196
|
+
* @param options Pluralisation rules keyed by CLDR categories or specific numbers
|
|
197
|
+
* @returns The plural form of the number
|
|
198
|
+
* @remark This is a macro and must be used with the relevant saykit plugin
|
|
199
|
+
*/
|
|
200
|
+
plural(_, options) {
|
|
201
|
+
throw new Error("'Say#plural' is a macro and must be used with the relevant saykit plugin");
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Define an ordinal message (e.g. "1st", "2nd", "3rd").
|
|
205
|
+
* The `#` symbol inside options is replaced with the numeric value.
|
|
206
|
+
*
|
|
207
|
+
* @example
|
|
208
|
+
* ```ts
|
|
209
|
+
* say.ordinal(position, {
|
|
210
|
+
* 1: '#st',
|
|
211
|
+
* 2: '#nd',
|
|
212
|
+
* 3: '#rd',
|
|
213
|
+
* other: '#th',
|
|
214
|
+
* })
|
|
215
|
+
* ```
|
|
216
|
+
*
|
|
217
|
+
* @param _ Number to determine the ordinal form of
|
|
218
|
+
* @param options Ordinal rules keyed by CLDR categories or specific numbers
|
|
219
|
+
* @returns The ordinal form of the number
|
|
220
|
+
* @remark This is a macro and must be used with the relevant saykit plugin
|
|
221
|
+
*/
|
|
222
|
+
ordinal(_, options) {
|
|
223
|
+
throw new Error("'Say#ordinal' is a macro and must be used with the relevant saykit plugin");
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Define a select message, useful for handling gender, status, or other categories.
|
|
227
|
+
*
|
|
228
|
+
* @example
|
|
229
|
+
* ```ts
|
|
230
|
+
* say.select(gender, {
|
|
231
|
+
* male: 'He',
|
|
232
|
+
* female: 'She',
|
|
233
|
+
* other: 'They',
|
|
234
|
+
* })
|
|
235
|
+
* ```
|
|
236
|
+
*
|
|
237
|
+
* @param _ Selector value to determine which option is chosen
|
|
238
|
+
* @param options A mapping of possible selector values to message strings
|
|
239
|
+
* @returns The select form of the value
|
|
240
|
+
* @remark This is a macro and must be used with the relevant saykit plugin
|
|
241
|
+
*/
|
|
242
|
+
select(_, options) {
|
|
243
|
+
throw new Error("'Say#select' is a macro and must be used with the relevant saykit plugin");
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
function _call(locale, messages, descriptor) {
|
|
247
|
+
const message = messages[descriptor.id];
|
|
248
|
+
if (typeof message !== "string") throw new Error(`Message for ${descriptor.id} is not a string`);
|
|
249
|
+
const key = `${locale}:${descriptor.id}`;
|
|
250
|
+
const format = _classPrivateFieldGet2(_formats, this).get(key) ?? _classPrivateFieldGet2(_formats, this).set(key, mf1ToMessage(locale, message)).get(key);
|
|
251
|
+
return String(format.format(descriptor));
|
|
252
|
+
}
|
|
253
|
+
//#endregion
|
|
254
|
+
export { Say };
|
package/package.json
CHANGED
|
@@ -1,4 +1,45 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "saykit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Type-safe i18n library with compile-time macro transforms",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"i18n",
|
|
7
|
+
"icu",
|
|
8
|
+
"internationalization",
|
|
9
|
+
"localization",
|
|
10
|
+
"macros",
|
|
11
|
+
"typescript"
|
|
12
|
+
],
|
|
13
|
+
"homepage": "https://github.com/k0d13/saykit#readme",
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/k0d13/saykit/issues"
|
|
16
|
+
},
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/k0d13/saykit.git",
|
|
21
|
+
"directory": "packages/integration"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"!dist/**/*.map"
|
|
26
|
+
],
|
|
27
|
+
"type": "module",
|
|
28
|
+
"exports": {
|
|
29
|
+
".": {
|
|
30
|
+
"types": "./dist/runtime.d.mts",
|
|
31
|
+
"default": "./dist/runtime.mjs"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public",
|
|
36
|
+
"provenance": true
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@messageformat/icu-messageformat-1": "^0.12.0"
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"check": "tsc --noEmit",
|
|
43
|
+
"build": "tsdown"
|
|
44
|
+
}
|
|
4
45
|
}
|