intor-translator 1.1.5 → 1.2.1
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 +34 -142
- package/dist/index.cjs +206 -85
- package/dist/index.d.cts +92 -81
- package/dist/index.d.ts +92 -81
- package/dist/index.js +207 -85
- package/package.json +15 -23
package/README.md
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
<div align="center">
|
|
4
4
|
|
|
5
|
-
A
|
|
5
|
+
A modern **i18n engine** powered by a customizable, type-safe translation pipeline.
|
|
6
|
+
Easy to use, modular at its core, and fully extensible.
|
|
6
7
|
|
|
7
8
|
</div>
|
|
8
9
|
|
|
@@ -10,44 +11,34 @@ A type safe translator that knows what to say and how to handle the rest.
|
|
|
10
11
|
|
|
11
12
|
[](https://www.npmjs.com/package/intor-translator)
|
|
12
13
|
[](https://bundlephobia.com/package/intor-translator)
|
|
13
|
-
[](https://coveralls.io/github/yiming-liao/intor-translator?branch=main)
|
|
14
15
|
[](https://www.typescriptlang.org/)
|
|
16
|
+
[](LICENSE)
|
|
15
17
|
|
|
16
18
|
</div>
|
|
17
19
|
|
|
18
|
-
>
|
|
19
|
-
> A type-safe i18n engine with fallback, scoped namespaces, and graceful loading.
|
|
20
|
-
|
|
21
|
-
---
|
|
20
|
+
> Structured • Predictable • Beautifully simple
|
|
22
21
|
|
|
23
22
|
## Features
|
|
24
23
|
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
-
|
|
28
|
-
- 🔁 Flexible replacement and interpolation support
|
|
29
|
-
- 🎨 Rich formatting for complex replacement content
|
|
30
|
-
- 🌀 Graceful handling of loading and async states
|
|
31
|
-
- 🔧 Configurable handlers for fallback, loading, and missing keys
|
|
32
|
-
- 🧩 Scoped translators for modules and namespaces
|
|
33
|
-
|
|
34
|
-
---
|
|
24
|
+
- 🔧 **Modular Pipeline** – A pluggable, hook-driven flow for any translation logic.
|
|
25
|
+
- ✨ **Typed Autocomplete** – Inferred keys and locales with precise, reliable completion.
|
|
26
|
+
- 🌐 **Framework-Agnostic** – A lightweight engine that runs anywhere in JavaScript.
|
|
35
27
|
|
|
36
|
-
##
|
|
28
|
+
## Installation
|
|
37
29
|
|
|
38
30
|
```bash
|
|
31
|
+
# npm
|
|
39
32
|
npm install intor-translator
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
or use **yarn**
|
|
43
33
|
|
|
44
|
-
|
|
34
|
+
# yarn
|
|
45
35
|
yarn add intor-translator
|
|
46
|
-
```
|
|
47
36
|
|
|
48
|
-
|
|
37
|
+
# pnpm
|
|
38
|
+
pnpm add intor-translator
|
|
39
|
+
```
|
|
49
40
|
|
|
50
|
-
##
|
|
41
|
+
## Quick Start
|
|
51
42
|
|
|
52
43
|
```typescript
|
|
53
44
|
import { Translator } from "intor-translator";
|
|
@@ -67,134 +58,35 @@ translator.t("hello"); // -> Hello World
|
|
|
67
58
|
translator.t("greeting", { name: "John doe" }); // -> Hello, John doe!
|
|
68
59
|
```
|
|
69
60
|
|
|
70
|
-
|
|
61
|
+
## Handlers & Hooks
|
|
71
62
|
|
|
72
|
-
|
|
63
|
+
Intor Translator is powered by **a flexible pipeline** that lets you control how translations behave and how they are rendered.
|
|
73
64
|
|
|
74
|
-
|
|
65
|
+
### Handlers — format the final output
|
|
75
66
|
|
|
76
|
-
|
|
77
|
-
const translator = new Translator({
|
|
78
|
-
locale: "en",
|
|
79
|
-
messages: {
|
|
80
|
-
en: {
|
|
81
|
-
welcome: "Welcome back, {name}",
|
|
82
|
-
},
|
|
83
|
-
zh: {
|
|
84
|
-
welcome: "歡迎回來,{name}",
|
|
85
|
-
notification: "你有 {count} 則新通知",
|
|
86
|
-
},
|
|
87
|
-
},
|
|
88
|
-
fallbackLocales: { en: ["zh"] }, // Use zh if message not found in en
|
|
89
|
-
placeholder: "Content unavailable", // Shown if key is missing in all locales
|
|
90
|
-
handlers: {
|
|
91
|
-
formatHandler: ({ locale, message }) =>
|
|
92
|
-
locale === "zh" ? `${message}。` : `${message}.`, // Auto punctuation per locale
|
|
93
|
-
},
|
|
94
|
-
});
|
|
67
|
+
<sup>_changing how translations look_.</sup>
|
|
95
68
|
|
|
96
|
-
|
|
97
|
-
console.log(translator.t("welcome", { name: "John" })); // -> Welcome back, John.
|
|
69
|
+
Handlers operate on the resolved message, use them to:
|
|
98
70
|
|
|
99
|
-
|
|
100
|
-
|
|
71
|
+
- format ICU messages
|
|
72
|
+
- apply custom plural logic
|
|
73
|
+
- post-process output
|
|
74
|
+
- style or transform the final string
|
|
101
75
|
|
|
102
|
-
|
|
103
|
-
console.log(translator.t("unknown.key")); // -> Content unavailable
|
|
104
|
-
```
|
|
76
|
+
### Hooks — shape the translation flow
|
|
105
77
|
|
|
106
|
-
|
|
78
|
+
<sup>_changing how translations work_.</sup>
|
|
107
79
|
|
|
108
|
-
|
|
109
|
-
import { Translator, FormatMessage } from "intor-translator";
|
|
110
|
-
import { IntlMessageFormat } from "intl-messageformat";
|
|
80
|
+
Hooks run through the pipeline and can intercept any stage, use them to:
|
|
111
81
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
};
|
|
82
|
+
- transform keys or messages
|
|
83
|
+
- adjust fallback behavior
|
|
84
|
+
- implement loading or missing logic
|
|
85
|
+
- attach metadata or analytics
|
|
117
86
|
|
|
118
|
-
|
|
119
|
-
en: {
|
|
120
|
-
notification:
|
|
121
|
-
"{name} has {count, plural, =0 {no messages} one {1 message} other {# messages}}.",
|
|
122
|
-
},
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
// Create a translator instance
|
|
126
|
-
const translator = new Translator({
|
|
127
|
-
locale: "en",
|
|
128
|
-
messages,
|
|
129
|
-
handlers: { formatHandler },
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
translator.t("notification", { name: "John", count: 0 }); // -> John has no messages.
|
|
133
|
-
translator.t("notification", { name: "John", count: 5 }); // -> John has 5 messages.
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
---
|
|
137
|
-
|
|
138
|
-
## API Reference
|
|
139
|
-
|
|
140
|
-
### Translator Parameters
|
|
141
|
-
|
|
142
|
-
| Option | Type | Description |
|
|
143
|
-
| ----------------- | ------------------------------------- | ------------------------------------------------------------------------ |
|
|
144
|
-
| `messages` | `Readonly<LocaleMessages>` | Translation messages grouped by locale and namespace |
|
|
145
|
-
| `locale` | `string` | Active locale key |
|
|
146
|
-
| `fallbackLocales` | `Record<Locale, Locale[]>` (optional) | Locales to fallback to when a key is missing |
|
|
147
|
-
| `placeholder` | `string` (optional) | Message to display when a key is missing in all locales |
|
|
148
|
-
| `loadingMessage` | `string` (optional) | Message to display during loading or async state |
|
|
149
|
-
| `handlers` | `TranslateHandlers` (optional) | Custom functions for formatting, loading state, and missing key handling |
|
|
150
|
-
|
|
151
|
-
**TranslateHandlers :**
|
|
152
|
-
|
|
153
|
-
```ts
|
|
154
|
-
type TranslateHandlers = {
|
|
155
|
-
formatHandler?: (
|
|
156
|
-
ctx: TranslateHandlerContext & { message: string },
|
|
157
|
-
) => unknown;
|
|
158
|
-
LoadingHandler?: (ctx: TranslateHandlerContext) => unknown;
|
|
159
|
-
MissingHandler?: (ctx: TranslateHandlerContext) => unknown;
|
|
160
|
-
};
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
> Use handlers to control how messages are formatted, what to show during loading, and how to respond to missing keys.
|
|
164
|
-
> Each handler receives a full translation context, including the current locale, key, and replacement values.
|
|
87
|
+
> Together, they form a customizable translation pipeline — structured, predictable, beautifully simple.
|
|
165
88
|
|
|
166
89
|
---
|
|
167
90
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
| Property | Type | Description |
|
|
171
|
-
| ----------- | ----------- | ------------------------------------------ |
|
|
172
|
-
| `messages` | `M` | Current messages object |
|
|
173
|
-
| `locale` | `Locale<M>` | Currently active locale |
|
|
174
|
-
| `isLoading` | `boolean` | Whether the translator is in loading state |
|
|
175
|
-
|
|
176
|
-
---
|
|
177
|
-
|
|
178
|
-
### Instance Methods
|
|
179
|
-
|
|
180
|
-
| Method | Signature | Description |
|
|
181
|
-
| ------------- | ------------------------------------------------- | ------------------------------------------------------------------- |
|
|
182
|
-
| `setMessages` | `(messages: M) => void` | Replaces the current message set |
|
|
183
|
-
| `setLocale` | `(locale: Locale<M>) => boolean` | Sets a new locale and returns whether it changed |
|
|
184
|
-
| `setLoading` | `(state: boolean) => void` | Sets the loading state manually |
|
|
185
|
-
| `hasKey` | `(key, targetLocale?) => boolean` | Checks whether the given key exists in the target or current locale |
|
|
186
|
-
| `t` | `<Result = string>(key, replacements?) => Result` | Translates a key with optional replacements |
|
|
187
|
-
| `scoped` | `(preKey: string) => { hasKey(), t() }` | Creates a scoped translator with a namespace prefix |
|
|
188
|
-
|
|
189
|
-
**translator.t(key, replacements?)**
|
|
190
|
-
|
|
191
|
-
- Fully type-safe key access
|
|
192
|
-
- Supports nested keys
|
|
193
|
-
- Supports both string and rich replacements
|
|
194
|
-
|
|
195
|
-
**translator.scoped(preKey)**
|
|
196
|
-
|
|
197
|
-
- Returns a scoped translator instance based on a message subtree
|
|
198
|
-
- Useful for organizing large sets of translations with shared prefixes
|
|
199
|
-
|
|
200
|
-
---
|
|
91
|
+
**_For more advanced usage, see the full examples._**
|
|
92
|
+
[View examples ↗](https://github.com/yiming-liao/intor-translator/tree/main/examples)
|
package/dist/index.cjs
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
var rura = require('rura');
|
|
4
|
+
|
|
5
|
+
// src/pipeline/hooks/find-message.ts
|
|
6
|
+
|
|
7
|
+
// src/translators/shared/utils/find-message-in-locales.ts
|
|
4
8
|
var findMessageInLocales = ({
|
|
5
9
|
messages,
|
|
6
10
|
candidateLocales,
|
|
@@ -23,28 +27,50 @@ var findMessageInLocales = ({
|
|
|
23
27
|
}
|
|
24
28
|
};
|
|
25
29
|
|
|
26
|
-
// src/
|
|
27
|
-
var
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
// src/pipeline/hooks/find-message.ts
|
|
31
|
+
var findMessage = rura.rura.createHook(
|
|
32
|
+
"findMessage",
|
|
33
|
+
(ctx) => {
|
|
34
|
+
ctx.rawMessage = findMessageInLocales({
|
|
35
|
+
messages: ctx.messages,
|
|
36
|
+
candidateLocales: ctx.candidateLocales,
|
|
37
|
+
key: ctx.key
|
|
38
|
+
});
|
|
39
|
+
},
|
|
40
|
+
200
|
|
41
|
+
);
|
|
32
42
|
|
|
33
|
-
// src/
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
// src/pipeline/utils/make-handler-context.ts
|
|
44
|
+
function makeHandlerContext(ctx) {
|
|
45
|
+
return Object.freeze({
|
|
46
|
+
locale: ctx.locale,
|
|
47
|
+
key: ctx.key,
|
|
48
|
+
replacements: ctx.replacements,
|
|
49
|
+
messages: ctx.messages,
|
|
50
|
+
candidateLocales: ctx.candidateLocales,
|
|
51
|
+
config: ctx.config,
|
|
52
|
+
isLoading: ctx.isLoading,
|
|
53
|
+
rawMessage: ctx.rawMessage,
|
|
54
|
+
formattedMessage: ctx.formattedMessage,
|
|
55
|
+
meta: ctx.meta
|
|
56
|
+
});
|
|
57
|
+
}
|
|
46
58
|
|
|
47
|
-
// src/
|
|
59
|
+
// src/pipeline/hooks/format.ts
|
|
60
|
+
var format = rura.rura.createHook(
|
|
61
|
+
"format",
|
|
62
|
+
(ctx) => {
|
|
63
|
+
const { config, rawMessage } = ctx;
|
|
64
|
+
const { formatHandler } = config.handlers || {};
|
|
65
|
+
if (!formatHandler || rawMessage === void 0) return;
|
|
66
|
+
ctx.formattedMessage = formatHandler(
|
|
67
|
+
makeHandlerContext(ctx)
|
|
68
|
+
);
|
|
69
|
+
},
|
|
70
|
+
500
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
// src/translators/shared/utils/replace-values.ts
|
|
48
74
|
var replaceValues = (message, params) => {
|
|
49
75
|
if (!params || typeof params !== "object" || Object.keys(params).length === 0) {
|
|
50
76
|
return message;
|
|
@@ -63,63 +89,113 @@ var replaceValues = (message, params) => {
|
|
|
63
89
|
return replaced;
|
|
64
90
|
};
|
|
65
91
|
|
|
66
|
-
// src/
|
|
67
|
-
var
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
92
|
+
// src/pipeline/hooks/interpolate.ts
|
|
93
|
+
var interpolate = rura.rura.createHook(
|
|
94
|
+
"interpolate",
|
|
95
|
+
(ctx) => {
|
|
96
|
+
const { rawMessage, formattedMessage, replacements } = ctx;
|
|
97
|
+
const message = formattedMessage ?? rawMessage;
|
|
98
|
+
if (typeof message !== "string" || !replacements) {
|
|
99
|
+
ctx.finalMessage = message;
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
ctx.finalMessage = replaceValues(message, replacements);
|
|
103
|
+
},
|
|
104
|
+
600
|
|
105
|
+
);
|
|
106
|
+
var loading = rura.rura.createHook(
|
|
107
|
+
"loading",
|
|
108
|
+
(ctx) => {
|
|
109
|
+
const { config, isLoading } = ctx;
|
|
110
|
+
if (!isLoading) return;
|
|
111
|
+
const { loadingHandler } = config.handlers || {};
|
|
112
|
+
if (loadingHandler) {
|
|
113
|
+
return {
|
|
114
|
+
early: true,
|
|
115
|
+
output: loadingHandler(makeHandlerContext(ctx))
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
const { loadingMessage } = config;
|
|
119
|
+
if (loadingMessage) {
|
|
120
|
+
return { early: true, output: loadingMessage };
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
300
|
|
124
|
+
);
|
|
125
|
+
var missing = rura.rura.createHook(
|
|
126
|
+
"missing",
|
|
127
|
+
(ctx) => {
|
|
128
|
+
const { config, key, rawMessage } = ctx;
|
|
129
|
+
if (rawMessage !== void 0) return;
|
|
130
|
+
const { missingHandler } = config.handlers || {};
|
|
131
|
+
if (missingHandler) {
|
|
132
|
+
return {
|
|
133
|
+
early: true,
|
|
134
|
+
output: missingHandler(makeHandlerContext(ctx))
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
const { placeholder } = config;
|
|
138
|
+
if (placeholder) {
|
|
139
|
+
return { early: true, output: placeholder };
|
|
140
|
+
}
|
|
141
|
+
return { early: true, output: key };
|
|
142
|
+
},
|
|
143
|
+
400
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
// src/translators/shared/utils/resolve-candidate-locales.ts
|
|
147
|
+
var resolveCandidateLocales = (locale, fallbackLocalesMap) => {
|
|
148
|
+
const fallbacks = fallbackLocalesMap?.[locale] || [];
|
|
149
|
+
const filteredFallbacks = fallbacks.filter((l) => l !== locale);
|
|
150
|
+
return [locale, ...filteredFallbacks];
|
|
97
151
|
};
|
|
98
152
|
|
|
153
|
+
// src/pipeline/hooks/resolve-locales.ts
|
|
154
|
+
var resolveLocales = rura.rura.createHook(
|
|
155
|
+
"resolveLocales",
|
|
156
|
+
(ctx) => {
|
|
157
|
+
ctx.candidateLocales = resolveCandidateLocales(
|
|
158
|
+
ctx.locale,
|
|
159
|
+
ctx.config.fallbackLocales
|
|
160
|
+
);
|
|
161
|
+
},
|
|
162
|
+
100
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
// src/pipeline/index.ts
|
|
166
|
+
var DEFAULT_HOOKS = [
|
|
167
|
+
resolveLocales,
|
|
168
|
+
findMessage,
|
|
169
|
+
loading,
|
|
170
|
+
missing,
|
|
171
|
+
format,
|
|
172
|
+
interpolate
|
|
173
|
+
];
|
|
174
|
+
|
|
99
175
|
// src/translators/base-translator/base-translator.ts
|
|
100
176
|
var BaseTranslator = class {
|
|
101
177
|
/** Current messages for translation */
|
|
102
|
-
|
|
178
|
+
_messages;
|
|
103
179
|
/** Current active locale */
|
|
104
|
-
|
|
180
|
+
_locale;
|
|
105
181
|
/** Current loading state */
|
|
106
|
-
|
|
182
|
+
_isLoading;
|
|
107
183
|
constructor(options) {
|
|
108
|
-
this.
|
|
109
|
-
this.
|
|
110
|
-
this.
|
|
184
|
+
this._messages = options.messages ?? {};
|
|
185
|
+
this._locale = options.locale;
|
|
186
|
+
this._isLoading = options.isLoading ?? false;
|
|
111
187
|
}
|
|
112
188
|
/** Get messages. */
|
|
113
189
|
get messages() {
|
|
114
|
-
return this.
|
|
190
|
+
return this._messages;
|
|
115
191
|
}
|
|
116
192
|
/** Get the current active locale. */
|
|
117
193
|
get locale() {
|
|
118
|
-
return this.
|
|
194
|
+
return this._locale;
|
|
119
195
|
}
|
|
120
196
|
/** Get the current loading state. */
|
|
121
197
|
get isLoading() {
|
|
122
|
-
return this.
|
|
198
|
+
return this._isLoading;
|
|
123
199
|
}
|
|
124
200
|
/**
|
|
125
201
|
* Replace messages with new ones.
|
|
@@ -128,7 +204,7 @@ var BaseTranslator = class {
|
|
|
128
204
|
* The type cast bypasses TypeScript restrictions on dynamic messages.
|
|
129
205
|
*/
|
|
130
206
|
setMessages(messages) {
|
|
131
|
-
this.
|
|
207
|
+
this._messages = messages;
|
|
132
208
|
}
|
|
133
209
|
/**
|
|
134
210
|
* Set the active locale.
|
|
@@ -136,30 +212,74 @@ var BaseTranslator = class {
|
|
|
136
212
|
* - Note: Unlike `setMessages`, the locale structure cannot be changed at runtime.
|
|
137
213
|
*/
|
|
138
214
|
setLocale(newLocale) {
|
|
139
|
-
this.
|
|
215
|
+
this._locale = newLocale;
|
|
140
216
|
}
|
|
141
217
|
/** Set the loading state. */
|
|
142
218
|
setLoading(state) {
|
|
143
|
-
this.
|
|
219
|
+
this._isLoading = state;
|
|
144
220
|
}
|
|
145
221
|
};
|
|
146
222
|
|
|
223
|
+
// src/translators/shared/has-key.ts
|
|
224
|
+
var hasKey = ({
|
|
225
|
+
messages,
|
|
226
|
+
locale,
|
|
227
|
+
key,
|
|
228
|
+
targetLocale
|
|
229
|
+
}) => {
|
|
230
|
+
const candidateLocales = resolveCandidateLocales(targetLocale || locale);
|
|
231
|
+
const message = findMessageInLocales({
|
|
232
|
+
messages,
|
|
233
|
+
candidateLocales,
|
|
234
|
+
key
|
|
235
|
+
});
|
|
236
|
+
return !!message;
|
|
237
|
+
};
|
|
238
|
+
function translate(options) {
|
|
239
|
+
const context = {
|
|
240
|
+
...options,
|
|
241
|
+
config: options.translateConfig,
|
|
242
|
+
candidateLocales: [],
|
|
243
|
+
meta: {}
|
|
244
|
+
};
|
|
245
|
+
const { early, ctx, output } = rura.rura.run(context, options.hooks);
|
|
246
|
+
if (early === true) return output;
|
|
247
|
+
return ctx.finalMessage;
|
|
248
|
+
}
|
|
249
|
+
|
|
147
250
|
// src/translators/core-translator/core-translator.ts
|
|
148
251
|
var CoreTranslator = class extends BaseTranslator {
|
|
149
|
-
options
|
|
252
|
+
/** User-provided options including messages, locale, and config. */
|
|
253
|
+
translateConfig;
|
|
254
|
+
/** Active pipeline hooks applied during translation. */
|
|
255
|
+
hooks = [...DEFAULT_HOOKS];
|
|
150
256
|
constructor(options) {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
257
|
+
const { locale, messages, isLoading, plugins, ...translateConfig } = options;
|
|
258
|
+
super({ locale, messages, isLoading });
|
|
259
|
+
this.translateConfig = translateConfig;
|
|
260
|
+
if (plugins) {
|
|
261
|
+
for (const plugin of plugins) this.use(plugin);
|
|
262
|
+
}
|
|
263
|
+
this.sortHooks();
|
|
264
|
+
}
|
|
265
|
+
/** Sort hooks by order value (lower runs earlier). */
|
|
266
|
+
sortHooks() {
|
|
267
|
+
this.hooks.sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
|
|
268
|
+
}
|
|
269
|
+
/** Register a plugin or a raw pipeline hook. */
|
|
270
|
+
use(plugin) {
|
|
271
|
+
if ("run" in plugin) this.hooks.push(plugin);
|
|
272
|
+
else if ("hook" in plugin && plugin.hook) {
|
|
273
|
+
const hooks = Array.isArray(plugin.hook) ? plugin.hook : [plugin.hook];
|
|
274
|
+
this.hooks.push(...hooks);
|
|
275
|
+
}
|
|
276
|
+
this.sortHooks();
|
|
157
277
|
}
|
|
158
278
|
/** Check if a key exists in the specified locale or current locale. */
|
|
159
279
|
hasKey = (key, targetLocale) => {
|
|
160
280
|
return hasKey({
|
|
161
|
-
|
|
162
|
-
|
|
281
|
+
messages: this._messages,
|
|
282
|
+
locale: this._locale,
|
|
163
283
|
key,
|
|
164
284
|
targetLocale
|
|
165
285
|
});
|
|
@@ -167,17 +287,18 @@ var CoreTranslator = class extends BaseTranslator {
|
|
|
167
287
|
/** Get the translated message for a key, with optional replacements. */
|
|
168
288
|
t = (key, replacements) => {
|
|
169
289
|
return translate({
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
290
|
+
hooks: this.hooks,
|
|
291
|
+
messages: this._messages,
|
|
292
|
+
locale: this._locale,
|
|
293
|
+
isLoading: this._isLoading,
|
|
294
|
+
translateConfig: this.translateConfig,
|
|
174
295
|
key,
|
|
175
296
|
replacements
|
|
176
297
|
});
|
|
177
298
|
};
|
|
178
299
|
};
|
|
179
300
|
|
|
180
|
-
// src/utils/get-full-key.ts
|
|
301
|
+
// src/translators/scope-translator/utils/get-full-key.ts
|
|
181
302
|
var getFullKey = (preKey = "", key = "") => {
|
|
182
303
|
if (!preKey) return key;
|
|
183
304
|
if (!key) return preKey;
|
|
@@ -195,8 +316,8 @@ var ScopeTranslator = class extends CoreTranslator {
|
|
|
195
316
|
hasKey: (key, targetLocale) => {
|
|
196
317
|
const fullKey = getFullKey(preKey, key);
|
|
197
318
|
return hasKey({
|
|
198
|
-
|
|
199
|
-
|
|
319
|
+
messages: this._messages,
|
|
320
|
+
locale: this._locale,
|
|
200
321
|
key: fullKey,
|
|
201
322
|
targetLocale
|
|
202
323
|
});
|
|
@@ -204,10 +325,11 @@ var ScopeTranslator = class extends CoreTranslator {
|
|
|
204
325
|
t: (key, replacements) => {
|
|
205
326
|
const fullKey = getFullKey(preKey, key);
|
|
206
327
|
return translate({
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
328
|
+
hooks: this.hooks,
|
|
329
|
+
messages: this._messages,
|
|
330
|
+
locale: this._locale,
|
|
331
|
+
isLoading: this._isLoading,
|
|
332
|
+
translateConfig: this.translateConfig,
|
|
211
333
|
key: fullKey,
|
|
212
334
|
replacements
|
|
213
335
|
});
|
|
@@ -216,5 +338,4 @@ var ScopeTranslator = class extends CoreTranslator {
|
|
|
216
338
|
}
|
|
217
339
|
};
|
|
218
340
|
|
|
219
|
-
exports.ScopeTranslator = ScopeTranslator;
|
|
220
341
|
exports.Translator = ScopeTranslator;
|