@itrocks/translate 0.2.3 → 0.2.4
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 +50 -253
- package/cjs/catalog.d.ts +5 -0
- package/cjs/catalog.js +42 -0
- package/cjs/translate.d.ts +3 -7
- package/cjs/translate.js +42 -69
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
# translate
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Translate English source strings with small, language-specific CSV catalogs.
|
|
10
10
|
|
|
11
11
|
*This documentation was written by an artificial intelligence and may contain errors or approximations.
|
|
12
12
|
It has not yet been fully reviewed by a human. If anything seems unclear or incomplete,
|
|
@@ -18,313 +18,110 @@ please feel free to contact the author of this package.*
|
|
|
18
18
|
npm i @itrocks/translate
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
This package has a runtime dependency on `papaparse`, which is installed automatically
|
|
22
|
-
as a transitive dependency when you install `@itrocks/translate`.
|
|
23
|
-
|
|
24
21
|
## Usage
|
|
25
22
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
You typically use it to:
|
|
29
|
-
|
|
30
|
-
- declare the current UI language with `trInit()`,
|
|
31
|
-
- load translation keys from a CSV file with `trLoad()`,
|
|
32
|
-
- isolate concurrent request languages with `trWithLanguage()`,
|
|
33
|
-
- translate strings at runtime with `tr()`,
|
|
34
|
-
- optionally inspect or extend the in-memory `translations` map.
|
|
35
|
-
|
|
36
|
-
The focus is on **dynamic translations of small text snippets** (labels, button
|
|
37
|
-
texts, messages) with support for:
|
|
38
|
-
|
|
39
|
-
- automatic case handling (uppercasing the first letter),
|
|
40
|
-
- placeholders like `$1`, `$2`, ... replaced by runtime values,
|
|
41
|
-
- composite expressions that can themselves contain expressions.
|
|
42
|
-
|
|
43
|
-
### Minimal example
|
|
44
|
-
|
|
45
|
-
```ts
|
|
46
|
-
import { tr, trInit, trLoad } from '@itrocks/translate'
|
|
47
|
-
|
|
48
|
-
async function main() {
|
|
49
|
-
// 1. Select the language and reset internal state
|
|
50
|
-
trInit('en-US')
|
|
51
|
-
|
|
52
|
-
// 2. Load translations from a ;‑separated CSV file
|
|
53
|
-
await trLoad('locales/en-US.csv')
|
|
23
|
+
Translation sources stay in English. Each semicolon-separated CSV file maps them to one language:
|
|
54
24
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
// 4. Translate a key with placeholders
|
|
59
|
-
console.log(tr('welcome.user.$1', ['John'])) // e.g. "Welcome, John"
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
main().catch(console.error)
|
|
25
|
+
```csv
|
|
26
|
+
hello;bonjour
|
|
27
|
+
private: $1 recipients;privée : $1 destinataires
|
|
63
28
|
```
|
|
64
29
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
expressions are translated by splitting them around punctuation and translating
|
|
69
|
-
each part separately while preserving spaces.
|
|
30
|
+
Catalog entries are context-free fragments: start both columns with a lowercase letter unless the word requires a
|
|
31
|
+
capital, and keep sentence-separating periods in the caller rather than at either end of a CSV value. The translator
|
|
32
|
+
restores an initial capital and translates each period-separated fragment independently.
|
|
70
33
|
|
|
71
|
-
|
|
72
|
-
|
|
34
|
+
An isolated `:` or `;` entry may translate to the same separator prefixed with a space when a target language requires
|
|
35
|
+
different spacing. The caller still owns the separator itself.
|
|
73
36
|
|
|
74
|
-
|
|
75
|
-
- appends the translated parts to the `parts` array,
|
|
76
|
-
- and reuses them to build the final translated string.
|
|
37
|
+
Initialize the default language and load every translated catalog once:
|
|
77
38
|
|
|
78
39
|
```ts
|
|
79
|
-
import {
|
|
80
|
-
|
|
81
|
-
async function initTranslations() {
|
|
82
|
-
// Initialize the language (any BCP 47 code string is accepted)
|
|
83
|
-
trInit('fr-FR')
|
|
84
|
-
|
|
85
|
-
// Load a ;‑separated CSV file with two columns: source;translation
|
|
86
|
-
// Example content:
|
|
87
|
-
// hello;Bonjour
|
|
88
|
-
// "Hello, $1";"Bonjour, $1"
|
|
89
|
-
// "You have $1 new messages";"Vous avez $1 nouveaux messages"
|
|
90
|
-
await trLoad('locales/fr-FR.csv')
|
|
91
|
-
|
|
92
|
-
console.log('Current language:', lang())
|
|
93
|
-
|
|
94
|
-
console.log(tr('hello')) // "Bonjour"
|
|
95
|
-
console.log(tr('Hello, $1', ['Marie'])) // "Bonjour, Marie"
|
|
96
|
-
console.log(tr('You have $1 new messages', ['3']))
|
|
97
|
-
// => "Vous avez 3 nouveaux messages"
|
|
40
|
+
import { tr, trInit, trLoad, trReverse, trWithLanguage } from '@itrocks/translate'
|
|
98
41
|
|
|
99
|
-
// The underlying maps are available if you need to inspect or extend them
|
|
100
|
-
console.log('Loaded translations:', translations.size)
|
|
101
|
-
console.log('Expression patterns:', expressions.size)
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
initTranslations().catch(console.error)
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
### Concurrent request languages
|
|
108
|
-
|
|
109
|
-
Load each catalog once, then run request work in its own asynchronous language context.
|
|
110
|
-
The context is preserved through promises without changing other concurrent requests:
|
|
111
|
-
|
|
112
|
-
```ts
|
|
113
42
|
trInit('en-US')
|
|
114
|
-
await trLoad('locales/fr-FR.csv',
|
|
43
|
+
await trLoad('locales/fr-FR.csv', 'fr-FR')
|
|
115
44
|
|
|
116
45
|
await trWithLanguage('fr-FR', async () => {
|
|
117
|
-
console.log(tr('Hello'))
|
|
46
|
+
console.log(tr('Hello')) // Bonjour
|
|
47
|
+
console.log(tr('Private: $1 recipients.', ['2'])) // Privée : 2 destinataires.
|
|
48
|
+
console.log(trReverse('Bonjour')) // Hello
|
|
118
49
|
})
|
|
119
50
|
```
|
|
120
51
|
|
|
121
|
-
|
|
122
|
-
source
|
|
123
|
-
temporarily contain literals in both the source and target languages.
|
|
124
|
-
|
|
125
|
-
> **Note**
|
|
126
|
-
> This package is intentionally minimal: it does not manage locales, fallbacks,
|
|
127
|
-
> or pluralization rules on its own. Those concerns are expected to be handled
|
|
128
|
-
> by your application or higher‑level framework.
|
|
52
|
+
The asynchronous language context is isolated between concurrent requests. A language with no catalog, such as the
|
|
53
|
+
English source language above, leaves source strings unchanged.
|
|
129
54
|
|
|
130
55
|
## API
|
|
131
56
|
|
|
132
|
-
### `DefaultOptions`
|
|
133
|
-
|
|
134
|
-
```ts
|
|
135
|
-
export const DefaultOptions: Options
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
The default options used by `tr()` when no explicit `options` are provided.
|
|
139
|
-
|
|
140
|
-
Currently only one option is defined:
|
|
141
|
-
|
|
142
|
-
- `ucFirst: boolean` (default `true`): when `true`, if the input text starts
|
|
143
|
-
with an uppercase ASCII letter (`A`–`Z`), the translated string is forced to
|
|
144
|
-
start with an uppercase letter as well.
|
|
145
|
-
|
|
146
|
-
You can override this behavior per call using the `options` argument of `tr()`.
|
|
147
|
-
|
|
148
|
-
### `expressions`
|
|
149
|
-
|
|
150
|
-
```ts
|
|
151
|
-
export const expressions: Set<RegExp>
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
The set of **compiled expression patterns** used for advanced matching in `tr()`.
|
|
155
|
-
|
|
156
|
-
You normally do not need to modify this set manually. It is populated by
|
|
157
|
-
`trLoad()` when a source key in the CSV file contains placeholders like `$1`.
|
|
158
|
-
|
|
159
|
-
Each such key generates a regular expression that is later used by `tr()` to
|
|
160
|
-
match dynamic sentences and extract sub‑parts for translation.
|
|
161
|
-
|
|
162
|
-
### `translations`
|
|
163
|
-
|
|
164
|
-
```ts
|
|
165
|
-
export const translations: Map<string, string>
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
The in‑memory translation dictionary. Keys are **source texts** (usually
|
|
169
|
-
English strings or stable identifiers), and values are their translated
|
|
170
|
-
counterparts in the currently active language.
|
|
171
|
-
|
|
172
|
-
This map is cleared each time you call `trInit()`. It is filled by `trLoad()`
|
|
173
|
-
and can be extended or inspected manually if needed.
|
|
174
|
-
|
|
175
|
-
### `type Options`
|
|
176
|
-
|
|
177
|
-
```ts
|
|
178
|
-
export type Options = {
|
|
179
|
-
ucFirst?: boolean
|
|
180
|
-
}
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
Additional options that can be passed to `tr()`:
|
|
184
|
-
|
|
185
|
-
- `ucFirst` (default: `DefaultOptions.ucFirst`): whether the first character of
|
|
186
|
-
the translated string should be uppercased when the original first character
|
|
187
|
-
is an uppercase ASCII letter.
|
|
188
|
-
|
|
189
57
|
### `lang()`
|
|
190
58
|
|
|
191
59
|
```ts
|
|
192
60
|
function lang(): string
|
|
193
61
|
```
|
|
194
62
|
|
|
195
|
-
Returns the
|
|
196
|
-
|
|
197
|
-
The package does not interpret the value: you can use any string (for example
|
|
198
|
-
`'en-US'`, `'fr-FR'`, `'de'`), as long as it is meaningful to your
|
|
199
|
-
application.
|
|
63
|
+
Returns the language of the current asynchronous context, or the default language set by `trInit()`.
|
|
200
64
|
|
|
201
65
|
### `tr()`
|
|
202
66
|
|
|
203
67
|
```ts
|
|
204
|
-
// Overload 1: no parts array, only options
|
|
205
68
|
function tr(text: string, options: Options): string
|
|
206
|
-
|
|
207
|
-
// Overload 2: explicit parts and optional options
|
|
208
69
|
function tr(text: string, parts?: string[], options?: Options): string
|
|
209
70
|
```
|
|
210
71
|
|
|
211
|
-
Translates
|
|
212
|
-
the
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
1. **Spacing preservation** – leading and trailing whitespace in `text` are
|
|
217
|
-
preserved around the translated content.
|
|
218
|
-
2. **Lookup strategy** – for the trimmed `text`, `tr()` looks up, in order:
|
|
219
|
-
- an exact match in `translations`,
|
|
220
|
-
- if `ucFirst` is enabled and the first character is uppercase, the same key
|
|
221
|
-
but with the first letter lower‑cased,
|
|
222
|
-
- the lower‑cased key,
|
|
223
|
-
- a match from expression patterns in `expressions` (see `trLoad()`).
|
|
224
|
-
3. **Composite sentences** – if no translation is found, `tr()` looks for a
|
|
225
|
-
punctuation separator (`.?!;:,()`). When found, the text is split around the
|
|
226
|
-
first such separator, each part is translated separately with `tr()`, and the
|
|
227
|
-
final string is reassembled while preserving spaces (including non‑breaking
|
|
228
|
-
spaces around the separator).
|
|
229
|
-
4. **Fallback** – if no translation or expression match is found, the original
|
|
230
|
-
trimmed text is returned.
|
|
231
|
-
5. **Placeholders** – if a `parts` array is provided, elements are substituted
|
|
232
|
-
into the translated string by replacing `$1`, `$2`, ... from the end of the
|
|
233
|
-
array backwards.
|
|
234
|
-
|
|
235
|
-
Usage patterns:
|
|
72
|
+
Translates `text` with the current language catalog. It supports `$1`, `$2`, … placeholders, preserves surrounding
|
|
73
|
+
spaces and can match catalog sources containing placeholders. If no translation exists, it returns the source text.
|
|
74
|
+
|
|
75
|
+
When `ucFirst` is enabled, an uppercase source initial also produces an uppercase translated initial:
|
|
236
76
|
|
|
237
77
|
```ts
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
78
|
+
export type Options = {
|
|
79
|
+
ucFirst?: boolean
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export const DefaultOptions: Options = {
|
|
83
|
+
ucFirst: true
|
|
84
|
+
}
|
|
242
85
|
```
|
|
243
86
|
|
|
244
87
|
### `trInit()`
|
|
245
88
|
|
|
246
89
|
```ts
|
|
247
|
-
function trInit(
|
|
90
|
+
function trInit(language: string): void
|
|
248
91
|
```
|
|
249
92
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
- sets the internal language code returned by `lang()`,
|
|
253
|
-
- clears all previously loaded `translations`,
|
|
254
|
-
- clears all compiled `expressions`.
|
|
255
|
-
|
|
256
|
-
Call this once per language at application startup, or whenever you change the
|
|
257
|
-
active language and want to reload translation data.
|
|
93
|
+
Sets the default language and clears all previously loaded catalogs. Call it once before loading catalogs.
|
|
258
94
|
|
|
259
95
|
### `trLoad()`
|
|
260
96
|
|
|
261
97
|
```ts
|
|
262
|
-
|
|
98
|
+
function trLoad(file: string, language?: string): Promise<void | unknown>
|
|
263
99
|
```
|
|
264
100
|
|
|
265
|
-
Loads
|
|
266
|
-
|
|
267
|
-
Behavior:
|
|
101
|
+
Loads a UTF-8, semicolon-separated `source;translation` file into `language`. The language defaults to the one passed
|
|
102
|
+
to `trInit()`. A missing file is ignored so applications can look for catalogs in several module directories.
|
|
268
103
|
|
|
269
|
-
|
|
270
|
-
returns without throwing.
|
|
271
|
-
- It reads the file as UTF‑8 and parses it using `papaparse` with `;` as the
|
|
272
|
-
delimiter.
|
|
273
|
-
- Each row is expected to have at least two columns: `row[0]` is the source
|
|
274
|
-
string, `row[1]` is the translated string. Extra columns are ignored.
|
|
275
|
-
- `options.language` selects the catalog to populate; it defaults to the language
|
|
276
|
-
selected by `trInit()`.
|
|
277
|
-
- `options.reverse` swaps the source and translated columns while loading.
|
|
278
|
-
- For each row, the pair is stored in `translations`.
|
|
279
|
-
- If `row[0]` contains a placeholder like `$1`, an expression `RegExp` is
|
|
280
|
-
created and added to `expressions` to support dynamic matching in `tr()`.
|
|
104
|
+
### `trReverse()`
|
|
281
105
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
```csv
|
|
285
|
-
hello;Hello
|
|
286
|
-
"Hello, $1";"Hello, $1"
|
|
287
|
-
"You have $1 new messages";"You have $1 new messages"
|
|
106
|
+
```ts
|
|
107
|
+
function trReverse(text: string): string
|
|
288
108
|
```
|
|
289
109
|
|
|
110
|
+
Looks for a translated value in the current language catalog and returns its English source, accepting an initial
|
|
111
|
+
capital added by `tr()`. If none matches, it returns `text` unchanged. This is intended for occasional input
|
|
112
|
+
normalization, such as converting a translated code entered in a search field back to the code stored in English. It
|
|
113
|
+
reads the current catalog backwards on demand; no reverse catalog is loaded or retained.
|
|
114
|
+
|
|
290
115
|
### `trWithLanguage()`
|
|
291
116
|
|
|
292
117
|
```ts
|
|
293
118
|
function trWithLanguage<T>(language: string, callback: () => T): T
|
|
294
119
|
```
|
|
295
120
|
|
|
296
|
-
Runs `callback` in an asynchronous language context
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
1. **Translating UI labels and messages in a Node.js application**
|
|
305
|
-
- Keep a simple `locales/<lang>.csv` file with two columns: source and
|
|
306
|
-
translation.
|
|
307
|
-
- At startup, call `trInit('<lang>')` and `trLoad('locales/<lang>.csv')`.
|
|
308
|
-
- Use `tr('settings')`, `tr('Save changes')`, etc., in your rendering
|
|
309
|
-
or logging code.
|
|
310
|
-
|
|
311
|
-
2. **Integrating with a template or transformer system**
|
|
312
|
-
- Combine `@itrocks/translate` with higher‑level packages such as
|
|
313
|
-
`@itrocks/transformer` or `@itrocks/property-translate` to automatically
|
|
314
|
-
translate values when rendering views or model properties.
|
|
315
|
-
|
|
316
|
-
3. **Dynamic messages with parameters**
|
|
317
|
-
- Define entries in your CSV containing `$1`, `$2`, ... placeholders.
|
|
318
|
-
- At runtime, call `tr('You have $1 new messages', ['3'])`.
|
|
319
|
-
- The placeholders are replaced by the elements of the `parts` array.
|
|
320
|
-
|
|
321
|
-
4. **Expression‑based translations**
|
|
322
|
-
- Use keys with `$1` in your CSV (for example, `"Hello, $1"`).
|
|
323
|
-
- When you call `tr('Hello, John')`, `@itrocks/translate` matches the
|
|
324
|
-
pattern, translates `"John"` if possible, and then builds the final
|
|
325
|
-
sentence using the captured parts.
|
|
326
|
-
|
|
327
|
-
5. **Inspecting and debugging translations**
|
|
328
|
-
- Use `translations.size` to quickly see how many entries were loaded.
|
|
329
|
-
- Inspect `translations.get('some key')` or iterate over the map when
|
|
330
|
-
debugging missing or incorrect translations.
|
|
121
|
+
Runs `callback` in an asynchronous language context used by `lang()`, `tr()` and `trReverse()`. The context is preserved
|
|
122
|
+
through promises without affecting concurrent callbacks.
|
|
123
|
+
|
|
124
|
+
## Scope
|
|
125
|
+
|
|
126
|
+
The package deliberately does not manage locale negotiation, plural rules or fallback chains. Applications and
|
|
127
|
+
higher-level frameworks remain responsible for those policies.
|
package/cjs/catalog.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export type Catalog = Map<string, string>;
|
|
2
|
+
export declare const translations: Map<string, string>;
|
|
3
|
+
export declare function catalog(language: string): Catalog;
|
|
4
|
+
export declare function catalogClear(language: string): void;
|
|
5
|
+
export declare function catalogLoad(file: string, language: string): Promise<void>;
|
package/cjs/catalog.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.translations = void 0;
|
|
4
|
+
exports.catalog = catalog;
|
|
5
|
+
exports.catalogClear = catalogClear;
|
|
6
|
+
exports.catalogLoad = catalogLoad;
|
|
7
|
+
const promises_1 = require("node:fs/promises");
|
|
8
|
+
const promises_2 = require("node:fs/promises");
|
|
9
|
+
const parseCsv = require('papaparse').parse;
|
|
10
|
+
const catalogs = new Map;
|
|
11
|
+
exports.translations = new Map;
|
|
12
|
+
catalogs.set('en-US', exports.translations);
|
|
13
|
+
function catalog(language) {
|
|
14
|
+
let result = catalogs.get(language);
|
|
15
|
+
if (!result) {
|
|
16
|
+
result = new Map;
|
|
17
|
+
catalogs.set(language, result);
|
|
18
|
+
}
|
|
19
|
+
return result;
|
|
20
|
+
}
|
|
21
|
+
function catalogClear(language) {
|
|
22
|
+
catalogs.clear();
|
|
23
|
+
exports.translations.clear();
|
|
24
|
+
catalogs.set(language, exports.translations);
|
|
25
|
+
}
|
|
26
|
+
async function catalogLoad(file, language) {
|
|
27
|
+
try {
|
|
28
|
+
await (0, promises_1.access)(file);
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const translations = catalog(language);
|
|
34
|
+
return (0, promises_2.readFile)(file, 'utf-8')
|
|
35
|
+
.then((data) => parseCsv(data, { delimiter: ';' }).data)
|
|
36
|
+
.then(data => data.forEach(([source, target]) => {
|
|
37
|
+
if ((typeof source !== 'string') || (typeof target !== 'string') || (source === target))
|
|
38
|
+
return;
|
|
39
|
+
translations.set(source, target);
|
|
40
|
+
}));
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=catalog.js.map
|
package/cjs/translate.d.ts
CHANGED
|
@@ -1,16 +1,12 @@
|
|
|
1
|
-
export type LoadOptions = {
|
|
2
|
-
language?: string;
|
|
3
|
-
reverse?: boolean;
|
|
4
|
-
};
|
|
5
1
|
export type Options = {
|
|
6
2
|
ucFirst?: boolean;
|
|
7
3
|
};
|
|
8
4
|
export declare const DefaultOptions: Options;
|
|
9
|
-
export declare const expressions: Set<RegExp>;
|
|
10
|
-
export declare const translations: Map<string, string>;
|
|
11
5
|
export declare function lang(): string;
|
|
12
6
|
export declare function tr(text: string, options: Options): string;
|
|
13
7
|
export declare function tr(text: string, parts?: string[], options?: Options): string;
|
|
14
8
|
export declare function trInit(language: string): void;
|
|
15
|
-
export declare function trLoad(file: string,
|
|
9
|
+
export declare function trLoad(file: string, language?: string): Promise<void>;
|
|
10
|
+
export declare function trReverse(text: string): string;
|
|
16
11
|
export declare function trWithLanguage<T>(language: string, callback: () => T): T;
|
|
12
|
+
export { translations } from './catalog';
|
package/cjs/translate.js
CHANGED
|
@@ -1,32 +1,23 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.translations = exports.
|
|
3
|
+
exports.translations = exports.DefaultOptions = void 0;
|
|
4
4
|
exports.lang = lang;
|
|
5
5
|
exports.tr = tr;
|
|
6
6
|
exports.trInit = trInit;
|
|
7
7
|
exports.trLoad = trLoad;
|
|
8
|
+
exports.trReverse = trReverse;
|
|
8
9
|
exports.trWithLanguage = trWithLanguage;
|
|
9
10
|
const node_async_hooks_1 = require("node:async_hooks");
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const
|
|
11
|
+
const catalog_1 = require("./catalog");
|
|
12
|
+
const catalog_2 = require("./catalog");
|
|
13
|
+
const catalog_3 = require("./catalog");
|
|
13
14
|
exports.DefaultOptions = {
|
|
14
15
|
ucFirst: true
|
|
15
16
|
};
|
|
16
|
-
exports.expressions = new Set;
|
|
17
|
-
exports.translations = new Map;
|
|
18
|
-
const catalogs = new Map;
|
|
19
|
-
const expressionData = new WeakMap;
|
|
20
17
|
const languageScope = new node_async_hooks_1.AsyncLocalStorage();
|
|
21
18
|
let defaultLanguage = 'en-US';
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
let value = catalogs.get(language);
|
|
25
|
-
if (!value) {
|
|
26
|
-
value = { expressions: new Set, translations: new Map };
|
|
27
|
-
catalogs.set(language, value);
|
|
28
|
-
}
|
|
29
|
-
return value;
|
|
19
|
+
function escapeRegExp(text) {
|
|
20
|
+
return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
30
21
|
}
|
|
31
22
|
function lang() {
|
|
32
23
|
return languageScope.getStore() ?? defaultLanguage;
|
|
@@ -41,11 +32,12 @@ function tr(text, parts, options) {
|
|
|
41
32
|
let partsCount = parts.length;
|
|
42
33
|
const firstChar = text[0];
|
|
43
34
|
const ucFirst = (options?.ucFirst ?? exports.DefaultOptions.ucFirst) && (firstChar >= 'A') && (firstChar <= 'Z');
|
|
44
|
-
const active = catalog();
|
|
45
|
-
let translated = active.
|
|
46
|
-
?? (ucFirst ? active.
|
|
47
|
-
?? active.
|
|
48
|
-
?? trMatch(text, parts, active)
|
|
35
|
+
const active = (0, catalog_1.catalog)(lang());
|
|
36
|
+
let translated = active.get(text)
|
|
37
|
+
?? (ucFirst ? active.get(firstChar.toLocaleLowerCase() + text.slice(1)) : undefined)
|
|
38
|
+
?? active.get(text.toLocaleLowerCase())
|
|
39
|
+
?? trMatch(text, parts, active)
|
|
40
|
+
?? (ucFirst ? trMatch(firstChar.toLocaleLowerCase() + text.slice(1), parts, active) : undefined);
|
|
49
41
|
if (!translated) {
|
|
50
42
|
const separator = (text.length > 1)
|
|
51
43
|
? ['.', '?', '!', ';', ':', ',', '(', ')'].find(c => text.includes(c))
|
|
@@ -66,61 +58,32 @@ function tr(text, parts, options) {
|
|
|
66
58
|
}
|
|
67
59
|
function trInit(language) {
|
|
68
60
|
defaultLanguage = language;
|
|
69
|
-
|
|
70
|
-
exports.expressions.clear();
|
|
71
|
-
exports.translations.clear();
|
|
72
|
-
catalogs.set(language, { expressions: exports.expressions, translations: exports.translations });
|
|
61
|
+
(0, catalog_2.catalogClear)(language);
|
|
73
62
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
await (0, promises_1.access)(file);
|
|
77
|
-
}
|
|
78
|
-
catch {
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
const active = catalog(options.language ?? defaultLanguage);
|
|
82
|
-
return (0, promises_2.readFile)(file, 'utf-8')
|
|
83
|
-
.then((data) => parseCsv(data, { delimiter: ';' }).data)
|
|
84
|
-
.then(data => data.forEach(row => {
|
|
85
|
-
const [source, target] = options.reverse ? [row[1], row[0]] : row;
|
|
86
|
-
if ((typeof source !== 'string') || (typeof target !== 'string'))
|
|
87
|
-
return;
|
|
88
|
-
active.translations.set(source, target);
|
|
89
|
-
if (source.includes('$')) {
|
|
90
|
-
const indexes = [];
|
|
91
|
-
let last = 0;
|
|
92
|
-
let pattern = '^';
|
|
93
|
-
for (const match of source.matchAll(/\$([1-9][0-9]*)/g)) {
|
|
94
|
-
pattern += escapeRegExp(source.slice(last, match.index)) + '(.*?)';
|
|
95
|
-
indexes.push(Number(match[1]));
|
|
96
|
-
last = match.index + match[0].length;
|
|
97
|
-
}
|
|
98
|
-
pattern += escapeRegExp(source.slice(last)) + '$';
|
|
99
|
-
const expression = RegExp(pattern);
|
|
100
|
-
expressionData.set(expression, { indexes, source });
|
|
101
|
-
active.expressions.add(expression);
|
|
102
|
-
}
|
|
103
|
-
}));
|
|
104
|
-
}
|
|
105
|
-
function trWithLanguage(language, callback) {
|
|
106
|
-
return languageScope.run(language, callback);
|
|
63
|
+
function trLoad(file, language = defaultLanguage) {
|
|
64
|
+
return (0, catalog_3.catalogLoad)(file, language);
|
|
107
65
|
}
|
|
108
66
|
function trMatch(text, parts, active) {
|
|
109
|
-
for (const
|
|
67
|
+
for (const [source, translated] of active) {
|
|
68
|
+
if (!source.includes('$'))
|
|
69
|
+
continue;
|
|
70
|
+
const indexes = [];
|
|
71
|
+
let last = 0;
|
|
72
|
+
let pattern = '^';
|
|
73
|
+
for (const match of source.matchAll(/\$([1-9][0-9]*)/g)) {
|
|
74
|
+
pattern += escapeRegExp(source.slice(last, match.index)) + '(.*?)';
|
|
75
|
+
indexes.push(Number(match[1]));
|
|
76
|
+
last = match.index + match[0].length;
|
|
77
|
+
}
|
|
78
|
+
pattern += escapeRegExp(source.slice(last)) + '$';
|
|
79
|
+
const expression = RegExp(pattern);
|
|
110
80
|
const match = text.match(expression);
|
|
111
81
|
if (!match)
|
|
112
82
|
continue;
|
|
113
|
-
const data = expressionData.get(expression);
|
|
114
|
-
let counter = 0;
|
|
115
|
-
const source = data?.source
|
|
116
|
-
?? expression.source.slice(1, -1).replace(/\(\.\*\??\)/g, () => '$' + ++counter);
|
|
117
|
-
const translated = active.translations.get(source);
|
|
118
|
-
if (!translated)
|
|
119
|
-
continue;
|
|
120
83
|
const trParts = [...parts];
|
|
121
84
|
for (const [offset, part] of match.slice(1).entries()) {
|
|
122
85
|
const translatedPart = tr(part);
|
|
123
|
-
trParts[
|
|
86
|
+
trParts[indexes[offset] ?? (offset + 1)] = (translatedPart && (translatedPart !== part))
|
|
124
87
|
? translatedPart[0].toLocaleLowerCase() + translatedPart.slice(1)
|
|
125
88
|
: translatedPart;
|
|
126
89
|
}
|
|
@@ -131,7 +94,17 @@ function trMatch(text, parts, active) {
|
|
|
131
94
|
return result;
|
|
132
95
|
}
|
|
133
96
|
}
|
|
134
|
-
function
|
|
135
|
-
|
|
97
|
+
function trReverse(text) {
|
|
98
|
+
for (const [source, translated] of (0, catalog_1.catalog)(lang())) {
|
|
99
|
+
if ((translated === text)
|
|
100
|
+
|| (translated === text[0].toLocaleLowerCase() + text.slice(1)))
|
|
101
|
+
return source;
|
|
102
|
+
}
|
|
103
|
+
return text;
|
|
104
|
+
}
|
|
105
|
+
function trWithLanguage(language, callback) {
|
|
106
|
+
return languageScope.run(language, callback);
|
|
136
107
|
}
|
|
108
|
+
var catalog_4 = require("./catalog");
|
|
109
|
+
Object.defineProperty(exports, "translations", { enumerable: true, get: function () { return catalog_4.translations; } });
|
|
137
110
|
//# sourceMappingURL=translate.js.map
|
package/package.json
CHANGED