intl-template 0.0.5 → 1.0.0-alpha.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/README.md +116 -0
- package/dist/intl.d.ts +37 -3
- package/dist/intl.js +127 -62
- package/dist/intl.js.map +1 -1
- package/package.json +21 -20
- package/src/intl.js +102 -58
- package/src/intl.test.js +101 -37
- package/tsconfig.json +1 -5
- package/dist/intl.test.d.ts +0 -1
- package/dist/intl.test.js +0 -44
- package/dist/intl.test.js.map +0 -1
package/README.md
CHANGED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# intel-template
|
|
2
|
+
|
|
3
|
+
A tiny i18n/l10n TTL(Tagged Template Literals) function
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [intel-template](#intel-template)
|
|
8
|
+
- [Table of Contents](#table-of-contents)
|
|
9
|
+
- [Installation](#installation)
|
|
10
|
+
- [Usage](#usage)
|
|
11
|
+
- [Use browser specifies locale](#use-browser-specifies-locale)
|
|
12
|
+
- [Use with React](#use-with-react)
|
|
13
|
+
- [Specify slot order](#specify-slot-order)
|
|
14
|
+
- [Nested](#nested)
|
|
15
|
+
- [Function slot](#function-slot)
|
|
16
|
+
- [Call as function](#call-as-function)
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install intl-template
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
import translation from "intl-template"
|
|
28
|
+
|
|
29
|
+
translation.templates["es-ES"] = {
|
|
30
|
+
"hello {}": "hola {}"
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const l10n = translation.translate.bind(null, "es-ES")
|
|
34
|
+
|
|
35
|
+
const name = "willow";
|
|
36
|
+
|
|
37
|
+
console.log(l10n`hello ${name}`)
|
|
38
|
+
// => hola willow
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Use browser specifies locale
|
|
42
|
+
```
|
|
43
|
+
import translation, { l10n } from "intl-template"
|
|
44
|
+
|
|
45
|
+
translation.templates["es-ES"] = {
|
|
46
|
+
"hello {}": "hola {}"
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// l10n = translation.translate.bind(null, navigator,language)
|
|
50
|
+
|
|
51
|
+
const name = "willow";
|
|
52
|
+
|
|
53
|
+
console.log(l10n`hello ${name}`)
|
|
54
|
+
// => hola willow
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Use with React
|
|
58
|
+
|
|
59
|
+
```javascript
|
|
60
|
+
|
|
61
|
+
function SomeComponent({ name }) {
|
|
62
|
+
return (
|
|
63
|
+
<div>
|
|
64
|
+
{l10n`hello ${<b>{name}</b>}`}
|
|
65
|
+
</div>
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Specify slot order
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
translation.templates["de-DE"] = {
|
|
74
|
+
"hello {} and {}": "hallo {1} und {0}"
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const l10n = translation.translate.bind(null, "de-DE")
|
|
78
|
+
|
|
79
|
+
const name1 = "willow"
|
|
80
|
+
const name2 = "jack"
|
|
81
|
+
|
|
82
|
+
console.log(l10n`hello ${name1} and ${name2}`)
|
|
83
|
+
// => holla jack und willow
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Nested
|
|
87
|
+
|
|
88
|
+
```javascript
|
|
89
|
+
translation.templates["de-DE"] = {
|
|
90
|
+
"bill": "schmidt",
|
|
91
|
+
"hello {}": "hallo {1}"
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const l10n = translation.translate.bind(null, "de-DE")
|
|
95
|
+
|
|
96
|
+
l10n`hello ${l10n`bill`}` // => hallo schmidt
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Function slot
|
|
100
|
+
|
|
101
|
+
```javascript
|
|
102
|
+
translation.templates["de-DE"] = {
|
|
103
|
+
"bill": "schmidt",
|
|
104
|
+
"hello {}": "hallo {1}"
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const l10n = translation.translate.bind(null, "de-DE")
|
|
108
|
+
|
|
109
|
+
l10n`hello ${(locale) => 123}` // => hallo 123
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Call as function
|
|
113
|
+
|
|
114
|
+
```javascript
|
|
115
|
+
l10n("hello {}", name)
|
|
116
|
+
```
|
package/dist/intl.d.ts
CHANGED
|
@@ -1,3 +1,37 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Parses a template string and extracts the template parts and order of slots.
|
|
3
|
+
* @param {string} templateString - The template string to parse.
|
|
4
|
+
* @returns {{ template: string[], order: number[] }}
|
|
5
|
+
*/
|
|
6
|
+
export function parseTemplate(templateString: string): {
|
|
7
|
+
template: string[];
|
|
8
|
+
order: number[];
|
|
9
|
+
};
|
|
10
|
+
export class Runes extends Array<any> {
|
|
11
|
+
constructor(arrayLength?: number | undefined);
|
|
12
|
+
constructor(arrayLength: number);
|
|
13
|
+
constructor(...items: any[]);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Represents a Translation object that handles string translation based on locale and templates.
|
|
17
|
+
*/
|
|
18
|
+
export class Translation {
|
|
19
|
+
/**
|
|
20
|
+
* Templates object that stores the translation templates for each locale.
|
|
21
|
+
* @type {Proxy}
|
|
22
|
+
*/
|
|
23
|
+
templates: ProxyConstructor;
|
|
24
|
+
/**
|
|
25
|
+
* Translates a string based on the provided locale and strings.
|
|
26
|
+
*
|
|
27
|
+
* @param {string} locale - The locale to use for translation.
|
|
28
|
+
* @param {string | string[]} strings - The string or array of strings to be translated.
|
|
29
|
+
* @param {...any} parts - The dynamic parts to be inserted into the translated string.
|
|
30
|
+
* @returns {Runes} - The translated string with dynamic parts inserted.
|
|
31
|
+
* @throws {Error} - If the length of the template parts does not match the length of the template.
|
|
32
|
+
*/
|
|
33
|
+
translate: (locale: string, strings: string | string[], ...parts: any[]) => Runes;
|
|
34
|
+
}
|
|
35
|
+
export default translation;
|
|
36
|
+
export const l10n: (strings: string | string[], ...parts: any[]) => Runes;
|
|
37
|
+
declare const translation: Translation;
|
package/dist/intl.js
CHANGED
|
@@ -1,73 +1,138 @@
|
|
|
1
|
+
var __extends = (this && this.__extends) || (function () {
|
|
2
|
+
var extendStatics = function (d, b) {
|
|
3
|
+
extendStatics = Object.setPrototypeOf ||
|
|
4
|
+
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
|
5
|
+
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
|
6
|
+
return extendStatics(d, b);
|
|
7
|
+
};
|
|
8
|
+
return function (d, b) {
|
|
9
|
+
if (typeof b !== "function" && b !== null)
|
|
10
|
+
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
|
|
11
|
+
extendStatics(d, b);
|
|
12
|
+
function __() { this.constructor = d; }
|
|
13
|
+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
14
|
+
};
|
|
15
|
+
})();
|
|
1
16
|
var _a;
|
|
2
|
-
var
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
17
|
+
var Runes = /** @class */ (function (_super) {
|
|
18
|
+
__extends(Runes, _super);
|
|
19
|
+
function Runes() {
|
|
20
|
+
return _super !== null && _super.apply(this, arguments) || this;
|
|
21
|
+
}
|
|
22
|
+
Runes.prototype.toString = function () {
|
|
23
|
+
return this.join("");
|
|
24
|
+
};
|
|
25
|
+
return Runes;
|
|
26
|
+
}(Array));
|
|
27
|
+
export { Runes };
|
|
28
|
+
/**
|
|
29
|
+
* Parses a template string and extracts the template parts and order of slots.
|
|
30
|
+
* @param {string} templateString - The template string to parse.
|
|
31
|
+
* @returns {{ template: string[], order: number[] }}
|
|
32
|
+
*/
|
|
33
|
+
export function parseTemplate(templateString) {
|
|
34
|
+
var order = [];
|
|
35
|
+
var template = [];
|
|
36
|
+
var parts = templateString.split(/({\d*})/);
|
|
37
|
+
parts.forEach(function (part) {
|
|
38
|
+
if (part.match(/^{\d*}$/)) {
|
|
10
39
|
if (part === '{}') {
|
|
11
|
-
if (index === 0 || index === parts.length - 1) {
|
|
12
|
-
template.push("");
|
|
13
|
-
}
|
|
14
40
|
order.push(order.length);
|
|
15
|
-
return;
|
|
16
41
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
template.push("");
|
|
20
|
-
}
|
|
42
|
+
else {
|
|
43
|
+
// slot with order, e.g. `{2}abc{1}def{0}`
|
|
21
44
|
order.push(parseInt(part.slice(1, -1), 10));
|
|
22
|
-
return;
|
|
23
45
|
}
|
|
24
|
-
template.push(part);
|
|
25
|
-
});
|
|
26
|
-
obj[key] = { template: template, order: order };
|
|
27
|
-
return obj;
|
|
28
|
-
}, {});
|
|
29
|
-
}
|
|
30
|
-
export function translate(locale, strings) {
|
|
31
|
-
var _a, _b;
|
|
32
|
-
var parts = [];
|
|
33
|
-
for (var _i = 2; _i < arguments.length; _i++) {
|
|
34
|
-
parts[_i - 2] = arguments[_i];
|
|
35
|
-
}
|
|
36
|
-
var key = strings.join("{}");
|
|
37
|
-
var translation = TRANSLATIONS === null || TRANSLATIONS === void 0 ? void 0 : TRANSLATIONS[locale];
|
|
38
|
-
var _c = (translation === null || translation === void 0 ? void 0 : translation[key]) || {}, template = _c.template, order = _c.order;
|
|
39
|
-
if (!template) {
|
|
40
|
-
if (((_b = (_a = import.meta) === null || _a === void 0 ? void 0 : _a.env) === null || _b === void 0 ? void 0 : _b.MODE) === "development") {
|
|
41
|
-
console.warn("not match translate key, ".concat(key), { translation: translation, locale: locale, strings: strings, parts: parts });
|
|
42
46
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
if (parts.length > 0 && template.length - 1 !== parts.length) {
|
|
47
|
-
throw new Error("translate template parts length not match. locale: ".concat(locale, ", key: ").concat(key));
|
|
48
|
-
}
|
|
49
|
-
var ret = template.reduce(function (ret, template, idx) {
|
|
50
|
-
ret.push(template);
|
|
51
|
-
var orderIdx = order[idx];
|
|
52
|
-
if (orderIdx >= 0) {
|
|
53
|
-
var part = parts[orderIdx];
|
|
54
|
-
if (typeof part == "function") {
|
|
55
|
-
ret.push(part(locale, translation));
|
|
56
|
-
}
|
|
57
|
-
else {
|
|
58
|
-
ret.push(part);
|
|
59
|
-
}
|
|
47
|
+
else {
|
|
48
|
+
template.push(part);
|
|
60
49
|
}
|
|
61
|
-
return ret;
|
|
62
|
-
}, []);
|
|
63
|
-
var text = ret.join("");
|
|
64
|
-
Object.defineProperty(ret, "toString", {
|
|
65
|
-
enumerable: false,
|
|
66
|
-
writable: false,
|
|
67
|
-
configurable: false,
|
|
68
|
-
value: function () { return text; }
|
|
69
50
|
});
|
|
70
|
-
return
|
|
51
|
+
return { template: template, order: order };
|
|
71
52
|
}
|
|
72
|
-
|
|
53
|
+
/**
|
|
54
|
+
* Represents a Translation object that handles string translation based on locale and templates.
|
|
55
|
+
*/
|
|
56
|
+
var Translation = /** @class */ (function () {
|
|
57
|
+
function Translation() {
|
|
58
|
+
var _this = this;
|
|
59
|
+
/**
|
|
60
|
+
* Templates object that stores the translation templates for each locale.
|
|
61
|
+
* @type {Proxy}
|
|
62
|
+
*/
|
|
63
|
+
this.templates = new Proxy({}, {
|
|
64
|
+
get: function (templates, locale) {
|
|
65
|
+
return new Proxy(templates[locale] || {}, {
|
|
66
|
+
set: function (region, key, value) {
|
|
67
|
+
if (typeof value !== "string") {
|
|
68
|
+
throw new Error("Template must be a string.");
|
|
69
|
+
}
|
|
70
|
+
region[key] = parseTemplate(value);
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
},
|
|
75
|
+
set: function (templates, locale, regionTemplates) {
|
|
76
|
+
templates[locale] = Object.entries(regionTemplates).reduce(function (region, _a) {
|
|
77
|
+
var key = _a[0], value = _a[1];
|
|
78
|
+
region[key] = parseTemplate(value);
|
|
79
|
+
return region;
|
|
80
|
+
}, {});
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
/**
|
|
85
|
+
* Translates a string based on the provided locale and strings.
|
|
86
|
+
*
|
|
87
|
+
* @param {string} locale - The locale to use for translation.
|
|
88
|
+
* @param {string | string[]} strings - The string or array of strings to be translated.
|
|
89
|
+
* @param {...any} parts - The dynamic parts to be inserted into the translated string.
|
|
90
|
+
* @returns {Runes} - The translated string with dynamic parts inserted.
|
|
91
|
+
* @throws {Error} - If the length of the template parts does not match the length of the template.
|
|
92
|
+
*/
|
|
93
|
+
this.translate = function (locale, strings) {
|
|
94
|
+
var _a, _b, _c;
|
|
95
|
+
var parts = [];
|
|
96
|
+
for (var _i = 2; _i < arguments.length; _i++) {
|
|
97
|
+
parts[_i - 2] = arguments[_i];
|
|
98
|
+
}
|
|
99
|
+
if (typeof strings === "string") {
|
|
100
|
+
strings = strings.split("{}");
|
|
101
|
+
}
|
|
102
|
+
var key = strings.join("{}");
|
|
103
|
+
var translation = (_a = _this.templates) === null || _a === void 0 ? void 0 : _a[locale];
|
|
104
|
+
var _d = (translation === null || translation === void 0 ? void 0 : translation[key]) || {}, template = _d.template, order = _d.order;
|
|
105
|
+
if (!template) {
|
|
106
|
+
if (((_c = (_b = import.meta) === null || _b === void 0 ? void 0 : _b.env) === null || _c === void 0 ? void 0 : _c.MODE) === "development") {
|
|
107
|
+
console.warn("not match translate key, ".concat(key), { translation: translation, locale: locale, strings: strings, parts: parts });
|
|
108
|
+
}
|
|
109
|
+
template = strings.slice();
|
|
110
|
+
order = parts.map(function (_, i) { return i; });
|
|
111
|
+
}
|
|
112
|
+
if (parts.length !== template.length - 1) {
|
|
113
|
+
throw new Error("translate template parts length does not match. locale: ".concat(locale, ", key: ").concat(key));
|
|
114
|
+
}
|
|
115
|
+
var runes = template.reduce(function (runes, template, idx) {
|
|
116
|
+
runes.push(template);
|
|
117
|
+
var orderIdx = order[idx];
|
|
118
|
+
if (orderIdx >= 0) {
|
|
119
|
+
var part = parts[orderIdx];
|
|
120
|
+
if (typeof part === "function") {
|
|
121
|
+
runes.push(part(locale));
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
runes.push(part);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return runes;
|
|
128
|
+
}, new Runes());
|
|
129
|
+
return runes;
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
return Translation;
|
|
133
|
+
}());
|
|
134
|
+
export { Translation };
|
|
135
|
+
var translation = new Translation();
|
|
136
|
+
export default translation;
|
|
137
|
+
export var l10n = translation.translate.bind(null, (_a = globalThis === null || globalThis === void 0 ? void 0 : globalThis.navigator) === null || _a === void 0 ? void 0 : _a.language);
|
|
73
138
|
//# sourceMappingURL=intl.js.map
|
package/dist/intl.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"intl.js","sourceRoot":"","sources":["../src/intl.js"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"intl.js","sourceRoot":"","sources":["../src/intl.js"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
2
|
+
"name": "intl-template",
|
|
3
|
+
"version": "1.0.0-alpha.0",
|
|
4
|
+
"description": "l10n tagged template literals",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/intl.js",
|
|
7
|
+
"module": "src/intl.js",
|
|
8
|
+
"repository": "github:performonkey/intl-template",
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc",
|
|
11
|
+
"test": "node --test"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"i18n",
|
|
15
|
+
"l10n",
|
|
16
|
+
"tagged template literals"
|
|
17
|
+
],
|
|
18
|
+
"author": "p10y",
|
|
19
|
+
"license": "ISC",
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"typescript": "^5.0.0"
|
|
22
|
+
}
|
|
22
23
|
}
|
package/src/intl.js
CHANGED
|
@@ -1,74 +1,118 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
export class Runes extends Array {
|
|
2
|
+
toString() {
|
|
3
|
+
return this.join("")
|
|
4
|
+
}
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Parses a template string and extracts the template parts and order of slots.
|
|
9
|
+
* @param {string} templateString - The template string to parse.
|
|
10
|
+
* @returns {{ template: string[], order: number[] }}
|
|
11
|
+
*/
|
|
12
|
+
export function parseTemplate(templateString) {
|
|
13
|
+
const order = [];
|
|
14
|
+
const template = [];
|
|
15
|
+
const parts = templateString.split(/({\d*})/)
|
|
16
|
+
parts.forEach((part) => {
|
|
17
|
+
if (part.match(/^{\d*}$/)) {
|
|
9
18
|
if (part === '{}') {
|
|
10
|
-
if (index === 0 || index === parts.length - 1) {
|
|
11
|
-
template.push("")
|
|
12
|
-
}
|
|
13
19
|
order.push(order.length)
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
if (part.match(/^{\d+}$/)) {
|
|
17
|
-
if (index === 0 || index === parts.length - 1) {
|
|
18
|
-
template.push("")
|
|
19
|
-
}
|
|
20
|
+
} else {
|
|
21
|
+
// slot with order, e.g. `{2}abc{1}def{0}`
|
|
20
22
|
order.push(parseInt(part.slice(1, -1), 10))
|
|
21
|
-
return
|
|
22
23
|
}
|
|
23
|
-
|
|
24
|
+
} else {
|
|
24
25
|
template.push(part)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
obj[key] = { template, order }
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
}, {})
|
|
29
|
+
return { template, order }
|
|
31
30
|
}
|
|
32
31
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
32
|
+
/**
|
|
33
|
+
* Represents a Translation object that handles string translation based on locale and templates.
|
|
34
|
+
*/
|
|
35
|
+
export class Translation {
|
|
36
|
+
/**
|
|
37
|
+
* Templates object that stores the translation templates for each locale.
|
|
38
|
+
* @type {Proxy}
|
|
39
|
+
*/
|
|
40
|
+
templates = new Proxy({}, {
|
|
41
|
+
get(templates, locale) {
|
|
42
|
+
return new Proxy(templates[locale] || {}, {
|
|
43
|
+
set(region, key, value) {
|
|
44
|
+
if (typeof value !== "string") {
|
|
45
|
+
throw new Error("Template must be a string.")
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
region[key] = parseTemplate(value)
|
|
49
|
+
|
|
50
|
+
return true
|
|
51
|
+
}
|
|
52
|
+
})
|
|
53
|
+
},
|
|
54
|
+
set(templates, locale, regionTemplates) {
|
|
55
|
+
templates[locale] = Object.entries(regionTemplates).reduce((region, [key, value]) => {
|
|
56
|
+
region[key] = parseTemplate(value)
|
|
57
|
+
|
|
58
|
+
return region
|
|
59
|
+
}, {})
|
|
60
|
+
|
|
61
|
+
return true
|
|
40
62
|
}
|
|
41
|
-
|
|
42
|
-
order = parts.map((_, i) => i)
|
|
43
|
-
}
|
|
63
|
+
})
|
|
44
64
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
65
|
+
/**
|
|
66
|
+
* Translates a string based on the provided locale and strings.
|
|
67
|
+
*
|
|
68
|
+
* @param {string} locale - The locale to use for translation.
|
|
69
|
+
* @param {string | string[]} strings - The string or array of strings to be translated.
|
|
70
|
+
* @param {...any} parts - The dynamic parts to be inserted into the translated string.
|
|
71
|
+
* @returns {Runes} - The translated string with dynamic parts inserted.
|
|
72
|
+
* @throws {Error} - If the length of the template parts does not match the length of the template.
|
|
73
|
+
*/
|
|
74
|
+
translate = (locale, strings, ...parts) => {
|
|
75
|
+
if (typeof strings === "string") {
|
|
76
|
+
strings = strings.split("{}")
|
|
77
|
+
}
|
|
48
78
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
if (
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
ret.push(part(locale, translation))
|
|
56
|
-
} else {
|
|
57
|
-
ret.push(part)
|
|
79
|
+
const key = strings.join("{}")
|
|
80
|
+
const translation = this.templates?.[locale]
|
|
81
|
+
let { template, order } = translation?.[key] || {}
|
|
82
|
+
if (!template) {
|
|
83
|
+
if (import.meta?.env?.MODE === "development") {
|
|
84
|
+
console.warn(`not match translate key, ${key}`, { translation, locale, strings, parts })
|
|
58
85
|
}
|
|
86
|
+
template = strings.slice()
|
|
87
|
+
order = parts.map((_, i) => i)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (parts.length !== template.length - 1) {
|
|
91
|
+
throw new Error(`translate template parts length does not match. locale: ${locale}, key: ${key}`)
|
|
59
92
|
}
|
|
60
|
-
return ret
|
|
61
|
-
}, [])
|
|
62
|
-
|
|
63
|
-
const text = ret.join("")
|
|
64
|
-
Object.defineProperty(ret, "toString", {
|
|
65
|
-
enumerable: false,
|
|
66
|
-
writable: false,
|
|
67
|
-
configurable: false,
|
|
68
|
-
value: () => text
|
|
69
|
-
})
|
|
70
93
|
|
|
71
|
-
|
|
94
|
+
const runes = template.reduce((runes, template, idx) => {
|
|
95
|
+
runes.push(template)
|
|
96
|
+
|
|
97
|
+
const orderIdx = order[idx]
|
|
98
|
+
if (orderIdx >= 0) {
|
|
99
|
+
const part = parts[orderIdx]
|
|
100
|
+
if (typeof part === "function") {
|
|
101
|
+
runes.push(part(locale))
|
|
102
|
+
} else {
|
|
103
|
+
runes.push(part)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return runes
|
|
108
|
+
}, new Runes())
|
|
109
|
+
|
|
110
|
+
return runes
|
|
111
|
+
}
|
|
72
112
|
}
|
|
73
113
|
|
|
74
|
-
|
|
114
|
+
const translation = new Translation()
|
|
115
|
+
|
|
116
|
+
export default translation
|
|
117
|
+
|
|
118
|
+
export const l10n = translation.translate.bind(null, globalThis?.navigator?.language);
|
package/src/intl.test.js
CHANGED
|
@@ -1,46 +1,110 @@
|
|
|
1
|
-
import assert from
|
|
2
|
-
import test from
|
|
3
|
-
import {
|
|
1
|
+
import assert from "node:assert"
|
|
2
|
+
import test from "node:test"
|
|
3
|
+
import { Translation, Runes } from "./intl.js"
|
|
4
4
|
|
|
5
|
+
test("template", async (t) => {
|
|
6
|
+
await t.test("template slot parse", () => {
|
|
7
|
+
const translation = new Translation()
|
|
8
|
+
const locale = "zh-CN"
|
|
9
|
+
translation.templates[locale] = {
|
|
10
|
+
"{}matched{}": "{}{}丁戊卯",
|
|
11
|
+
}
|
|
5
12
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
13
|
+
Object.assign(translation.templates[locale], {
|
|
14
|
+
"assign{}": "123{}"
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
assert.deepEqual(
|
|
18
|
+
translation.templates[locale],
|
|
19
|
+
{
|
|
20
|
+
"{}matched{}": {
|
|
21
|
+
template: ["", "", "丁戊卯"],
|
|
22
|
+
order: [0, 1]
|
|
23
|
+
},
|
|
24
|
+
"assign{}": {
|
|
25
|
+
template: ["123", ""],
|
|
26
|
+
order: [0]
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
)
|
|
9
30
|
})
|
|
10
|
-
const l10n = translate.bind(null, 'zh-CN')
|
|
11
|
-
assert.deepEqual(l10n`abc ${123} def ${345}`, ['甲乙丙 ', 123, ' ', 345, ' 丁戊卯'])
|
|
12
|
-
});
|
|
13
31
|
|
|
14
|
-
test(
|
|
15
|
-
|
|
16
|
-
|
|
32
|
+
await t.test("template slot order parse", () => {
|
|
33
|
+
const translation = new Translation()
|
|
34
|
+
const locale = "zh-CN"
|
|
35
|
+
const key = "{}matched{}"
|
|
36
|
+
translation.templates[locale] = {
|
|
37
|
+
[key]: "{1}{0}丁戊卯",
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
assert.deepEqual(
|
|
41
|
+
translation.templates[locale][key],
|
|
42
|
+
{
|
|
43
|
+
template: ["", "", "丁戊卯"],
|
|
44
|
+
order: [1, 0]
|
|
45
|
+
}
|
|
46
|
+
)
|
|
17
47
|
})
|
|
18
|
-
|
|
19
|
-
assert.equal(l10n`abc ${123} def ${345}`.toString(), '甲乙丙 345 123 丁戊卯')
|
|
20
|
-
});
|
|
48
|
+
})
|
|
21
49
|
|
|
22
|
-
test(
|
|
23
|
-
|
|
24
|
-
|
|
50
|
+
test("translation.translate", async (t) => {
|
|
51
|
+
await t.test("apply template", () => {
|
|
52
|
+
const translation = new Translation()
|
|
53
|
+
translation.templates["zh-CN"] = {
|
|
54
|
+
"abc {} def {}": "甲乙丙 {} {} 丁戊卯",
|
|
55
|
+
}
|
|
56
|
+
const l10n = translation.translate.bind(null, "zh-CN")
|
|
57
|
+
assert.deepEqual(l10n`abc ${123} def ${345}`, ["甲乙丙 ", 123, " ", 345, " 丁戊卯"])
|
|
25
58
|
})
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
59
|
+
|
|
60
|
+
await t.test("change slot order", () => {
|
|
61
|
+
const translation = new Translation()
|
|
62
|
+
translation.templates["zh-CN"] = {
|
|
63
|
+
"abc {} def {}": "甲乙丙 {1} {0} 丁戊卯",
|
|
64
|
+
}
|
|
65
|
+
const l10n = translation.translate.bind(null, "zh-CN")
|
|
66
|
+
assert.equal(l10n`abc ${123} def ${345}`.toString(), "甲乙丙 345 123 丁戊卯")
|
|
34
67
|
})
|
|
35
|
-
const l10n = translate.bind(null, 'zh-CN')
|
|
36
|
-
try {
|
|
37
|
-
l10n`${locale => locale} def ${345}`
|
|
38
|
-
} catch (err) {
|
|
39
|
-
assert.equal(
|
|
40
|
-
err.message,
|
|
41
|
-
`translate template parts length not match. locale: zh-CN, key: {} def {}`
|
|
42
|
-
)
|
|
43
|
-
}
|
|
44
68
|
|
|
45
|
-
|
|
46
|
-
|
|
69
|
+
await t.test("slot function", () => {
|
|
70
|
+
const translation = new Translation()
|
|
71
|
+
translation.templates["zh-CN"] = {
|
|
72
|
+
"{} def {}": "{1} {0} 丁戊卯",
|
|
73
|
+
}
|
|
74
|
+
const l10n = translation.translate.bind(null, "zh-CN")
|
|
75
|
+
assert.equal(l10n`${locale => locale} def ${345}`.toString(), "345 zh-CN 丁戊卯")
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
await t.test("slot count match", () => {
|
|
79
|
+
const translation = new Translation()
|
|
80
|
+
translation.templates["zh-CN"] = {
|
|
81
|
+
"{} def {}": "{} 丁戊卯",
|
|
82
|
+
"{}matched{}": "{}{}丁戊卯",
|
|
83
|
+
}
|
|
84
|
+
const l10n = translation.translate.bind(null, "zh-CN")
|
|
85
|
+
try {
|
|
86
|
+
l10n`${locale => locale} def ${345}`
|
|
87
|
+
} catch (err) {
|
|
88
|
+
assert.equal(
|
|
89
|
+
err.message,
|
|
90
|
+
`translate template parts length does not match. locale: zh-CN, key: {} def {}`
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
assert.equal(l10n`${locale => locale}matched${345}`.toString(), `zh-CN345丁戊卯`)
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
await t.test("call as function", () => {
|
|
98
|
+
const translation = new Translation()
|
|
99
|
+
translation.templates["zh-CN"] = {}
|
|
100
|
+
const l10n = translation.translate.bind(null, "zh-CN")
|
|
101
|
+
assert.equal(l10n("{} def {} {}", 1, "a", "b").toString(), '1 def a b')
|
|
102
|
+
})
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
test("Runes", async (t) => {
|
|
106
|
+
assert.equal(
|
|
107
|
+
new Runes('a', ' b ', 1, 2).toString(),
|
|
108
|
+
'a b 12'
|
|
109
|
+
)
|
|
110
|
+
})
|
package/tsconfig.json
CHANGED
package/dist/intl.test.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/intl.test.js
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) {
|
|
2
|
-
if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; }
|
|
3
|
-
return cooked;
|
|
4
|
-
};
|
|
5
|
-
import assert from 'node:assert';
|
|
6
|
-
import test from 'node:test';
|
|
7
|
-
import { translate, setTransition } from './intl.js';
|
|
8
|
-
test('apply template', function (t) {
|
|
9
|
-
setTransition('zh-CN', {
|
|
10
|
-
'abc {} def {}': '甲乙丙 {} {} 丁戊卯',
|
|
11
|
-
});
|
|
12
|
-
var l10n = translate.bind(null, 'zh-CN');
|
|
13
|
-
assert.deepEqual(l10n(templateObject_1 || (templateObject_1 = __makeTemplateObject(["abc ", " def ", ""], ["abc ", " def ", ""])), 123, 345), ['甲乙丙 ', 123, ' ', 345, ' 丁戊卯']);
|
|
14
|
-
});
|
|
15
|
-
test('change slot order', function (t) {
|
|
16
|
-
setTransition('zh-CN', {
|
|
17
|
-
'abc {} def {}': '甲乙丙 {1} {0} 丁戊卯',
|
|
18
|
-
});
|
|
19
|
-
var l10n = translate.bind(null, 'zh-CN');
|
|
20
|
-
assert.equal(l10n(templateObject_2 || (templateObject_2 = __makeTemplateObject(["abc ", " def ", ""], ["abc ", " def ", ""])), 123, 345).toString(), '甲乙丙 345 123 丁戊卯');
|
|
21
|
-
});
|
|
22
|
-
test('slot function', function (t) {
|
|
23
|
-
setTransition('zh-CN', {
|
|
24
|
-
'{} def {}': '{1} {0} 丁戊卯',
|
|
25
|
-
});
|
|
26
|
-
var l10n = translate.bind(null, 'zh-CN');
|
|
27
|
-
assert.equal(l10n(templateObject_3 || (templateObject_3 = __makeTemplateObject(["", " def ", ""], ["", " def ", ""])), function (locale) { return locale; }, 345).toString(), '345 zh-CN 丁戊卯');
|
|
28
|
-
});
|
|
29
|
-
test('slot count match', function (t) {
|
|
30
|
-
setTransition('zh-CN', {
|
|
31
|
-
'{} def {}': '{} 丁戊卯',
|
|
32
|
-
'{}matched{}': '{}{}丁戊卯',
|
|
33
|
-
});
|
|
34
|
-
var l10n = translate.bind(null, 'zh-CN');
|
|
35
|
-
try {
|
|
36
|
-
l10n(templateObject_4 || (templateObject_4 = __makeTemplateObject(["", " def ", ""], ["", " def ", ""])), function (locale) { return locale; }, 345);
|
|
37
|
-
}
|
|
38
|
-
catch (err) {
|
|
39
|
-
assert.equal(err.message, "translate template parts length not match. locale: zh-CN, key: {} def {}");
|
|
40
|
-
}
|
|
41
|
-
assert.equal(l10n(templateObject_5 || (templateObject_5 = __makeTemplateObject(["", "matched", ""], ["", "matched", ""])), function (locale) { return locale; }, 345).toString(), "zh-CN345\u4E01\u620A\u536F");
|
|
42
|
-
});
|
|
43
|
-
var templateObject_1, templateObject_2, templateObject_3, templateObject_4, templateObject_5;
|
|
44
|
-
//# sourceMappingURL=intl.test.js.map
|
package/dist/intl.test.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"intl.test.js","sourceRoot":"","sources":["../src/intl.test.js"],"names":[],"mappings":";;;;AAAA,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAGrD,IAAI,CAAC,gBAAgB,EAAE,UAAC,CAAC;IACxB,aAAa,CAAC,OAAO,EAAE;QACtB,eAAe,EAAE,eAAe;KAChC,CAAC,CAAA;IACF,IAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IAC1C,MAAM,CAAC,SAAS,CAAC,IAAI,sFAAA,MAAO,EAAG,OAAQ,EAAG,EAAE,KAAhB,GAAG,EAAQ,GAAG,GAAI,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC,CAAA;AAC/E,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,mBAAmB,EAAE,UAAC,CAAC;IAC3B,aAAa,CAAC,OAAO,EAAE;QACtB,eAAe,EAAE,iBAAiB;KAClC,CAAC,CAAA;IACF,IAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IAC1C,MAAM,CAAC,KAAK,CAAC,IAAI,sFAAA,MAAO,EAAG,OAAQ,EAAG,EAAE,KAAhB,GAAG,EAAQ,GAAG,EAAG,QAAQ,EAAE,EAAE,iBAAiB,CAAC,CAAA;AACxE,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,eAAe,EAAE,UAAC,CAAC;IACvB,aAAa,CAAC,OAAO,EAAE;QACtB,WAAW,EAAE,aAAa;KAC1B,CAAC,CAAA;IACF,IAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IAC1C,MAAM,CAAC,KAAK,CAAC,IAAI,kFAAA,EAAG,EAAgB,OAAQ,EAAG,EAAE,KAA7B,UAAA,MAAM,IAAI,OAAA,MAAM,EAAN,CAAM,EAAQ,GAAG,EAAG,QAAQ,EAAE,EAAE,eAAe,CAAC,CAAA;AAC/E,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,kBAAkB,EAAE,UAAC,CAAC;IAC1B,aAAa,CAAC,OAAO,EAAE;QACtB,WAAW,EAAE,QAAQ;QACrB,aAAa,EAAE,SAAS;KACxB,CAAC,CAAA;IACF,IAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IAC1C,IAAI,CAAC;QACJ,IAAI,kFAAA,EAAG,EAAgB,OAAQ,EAAG,EAAE,KAA7B,UAAA,MAAM,IAAI,OAAA,MAAM,EAAN,CAAM,EAAQ,GAAG,EAAE;IACrC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,MAAM,CAAC,KAAK,CACX,GAAG,CAAC,OAAO,EACX,0EAA0E,CAC1E,CAAA;IACF,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,IAAI,oFAAA,EAAG,EAAgB,SAAU,EAAG,EAAE,KAA/B,UAAA,MAAM,IAAI,OAAA,MAAM,EAAN,CAAM,EAAU,GAAG,EAAG,QAAQ,EAAE,EAAE,4BAAa,CAAC,CAAA;AAC/E,CAAC,CAAC,CAAC"}
|