@svelstack/translator 0.9.0 → 0.9.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 +123 -0
- package/dist/index.js +36 -6
- package/package.json +1 -1
- package/src/types.d.ts +1 -0
package/README.md
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# TypeSafe Internationalization
|
|
2
|
+
|
|
3
|
+
Features:
|
|
4
|
+
- Converting translations from YAML to .ts files
|
|
5
|
+
- Loading translations from .ts files
|
|
6
|
+
- Variable interpolation
|
|
7
|
+
- Typechecking for keys and domains
|
|
8
|
+
- Typechecking for variable interpolation
|
|
9
|
+
- Pluralization support
|
|
10
|
+
- Svelte5 plugin
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm i @svelstack/translator
|
|
16
|
+
npm i @svelstack/translator-svelte # optional
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Standalone usage
|
|
20
|
+
|
|
21
|
+
First, create a YAML file with translations in `translations/domain.en.yaml`:
|
|
22
|
+
```yaml
|
|
23
|
+
hello: 'Hello!'
|
|
24
|
+
advanced:
|
|
25
|
+
hello: 'Hello, {name}!'
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Generate dictionaries, d.ts files:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npx @svelstack/translator-extractor translations output
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The output will be:
|
|
35
|
+
```
|
|
36
|
+
- dictionary
|
|
37
|
+
- en.ts
|
|
38
|
+
- types.d.ts
|
|
39
|
+
- dictionaries.ts
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Create instance of Translator:
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
import { Translator } from '@svelstack/translator';
|
|
46
|
+
import { dictionaries } from './output/dictionaries';
|
|
47
|
+
|
|
48
|
+
const translator = new Translator({
|
|
49
|
+
language: 'en',
|
|
50
|
+
fallbackLanguage: 'en',
|
|
51
|
+
dictionaries,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// first argument is a domain (from filename), second is a key
|
|
55
|
+
console.log(translator.trans('domain', 'hello')); // Hello!
|
|
56
|
+
console.log(translator.trans('domain', 'advanced.hello', { name: 'John' })); // Hello, John!
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Typechecking
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
import { Translator } from '@svelstack/translator';
|
|
63
|
+
import { dictionaries } from './output/dictionaries';
|
|
64
|
+
import type { MappingForTranslator } from './output/types';
|
|
65
|
+
|
|
66
|
+
export type AppTranslator = TypesafeTranslator<MappingForTranslator>;
|
|
67
|
+
|
|
68
|
+
const translator = new Translator({
|
|
69
|
+
language: 'en',
|
|
70
|
+
fallbackLanguage: 'en',
|
|
71
|
+
dictionaries,
|
|
72
|
+
}) as AppTranslator;
|
|
73
|
+
|
|
74
|
+
translator.trans('domain', 'hello'); // IDE will suggest domains, keys and variables
|
|
75
|
+
|
|
76
|
+
translator.trans('messages', 'hello'); // Error: messages is not a valid domain
|
|
77
|
+
translator.trans('domain', 'hello', {}); // Error: hello does not accept variables
|
|
78
|
+
translator.trans('domain', 'advanced.hello'); // Error: advanced.hello requires variables
|
|
79
|
+
translator.trans('domain', 'advanced.hello', { name: 'John', extra: '' }); // Error: unexpected variable extra
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Svelte
|
|
84
|
+
|
|
85
|
+
```sveltehtml
|
|
86
|
+
<script lang="ts">
|
|
87
|
+
import { SvelteTranslator, type TypesafeSvelteTranslatorConstructor } from '@svelstack/translator-svelte';
|
|
88
|
+
import { dictionaries } from './output/dictionaries';
|
|
89
|
+
import type { MappingForTranslator } from './output/types';
|
|
90
|
+
import { setContext } from 'svelte';
|
|
91
|
+
|
|
92
|
+
export const AppTranslator = SvelteTranslator as TypesafeSvelteTranslatorConstructor<MappingForTranslator>;
|
|
93
|
+
|
|
94
|
+
const translator = new AppTranslator({
|
|
95
|
+
language: 'en',
|
|
96
|
+
fallbackLanguage: 'en',
|
|
97
|
+
dictionaries,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
setContext(AppTranslator, translator);
|
|
101
|
+
</script>
|
|
102
|
+
|
|
103
|
+
<!-- Translations are loaded asynchronously -->
|
|
104
|
+
{#await translator.wait()}
|
|
105
|
+
Loading translations...
|
|
106
|
+
{:then _}
|
|
107
|
+
....
|
|
108
|
+
{/await}
|
|
109
|
+
|
|
110
|
+
{translator.trans('domain', 'hello')} <!-- Fully reactive -->
|
|
111
|
+
{translator.language} <!-- Fully reactive -->
|
|
112
|
+
|
|
113
|
+
<button onclick={() => translator.changeLanguage('de')}>Change language</button>
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Another component:
|
|
117
|
+
```sveltehtml
|
|
118
|
+
<script lang="ts">
|
|
119
|
+
const translator = AppTranslator.of();
|
|
120
|
+
</script>
|
|
121
|
+
|
|
122
|
+
{translator.trans('domain', 'hello')}
|
|
123
|
+
```
|
package/dist/index.js
CHANGED
|
@@ -257,6 +257,28 @@ var PluralMessageFormatter = class {
|
|
|
257
257
|
}
|
|
258
258
|
};
|
|
259
259
|
var ParameterMessageFormatter = class {
|
|
260
|
+
/** @type {RegExp} */
|
|
261
|
+
parameterRegex;
|
|
262
|
+
/** @type {string} */
|
|
263
|
+
parameterPlaceholderStart;
|
|
264
|
+
/** @type {string} */
|
|
265
|
+
parameterPlaceholderEnd;
|
|
266
|
+
/**
|
|
267
|
+
* @param {{ parameterPlaceholder: { start: string, end: string } }} settings
|
|
268
|
+
*/
|
|
269
|
+
constructor(settings) {
|
|
270
|
+
this.parameterPlaceholderStart = settings.parameterPlaceholder.start;
|
|
271
|
+
this.parameterPlaceholderEnd = settings.parameterPlaceholder.end;
|
|
272
|
+
if (typeof this.parameterPlaceholderStart !== "string") {
|
|
273
|
+
throw new Error("ParameterMessageFormatter: parameterPlaceholder.start must be a string.");
|
|
274
|
+
}
|
|
275
|
+
if (typeof this.parameterPlaceholderEnd !== "string") {
|
|
276
|
+
throw new Error("ParameterMessageFormatter: parameterPlaceholder.end must be a string.");
|
|
277
|
+
}
|
|
278
|
+
const quotedStart = escapeRegExp(this.parameterPlaceholderStart);
|
|
279
|
+
const quotedEnd = escapeRegExp(this.parameterPlaceholderEnd);
|
|
280
|
+
this.parameterRegex = new RegExp(`${quotedStart}\\s*(.*?)\\s*${quotedEnd}`, "g");
|
|
281
|
+
}
|
|
260
282
|
/**
|
|
261
283
|
* Formats the message by replacing placeholders with parameter values.
|
|
262
284
|
* @param {string} message - The message to format.
|
|
@@ -268,15 +290,18 @@ var ParameterMessageFormatter = class {
|
|
|
268
290
|
if (parameters === void 0) {
|
|
269
291
|
return message;
|
|
270
292
|
}
|
|
271
|
-
return message.replace(
|
|
293
|
+
return message.replace(this.parameterRegex, (_, key) => {
|
|
272
294
|
const val = parameters[key];
|
|
273
295
|
if (val == null) {
|
|
274
|
-
return
|
|
296
|
+
return `${this.parameterPlaceholderStart}${key}${this.parameterPlaceholderEnd}`;
|
|
275
297
|
}
|
|
276
298
|
return val.toString();
|
|
277
299
|
});
|
|
278
300
|
}
|
|
279
301
|
};
|
|
302
|
+
function escapeRegExp(str) {
|
|
303
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
304
|
+
}
|
|
280
305
|
|
|
281
306
|
// src/index.js
|
|
282
307
|
var Translator = class {
|
|
@@ -304,10 +329,7 @@ var Translator = class {
|
|
|
304
329
|
* @private
|
|
305
330
|
* @type {ChainMessageFormatter}
|
|
306
331
|
*/
|
|
307
|
-
formatter
|
|
308
|
-
new PluralMessageFormatter(),
|
|
309
|
-
new ParameterMessageFormatter()
|
|
310
|
-
]);
|
|
332
|
+
formatter;
|
|
311
333
|
/**
|
|
312
334
|
* @param {import('./types.js').TranslatorOptions} options - Configuration options for the Translator.
|
|
313
335
|
* @throws {Error} If the fallback language is not in the dictionaries.
|
|
@@ -320,6 +342,14 @@ var Translator = class {
|
|
|
320
342
|
this._language = this.getLanguage(this.options.language);
|
|
321
343
|
this.report = this.options.report || function() {
|
|
322
344
|
};
|
|
345
|
+
if (Array.isArray(options.formatters)) {
|
|
346
|
+
this.formatter = new ChainMessageFormatter(options.formatters);
|
|
347
|
+
} else {
|
|
348
|
+
this.formatter = new ChainMessageFormatter([
|
|
349
|
+
new PluralMessageFormatter(),
|
|
350
|
+
new ParameterMessageFormatter({ parameterPlaceholder: { start: "{", end: "}" } })
|
|
351
|
+
]);
|
|
352
|
+
}
|
|
323
353
|
this.load(this._language);
|
|
324
354
|
}
|
|
325
355
|
/**
|
package/package.json
CHANGED