@nxgt/i18n 1.0.1 → 1.0.3
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 +60 -27
- package/dist/index.js +3 -3
- package/dist/index.js.map +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,39 +1,72 @@
|
|
|
1
1
|
# @nxgt/i18n
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
`LocaleKey`/`FlatObject`/`Path` types, and the base resource bundle
|
|
7
|
-
(`common`, `errors`, `validation`, `zod`) that every service merges into its own.
|
|
3
|
+
Message catalogues and ICU formatting (`intl-messageformat`) for every service,
|
|
4
|
+
plus the `LocaleKey` type that `@nxgt/shared-exceptions` uses to type the
|
|
5
|
+
message of a thrown error.
|
|
8
6
|
|
|
9
|
-
|
|
7
|
+
An error message is a **key**, not a sentence: `CustomException` carries
|
|
8
|
+
`'users.errors.not-found'` and the boundary that renders it decides the
|
|
9
|
+
language. That is why `LocaleKey` lives this low in the layering.
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
`apps/storex/storex-ui`) do **not** use this package. They share their i18n
|
|
13
|
-
mechanism through `@nxgt/material/i18n`, which adds the React side this package
|
|
14
|
-
deliberately has none of: an `I18nProvider` holding the language in one context
|
|
15
|
-
so a `changeLanguage()` re-renders every consumer, and `createTypedTranslation`
|
|
16
|
-
to build each app's typed `useTranslation()`. Do not add a frontend consumer
|
|
17
|
-
here — extend `@nxgt/material/i18n` instead.
|
|
11
|
+
## Install
|
|
18
12
|
|
|
19
|
-
|
|
13
|
+
```bash
|
|
14
|
+
bun add @nxgt/i18n
|
|
15
|
+
```
|
|
20
16
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
- `packages/shared-exceptions`, `packages/shared-hono`, `packages/shared-mongo`,
|
|
25
|
-
`packages/shared-storage`
|
|
17
|
+
Public on npmjs; no token needed to install. TypeScript is a peer, pinned to
|
|
18
|
+
`^6.0.3` across every `@nxgt/*` package — the set is unsatisfiable if one of
|
|
19
|
+
them widens it.
|
|
26
20
|
|
|
27
|
-
|
|
28
|
-
package's base bundle:
|
|
21
|
+
## Usage
|
|
29
22
|
|
|
30
23
|
```ts
|
|
31
|
-
import {
|
|
24
|
+
import {
|
|
25
|
+
createTranslator,
|
|
26
|
+
getLanguage,
|
|
27
|
+
translate,
|
|
28
|
+
FALLBACK_LANGUAGE,
|
|
29
|
+
SUPPORTED_LANGUAGES,
|
|
30
|
+
LANGUAGE_KEY,
|
|
31
|
+
} from '@nxgt/i18n';
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
en: { ...en, ...shared.en },
|
|
35
|
-
fr: { ...fr, ...shared.fr },
|
|
36
|
-
};
|
|
33
|
+
translate('errors.not-found');
|
|
37
34
|
|
|
38
|
-
|
|
35
|
+
const t = createTranslator(myCatalogues, getLanguage);
|
|
36
|
+
t('users.greeting', { name });
|
|
39
37
|
```
|
|
38
|
+
|
|
39
|
+
`translate` is bound to this package's catalogues (`en` and `fr`, under
|
|
40
|
+
`errors`, `common`, `validation`, `zod`). Pass your own catalogues to
|
|
41
|
+
`createTranslator` when a service has messages this package does not. A missing
|
|
42
|
+
key returns the key itself, not a throw.
|
|
43
|
+
|
|
44
|
+
`LocaleKey` is `keyof` the flattened English catalogue — that is the type
|
|
45
|
+
`CustomException.message` uses. `Language` is `'en' | 'fr'`. `resources`,
|
|
46
|
+
`en` and `fr` are the catalogues themselves. Shared keys include
|
|
47
|
+
`errors.not-found`, `errors.unauthenticated`, `errors.insufficient-permissions`
|
|
48
|
+
and `errors.service-unavailable`.
|
|
49
|
+
|
|
50
|
+
## Where the language comes from
|
|
51
|
+
|
|
52
|
+
`getLanguage()` asks, most specific first:
|
|
53
|
+
|
|
54
|
+
1. the Hono request context (`LANGUAGE_KEY`, `'language'`)
|
|
55
|
+
2. `localStorage.language`, when that object exists
|
|
56
|
+
3. `FALLBACK_LANGUAGE` (`'en'`)
|
|
57
|
+
|
|
58
|
+
The two consuming monorepos had forked exactly this — one read the request, the
|
|
59
|
+
other read `localStorage` — and neither could run where the other did. Asking
|
|
60
|
+
both serves either without a caller changing anything. Pass a provider (a
|
|
61
|
+
function or a `Language` literal) to `createTranslator` if you want one source
|
|
62
|
+
and not the other.
|
|
63
|
+
|
|
64
|
+
`SUPPORTED_LANGUAGES` is derived from the catalogue keys, not typed out.
|
|
65
|
+
|
|
66
|
+
## Things that bite
|
|
67
|
+
|
|
68
|
+
- **Messages are keys.** Rendering at the throw site (or translating in a
|
|
69
|
+
service) freezes the language of whoever threw, not of whoever reads.
|
|
70
|
+
- **ICU formatting failures are swallowed** (`console.error`) and the
|
|
71
|
+
unformatted message is returned. A bad `{name}` in a catalogue is a log
|
|
72
|
+
line, not a 500.
|
package/dist/index.js
CHANGED
|
@@ -474,10 +474,10 @@ function getLanguage() {
|
|
|
474
474
|
}
|
|
475
475
|
return FALLBACK_LANGUAGE;
|
|
476
476
|
}
|
|
477
|
-
function createTranslator(
|
|
477
|
+
function createTranslator(resources = {}, localeProvider = getLanguage) {
|
|
478
478
|
return (key, context = undefined, provideLanguage = localeProvider) => {
|
|
479
479
|
const language = typeof provideLanguage === "function" ? provideLanguage() : provideLanguage;
|
|
480
|
-
let message = _.get(
|
|
480
|
+
let message = _.get(resources[language], key) ?? key;
|
|
481
481
|
try {
|
|
482
482
|
message = new IntlMessageFormat(message, language).format(context);
|
|
483
483
|
} catch (e) {
|
|
@@ -497,5 +497,5 @@ export {
|
|
|
497
497
|
translate
|
|
498
498
|
};
|
|
499
499
|
|
|
500
|
-
//# debugId=
|
|
500
|
+
//# debugId=9824428B699DC6DF64756E2164756E21
|
|
501
501
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"import { resources } from './resources';\nimport type { Language } from './types';\n\nexport const LANGUAGE_KEY = 'language';\n\nexport const SUPPORTED_LANGUAGES = Object.keys(resources) as Language[];\n\nexport const FALLBACK_LANGUAGE: Language = 'en';\n",
|
|
9
9
|
"import { tryGetContext } from 'hono/context-storage';\nimport { IntlMessageFormat } from 'intl-messageformat';\nimport _ from 'lodash';\nimport { FALLBACK_LANGUAGE, LANGUAGE_KEY, SUPPORTED_LANGUAGES } from './consts';\nimport { resources } from './resources';\nimport type {\n\tLanguage,\n\tLanguageProvider,\n\tLocaleKey,\n\tTranslationContext,\n} from './types';\n\n/**\n * Where the language comes from when the caller does not say.\n *\n * The two repositories had forked exactly this: sellix-monorepo read\n * `localStorage`, nxgt-federation read the Hono request context, and neither\n * could run where the other did — federation's fell back to `'en'` outside a\n * request, sellix's knew nothing about one. Asking both, most specific first,\n * serves either without a caller changing anything.\n *\n * `createTranslator` still takes a provider, so a caller that wants one source\n * and not the other passes it.\n */\nexport function getLanguage(): Language {\n\tconst fromRequest: unknown = tryGetContext()?.get(LANGUAGE_KEY as never);\n\tif (\n\t\ttypeof fromRequest === 'string' &&\n\t\tSUPPORTED_LANGUAGES.includes(fromRequest as Language)\n\t) {\n\t\treturn fromRequest as Language;\n\t}\n\tif (typeof localStorage !== 'undefined') {\n\t\tconst stored = localStorage.getItem(LANGUAGE_KEY);\n\t\tif (SUPPORTED_LANGUAGES.includes(stored as Language)) {\n\t\t\treturn stored as Language;\n\t\t}\n\t}\n\treturn FALLBACK_LANGUAGE;\n}\n\nexport function createTranslator<K extends string = LocaleKey>(\n\tresources: Record<string, any> = {},\n\tlocaleProvider: LanguageProvider = getLanguage,\n) {\n\treturn (\n\t\tkey: K,\n\t\tcontext: TranslationContext = undefined,\n\t\tprovideLanguage: LanguageProvider = localeProvider,\n\t): string => {\n\t\tconst language =\n\t\t\ttypeof provideLanguage === 'function'\n\t\t\t\t? provideLanguage()\n\t\t\t\t: provideLanguage;\n\t\tlet message: string = _.get(resources[language], key) ?? key;\n\t\ttry {\n\t\t\tmessage = new IntlMessageFormat(message, language).format(context);\n\t\t} catch (e) {\n\t\t\tconsole.error(e);\n\t\t}\n\n\t\treturn message;\n\t};\n}\n\nexport const translate = createTranslator(resources);\n"
|
|
10
10
|
],
|
|
11
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAKO,IAAM,KAAK;AAAA,KACd;AAAA,EACH;AAAA,EACA;AAAA,EACA;AACD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACLO,IAAM,KAAK;AAAA,KACd;AAAA,EACH;AAAA,EACA;AAAA,EACA;AACD;;;ACPO,IAAM,YAAY;AAAA,EACxB;AAAA,EACA;AACD;;;ACHO,IAAM,eAAe;AAErB,IAAM,sBAAsB,OAAO,KAAK,SAAS;AAEjD,IAAM,oBAA8B;;ACP3C;AACA;AACA;AAsBO,SAAS,WAAW,GAAa;AAAA,EACvC,MAAM,cAAuB,cAAc,GAAG,IAAI,YAAqB;AAAA,EACvE,IACC,OAAO,gBAAgB,YACvB,oBAAoB,SAAS,WAAuB,GACnD;AAAA,IACD,OAAO;AAAA,EACR;AAAA,EACA,IAAI,OAAO,iBAAiB,aAAa;AAAA,IACxC,MAAM,SAAS,aAAa,QAAQ,YAAY;AAAA,IAChD,IAAI,oBAAoB,SAAS,MAAkB,GAAG;AAAA,MACrD,OAAO;AAAA,IACR;AAAA,EACD;AAAA,EACA,OAAO;AAAA;AAGD,SAAS,gBAA8C,CAC7D,
|
|
12
|
-
"debugId": "
|
|
11
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAKO,IAAM,KAAK;AAAA,KACd;AAAA,EACH;AAAA,EACA;AAAA,EACA;AACD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACLO,IAAM,KAAK;AAAA,KACd;AAAA,EACH;AAAA,EACA;AAAA,EACA;AACD;;;ACPO,IAAM,YAAY;AAAA,EACxB;AAAA,EACA;AACD;;;ACHO,IAAM,eAAe;AAErB,IAAM,sBAAsB,OAAO,KAAK,SAAS;AAEjD,IAAM,oBAA8B;;ACP3C;AACA;AACA;AAsBO,SAAS,WAAW,GAAa;AAAA,EACvC,MAAM,cAAuB,cAAc,GAAG,IAAI,YAAqB;AAAA,EACvE,IACC,OAAO,gBAAgB,YACvB,oBAAoB,SAAS,WAAuB,GACnD;AAAA,IACD,OAAO;AAAA,EACR;AAAA,EACA,IAAI,OAAO,iBAAiB,aAAa;AAAA,IACxC,MAAM,SAAS,aAAa,QAAQ,YAAY;AAAA,IAChD,IAAI,oBAAoB,SAAS,MAAkB,GAAG;AAAA,MACrD,OAAO;AAAA,IACR;AAAA,EACD;AAAA,EACA,OAAO;AAAA;AAGD,SAAS,gBAA8C,CAC7D,YAAiC,CAAC,GAClC,iBAAmC,aAClC;AAAA,EACD,OAAO,CACN,KACA,UAA8B,WAC9B,kBAAoC,mBACxB;AAAA,IACZ,MAAM,WACL,OAAO,oBAAoB,aACxB,gBAAgB,IAChB;AAAA,IACJ,IAAI,UAAkB,EAAE,IAAI,UAAU,WAAW,GAAG,KAAK;AAAA,IACzD,IAAI;AAAA,MACH,UAAU,IAAI,kBAAkB,SAAS,QAAQ,EAAE,OAAO,OAAO;AAAA,MAChE,OAAO,GAAG;AAAA,MACX,QAAQ,MAAM,CAAC;AAAA;AAAA,IAGhB,OAAO;AAAA;AAAA;AAIF,IAAM,YAAY,iBAAiB,SAAS;",
|
|
12
|
+
"debugId": "9824428B699DC6DF64756E2164756E21",
|
|
13
13
|
"names": []
|
|
14
14
|
}
|