@alxia/i18n 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.
@@ -0,0 +1,472 @@
1
+ # Troubleshooting
2
+
3
+ Each entry is headed by the text you see: the error `createI18n()` throws,
4
+ an error from `tsc`, a line in the log, or — for a trap that prints
5
+ nothing — what the response does that you did not expect.
6
+
7
+ **At start-up**
8
+
9
+ - [`TypeError: language(): the fallback "de" is not supported`](#typeerror-language-the-fallback-de-is-not-supported)
10
+
11
+ **Types**
12
+
13
+ - [`Type '"de"' is not assignable to type '"en" | "fr"'`](#type-de-is-not-assignable-to-type-en--fr)
14
+ - [`Argument of type '"cart.itmes"' is not assignable to parameter of type '"cart.items"'`](#argument-of-type-cartitmes-is-not-assignable-to-parameter-of-type-cartitems)
15
+ - [`Property 't' does not exist on type 'Context<Empty, "/", Empty>'`](#property-t-does-not-exist-on-type-contextempty--empty)
16
+ - [`Type 'Alxia<Empty, Empty, "", never>' is missing the following properties from type 'I18nOptions<Readonly<Record<string, Readonly<Record<string, unknown>>>>, string, BaseContext>': resources, fallback`](#type-alxiaempty-empty--never-is-missing-the-following-properties-from-type-i18noptionsreadonlyrecordstring-readonlyrecordstring-unknown-string-basecontext-resources-fallback)
17
+ - [`Object literal may only specify known properties, and 'supported' does not exist in type 'I18nOptions<…>'`](#object-literal-may-only-specify-known-properties-and-supported-does-not-exist-in-type-i18noptions)
18
+ - [`Cannot invoke an object which is possibly 'undefined'`](#cannot-invoke-an-object-which-is-possibly-undefined)
19
+ - [`t()` accepts any key, typos included](#t-accepts-any-key-typos-included)
20
+ - [`Property 'user' does not exist on type 'BaseContext'`](#property-user-does-not-exist-on-type-basecontext)
21
+ - [`the plugin reads "user", which this app's context does not give: use the plugin that adds it first`](#the-plugin-reads-user-which-this-apps-context-does-not-give-use-the-plugin-that-adds-it-first)
22
+ - [`the plugin reads "user", which this app's context gives with another type`](#the-plugin-reads-user-which-this-apps-context-gives-with-another-type)
23
+ - [`the plugin's resolve reads its context as any: annotate what it reads, or leave it unannotated`](#the-plugins-resolve-reads-its-context-as-any-annotate-what-it-reads-or-leave-it-unannotated)
24
+
25
+ **Messages**
26
+
27
+ - [`The intl string context variable "name" was not provided to the string "Hello {name}"`](#the-intl-string-context-variable-name-was-not-provided-to-the-string-hello-name)
28
+ - [`SyntaxError: MISSING_OTHER_CLAUSE`](#syntaxerror-missing_other_clause)
29
+ - [The page shows `home.title` instead of a message](#the-page-shows-hometitle-instead-of-a-message)
30
+
31
+ **Language**
32
+
33
+ - [`i18n.t()` answers in the fallback before the language is read](#i18nt-answers-in-the-fallback-before-the-language-is-read)
34
+ - [`@nxgt/i18n`'s `translate` answers in English on a German request](#nxgti18ns-translate-answers-in-english-on-a-german-request)
35
+ - [`getLanguage()` answers `en` although the fallback is `fr`](#getlanguage-answers-en-although-the-fallback-is-fr)
36
+ - [A cache serves one language to every visitor](#a-cache-serves-one-language-to-every-visitor)
37
+
38
+ ## At start-up
39
+
40
+ ### `TypeError: language(): the fallback "de" is not supported`
41
+
42
+ **When:** `createI18n()` is called — when the module that builds it is
43
+ imported — with a `fallback` that is not one of `resources`' keys.
44
+
45
+ **Why:** the fallback is the language of every request that names none the
46
+ catalogues have, so it needs a catalogue. The types refuse it when they can
47
+ see the keys of `resources`; they cannot when `fallback` comes from the
48
+ environment through a cast, or `resources` is typed `Record<string, …>`.
49
+ The message is `@alxia/language`'s, which `createI18n()` calls.
50
+
51
+ **Fix:** give the fallback a catalogue, and keep `resources` a literal so
52
+ the compiler checks it next time:
53
+
54
+ ```ts
55
+ import { createI18n } from '@alxia/i18n';
56
+
57
+ const en = { home: { title: 'Welcome' } };
58
+ const de = { home: { title: 'Willkommen' } };
59
+
60
+ createI18n({ resources: { en, de }, fallback: 'de' });
61
+ ```
62
+
63
+ ## Types
64
+
65
+ ### `Type '"de"' is not assignable to type '"en" | "fr"'`
66
+
67
+ ```text
68
+ error TS2322: Type '"de"' is not assignable to type '"en" | "fr"'.
69
+ ```
70
+
71
+ **When:** `createI18n({ resources: { en, fr }, fallback: 'de' })`.
72
+
73
+ **Why:** `fallback` is typed as one of `resources`' keys. This is the
74
+ compile-time form of [the start-up error](#typeerror-language-the-fallback-de-is-not-supported).
75
+
76
+ **Fix:** a fallback with a catalogue:
77
+
78
+ ```ts
79
+ createI18n({ resources: { en, fr }, fallback: 'en' });
80
+ ```
81
+
82
+ ### `Argument of type '"cart.itmes"' is not assignable to parameter of type '"cart.items"'`
83
+
84
+ ```text
85
+ error TS2345: Argument of type '"cart.itmes"' is not assignable to parameter of type '"cart.items"'.
86
+ ```
87
+
88
+ **When:** `t('cart.itmes')` — a typo — or `t('cart.extra')`, a key that
89
+ only another language's catalogue has.
90
+
91
+ **Why:** `t` takes the dotted keys of the **fallback's** catalogue, and no
92
+ other. A key the fallback lacks would answer itself in the fallback's
93
+ language.
94
+
95
+ **Fix:** correct the key, or add it to the fallback's catalogue first:
96
+
97
+ ```ts
98
+ const en = { cart: { items: '{count, plural, one {One item} other {# items}}', extra: 'Nothing' } };
99
+ const fr = { cart: { items: '{count, plural, one {Un article} other {# articles}}', extra: 'Rien' } };
100
+
101
+ const i18n = createI18n({ resources: { en, fr }, fallback: 'en' });
102
+ i18n.t('cart.extra');
103
+ ```
104
+
105
+ ### `Property 't' does not exist on type 'Context<Empty, "/", Empty>'`
106
+
107
+ ```text
108
+ error TS2339: Property 't' does not exist on type 'Context<Empty, "/", Empty>'.
109
+ ```
110
+
111
+ **When:** a route reads `t` — or `language` — but is declared before
112
+ `.use(i18n)`, or in another app or group than the one that uses it.
113
+
114
+ **Why:** the plugin is a route hook: it applies to the routes declared
115
+ after it, at runtime and in the types alike.
116
+
117
+ **Fix:** use the plugin first:
118
+
119
+ ```ts
120
+ const app = alxia()
121
+ .use(i18n)
122
+ .get('/', ({ t, reply }) => reply(200, t('home.title')));
123
+ ```
124
+
125
+ ### `Type 'Alxia<Empty, Empty, "", never>' is missing the following properties from type 'I18nOptions<Readonly<Record<string, Readonly<Record<string, unknown>>>>, string, BaseContext>': resources, fallback`
126
+
127
+ ```text
128
+ error TS2769: No overload matches this call.
129
+ Overload 1 of 2, '(plugin: (app: Alxia<Empty, Empty, "", never>) => Alxia<Empty & LanguageContext<string> & I18nContext<string>, Empty & Prefixed<...>, "", never> & Requiring<...> & { ...; }): Alxia<...> & ... 1 more ... & { ...; }', gave the following error.
130
+ Argument of type '<const C extends Catalogues, const Fallback extends keyof C & string, Ctx extends object = BaseContext>(options: I18nOptions<C, Fallback, Ctx>) => …' is not assignable to parameter of type '(app: Alxia<Empty, Empty, "", never>) => …'.
131
+ Types of parameters 'options' and 'app' are incompatible.
132
+ Type 'Alxia<Empty, Empty, "", never>' is missing the following properties from type 'I18nOptions<Readonly<Record<string, Readonly<Record<string, unknown>>>>, string, BaseContext>': resources, fallback
133
+ ```
134
+
135
+ **When:** `app.use(createI18n)`, without calling it.
136
+
137
+ **Why:** `createI18n` makes the plugin from its options; it is not the
138
+ plugin.
139
+
140
+ **Fix:** call it once, in a module of its own, and use what it returns:
141
+
142
+ ```ts
143
+ export const i18n = createI18n({ resources: { en, fr }, fallback: 'en' });
144
+
145
+ const app = alxia().use(i18n);
146
+ ```
147
+
148
+ ### `Object literal may only specify known properties, and 'supported' does not exist in type 'I18nOptions<…>'`
149
+
150
+ ```text
151
+ error TS2353: Object literal may only specify known properties, and 'supported' does not exist in type 'I18nOptions<{ readonly en: { cart: { items: string; }; }; readonly fr: { cart: { items: string; extra: string; }; }; }, "en", BaseContext>'.
152
+ ```
153
+
154
+ **When:** `createI18n({ resources, fallback: 'en', supported: ['en'] })`,
155
+ the way `@alxia/language` is called.
156
+
157
+ **Why:** the languages supported are `resources`' keys; `createI18n()`
158
+ passes them to `@alxia/language` itself. Through a cast, `supported` is
159
+ ignored.
160
+
161
+ **Fix:** leave it out. To support fewer languages, pass fewer catalogues:
162
+
163
+ ```ts
164
+ createI18n({ resources: { en }, fallback: 'en' });
165
+ ```
166
+
167
+ ### `Cannot invoke an object which is possibly 'undefined'`
168
+
169
+ ```text
170
+ error TS2722: Cannot invoke an object which is possibly 'undefined'.
171
+ ```
172
+
173
+ **When:** an `onError` hook calls `t('…')` from its context.
174
+
175
+ **Why:** `onError` also handles what was thrown before the plugin ran —
176
+ by a hook declared before it — so what the plugin adds is optional there.
177
+
178
+ **Fix:** call it optionally, with an answer for when it is missing:
179
+
180
+ ```ts
181
+ alxia()
182
+ .use(i18n)
183
+ .onError((error, { t, reply }) =>
184
+ error instanceof HttpError && error.status === 404
185
+ ? reply(404, { error: t?.('errors.not-found') ?? 'Not found' })
186
+ : undefined,
187
+ );
188
+ ```
189
+
190
+ Or call the plugin's own `i18n.t()`, which answers in the request's
191
+ language there too, and in the fallback when the error was thrown before
192
+ the language was read.
193
+
194
+ ### `t()` accepts any key, typos included
195
+
196
+ **When:** the catalogues are built at runtime, read with
197
+ `await Bun.file(…).json()`, or annotated
198
+ `Record<string, Record<string, string>>`.
199
+
200
+ **Why:** the keys are read from the fallback catalogue's type. A catalogue
201
+ typed `Record<string, …>` has none to read, so `t` takes any string — and
202
+ a wrong one [answers itself](#the-page-shows-hometitle-instead-of-a-message).
203
+
204
+ **Fix:** declare the catalogues as literals, or import them from JSON
205
+ files, which keeps their keys in the type (`resolveJsonModule` in your
206
+ `tsconfig.json`):
207
+
208
+ ```ts
209
+ import en from './locales/en.json';
210
+ import fr from './locales/fr.json';
211
+
212
+ export const i18n = createI18n({ resources: { en, fr }, fallback: 'en' });
213
+ ```
214
+
215
+ ### `Property 'user' does not exist on type 'BaseContext'`
216
+
217
+ ```text
218
+ error TS2339: Property 'user' does not exist on type 'BaseContext'.
219
+ ```
220
+
221
+ **When:** `resolve` reads something a `derive` or a plugin before the
222
+ i18n plugin added — a user, a session — and its parameter is not
223
+ annotated: `resolve: (ctx) => ctx.user.language`.
224
+
225
+ **Why:** an unannotated `resolve` is typed with the request's `BaseContext`
226
+ — `request`, `url`, `ip`, `pathParams`, `set` — not with what other hooks
227
+ added. The plugin is built before it is used, so it cannot see the app it
228
+ will be used on.
229
+
230
+ **Fix:** annotate the parameter with what it reads; the plugin then
231
+ requires it of the app, before the plugin:
232
+
233
+ ```ts
234
+ const i18n = createI18n({
235
+ resources: { en, fr },
236
+ fallback: 'en',
237
+ resolve: ({ user }: BaseContext & { user: User | null }) => user?.language ?? undefined,
238
+ });
239
+
240
+ alxia().use(auth).use(i18n); // auth derives user
241
+ ```
242
+
243
+ See [Reading the app's context](guide.md#reading-the-apps-context).
244
+
245
+ ### `the plugin reads "user", which this app's context does not give: use the plugin that adds it first`
246
+
247
+ ```text
248
+ error TS2769: No overload matches this call.
249
+ …
250
+ Types of property ''~requires'' are incompatible.
251
+ Type '{ user: User | null; }' is not assignable to type '"the plugin reads \"user\", which this app's context does not give: use the plugin that adds it first"'.
252
+ ```
253
+
254
+ **When:** `resolve` is annotated to read `user` —
255
+ `({ user }: BaseContext & { user: User | null }) => …` — and the plugin is
256
+ used on an app, or in a group, whose context has no `user` at that point:
257
+ `alxia().use(i18n)`, or `use(i18n)` before `use(auth)`.
258
+
259
+ **Why:** an annotated `resolve` makes the plugin require what it reads, and
260
+ `use` checks the app's context against it, so `resolve` never runs without
261
+ it. An app whose `user` has another type is refused too, with
262
+ `the plugin reads "user", which this app's context gives with another type`.
263
+
264
+ **Fix:** use the plugin that adds `user` first, with the type `resolve`
265
+ reads:
266
+
267
+ ```ts
268
+ alxia().use(auth).use(i18n);
269
+ ```
270
+
271
+ More on this message in
272
+ [`@alxia/language`'s troubleshooting](https://github.com/softistx/alxia/blob/develop/packages/language/docs/troubleshooting.md#the-plugin-reads-user-which-this-apps-context-does-not-give-use-the-plugin-that-adds-it-first).
273
+
274
+ ### `the plugin reads "user", which this app's context gives with another type`
275
+
276
+ ```text
277
+ error TS2769: No overload matches this call.
278
+ …
279
+ Type '{ user: User; }' is not assignable to type '"the plugin reads \"user\", which this app's context gives with another type"'.
280
+ ```
281
+
282
+ **When:** the app gives a `user`, but of a type that does not fit the one
283
+ `resolve`'s parameter is annotated with: a `User | null` where `resolve`
284
+ reads `User`, or a user of another shape.
285
+
286
+ **Why:** `use` checks each key the plugin reads against the app's context;
287
+ a narrower type passes, a wider or different one does not.
288
+
289
+ **Fix:** annotate `resolve` with the type the app gives, and handle it
290
+ inside:
291
+
292
+ ```ts
293
+ resolve: ({ user }: { user: User | null }) => user?.language ?? undefined,
294
+ ```
295
+
296
+ More on this message in
297
+ [`@alxia/language`'s troubleshooting](https://github.com/softistx/alxia/blob/develop/packages/language/docs/troubleshooting.md#the-plugin-reads-user-which-this-apps-context-gives-with-another-type).
298
+
299
+ ### `the plugin's resolve reads its context as any: annotate what it reads, or leave it unannotated`
300
+
301
+ ```text
302
+ error TS2769: No overload matches this call.
303
+ …
304
+ Types of property ''~requires'' are incompatible.
305
+ Type '{ readonly '~any': "the plugin's resolve reads its context as any: annotate what it reads, or leave it unannotated"; }' is not assignable to type '"the plugin's resolve reads its context as any: annotate what it reads, or leave it unannotated"'.
306
+ ```
307
+
308
+ **When:** `resolve`'s parameter is annotated `any` —
309
+ `resolve: (ctx: any) => ctx.user.language` — or `Record<string, any>`, and
310
+ the plugin is used, on any app, whatever its context gives.
311
+
312
+ **Why:** an `any` parameter reads any key and says nothing of what it
313
+ reads, so the plugin would require nothing, and an app without a `user`
314
+ would be accepted, and throw on every request. The plugin is refused
315
+ instead.
316
+
317
+ **Fix:** annotate what `resolve` reads —
318
+ `({ user }: BaseContext & { user: User | null }) => user?.language ?? undefined` —
319
+ and use the plugin that adds it first; or leave it unannotated when it
320
+ reads only the request.
321
+
322
+ More on this message in
323
+ [`@alxia/core`'s troubleshooting](https://github.com/softistx/alxia/blob/develop/packages/core/docs/troubleshooting.md#the-plugins--reads-its-context-as-any-annotate-what-it-reads-or-leave-it-unannotated).
324
+
325
+ ## Messages
326
+
327
+ ### `The intl string context variable "name" was not provided to the string "Hello {name}"`
328
+
329
+ **When:** in the log, with the response holding the message unformatted:
330
+ `Hello {name}`. `t('greeting')` was called without the values its message
331
+ names.
332
+
333
+ **Why:** the values of `t` are not typed by the message. A message that
334
+ cannot be formatted is logged with `console.error` — this one with
335
+ `code: "MISSING_VALUE"` — and answered as it is; `t` never throws.
336
+
337
+ **Fix:** pass every value the message names:
338
+
339
+ ```ts
340
+ t('greeting', { name: 'Ada' }); // 'Hello Ada'
341
+ ```
342
+
343
+ ### `SyntaxError: MISSING_OTHER_CLAUSE`
344
+
345
+ **When:** in the log, with the response holding the message unformatted —
346
+ `{count, plural, one {One item}}` — or, for an unclosed brace,
347
+ `SyntaxError: EXPECT_ARGUMENT_CLOSING_BRACE` and `Hello {name`.
348
+
349
+ **Why:** the catalogue holds a message that is not valid ICU. Every
350
+ `plural` and `select` needs an `other` case; every `{` its `}`. It is
351
+ logged and answered as it is, at every call.
352
+
353
+ **Fix:** correct the catalogue:
354
+
355
+ ```ts
356
+ const en = { cart: { items: '{count, plural, one {One item} other {# items}}' } };
357
+ ```
358
+
359
+ ### The page shows `home.title` instead of a message
360
+
361
+ **When:** a response holds a key — `home.title`, `errors.not-found` —
362
+ where a message should be, in some languages and not in others.
363
+
364
+ **Why:** a key the request's catalogue lacks answers **itself**, not the
365
+ fallback's message. Either that catalogue is partial — a language added
366
+ before every message was translated — or it lacks `@nxgt/i18n`'s shared
367
+ keys, which exist only in English and French.
368
+
369
+ **Fix:** fill a partial catalogue from the fallback's, nested
370
+ ([Guide](guide.md#messages) shows a `fill` helper), and give a language
371
+ `@nxgt/i18n` lacks the English shared keys:
372
+
373
+ ```ts
374
+ import { resources as shared } from '@nxgt/i18n';
375
+
376
+ const en = { ...shared.en, home: { title: 'Welcome' } };
377
+ const de = { ...shared.en, home: { title: 'Willkommen' } }; // shared messages in English
378
+
379
+ export const i18n = createI18n({ resources: { en, de }, fallback: 'en' });
380
+ ```
381
+
382
+ ## Language
383
+
384
+ ### `i18n.t()` answers in the fallback before the language is read
385
+
386
+ **When:** a request is in French, but `i18n.t()`, `i18n.language()` or
387
+ `@nxgt/i18n`'s `translate` answers in the fallback's language in an
388
+ `onRequest` hook, an `around` hook declared before `.use(i18n)`, a route
389
+ or `derive` declared before it, or the `onError` hook of an error one of
390
+ those threw.
391
+
392
+ **Why:** the plugin reads the request's language in a route hook, after
393
+ the global hooks and the route hooks declared before it. Until then there
394
+ is none to answer in. From there on — the route, what it calls, its
395
+ `onError` and `onResponse` hooks — every call answers in it.
396
+
397
+ **Fix:** translate after the plugin: declare the routes and hooks that
398
+ translate after `.use(i18n)`, and move what an `onRequest` hook renders
399
+ into a `derive` declared after it:
400
+
401
+ ```ts
402
+ alxia()
403
+ .use(i18n)
404
+ .derive(({ request, reply }) =>
405
+ request.headers.has('x-busy') ? reply(503, { error: i18n.t('errors.service-unavailable') }) : undefined,
406
+ );
407
+ ```
408
+
409
+ ### `@nxgt/i18n`'s `translate` answers in English on a German request
410
+
411
+ **When:** your catalogues add a language — `de` — and `@nxgt/i18n`'s
412
+ `getLanguage()` answers `'en'` on a request in it, and its `translate` and
413
+ the packages that translate through it answer in English.
414
+
415
+ **Why:** `@nxgt/i18n` only speaks the languages of its own catalogues,
416
+ `en` and `fr`. It skips a source that answers another, and falls back to
417
+ `'en'`.
418
+
419
+ **Fix:** translate what the response shows with `t`, whose catalogues are
420
+ yours:
421
+
422
+ ```ts
423
+ const de = { ...shared.en, errors: { ...shared.en.errors, 'not-found': 'Nicht gefunden.' } };
424
+
425
+ const i18n = createI18n({ resources: { en: shared.en, de }, fallback: 'en' });
426
+
427
+ alxia()
428
+ .use(i18n)
429
+ .get('/', ({ t, reply }) => reply(404, { error: t('errors.not-found') })); // ?lang=de → 'Nicht gefunden.'
430
+ ```
431
+
432
+ ### `getLanguage()` answers `en` although the fallback is `fr`
433
+
434
+ **When:** outside a request — at start-up, in a timer, a queue consumer —
435
+ or in a hook [before the language is read](#i18nt-answers-in-the-fallback-before-the-language-is-read),
436
+ `@nxgt/i18n`'s `getLanguage()` answers `'en'` while `i18n.language()`
437
+ answers `'fr'`.
438
+
439
+ **Why:** with no request to read, `@nxgt/i18n` falls back to its own
440
+ fallback, `'en'`. Your `fallback` is `createI18n()`'s, and only
441
+ `i18n.t()` and `i18n.language()` use it.
442
+
443
+ **Fix:** call `i18n.t()` rather than `translate` where there is no request,
444
+ or pass `translate` the language:
445
+
446
+ ```ts
447
+ import { translate } from '@nxgt/i18n';
448
+
449
+ translate('errors.not-found', undefined, 'fr');
450
+ ```
451
+
452
+ ### A cache serves one language to every visitor
453
+
454
+ **When:** behind a CDN, a reverse proxy, or `@alxia/cache`, the first
455
+ visitor's language is served to everyone after.
456
+
457
+ **Why:** the response depends on `Accept-Language` and the language
458
+ cookie. The plugin says so in `Vary`; a cache keyed on the URL alone
459
+ ignores it.
460
+
461
+ **Fix:** give the cache the same headers:
462
+
463
+ ```ts
464
+ alxia()
465
+ .use(i18n)
466
+ .use(cache({ ttl: 60, vary: ['accept-language', 'cookie'] }));
467
+ ```
468
+
469
+ An `onResponse` hook that sets `Vary` with `headers.set` replaces the
470
+ plugin's: see `@alxia/language`'s [troubleshooting](https://github.com/softistx/alxia/blob/develop/packages/language/docs/troubleshooting.md#a-cache-serves-one-language-to-every-visitor),
471
+ which also covers a response in the wrong language — `curl` getting the
472
+ fallback, a `?lang=` that does not stick, a `404` on `/fr/products`.
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@alxia/i18n",
3
+ "version": "0.1.0",
4
+ "description": "Translations for alxia on @nxgt/i18n: t() bound to the request's language, keys typed by your catalogue, ICU messages",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "files": [
10
+ "dist",
11
+ "docs",
12
+ "README.md",
13
+ "package.json",
14
+ "LICENSE"
15
+ ],
16
+ "exports": {
17
+ ".": {
18
+ "types": "./dist/index.d.ts",
19
+ "import": "./dist/index.js",
20
+ "default": "./dist/index.js"
21
+ },
22
+ "./package.json": "./package.json"
23
+ },
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "git+https://github.com/softistx/alxia.git",
27
+ "directory": "packages/i18n"
28
+ },
29
+ "publishConfig": {
30
+ "registry": "https://registry.npmjs.org",
31
+ "access": "public"
32
+ },
33
+ "scripts": {
34
+ "build": "bun run ../../build.ts",
35
+ "test": "bun test src",
36
+ "typecheck": "tsc --noEmit"
37
+ },
38
+ "alxia": {
39
+ "entrypoints": [
40
+ "src/index.ts"
41
+ ]
42
+ },
43
+ "devDependencies": {
44
+ "@alxia/core": "^0.1.0",
45
+ "@alxia/language": "^0.1.0",
46
+ "@nxgt/i18n": "^2.0.0",
47
+ "@types/bun": "^1.4.2"
48
+ },
49
+ "peerDependencies": {
50
+ "@alxia/core": "^0.1.0",
51
+ "@alxia/language": "^0.1.0",
52
+ "@nxgt/i18n": "^2.0.0",
53
+ "typescript": "^6.0.3 || ^7.0.0"
54
+ }
55
+ }