@lakutata/i18n 3.0.0-beta.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/LICENSE +23 -0
- package/README.md +347 -0
- package/dist/cjs/exports/I18n.d.ts +15 -0
- package/dist/cjs/exports/I18n.js +45 -0
- package/dist/cjs/exports/I18nMiddleware.d.ts +2 -0
- package/dist/cjs/exports/I18nMiddleware.js +21 -0
- package/dist/cjs/i18n/I18n.d.ts +313 -0
- package/dist/cjs/i18n/I18n.js +462 -0
- package/dist/cjs/i18n/Translator.d.ts +124 -0
- package/dist/cjs/i18n/Translator.js +156 -0
- package/dist/cjs/i18n/exceptions/I18nCatalogException.d.ts +15 -0
- package/dist/cjs/i18n/exceptions/I18nCatalogException.js +22 -0
- package/dist/cjs/i18n/exceptions/I18nMessageSyntaxException.d.ts +14 -0
- package/dist/cjs/i18n/exceptions/I18nMessageSyntaxException.js +21 -0
- package/dist/cjs/i18n/exceptions/I18nMissingKeyException.d.ts +12 -0
- package/dist/cjs/i18n/exceptions/I18nMissingKeyException.js +19 -0
- package/dist/cjs/i18n/exceptions/I18nMissingParameterException.d.ts +13 -0
- package/dist/cjs/i18n/exceptions/I18nMissingParameterException.js +20 -0
- package/dist/cjs/i18n/exceptions/I18nTranslatorException.d.ts +12 -0
- package/dist/cjs/i18n/exceptions/I18nTranslatorException.js +19 -0
- package/dist/cjs/i18n/interfaces/I18nOptions.d.ts +70 -0
- package/dist/cjs/i18n/interfaces/I18nOptions.js +2 -0
- package/dist/cjs/i18n/interfaces/ITranslator.d.ts +57 -0
- package/dist/cjs/i18n/interfaces/ITranslator.js +2 -0
- package/dist/cjs/i18n/lib/Catalog.d.ts +34 -0
- package/dist/cjs/i18n/lib/Catalog.js +104 -0
- package/dist/cjs/i18n/lib/CatalogTools.d.ts +86 -0
- package/dist/cjs/i18n/lib/CatalogTools.js +218 -0
- package/dist/cjs/i18n/lib/Locale.d.ts +95 -0
- package/dist/cjs/i18n/lib/Locale.js +182 -0
- package/dist/cjs/i18n/lib/LocaleTranslator.d.ts +93 -0
- package/dist/cjs/i18n/lib/LocaleTranslator.js +105 -0
- package/dist/cjs/i18n/lib/MessageFormat.d.ts +144 -0
- package/dist/cjs/i18n/lib/MessageFormat.js +372 -0
- package/dist/cjs/i18n/middleware/CommanderTranslator.d.ts +71 -0
- package/dist/cjs/i18n/middleware/CommanderTranslator.js +69 -0
- package/dist/cjs/i18n/middleware/I18nMiddleware.d.ts +126 -0
- package/dist/cjs/i18n/middleware/I18nMiddleware.js +210 -0
- package/dist/cjs/i18n/types/I18nMessages.d.ts +24 -0
- package/dist/cjs/i18n/types/I18nMessages.js +2 -0
- package/dist/cjs/package.json +1 -0
- package/dist/esm/exports/I18n.js +14 -0
- package/dist/esm/exports/I18nMiddleware.js +2 -0
- package/dist/esm/i18n/I18n.js +454 -0
- package/dist/esm/i18n/Translator.js +153 -0
- package/dist/esm/i18n/exceptions/I18nCatalogException.js +18 -0
- package/dist/esm/i18n/exceptions/I18nMessageSyntaxException.js +17 -0
- package/dist/esm/i18n/exceptions/I18nMissingKeyException.js +15 -0
- package/dist/esm/i18n/exceptions/I18nMissingParameterException.js +16 -0
- package/dist/esm/i18n/exceptions/I18nTranslatorException.js +15 -0
- package/dist/esm/i18n/interfaces/I18nOptions.js +1 -0
- package/dist/esm/i18n/interfaces/ITranslator.js +1 -0
- package/dist/esm/i18n/lib/Catalog.js +96 -0
- package/dist/esm/i18n/lib/CatalogTools.js +213 -0
- package/dist/esm/i18n/lib/Locale.js +173 -0
- package/dist/esm/i18n/lib/LocaleTranslator.js +101 -0
- package/dist/esm/i18n/lib/MessageFormat.js +367 -0
- package/dist/esm/i18n/middleware/CommanderTranslator.js +66 -0
- package/dist/esm/i18n/middleware/I18nMiddleware.js +206 -0
- package/dist/esm/i18n/types/I18nMessages.js +1 -0
- package/dist/types/exports/I18n.d.ts +15 -0
- package/dist/types/exports/I18nMiddleware.d.ts +2 -0
- package/dist/types/i18n/I18n.d.ts +313 -0
- package/dist/types/i18n/Translator.d.ts +124 -0
- package/dist/types/i18n/exceptions/I18nCatalogException.d.ts +15 -0
- package/dist/types/i18n/exceptions/I18nMessageSyntaxException.d.ts +14 -0
- package/dist/types/i18n/exceptions/I18nMissingKeyException.d.ts +12 -0
- package/dist/types/i18n/exceptions/I18nMissingParameterException.d.ts +13 -0
- package/dist/types/i18n/exceptions/I18nTranslatorException.d.ts +12 -0
- package/dist/types/i18n/interfaces/I18nOptions.d.ts +70 -0
- package/dist/types/i18n/interfaces/ITranslator.d.ts +57 -0
- package/dist/types/i18n/lib/Catalog.d.ts +34 -0
- package/dist/types/i18n/lib/CatalogTools.d.ts +86 -0
- package/dist/types/i18n/lib/Locale.d.ts +95 -0
- package/dist/types/i18n/lib/LocaleTranslator.d.ts +93 -0
- package/dist/types/i18n/lib/MessageFormat.d.ts +144 -0
- package/dist/types/i18n/middleware/CommanderTranslator.d.ts +71 -0
- package/dist/types/i18n/middleware/I18nMiddleware.d.ts +126 -0
- package/dist/types/i18n/types/I18nMessages.d.ts +24 -0
- package/package.json +60 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The messages of a language, nested by the parts of their keys: `{order: {count: '...'}}` holds the key
|
|
3
|
+
* `order.count`. A key part cannot be empty or contain `.`, and every leaf is a string (ICU MessageFormat, or a
|
|
4
|
+
* validation template under `validation`).
|
|
5
|
+
*/
|
|
6
|
+
export type NestedMessages = {
|
|
7
|
+
[key: string]: string | NestedMessages;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Loaded messages, by canonical language tag then by full key (`order.count`), as {@link loadCatalogs} gives them to
|
|
11
|
+
* {@link checkCatalogs} and {@link generateMessageTypes}.
|
|
12
|
+
*/
|
|
13
|
+
export type Catalogs = Map<string, Map<string, string>>;
|
|
14
|
+
/**
|
|
15
|
+
* The messages of nested objects by their keys: {"order": {"count": "..."}} → order.count
|
|
16
|
+
* @param messages
|
|
17
|
+
* @param source where the messages are, for the errors
|
|
18
|
+
* @param prefix
|
|
19
|
+
*/
|
|
20
|
+
export declare function flattenMessages(messages: unknown, source: string, prefix?: string): Map<string, string>;
|
|
21
|
+
/**
|
|
22
|
+
* Add the messages of a language to catalogs, the ones already there replaced
|
|
23
|
+
* @param catalogs
|
|
24
|
+
* @param locale
|
|
25
|
+
* @param messages
|
|
26
|
+
*/
|
|
27
|
+
export declare function mergeMessages(catalogs: Catalogs, locale: string, messages: Map<string, string>): void;
|
|
28
|
+
/**
|
|
29
|
+
* Load the messages of a directory into catalogs: a file by language (zh-CN.json), or a directory by language whose
|
|
30
|
+
* files (and their directories) prefix their keys (zh-CN/order.json: order.*, zh-CN/admin/user.json: admin.user.*)
|
|
31
|
+
* @param catalogs
|
|
32
|
+
* @param directory
|
|
33
|
+
*/
|
|
34
|
+
export declare function loadDirectory(catalogs: Catalogs, directory: string): Promise<void>;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { type Catalogs } from './Catalog.js';
|
|
2
|
+
/**
|
|
3
|
+
* A problem found by {@link checkCatalogs} in the messages.
|
|
4
|
+
*/
|
|
5
|
+
export type CatalogIssue = {
|
|
6
|
+
/**
|
|
7
|
+
* `'error'` for a message which cannot be right (it fails or loses an argument when it is formatted), `'warning'`
|
|
8
|
+
* for a message to look at (missing, or not translated yet).
|
|
9
|
+
*/
|
|
10
|
+
level: 'error' | 'warning';
|
|
11
|
+
/**
|
|
12
|
+
* The language tag of the message.
|
|
13
|
+
*/
|
|
14
|
+
locale: string;
|
|
15
|
+
/**
|
|
16
|
+
* The key of the message (`order.count`); `''` when the default language has no messages at all.
|
|
17
|
+
*/
|
|
18
|
+
key: string;
|
|
19
|
+
/**
|
|
20
|
+
* What is wrong, in English.
|
|
21
|
+
*/
|
|
22
|
+
message: string;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Load the messages of directories as the {@link I18n} component does, to check them or generate their types out of
|
|
26
|
+
* an application (in a script, a test or the CI).
|
|
27
|
+
* @param directories The directories, each holding a JSON file (`zh-CN.json`) or a directory (`zh-CN/order.json`) per
|
|
28
|
+
* language; paths resolved from the working directory (the aliases are not resolved). The later directories replace
|
|
29
|
+
* the same keys of the earlier ones.
|
|
30
|
+
* @returns The messages, by language then by key.
|
|
31
|
+
* @throws {I18nCatalogException} When a directory is missing, a file is not valid JSON, a name is not a language tag
|
|
32
|
+
* or a key is invalid.
|
|
33
|
+
* @example
|
|
34
|
+
* ```typescript
|
|
35
|
+
* import {loadCatalogs, type Catalogs} from 'lakutata/com/i18n'
|
|
36
|
+
*
|
|
37
|
+
* const catalogs: Catalogs = await loadCatalogs(['./locales'])
|
|
38
|
+
* for (const [locale, messages] of catalogs) console.log(locale, messages.size)
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export declare function loadCatalogs(directories: string[]): Promise<Catalogs>;
|
|
42
|
+
/**
|
|
43
|
+
* Check every language's messages against the default language's messages, as `lakutata i18n check` does. It reports:
|
|
44
|
+
* - errors: an ICU syntax error (in any language), a key the default language does not have, an argument the default
|
|
45
|
+
* message does not have (the code does not give it), a `validation.<code>` message whose code is unknown or which
|
|
46
|
+
* uses a variable that code does not have (`{{#limit}}`);
|
|
47
|
+
* - warnings: a key of the default language missing in a language (its fallback is given), an argument of the default
|
|
48
|
+
* message not used, a message identical to the default one and containing letters (not translated yet).
|
|
49
|
+
*
|
|
50
|
+
* The `validation.*` messages are not compared between languages; they need not exist in the default language.
|
|
51
|
+
* @param catalogs The messages, as {@link loadCatalogs} gives them.
|
|
52
|
+
* @param defaultLocale The default language tag, normalized (`zh_CN` is `zh-CN`).
|
|
53
|
+
* @returns The issues, by language in alphabetical order; empty when everything is right. Only one error when the
|
|
54
|
+
* default language has no messages.
|
|
55
|
+
* @example
|
|
56
|
+
* ```typescript
|
|
57
|
+
* import {checkCatalogs, loadCatalogs, type CatalogIssue} from 'lakutata/com/i18n'
|
|
58
|
+
*
|
|
59
|
+
* const issues: CatalogIssue[] = checkCatalogs(await loadCatalogs(['./locales']), 'en')
|
|
60
|
+
* for (const issue of issues) console.log(`${issue.level} ${issue.locale} ${issue.key}: ${issue.message}`)
|
|
61
|
+
* if (issues.some((issue: CatalogIssue): boolean => issue.level === 'error')) process.exitCode = 1
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
export declare function checkCatalogs(catalogs: Catalogs, defaultLocale: string): CatalogIssue[];
|
|
65
|
+
/**
|
|
66
|
+
* Generate the TypeScript declaration of the default language's message keys and their arguments (the
|
|
67
|
+
* {@link I18nMessages} augmentation), as `lakutata i18n types` does. Each argument is typed by its use: `number |
|
|
68
|
+
* bigint` for `{n, number}`, `number` for a plural, `string` for a select, `Date | number | string` for a date or a
|
|
69
|
+
* time, any scalar for a plain argument; a currency argument without its currency adds an optional `currency`. The
|
|
70
|
+
* `validation.*` messages are left out.
|
|
71
|
+
* @param catalogs The messages, as {@link loadCatalogs} gives them.
|
|
72
|
+
* @param defaultLocale The default language tag, whose messages are declared.
|
|
73
|
+
* @param moduleName The module the application imports the i18n from, which the declaration augments:
|
|
74
|
+
* `'lakutata/com/i18n'` by default, `'@lakutata/i18n'` for the applications importing the package directly.
|
|
75
|
+
* @returns The source of a `.d.ts` file.
|
|
76
|
+
* @throws {I18nCatalogException} When the default language has no messages.
|
|
77
|
+
* @throws {I18nMessageSyntaxException} When a message of the default language is not valid ICU MessageFormat.
|
|
78
|
+
* @example
|
|
79
|
+
* ```typescript
|
|
80
|
+
* import {writeFile} from 'node:fs/promises'
|
|
81
|
+
* import {generateMessageTypes, loadCatalogs} from 'lakutata/com/i18n'
|
|
82
|
+
*
|
|
83
|
+
* await writeFile('src/i18n.d.ts', generateMessageTypes(await loadCatalogs(['./locales']), 'en'))
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
86
|
+
export declare function generateMessageTypes(catalogs: Catalogs, defaultLocale: string, moduleName?: string): string;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Give the canonical BCP 47 form of a language tag (`zh-cn` gives `zh-CN`, `iw` gives `he`). The POSIX locales of the
|
|
3
|
+
* environment are converted: their encoding and modifier dropped, `_` replaced by `-` (`zh_CN.UTF-8` gives `zh-CN`,
|
|
4
|
+
* `en_US@euro` gives `en-US`).
|
|
5
|
+
* @param tag The tag; surrounding spaces are ignored.
|
|
6
|
+
* @returns The canonical tag; `undefined` for an invalid tag, a tag whose language is not 2 or 3 letters, `C`,
|
|
7
|
+
* `POSIX`, `*` and the empty string.
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import {normalizeLocale} from 'lakutata/com/i18n'
|
|
11
|
+
*
|
|
12
|
+
* console.log(normalizeLocale('zh_CN.UTF-8')) //zh-CN
|
|
13
|
+
* console.log(normalizeLocale('zh-hant-tw')) //zh-Hant-TW
|
|
14
|
+
* console.log(normalizeLocale('C')) //undefined
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export declare function normalizeLocale(tag: string): string | undefined;
|
|
18
|
+
/**
|
|
19
|
+
* The language of a tag, without its script and region: zh-Hant-TW → zh
|
|
20
|
+
* @param locale
|
|
21
|
+
*/
|
|
22
|
+
export declare function languageOf(locale: string): string;
|
|
23
|
+
/**
|
|
24
|
+
* Read the languages of an `Accept-Language` header, preferred first: by decreasing quality (`q`, 1 when absent),
|
|
25
|
+
* then in their order in the header. The tags are normalized ({@link normalizeLocale}); the invalid ones, `*`, and the
|
|
26
|
+
* ones of quality 0 or an invalid quality are dropped.
|
|
27
|
+
* @param header The value of the header (`zh-CN,zh;q=0.9,en;q=0.8`).
|
|
28
|
+
* @returns The language tags, preferred first; empty without a header.
|
|
29
|
+
* @example
|
|
30
|
+
* ```typescript
|
|
31
|
+
* import {parseAcceptLanguage} from 'lakutata/com/i18n'
|
|
32
|
+
*
|
|
33
|
+
* console.log(parseAcceptLanguage('en;q=0.5,fr,*;q=0.1')) //['fr', 'en']
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export declare function parseAcceptLanguage(header: string | undefined): string[];
|
|
37
|
+
/**
|
|
38
|
+
* Read the languages of a process environment, preferred first, as gettext does: the locale is the first of `LC_ALL`,
|
|
39
|
+
* `LC_MESSAGES` and `LANG` which is set; the colon-separated list of `LANGUAGE` comes before it, but only when that
|
|
40
|
+
* locale is set and valid (not `C` or `POSIX`). The tags are normalized ({@link normalizeLocale}), the invalid ones
|
|
41
|
+
* and the duplicates dropped.
|
|
42
|
+
* @param environment The environment variables: `process.env` by default.
|
|
43
|
+
* @returns The language tags, preferred first; empty when the environment gives none.
|
|
44
|
+
* @example
|
|
45
|
+
* ```typescript
|
|
46
|
+
* import {environmentLocales} from 'lakutata/com/i18n'
|
|
47
|
+
*
|
|
48
|
+
* console.log(environmentLocales({LANG: 'zh_CN.UTF-8', LANGUAGE: 'zh_TW:en'})) //['zh-TW', 'en', 'zh-CN']
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export declare function environmentLocales(environment?: NodeJS.ProcessEnv): string[];
|
|
52
|
+
/**
|
|
53
|
+
* The languages of the process, preferred first: the one of an environment variable of the application
|
|
54
|
+
* (LAKUTATA_LANG), the ones of the environment (environmentLocales), the language of the process (of its ICU)
|
|
55
|
+
* @param variable the environment variable of the application
|
|
56
|
+
* @param environment
|
|
57
|
+
*/
|
|
58
|
+
export declare function processLocales(variable?: string, environment?: NodeJS.ProcessEnv): string[];
|
|
59
|
+
/**
|
|
60
|
+
* Choose the supported language for the requested ones. Each requested language is tried in turn, preferred first,
|
|
61
|
+
* and the first match wins: the same tag (case-insensitive), then its language alone (`en-GB` matches `en`), then
|
|
62
|
+
* another tag of its language (`pt-PT` matches `pt-BR`, the first one in the order of `supported`). The invalid
|
|
63
|
+
* requested tags are skipped.
|
|
64
|
+
* @param requested The requested language tags, preferred first (from {@link parseAcceptLanguage} or
|
|
65
|
+
* {@link environmentLocales}).
|
|
66
|
+
* @param supported The supported language tags.
|
|
67
|
+
* @param defaultLocale The language returned when none of the requested ones matches.
|
|
68
|
+
* @returns The matching tag as written in `supported`, or `defaultLocale`.
|
|
69
|
+
* @example
|
|
70
|
+
* ```typescript
|
|
71
|
+
* import {negotiateLocale} from 'lakutata/com/i18n'
|
|
72
|
+
*
|
|
73
|
+
* console.log(negotiateLocale(['pt-PT', 'en'], ['en', 'pt-BR'], 'en')) //pt-BR
|
|
74
|
+
* console.log(negotiateLocale(['de'], ['en', 'zh-CN'], 'en')) //en
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
export declare function negotiateLocale(requested: string[], supported: string[], defaultLocale: string): string;
|
|
78
|
+
/**
|
|
79
|
+
* List the languages a message of a language is looked up in, in their order, each once: the language, then each of
|
|
80
|
+
* its configured fallbacks (followed by their own fallbacks and less specific tags), then its less specific tags
|
|
81
|
+
* (`zh-Hant-TW`, `zh-Hant`, `zh`), then the default language (and its own fallbacks and less specific tags). The tags
|
|
82
|
+
* are not normalized: give canonical ones.
|
|
83
|
+
* @param locale The language tag of the message asked.
|
|
84
|
+
* @param fallbacks The configured fallbacks, by language tag.
|
|
85
|
+
* @param defaultLocale The default language tag.
|
|
86
|
+
* @returns The language tags, in lookup order.
|
|
87
|
+
* @example
|
|
88
|
+
* ```typescript
|
|
89
|
+
* import {fallbackChain} from 'lakutata/com/i18n'
|
|
90
|
+
*
|
|
91
|
+
* console.log(fallbackChain('zh-TW', {'zh-TW': ['zh-CN']}, 'en')) //['zh-TW', 'zh-CN', 'zh', 'en']
|
|
92
|
+
* console.log(fallbackChain('zh-Hant-TW', {}, 'en')) //['zh-Hant-TW', 'zh-Hant', 'zh', 'en']
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
export declare function fallbackChain(locale: string, fallbacks: Record<string, string[]>, defaultLocale: string): string[];
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { I18n } from '../I18n.js';
|
|
2
|
+
import type { ITranslator } from '../interfaces/ITranslator.js';
|
|
3
|
+
import type { MessageKey, MessageParams } from '../types/I18nMessages.js';
|
|
4
|
+
/**
|
|
5
|
+
* The messages of one language, without giving the language at each call: {@link I18n.translator} creates one, to use
|
|
6
|
+
* out of a request (a schedule, a singleton component, a background job). Its methods call the component's methods
|
|
7
|
+
* with its language, so the messages added later with {@link I18n.addMessages} are seen too. In a request, inject the
|
|
8
|
+
* {@link Translator} instead.
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import {Application} from 'lakutata'
|
|
12
|
+
* import {buildI18nOptions, type I18n, type ITranslator} from 'lakutata/com/i18n'
|
|
13
|
+
*
|
|
14
|
+
* Application
|
|
15
|
+
* .run(() => ({
|
|
16
|
+
* id: 'i18n.app',
|
|
17
|
+
* name: 'I18n',
|
|
18
|
+
* components: {
|
|
19
|
+
* i18n: buildI18nOptions({
|
|
20
|
+
* messages: {
|
|
21
|
+
* 'en': {order: {count: '{count, plural, =0 {No orders} one {# order} other {# orders}}'}},
|
|
22
|
+
* 'de': {order: {count: '{count, plural, =0 {Keine Bestellungen} one {# Bestellung} other {# Bestellungen}}'}}
|
|
23
|
+
* }
|
|
24
|
+
* })
|
|
25
|
+
* }
|
|
26
|
+
* }))
|
|
27
|
+
* .onLaunched(async (app: Application): Promise<void> => {
|
|
28
|
+
* const i18n: I18n = await app.getObject('i18n')
|
|
29
|
+
* const german: ITranslator = i18n.translator('de-DE')
|
|
30
|
+
* console.log(german.locale) //de-DE, its messages looked up in de-DE, then de, then en
|
|
31
|
+
* console.log(german.t('order.count', {count: 1000})) //1.000 Bestellungen
|
|
32
|
+
* console.log(german.number(1234.5)) //1.234,5
|
|
33
|
+
* })
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export declare class LocaleTranslator implements ITranslator {
|
|
37
|
+
protected readonly i18n: I18n;
|
|
38
|
+
readonly locale: string;
|
|
39
|
+
/**
|
|
40
|
+
* Create the translator of a language; {@link I18n.translator} does it, normalizing the language first.
|
|
41
|
+
* @param i18n The component holding the messages.
|
|
42
|
+
* @param locale The language tag, canonical.
|
|
43
|
+
*/
|
|
44
|
+
constructor(i18n: I18n, locale: string);
|
|
45
|
+
/**
|
|
46
|
+
* Give the message of a key in the translator's language, formatted with its arguments (see {@link I18n.t}).
|
|
47
|
+
* @param key The key of the message (`order.count`).
|
|
48
|
+
* @param params The arguments of the message, by their names.
|
|
49
|
+
* @returns The formatted message. With `missing: 'key'`, the key itself when it is missing, `{name}` in place of a
|
|
50
|
+
* missing argument, a plain number for a currency argument without a currency (each with a warning, once).
|
|
51
|
+
* @throws {I18nMissingKeyException} When no message has the key and the `missing` option is `'throw'`.
|
|
52
|
+
* @throws {I18nMissingParameterException} When an argument is missing, or a currency argument has no currency, and
|
|
53
|
+
* the `missing` option is `'throw'`.
|
|
54
|
+
* @throws {I18nMessageSyntaxException} When the message is not valid ICU MessageFormat.
|
|
55
|
+
*/
|
|
56
|
+
t<K extends MessageKey>(key: K, params?: MessageParams<K>): string;
|
|
57
|
+
/**
|
|
58
|
+
* Tell whether a key has a message in the translator's language or in the languages it falls back to.
|
|
59
|
+
* @param key The key of the message.
|
|
60
|
+
* @returns `true` when {@link LocaleTranslator.t} finds a message for the key.
|
|
61
|
+
*/
|
|
62
|
+
has(key: string): boolean;
|
|
63
|
+
/**
|
|
64
|
+
* Format an ICU MessageFormat message which is not in the catalogs, in the translator's language.
|
|
65
|
+
* @param message The message (`Hello, {name}`).
|
|
66
|
+
* @param params The arguments of the message, by their names.
|
|
67
|
+
* @returns The formatted message. With `missing: 'key'`, `{name}` in place of a missing argument, a plain number
|
|
68
|
+
* for a currency argument without a currency (each with a warning, once).
|
|
69
|
+
* @throws {I18nMessageSyntaxException} When the message is not valid ICU MessageFormat.
|
|
70
|
+
* @throws {I18nMissingParameterException} When an argument is missing, or a currency argument has no currency, and
|
|
71
|
+
* the `missing` option is `'throw'`.
|
|
72
|
+
*/
|
|
73
|
+
format(message: string, params?: Record<string, unknown>): string;
|
|
74
|
+
/**
|
|
75
|
+
* Format a number in the translator's language (`1234.5` gives `1,234.5` in English, `1.234,5` in German).
|
|
76
|
+
* @param value The number.
|
|
77
|
+
* @param options The options of `Intl.NumberFormat` (`{style: 'currency', currency: 'EUR'}`); the component's
|
|
78
|
+
* `currency` option is not applied here.
|
|
79
|
+
* @returns The formatted number.
|
|
80
|
+
* @throws {RangeError} When the options are invalid (a currency style without its currency).
|
|
81
|
+
*/
|
|
82
|
+
number(value: number | bigint, options?: Intl.NumberFormatOptions): string;
|
|
83
|
+
/**
|
|
84
|
+
* Format a date (and its time when the options ask for it) in the translator's language, in the component's
|
|
85
|
+
* `timeZone` (the process's one without it).
|
|
86
|
+
* @param value The date, or a timestamp in milliseconds.
|
|
87
|
+
* @param options The options of `Intl.DateTimeFormat` (`{dateStyle: 'long'}`); their `timeZone` replaces the
|
|
88
|
+
* component's one.
|
|
89
|
+
* @returns The formatted date.
|
|
90
|
+
* @throws {RangeError} When the date is invalid or the options are.
|
|
91
|
+
*/
|
|
92
|
+
date(value: Date | number, options?: Intl.DateTimeFormatOptions): string;
|
|
93
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A part of a message parsed by {@link parseMessage}: a literal text (a string, its quoting already removed), a plain
|
|
3
|
+
* argument (`{name}`), a number argument (`{n, number, percent}`), a date or time argument (`{at, date, short}`), a
|
|
4
|
+
* choice (`plural`, `selectordinal`, `select`), or the `#` of a plural (its number).
|
|
5
|
+
*/
|
|
6
|
+
export type MessagePart = string | {
|
|
7
|
+
/**
|
|
8
|
+
* A plain argument, `{name}`: a number formatted in the language, a `Date` as a date, anything else as a string.
|
|
9
|
+
*/
|
|
10
|
+
type: 'argument';
|
|
11
|
+
/**
|
|
12
|
+
* The name of the argument.
|
|
13
|
+
*/
|
|
14
|
+
name: string;
|
|
15
|
+
} | {
|
|
16
|
+
/**
|
|
17
|
+
* A number argument, `{name, number}` or `{name, number, style}`.
|
|
18
|
+
*/
|
|
19
|
+
type: 'number';
|
|
20
|
+
/**
|
|
21
|
+
* The name of the argument.
|
|
22
|
+
*/
|
|
23
|
+
name: string;
|
|
24
|
+
/**
|
|
25
|
+
* The style: `integer`, `percent`, `currency` or `currency/XXX` (an ISO 4217 code); absent for a plain number.
|
|
26
|
+
*/
|
|
27
|
+
style?: string;
|
|
28
|
+
} | {
|
|
29
|
+
/**
|
|
30
|
+
* A date argument (`{name, date, style}`) or a time argument (`{name, time, style}`).
|
|
31
|
+
*/
|
|
32
|
+
type: 'date' | 'time';
|
|
33
|
+
/**
|
|
34
|
+
* The name of the argument.
|
|
35
|
+
*/
|
|
36
|
+
name: string;
|
|
37
|
+
/**
|
|
38
|
+
* The style: `short`, `medium`, `long` or `full`; absent for `medium`.
|
|
39
|
+
*/
|
|
40
|
+
style?: string;
|
|
41
|
+
} | {
|
|
42
|
+
/**
|
|
43
|
+
* A cardinal (`plural`) or ordinal (`selectordinal`) choice on a number argument.
|
|
44
|
+
*/
|
|
45
|
+
type: 'plural' | 'selectordinal';
|
|
46
|
+
/**
|
|
47
|
+
* The name of the argument.
|
|
48
|
+
*/
|
|
49
|
+
name: string;
|
|
50
|
+
/**
|
|
51
|
+
* The `offset:` of the choice, subtracted from the number before its plural rule and its `#`; 0 without it.
|
|
52
|
+
*/
|
|
53
|
+
offset: number;
|
|
54
|
+
/**
|
|
55
|
+
* The messages by selector: `=0` (an exact value, matched before the offset), a plural category of the
|
|
56
|
+
* language (`zero`, `one`, `two`, `few`, `many`), and the required `other`.
|
|
57
|
+
*/
|
|
58
|
+
options: Record<string, MessagePart[]>;
|
|
59
|
+
} | {
|
|
60
|
+
/**
|
|
61
|
+
* A choice on the string value of an argument, `{name, select, admin {...} other {...}}`.
|
|
62
|
+
*/
|
|
63
|
+
type: 'select';
|
|
64
|
+
/**
|
|
65
|
+
* The name of the argument.
|
|
66
|
+
*/
|
|
67
|
+
name: string;
|
|
68
|
+
/**
|
|
69
|
+
* The messages by value of the argument, and the required `other`.
|
|
70
|
+
*/
|
|
71
|
+
options: Record<string, MessagePart[]>;
|
|
72
|
+
} | {
|
|
73
|
+
/**
|
|
74
|
+
* The `#` of a plural: the number of the closest enclosing `plural` or `selectordinal` (minus its offset),
|
|
75
|
+
* formatted in the language.
|
|
76
|
+
*/
|
|
77
|
+
type: 'pound';
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* How a message is formatted
|
|
81
|
+
*/
|
|
82
|
+
export type FormatContext = {
|
|
83
|
+
locale: string;
|
|
84
|
+
/**
|
|
85
|
+
* The currency of {price, number, currency} (ISO 4217)
|
|
86
|
+
*/
|
|
87
|
+
currency?: string;
|
|
88
|
+
/**
|
|
89
|
+
* The time zone of the dates and the times (IANA)
|
|
90
|
+
*/
|
|
91
|
+
timeZone?: string;
|
|
92
|
+
/**
|
|
93
|
+
* An argument or a currency missing: thrown, or kept (the argument as it is written, {name}; the number without
|
|
94
|
+
* currency) with a warning
|
|
95
|
+
*/
|
|
96
|
+
missing: 'throw' | 'keep';
|
|
97
|
+
/**
|
|
98
|
+
* Told what is kept when missing is 'keep'
|
|
99
|
+
*/
|
|
100
|
+
warn?: (warning: string) => void;
|
|
101
|
+
};
|
|
102
|
+
/**
|
|
103
|
+
* Parse an ICU MessageFormat message into its parts, to inspect it (its arguments: {@link messageArguments}). The
|
|
104
|
+
* syntax: `{name}`; `{name, number}` with the styles `integer`, `percent`, `currency`, `currency/XXX`; `{name, date}`
|
|
105
|
+
* and `{name, time}` with the styles `short`, `medium`, `long`, `full`; `{name, plural, ...}` (with `offset:n`),
|
|
106
|
+
* `{name, selectordinal, ...}` and `{name, select, ...}`, each requiring an `other` option; `#` in a plural; `''` for
|
|
107
|
+
* an apostrophe and `'{...}'` to quote the braces (and `#` in a plural). Other argument types and the skeletons
|
|
108
|
+
* (`::currency/EUR`) are not supported.
|
|
109
|
+
* @param message The message.
|
|
110
|
+
* @param source Where the message comes from (`zh-CN: order.count`), the prefix of the error messages: `'message'` by
|
|
111
|
+
* default.
|
|
112
|
+
* @returns The parts of the message.
|
|
113
|
+
* @throws {I18nMessageSyntaxException} When the message is not valid, with the source and the position.
|
|
114
|
+
* @example
|
|
115
|
+
* ```typescript
|
|
116
|
+
* import {parseMessage, type MessagePart} from 'lakutata/com/i18n'
|
|
117
|
+
*
|
|
118
|
+
* const parts: MessagePart[] = parseMessage('{count, plural, one {# order} other {# orders}} for {customer}')
|
|
119
|
+
* console.log(parts.length) //3
|
|
120
|
+
* ```
|
|
121
|
+
*/
|
|
122
|
+
export declare function parseMessage(message: string, source?: string): MessagePart[];
|
|
123
|
+
/**
|
|
124
|
+
* List the arguments a parsed message uses, nested choices included, with the type of their first use: `argument`,
|
|
125
|
+
* `number`, `date`, `time`, `plural`, `selectordinal` or `select`.
|
|
126
|
+
* @param parts The parts of the message, from {@link parseMessage}.
|
|
127
|
+
* @returns The argument types by argument name, in their order in the message.
|
|
128
|
+
* @example
|
|
129
|
+
* ```typescript
|
|
130
|
+
* import {messageArguments, parseMessage} from 'lakutata/com/i18n'
|
|
131
|
+
*
|
|
132
|
+
* const args: Map<string, string> = messageArguments(parseMessage('{count, plural, other {# orders}} for {customer}'))
|
|
133
|
+
* console.log([...args]) //[['count', 'plural'], ['customer', 'argument']]
|
|
134
|
+
* ```
|
|
135
|
+
*/
|
|
136
|
+
export declare function messageArguments(parts: MessagePart[]): Map<string, string>;
|
|
137
|
+
/**
|
|
138
|
+
* Format a parsed message
|
|
139
|
+
* @param parts
|
|
140
|
+
* @param params
|
|
141
|
+
* @param context
|
|
142
|
+
* @param source where the message is (its key), for the errors
|
|
143
|
+
*/
|
|
144
|
+
export declare function formatMessage(parts: MessagePart[], params: Record<string, unknown>, context: FormatContext, source?: string): string;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { Module } from '@lakutata/core';
|
|
2
|
+
import type { CLITranslate } from '@lakutata/entrypoint/adapter/commander';
|
|
3
|
+
/**
|
|
4
|
+
* The options of {@link commanderTranslator}.
|
|
5
|
+
*/
|
|
6
|
+
export type CommanderTranslatorOptions = {
|
|
7
|
+
/**
|
|
8
|
+
* The name the {@link I18n} component is registered under.
|
|
9
|
+
* @default 'i18n'
|
|
10
|
+
*/
|
|
11
|
+
component?: string;
|
|
12
|
+
/**
|
|
13
|
+
* The prefix of the message keys of the texts: with `'cli'`, the description of the command `greet` is the message
|
|
14
|
+
* `cli.commands.greet.description`; `''` for no prefix.
|
|
15
|
+
* @default 'cli'
|
|
16
|
+
*/
|
|
17
|
+
prefix?: string;
|
|
18
|
+
/**
|
|
19
|
+
* The name of an environment variable (`LAKUTATA_LANG`) whose language is preferred to the ones of the environment.
|
|
20
|
+
* @default none
|
|
21
|
+
*/
|
|
22
|
+
environment?: string;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Translate the help and the prompts of the Commander CLI adapter with the messages of an {@link I18n} component: give
|
|
26
|
+
* it to the `translate` option of `commanderCLIEntrypoint()`. Each text is the message of its key under the prefix
|
|
27
|
+
* (`cli.description`, `cli.commands.<command>.description`, `cli.commands.<command>.arguments.<name>`,
|
|
28
|
+
* `cli.commands.<command>.options.<property>`, `cli.help.usage`, `cli.help.options`, `cli.help.default`,
|
|
29
|
+
* `cli.prompt.json`…), formatted with the text's parameters (`{value}`); a text without a message keeps its English
|
|
30
|
+
* text. The language is chosen once, when the adapter starts: the environment variable of the `environment` option,
|
|
31
|
+
* the environment ({@link environmentLocales}), then the process's language, negotiated with the component's
|
|
32
|
+
* supported languages. The names of the commands and of the options are never translated.
|
|
33
|
+
* @param options The component, the key prefix and the environment variable.
|
|
34
|
+
* @returns The `translate` function of the adapter, which gets the component from the application's module.
|
|
35
|
+
* @example
|
|
36
|
+
* ```typescript
|
|
37
|
+
* import {Application} from 'lakutata'
|
|
38
|
+
* import {buildEntrypoints, Controller} from 'lakutata/com/entrypoint'
|
|
39
|
+
* import {commanderCLIEntrypoint} from 'lakutata/adapter/commander'
|
|
40
|
+
* import {CLIAction} from 'lakutata/decorator/ctrl'
|
|
41
|
+
* import {buildI18nOptions, commanderTranslator, I18nMiddleware} from 'lakutata/com/i18n'
|
|
42
|
+
*
|
|
43
|
+
* class GreetController extends Controller {
|
|
44
|
+
* @CLIAction('greet', {description: 'Greet someone'})
|
|
45
|
+
* public async greet(): Promise<string> {
|
|
46
|
+
* return 'Hello'
|
|
47
|
+
* }
|
|
48
|
+
* }
|
|
49
|
+
*
|
|
50
|
+
* Application.run(() => ({
|
|
51
|
+
* id: 'tool.app',
|
|
52
|
+
* name: 'Tool',
|
|
53
|
+
* components: {
|
|
54
|
+
* i18n: buildI18nOptions({
|
|
55
|
+
* messages: {
|
|
56
|
+
* 'zh-CN': {cli: {commands: {greet: {description: '问候某人'}}, help: {usage: '用法:', options: '选项:'}}}
|
|
57
|
+
* }
|
|
58
|
+
* }),
|
|
59
|
+
* entrypoint: buildEntrypoints({
|
|
60
|
+
* controllers: [GreetController],
|
|
61
|
+
* //The controllers' Translator in the same language
|
|
62
|
+
* middlewares: [I18nMiddleware],
|
|
63
|
+
* //LANG=zh_CN.UTF-8 tool --help: the help in Chinese
|
|
64
|
+
* cli: commanderCLIEntrypoint({bin: 'tool', translate: commanderTranslator({environment: 'TOOL_LANG'})})
|
|
65
|
+
* })
|
|
66
|
+
* },
|
|
67
|
+
* bootstrap: ['entrypoint']
|
|
68
|
+
* }))
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
export declare function commanderTranslator(options?: CommanderTranslatorOptions): (module: Module) => Promise<CLITranslate>;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import type { IncomingMessage } from 'node:http';
|
|
2
|
+
import { Middleware, type ActionInvocation, type BaseContext, type Next } from '@lakutata/entrypoint';
|
|
3
|
+
import type { I18n } from '../I18n.js';
|
|
4
|
+
import { Translator } from '../Translator.js';
|
|
5
|
+
/**
|
|
6
|
+
* The entrypoint middleware choosing the language of each request. Add it to the `middlewares` of
|
|
7
|
+
* `buildEntrypoints()`. For each request, it reads the languages the request asks for ({@link I18nMiddleware.requested}),
|
|
8
|
+
* negotiates them with the supported languages of the {@link I18n} component ({@link I18n.negotiate}, the default
|
|
9
|
+
* language when none matches), and makes the result:
|
|
10
|
+
* - the request's `context.locale`;
|
|
11
|
+
* - the language of the {@link Translator} the request's objects inject;
|
|
12
|
+
* - the language of the validation errors of the request (its input, `@Accept`/`@Return`, the `DTO.validate` calls of
|
|
13
|
+
* its code);
|
|
14
|
+
* - the language of the exceptions it throws: a lakutata `Exception` whose `errno` has a message `errors.<errno>`
|
|
15
|
+
* (`errors.E_ORDER_NOT_FOUND`) gets that message, formatted with the exception's template parameters.
|
|
16
|
+
*
|
|
17
|
+
* The languages are read, preferred first: for HTTP, the `lang` query parameter, the `lang` cookie, then
|
|
18
|
+
* `Accept-Language`; for Socket.IO, the same from the connection's handshake; for the HTTP service adapter and the
|
|
19
|
+
* JSON-RPC adapter over HTTP or WebSocket, the same from the HTTP request (for WebSocket, the request opening the
|
|
20
|
+
* connection); for the command line and the other service transports, the process's environment
|
|
21
|
+
* ({@link environmentLocales}) then the process's language; for the schedules, none. It uses the component registered as `i18n`; a subclass changes the component, the names of the query
|
|
22
|
+
* parameter and of the cookie, or the languages read (`requested()`).
|
|
23
|
+
* @example
|
|
24
|
+
* ```typescript
|
|
25
|
+
* import {type BaseContext} from 'lakutata/com/entrypoint'
|
|
26
|
+
* import {Inject} from 'lakutata/decorator/di'
|
|
27
|
+
* import {I18nMiddleware, type I18n} from 'lakutata/com/i18n'
|
|
28
|
+
*
|
|
29
|
+
* //BuildEntrypoints({middlewares: [LanguageMiddleware], ...}) in place of I18nMiddleware
|
|
30
|
+
* export class LanguageMiddleware extends I18nMiddleware {
|
|
31
|
+
* //The component registered as "lang"
|
|
32
|
+
* @Inject('lang')
|
|
33
|
+
* protected readonly i18n: I18n
|
|
34
|
+
*
|
|
35
|
+
* //?locale=zh-CN
|
|
36
|
+
* protected readonly queryParameter: string = 'locale'
|
|
37
|
+
*
|
|
38
|
+
* protected readonly environmentVariable: string = 'SHOP_LANG'
|
|
39
|
+
*
|
|
40
|
+
* //The language saved by the signed-in user first
|
|
41
|
+
* protected requested(context: BaseContext): string[] {
|
|
42
|
+
* const saved: unknown = context.data.userLanguage
|
|
43
|
+
* return [...(typeof saved === 'string' ? [saved] : []), ...super.requested(context)]
|
|
44
|
+
* }
|
|
45
|
+
* }
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export declare class I18nMiddleware extends Middleware {
|
|
49
|
+
/**
|
|
50
|
+
* The {@link I18n} component giving the supported languages and the messages: the object registered as `i18n`.
|
|
51
|
+
*/
|
|
52
|
+
protected readonly i18n: I18n;
|
|
53
|
+
/**
|
|
54
|
+
* The name of the query parameter giving the language of an HTTP request (of the HTTP channel, the HTTP service and
|
|
55
|
+
* JSON-RPC adapters) or a Socket.IO connection (`?lang=zh-CN`); a value which is not a language tag is ignored.
|
|
56
|
+
* @default 'lang'
|
|
57
|
+
*/
|
|
58
|
+
protected readonly queryParameter: string;
|
|
59
|
+
/**
|
|
60
|
+
* The name of the cookie giving the language of an HTTP request (of the HTTP channel, the HTTP service and JSON-RPC
|
|
61
|
+
* adapters) or a Socket.IO connection; a value which is not a language tag is ignored.
|
|
62
|
+
* @default 'lang'
|
|
63
|
+
*/
|
|
64
|
+
protected readonly cookie: string;
|
|
65
|
+
/**
|
|
66
|
+
* The name of an environment variable (`SHOP_LANG`) whose language is preferred to the ones of the environment, for
|
|
67
|
+
* the command line and the service transports reading the process's languages.
|
|
68
|
+
* @default none
|
|
69
|
+
*/
|
|
70
|
+
protected readonly environmentVariable?: string;
|
|
71
|
+
/**
|
|
72
|
+
* Negotiate the request's language, set `context.locale`, create the request's {@link Translator}, then run the
|
|
73
|
+
* next middlewares and the action with the validations in that language, translating the exception they throw.
|
|
74
|
+
* @param invocation The action invocation: its context and its container.
|
|
75
|
+
* @param next Runs the rest of the chain.
|
|
76
|
+
* @returns The result of the rest of the chain; an exception it throws is rethrown, its message translated by
|
|
77
|
+
* {@link I18nMiddleware.translate}.
|
|
78
|
+
*/
|
|
79
|
+
handle(invocation: ActionInvocation, next: Next): Promise<unknown>;
|
|
80
|
+
/**
|
|
81
|
+
* Translate an exception thrown by a request into the request's language. A lakutata `Exception` with an `errno`
|
|
82
|
+
* whose key `errors.<errno>` has a message (`errors.E_ORDER_NOT_FOUND`) gets that message as its `message` and
|
|
83
|
+
* `errMsg`, formatted with the parameters of its template (`{id}` for named ones, `{0}`, `{1}` for the ones given one
|
|
84
|
+
* by one); its `errno` and its other properties are kept. The other errors, the exceptions without such a message,
|
|
85
|
+
* and the ones whose message cannot be formatted (a process warning `I18nWarning` is then emitted) are returned
|
|
86
|
+
* unchanged.
|
|
87
|
+
* @param error The thrown value.
|
|
88
|
+
* @param translator The request's translator.
|
|
89
|
+
* @returns The same value, its message replaced when it is translated.
|
|
90
|
+
*/
|
|
91
|
+
protected translate(error: unknown, translator: Translator): unknown;
|
|
92
|
+
/**
|
|
93
|
+
* Read the languages a request asks for, preferred first:
|
|
94
|
+
* - HTTP: the query parameter, the cookie, then `Accept-Language`;
|
|
95
|
+
* - Socket.IO: the query parameter, the cookie and `Accept-Language` of the connection's handshake;
|
|
96
|
+
* - the HTTP service adapter, and the JSON-RPC adapter over HTTP or WebSocket: the query parameter, the cookie and
|
|
97
|
+
* `Accept-Language` of the HTTP request (for WebSocket, the request opening the connection);
|
|
98
|
+
* - the command line and the other service transports (Electron IPC, JSON-RPC over stdio): the environment variable
|
|
99
|
+
* of `environmentVariable`, the environment (`LANGUAGE`, `LC_ALL`, `LC_MESSAGES`, `LANG`), then the process's
|
|
100
|
+
* language;
|
|
101
|
+
* - the schedules and the other contexts: none (the default language is chosen).
|
|
102
|
+
* @param context The request's context.
|
|
103
|
+
* @returns The requested language tags, preferred first.
|
|
104
|
+
*/
|
|
105
|
+
protected requested(context: BaseContext): string[];
|
|
106
|
+
/**
|
|
107
|
+
* Read the languages of an HTTP request: its query parameter, its cookie, then its `Accept-Language`.
|
|
108
|
+
* @param request The Node.js HTTP request.
|
|
109
|
+
* @returns The language tags, preferred first.
|
|
110
|
+
*/
|
|
111
|
+
protected fromRequest(request: IncomingMessage): string[];
|
|
112
|
+
/**
|
|
113
|
+
* Read the languages of a query parameter, the cookie and `Accept-Language`, in that order; the query and cookie
|
|
114
|
+
* values which are not language tags are skipped.
|
|
115
|
+
* @param query The value of the query parameter; ignored unless it is a string.
|
|
116
|
+
* @param headers The request headers, by lower-case name; for a repeated header, its first value is read.
|
|
117
|
+
* @returns The language tags, preferred first.
|
|
118
|
+
*/
|
|
119
|
+
protected fromHeaders(query: unknown, headers: Record<string, string | string[] | undefined>): string[];
|
|
120
|
+
/**
|
|
121
|
+
* Read the value of the language cookie (named by `cookie`) in a `Cookie` header, URI-decoded.
|
|
122
|
+
* @param header The value of the `Cookie` header.
|
|
123
|
+
* @returns The value of the first cookie of that name; `undefined` without it, or when it cannot be decoded.
|
|
124
|
+
*/
|
|
125
|
+
protected cookieValue(header: string | undefined): string | undefined;
|
|
126
|
+
}
|