@dws-std/i18n 1.0.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.md +21 -0
- package/README.md +148 -0
- package/dist/entry.d.ts +20 -0
- package/dist/exception/define-exception-catalog.d.ts +30 -0
- package/dist/exception/localized-http-exception.d.ts +38 -0
- package/dist/exception/type/exception-entry.d.ts +17 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +68 -0
- package/dist/message/define-message-catalog.d.ts +27 -0
- package/dist/message/type/message-entry.d.ts +14 -0
- package/dist/message/type/resolved-message.d.ts +15 -0
- package/dist/resolve-message.d.ts +16 -0
- package/dist/type/translations.d.ts +2 -0
- package/package.json +56 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Dominus Web Services (DWS)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# π DWS I18n
|
|
2
|
+
|
|
3
|
+
Type-safe internationalization for TypeScript.
|
|
4
|
+
Define your translation catalogs once, and get localized messages and HTTP exceptions with full traceability, all validated at compile time.
|
|
5
|
+
|
|
6
|
+
## Why this package?
|
|
7
|
+
|
|
8
|
+
Internationalization is often treated as an afterthought, strings scattered across files, parameters interpolated by hand, no type safety.
|
|
9
|
+
This package takes a different approach: you declare structured catalogs with `entry()`, and the compiler does the rest.
|
|
10
|
+
Parameters, locales, HTTP statuses, if something's wrong, you'll know before your code even runs.
|
|
11
|
+
|
|
12
|
+
It also plays nicely with `@dws-std/error`. Exception catalogs produce `LocalizedHttpException` instances that carry translations,
|
|
13
|
+
a UUID v7, and an HTTP status code, so your error handling stays consistent and traceable.
|
|
14
|
+
|
|
15
|
+
## π Table of Contents
|
|
16
|
+
|
|
17
|
+
- [Features](#-features)
|
|
18
|
+
- [Installation](#-installation)
|
|
19
|
+
- [Usage](#-usage)
|
|
20
|
+
- [API Reference](#-api-reference)
|
|
21
|
+
- [License](#-license)
|
|
22
|
+
- [Contact](#-contact)
|
|
23
|
+
|
|
24
|
+
## β¨ Features
|
|
25
|
+
|
|
26
|
+
- π **Type-safe catalogs** : Parameters, locales, and HTTP status are all validated at compile time thanks to `entry()`.
|
|
27
|
+
- π **Multi-locale** : Each entry holds all its translations; pick the right one at call-time.
|
|
28
|
+
- π¨ **Localized exceptions** : `defineExceptionCatalog` gives you factory functions that create `LocalizedHttpException` instances, complete with status codes and UUID tracking.
|
|
29
|
+
- π¬ **Localized messages** : `defineMessageCatalog` does the same for plain messages : confirmations, notifications, anything that isn't an error.
|
|
30
|
+
- π **Template interpolation** : Use `{{param}}` placeholders in translations; `resolveMessage` fills them in.
|
|
31
|
+
- π **UUID v7 tracking** : Every exception inherits traceability from `@dws-std/error`.
|
|
32
|
+
|
|
33
|
+
## π§ Installation
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
bun add @dws-std/i18n
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
> **Peer dependency:** `@dws-std/error` must be installed alongside.
|
|
40
|
+
|
|
41
|
+
## βοΈ Usage
|
|
42
|
+
|
|
43
|
+
### Defining entries
|
|
44
|
+
|
|
45
|
+
`entry()` is the building block. Give it a `status` and it becomes an exception entry; leave `status` out and it's a plain message entry.
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
import { entry } from '@dws-std/i18n';
|
|
49
|
+
|
|
50
|
+
// This will produce a LocalizedHttpException when used in an exception catalog
|
|
51
|
+
const unauthorized = entry({
|
|
52
|
+
status: 'UNAUTHORIZED',
|
|
53
|
+
translations: {
|
|
54
|
+
en: 'Invalid credentials',
|
|
55
|
+
fr: 'Identifiants invalides'
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// This will produce a plain ResolvedMessage when used in a message catalog
|
|
60
|
+
const welcome = entry<{ name: string }>({
|
|
61
|
+
translations: {
|
|
62
|
+
en: 'Welcome, {{name}}!',
|
|
63
|
+
fr: 'Bienvenue, {{name}} !'
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Exception catalogs
|
|
69
|
+
|
|
70
|
+
Group related exception entries under a namespace. Each key becomes a factory function you can call to throw a localized exception.
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
import { defineExceptionCatalog, entry } from '@dws-std/i18n';
|
|
74
|
+
|
|
75
|
+
const AUTH_ERRORS = defineExceptionCatalog({
|
|
76
|
+
namespace: 'auth',
|
|
77
|
+
defaultLocale: 'en',
|
|
78
|
+
definitions: {
|
|
79
|
+
invalidCredentials: entry({
|
|
80
|
+
status: 'UNAUTHORIZED',
|
|
81
|
+
translations: {
|
|
82
|
+
en: 'Invalid credentials',
|
|
83
|
+
fr: 'Identifiants invalides'
|
|
84
|
+
}
|
|
85
|
+
}),
|
|
86
|
+
emailTaken: entry<{ email: string }>({
|
|
87
|
+
status: 'CONFLICT',
|
|
88
|
+
translations: {
|
|
89
|
+
en: 'Email "{{email}}" is already taken',
|
|
90
|
+
fr: 'L\'email "{{email}}" est dΓ©jΓ utilisΓ©'
|
|
91
|
+
}
|
|
92
|
+
})
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// Throws a LocalizedHttpException with status 401
|
|
97
|
+
throw AUTH_ERRORS.invalidCredentials();
|
|
98
|
+
|
|
99
|
+
// With parameters, type-checked, so you can't forget one
|
|
100
|
+
throw AUTH_ERRORS.emailTaken({ email: 'user@example.com' });
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Message catalogs
|
|
104
|
+
|
|
105
|
+
Same idea, but for things that aren't errors, success confirmations, notifications, labels, etc.
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
import { defineMessageCatalog, entry } from '@dws-std/i18n';
|
|
109
|
+
|
|
110
|
+
const DNS_MESSAGES = defineMessageCatalog({
|
|
111
|
+
defaultLocale: 'en',
|
|
112
|
+
definitions: {
|
|
113
|
+
recordCreated: entry({
|
|
114
|
+
translations: {
|
|
115
|
+
en: 'DNS record created successfully',
|
|
116
|
+
fr: 'Enregistrement DNS créé avec succès'
|
|
117
|
+
}
|
|
118
|
+
})
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const msg = DNS_MESSAGES.recordCreated();
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Resolving to a specific locale
|
|
126
|
+
|
|
127
|
+
`resolveMessage` takes a `LocalizedHttpException` or a `ResolvedMessage` and returns the interpolated string for the locale you want.
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
import { resolveMessage } from '@dws-std/i18n';
|
|
131
|
+
|
|
132
|
+
const error = AUTH_ERRORS.emailTaken({ email: 'a@b.com' });
|
|
133
|
+
|
|
134
|
+
resolveMessage(error); // default locale β "Email "a@b.com" is already taken"
|
|
135
|
+
resolveMessage(error, 'fr'); // β "L'email "a@b.com" est dΓ©jΓ utilisΓ©"
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## π API Reference
|
|
139
|
+
|
|
140
|
+
Full docs: [Dominus-Web-Service.github.io/packages](https://Dominus-Web-Service.github.io/packages/)
|
|
141
|
+
|
|
142
|
+
## βοΈ License
|
|
143
|
+
|
|
144
|
+
MIT - Feel free to use it.
|
|
145
|
+
|
|
146
|
+
## π§ Contact
|
|
147
|
+
|
|
148
|
+
- GitHub: [Dominus-Web-Service](https://github.com/Dominus-Web-Service/packages)
|
package/dist/entry.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { HttpStatusCode, HttpStatusKey } from '@dws-std/error';
|
|
2
|
+
import type { ExceptionEntry } from './exception/type/exception-entry';
|
|
3
|
+
import type { MessageEntry } from './message/type/message-entry';
|
|
4
|
+
/**
|
|
5
|
+
* Creates a single catalog entry for use inside `defineExceptionCatalog` or `defineMessageCatalog`.
|
|
6
|
+
*
|
|
7
|
+
* When `status` is included in the definition the return type narrows to
|
|
8
|
+
* {@link ExceptionEntry}; without it the return type is {@link MessageEntry}.
|
|
9
|
+
*
|
|
10
|
+
* @param definition - Translations (and optional status) for this entry.
|
|
11
|
+
*
|
|
12
|
+
* @returns The definition object, typed as either {@link ExceptionEntry} or {@link MessageEntry} based on the presence of `status`.
|
|
13
|
+
*/
|
|
14
|
+
export declare function entry<TParams extends Record<string, string> = Record<string, string>, TLocales extends string = string>(definition: {
|
|
15
|
+
readonly status: HttpStatusKey | HttpStatusCode;
|
|
16
|
+
readonly translations: Readonly<Record<TLocales, string>>;
|
|
17
|
+
}): ExceptionEntry<TParams, TLocales>;
|
|
18
|
+
export declare function entry<TParams extends Record<string, string> = Record<string, string>, TLocales extends string = string>(definition: {
|
|
19
|
+
readonly translations: Readonly<Record<TLocales, string>>;
|
|
20
|
+
}): MessageEntry<TParams, TLocales>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { LocalizedHttpException } from './localized-http-exception';
|
|
2
|
+
import type { ExceptionEntry } from './type/exception-entry';
|
|
3
|
+
export type ExceptionCatalog<TDefs extends Record<string, ExceptionEntry<any>>> = {
|
|
4
|
+
readonly [K in keyof TDefs]: TDefs[K] extends ExceptionEntry<infer P> ? [P] extends [Record<string, never>] ? () => LocalizedHttpException : (params: P) => LocalizedHttpException : never;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Configuration for {@link defineExceptionCatalog}.
|
|
8
|
+
*
|
|
9
|
+
* @template TDefs - Shape of the exception definitions map.
|
|
10
|
+
*/
|
|
11
|
+
export interface DefineExceptionCatalogOptions<TDefs extends Record<string, ExceptionEntry<any>>> {
|
|
12
|
+
/** Prefix prepended to every error code (e.g. `'dns'` β `'dns.invalidRecordType'`). */
|
|
13
|
+
readonly namespace: string;
|
|
14
|
+
/** Locale used to build the default `message` when no locale is specified. */
|
|
15
|
+
readonly defaultLocale: keyof TDefs[keyof TDefs]['translations'];
|
|
16
|
+
/** Map of exception definitions keyed by error name. */
|
|
17
|
+
readonly definitions: TDefs;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Builds a typed exception catalog from a set of {@link ExceptionEntry} definitions.
|
|
21
|
+
*
|
|
22
|
+
* Each key in `definitions` becomes a factory function that creates
|
|
23
|
+
* a {@link LocalizedHttpException} pre-filled with the right translations,
|
|
24
|
+
* HTTP status, and error code (`namespace.key`).
|
|
25
|
+
*
|
|
26
|
+
* @param options - Namespace, default locale, and exception definitions.
|
|
27
|
+
*
|
|
28
|
+
* @returns An object whose keys mirror `definitions`, each a factory function.
|
|
29
|
+
*/
|
|
30
|
+
export declare const defineExceptionCatalog: <const TDefs extends Record<string, ExceptionEntry<any>>>(options: DefineExceptionCatalogOptions<TDefs>) => ExceptionCatalog<TDefs>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { HttpException, type HttpExceptionOptions } from '@dws-std/error';
|
|
2
|
+
import type { Translations } from '../type/translations';
|
|
3
|
+
/**
|
|
4
|
+
* Options accepted by the {@link LocalizedHttpException} constructor.
|
|
5
|
+
*
|
|
6
|
+
* @template TCause - Type of the underlying cause.
|
|
7
|
+
*/
|
|
8
|
+
export interface LocalizedHttpExceptionOptions<TCause = unknown> extends HttpExceptionOptions<TCause> {
|
|
9
|
+
/** All available translations keyed by locale. */
|
|
10
|
+
readonly translations: Translations;
|
|
11
|
+
/** Parameter values to interpolate into `{{placeholder}}` tokens. */
|
|
12
|
+
readonly params?: Readonly<Record<string, string>> | undefined;
|
|
13
|
+
/** Locale used to build the default `message` string. */
|
|
14
|
+
readonly defaultLocale: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* HTTP exception that carries translated messages.
|
|
18
|
+
*
|
|
19
|
+
* The `message` property is automatically resolved to the default locale.
|
|
20
|
+
* Use {@link resolveMessage} to get a translation in a different locale.
|
|
21
|
+
*
|
|
22
|
+
* @template TCause - Type of the underlying cause.
|
|
23
|
+
*/
|
|
24
|
+
export declare class LocalizedHttpException<const TCause = unknown> extends HttpException<TCause> {
|
|
25
|
+
/** All available translations keyed by locale. */
|
|
26
|
+
readonly translations: Translations;
|
|
27
|
+
/** Parameter values interpolated into `{{placeholder}}` tokens. */
|
|
28
|
+
readonly params: Readonly<Record<string, string>> | undefined;
|
|
29
|
+
/** Locale used to build the default `message` string. */
|
|
30
|
+
readonly defaultLocale: string;
|
|
31
|
+
/**
|
|
32
|
+
* Creates a new localized HTTP exception.
|
|
33
|
+
*
|
|
34
|
+
* @param code - Application-specific error code (e.g. `'dns.invalidRecordType'`).
|
|
35
|
+
* @param init - Translations, params, status, and cause.
|
|
36
|
+
*/
|
|
37
|
+
constructor(code: string, init: LocalizedHttpExceptionOptions<TCause>);
|
|
38
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { HttpStatusCode, HttpStatusKey } from '@dws-std/error';
|
|
2
|
+
/**
|
|
3
|
+
* Blueprint for a translatable HTTP exception.
|
|
4
|
+
*
|
|
5
|
+
* Used inside an exception catalog created with `defineExceptionCatalog`.
|
|
6
|
+
*
|
|
7
|
+
* @template TParams - Parameter placeholders the message expects (e.g. `{ field: string }`).
|
|
8
|
+
* @template TLocales - Locale keys that must be provided (e.g. `'en' | 'fr'`).
|
|
9
|
+
*/
|
|
10
|
+
export interface ExceptionEntry<TParams extends Record<string, string> = Record<string, string>, TLocales extends string = string> {
|
|
11
|
+
/** HTTP status to attach (key name like `'NOT_FOUND'` or numeric code like `404`). */
|
|
12
|
+
readonly status: HttpStatusKey | HttpStatusCode;
|
|
13
|
+
/** Translated error messages keyed by locale. */
|
|
14
|
+
readonly translations: Readonly<Record<TLocales, string>>;
|
|
15
|
+
/** Placeholder values to interpolate into the translated string. */
|
|
16
|
+
readonly params?: TParams;
|
|
17
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { entry } from './entry';
|
|
2
|
+
export { defineExceptionCatalog } from './exception/define-exception-catalog';
|
|
3
|
+
export type { DefineExceptionCatalogOptions, ExceptionCatalog } from './exception/define-exception-catalog';
|
|
4
|
+
export { LocalizedHttpException } from './exception/localized-http-exception';
|
|
5
|
+
export type { LocalizedHttpExceptionOptions as LocalizedHttpExceptionInit } from './exception/localized-http-exception';
|
|
6
|
+
export type { ExceptionEntry } from './exception/type/exception-entry';
|
|
7
|
+
export { defineMessageCatalog } from './message/define-message-catalog';
|
|
8
|
+
export type { DefineMessageCatalogOptions, MessageCatalog } from './message/define-message-catalog';
|
|
9
|
+
export type { MessageEntry } from './message/type/message-entry';
|
|
10
|
+
export type { ResolvedMessage } from './message/type/resolved-message';
|
|
11
|
+
export { resolveMessage } from './resolve-message';
|
|
12
|
+
export type { Translations } from './type/translations';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// src/entry.ts
|
|
3
|
+
function entry(definition) {
|
|
4
|
+
return definition;
|
|
5
|
+
}
|
|
6
|
+
// src/exception/localized-http-exception.ts
|
|
7
|
+
import { HttpException } from "@dws-std/error";
|
|
8
|
+
|
|
9
|
+
// src/resolve-message.ts
|
|
10
|
+
var _interpolate = (template, params) => template.replace(/\{\{(\w+)\}\}/g, (_, key) => params[key] ?? `{{${key}}}`);
|
|
11
|
+
var resolveMessage = (target, locale) => target.params ? _interpolate(target.translations[locale ?? target.defaultLocale] ?? "", target.params) : target.translations[locale ?? target.defaultLocale] ?? "";
|
|
12
|
+
|
|
13
|
+
// src/exception/localized-http-exception.ts
|
|
14
|
+
class LocalizedHttpException extends HttpException {
|
|
15
|
+
translations;
|
|
16
|
+
params;
|
|
17
|
+
defaultLocale;
|
|
18
|
+
constructor(code, init) {
|
|
19
|
+
super(resolveMessage({
|
|
20
|
+
translations: init.translations,
|
|
21
|
+
params: init.params,
|
|
22
|
+
defaultLocale: init.defaultLocale
|
|
23
|
+
}), {
|
|
24
|
+
cause: init.cause,
|
|
25
|
+
status: init.status,
|
|
26
|
+
code
|
|
27
|
+
});
|
|
28
|
+
this.translations = init.translations;
|
|
29
|
+
this.params = init.params;
|
|
30
|
+
this.defaultLocale = init.defaultLocale;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// src/exception/define-exception-catalog.ts
|
|
35
|
+
var defineExceptionCatalog = (options) => {
|
|
36
|
+
const { namespace, defaultLocale, definitions } = options;
|
|
37
|
+
const catalog = {};
|
|
38
|
+
for (const [key, def] of Object.entries(definitions)) {
|
|
39
|
+
const exceptionDef = def;
|
|
40
|
+
catalog[key] = (params = {}) => new LocalizedHttpException(`${namespace}.${key}`, {
|
|
41
|
+
status: exceptionDef.status,
|
|
42
|
+
translations: exceptionDef.translations,
|
|
43
|
+
params,
|
|
44
|
+
defaultLocale
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
return catalog;
|
|
48
|
+
};
|
|
49
|
+
// src/message/define-message-catalog.ts
|
|
50
|
+
var defineMessageCatalog = (options) => {
|
|
51
|
+
const catalog = {};
|
|
52
|
+
for (const [key, def] of Object.entries(options.definitions)) {
|
|
53
|
+
const msgDef = def;
|
|
54
|
+
catalog[key] = (params = {}) => ({
|
|
55
|
+
translations: msgDef.translations,
|
|
56
|
+
params,
|
|
57
|
+
defaultLocale: options.defaultLocale
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
return catalog;
|
|
61
|
+
};
|
|
62
|
+
export {
|
|
63
|
+
resolveMessage,
|
|
64
|
+
entry,
|
|
65
|
+
defineMessageCatalog,
|
|
66
|
+
defineExceptionCatalog,
|
|
67
|
+
LocalizedHttpException
|
|
68
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { MessageEntry } from './type/message-entry';
|
|
2
|
+
import type { ResolvedMessage } from './type/resolved-message';
|
|
3
|
+
export type MessageCatalog<TDefs extends Record<string, MessageEntry<any>>> = {
|
|
4
|
+
readonly [K in keyof TDefs]: TDefs[K] extends MessageEntry<infer P> ? [P] extends [Record<string, never>] ? () => ResolvedMessage : (params: P) => ResolvedMessage : never;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Configuration for {@link defineMessageCatalog}.
|
|
8
|
+
*
|
|
9
|
+
* @template TDefs - Shape of the message definitions map.
|
|
10
|
+
*/
|
|
11
|
+
export interface DefineMessageCatalogOptions<TDefs extends Record<string, MessageEntry<any>>> {
|
|
12
|
+
/** Locale used when no explicit locale is passed to `resolveMessage`. */
|
|
13
|
+
readonly defaultLocale: keyof TDefs[keyof TDefs]['translations'];
|
|
14
|
+
/** Map of message definitions keyed by message name. */
|
|
15
|
+
readonly definitions: TDefs;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Builds a typed message catalog from a set of {@link MessageEntry} definitions.
|
|
19
|
+
*
|
|
20
|
+
* Each key in `definitions` becomes a factory function that creates
|
|
21
|
+
* a {@link ResolvedMessage} pre-filled with the right translations and default locale.
|
|
22
|
+
*
|
|
23
|
+
* @param options - Default locale and message definitions.
|
|
24
|
+
*
|
|
25
|
+
* @returns An object whose keys mirror `definitions`, each a factory function.
|
|
26
|
+
*/
|
|
27
|
+
export declare const defineMessageCatalog: <TDefs extends Record<string, MessageEntry<any>>>(options: DefineMessageCatalogOptions<TDefs>) => MessageCatalog<TDefs>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Blueprint for a translatable message.
|
|
3
|
+
*
|
|
4
|
+
* Used inside a message catalog created with `defineMessageCatalog`.
|
|
5
|
+
*
|
|
6
|
+
* @template TParams - Parameter placeholders the message expects (e.g. `{ domain: string }`).
|
|
7
|
+
* @template TLocales - Locale keys that must be provided (e.g. `'en' | 'fr'`).
|
|
8
|
+
*/
|
|
9
|
+
export interface MessageEntry<TParams extends Record<string, string> = Record<string, string>, TLocales extends string = string> {
|
|
10
|
+
/** Translated strings keyed by locale. */
|
|
11
|
+
readonly translations: Readonly<Record<TLocales, string>>;
|
|
12
|
+
/** Placeholder values to interpolate into the translated string. */
|
|
13
|
+
readonly params?: TParams;
|
|
14
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Translations } from '../../type/translations';
|
|
2
|
+
/**
|
|
3
|
+
* Fully resolved message ready to be rendered.
|
|
4
|
+
*
|
|
5
|
+
* Returned by the factory functions generated by `defineMessageCatalog`.
|
|
6
|
+
* Pass it to {@link resolveMessage} to get the final string for a given locale.
|
|
7
|
+
*/
|
|
8
|
+
export interface ResolvedMessage {
|
|
9
|
+
/** All available translations keyed by locale. */
|
|
10
|
+
readonly translations: Translations;
|
|
11
|
+
/** Parameter values to interpolate into `{{placeholder}}` tokens. */
|
|
12
|
+
readonly params?: Readonly<Record<string, string>> | undefined;
|
|
13
|
+
/** Locale used when no explicit locale is passed to `resolveMessage`. */
|
|
14
|
+
readonly defaultLocale: string;
|
|
15
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { LocalizedHttpException } from './exception/localized-http-exception';
|
|
2
|
+
import type { ResolvedMessage } from './message/type/resolved-message';
|
|
3
|
+
/**
|
|
4
|
+
* Turns a {@link ResolvedMessage} or {@link LocalizedHttpException} into
|
|
5
|
+
* a plain string for the requested locale.
|
|
6
|
+
*
|
|
7
|
+
* `{{placeholder}}` tokens are replaced with matching values from `target.params`.
|
|
8
|
+
* Falls back to `target.defaultLocale` when `locale` is omitted.
|
|
9
|
+
* Returns an empty string when the requested locale has no translation.
|
|
10
|
+
*
|
|
11
|
+
* @param target - Message or exception to resolve.
|
|
12
|
+
* @param locale - Desired locale (e.g. `'fr'`). Defaults to `target.defaultLocale`.
|
|
13
|
+
*
|
|
14
|
+
* @returns Translated string with placeholders interpolated.
|
|
15
|
+
*/
|
|
16
|
+
export declare const resolveMessage: (target: LocalizedHttpException | ResolvedMessage, locale?: string) => string;
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dws-std/i18n",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Type-safe i18n for TypeScript β define localized exception and message catalogs with compile-time validated parameters.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"bun",
|
|
7
|
+
"dws",
|
|
8
|
+
"exception",
|
|
9
|
+
"http-error",
|
|
10
|
+
"i18n",
|
|
11
|
+
"internationalization",
|
|
12
|
+
"l10n",
|
|
13
|
+
"localization",
|
|
14
|
+
"translation"
|
|
15
|
+
],
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"author": "Dominus Web Services (DWS)",
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "https://github.com/Dominus-Web-Service/std",
|
|
21
|
+
"directory": "packages/i18n"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist"
|
|
25
|
+
],
|
|
26
|
+
"type": "module",
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"default": "./dist/index.js"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "bun builder.ts",
|
|
35
|
+
"docs": "bunx typedoc --tsconfig tsconfig.build.json",
|
|
36
|
+
"fmt:check": "oxfmt --check",
|
|
37
|
+
"fmt": "oxfmt",
|
|
38
|
+
"lint:fix": "oxlint --type-aware --type-check --fix ./src",
|
|
39
|
+
"lint:github": "oxlint --type-aware --type-check --format=github ./src",
|
|
40
|
+
"lint": "oxlint --type-aware --type-check ./src",
|
|
41
|
+
"test": "bun test --pass-with-no-tests --coverage"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@dws-std/error": "^2.0.0"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@types/bun": "^1.3.10",
|
|
48
|
+
"oxfmt": "^0.40.0",
|
|
49
|
+
"oxlint": "^1.55.0",
|
|
50
|
+
"oxlint-tsgolint": "^0.16.0",
|
|
51
|
+
"typescript": "^5.9.3"
|
|
52
|
+
},
|
|
53
|
+
"peerDependencies": {
|
|
54
|
+
"@dws-std/error": "^2.0.0"
|
|
55
|
+
}
|
|
56
|
+
}
|