intl-template 1.0.6 → 1.0.7

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.
Files changed (5) hide show
  1. package/README.md +138 -58
  2. package/intl.cjs +91 -56
  3. package/intl.d.ts +5 -5
  4. package/intl.js +106 -64
  5. package/package.json +3 -2
package/README.md CHANGED
@@ -1,19 +1,8 @@
1
- # intel-template
1
+ # intl-template
2
2
 
3
- A tiny i18n/l10n TTL(Tagged Template Literals) function
3
+ A tiny i18n/l10n helper built on JavaScript Tagged Template Literals.
4
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)
5
+ `intl-template` lets source strings stay close to your UI code while translations live in a simple locale-indexed template map.
17
6
 
18
7
  ## Installation
19
8
 
@@ -21,96 +10,187 @@ A tiny i18n/l10n TTL(Tagged Template Literals) function
21
10
  npm install intl-template
22
11
  ```
23
12
 
24
- ## Usage
13
+ ## Quick Start
25
14
 
26
- ```
27
- import translation from "intl-template"
15
+ ```javascript
16
+ import translation, { l10n } from "intl-template"
28
17
 
18
+ translation.locale = "es-ES"
29
19
  translation.templates["es-ES"] = {
30
- "hello {}": "hola {}"
20
+ "hello {}": "hola {}",
31
21
  }
32
22
 
33
- const l10n = translation.translate.bind(null, "es-ES")
23
+ const name = "Willow"
34
24
 
35
- const name = "willow";
36
-
37
- console.log(l10n`hello ${name}`)
38
- // => hola willow
25
+ console.log(l10n`hello ${name}`.toString())
26
+ // => hola Willow
39
27
  ```
40
28
 
41
- ### Use browser specifies locale
29
+ Every interpolation in a tagged template becomes `{}` in the translation key. For example, `l10n`hello ${name}`` looks up the key `"hello {}"`.
30
+
31
+ By default, translations return a `Runes` array so React nodes and other non-string values can pass through unchanged. Call `.toString()` when you need plain text, or create a `Translation` instance in `"string"` mode.
32
+
33
+ ## Templates
34
+
35
+ ```javascript
36
+ translation.templates["en-US"] = {
37
+ "hello {}": "hello {}",
38
+ "{} invited {}": "{} invited {}",
39
+ }
40
+
41
+ translation.templates["de-DE"] = {
42
+ "hello {}": "hallo {}",
43
+ "{} invited {}": "{1} wurde von {0} eingeladen",
44
+ }
42
45
  ```
46
+
47
+ Use `{}` to keep the original slot order. Use `{0}`, `{1}`, and later indexes when a locale needs a different order.
48
+
49
+ ## Locale
50
+
51
+ The shared `translation` instance uses `navigator.language` when it is available, then falls back to `"en"`. You can also set the locale explicitly.
52
+
53
+ ```javascript
43
54
  import translation, { l10n } from "intl-template"
44
55
 
45
- translation.templates["es-ES"] = {
46
- "hello {}": "hola {}"
56
+ const browserLocale = navigator.language
57
+
58
+ translation.locale = browserLocale
59
+ translation.templates[browserLocale] = {
60
+ "hello {}": "hola {}",
47
61
  }
48
62
 
49
- // l10n = translation.translate.bind(null, navigator,language)
63
+ console.log(l10n`hello ${"Willow"}`.toString())
64
+ // => hola Willow
65
+ ```
50
66
 
51
- const name = "willow";
67
+ ## Examples
52
68
 
53
- console.log(l10n`hello ${name}`)
54
- // => hola willow
55
- ```
69
+ ### React
56
70
 
57
- ### Use with React
71
+ The default `"react"` mode keeps interpolated values as values, so JSX can be used inside a translation.
58
72
 
59
- ```javascript
73
+ ```jsx
74
+ import translation, { l10n } from "intl-template"
60
75
 
61
- function SomeComponent({ name }) {
76
+ translation.locale = "es-ES"
77
+ translation.templates["es-ES"] = {
78
+ "hello {}": "hola {}",
79
+ }
80
+
81
+ function Greeting({ name }) {
62
82
  return (
63
- <div>
64
- {l10n`hello ${<b>{name}</b>}`}
65
- </div>
83
+ <p>
84
+ {l10n`hello ${<strong key="name">{name}</strong>}`}
85
+ </p>
66
86
  )
67
87
  }
68
88
  ```
69
89
 
70
- ### Specify slot order
90
+ ### Slot Order
71
91
 
72
- ```
92
+ ```javascript
93
+ translation.locale = "de-DE"
73
94
  translation.templates["de-DE"] = {
74
- "hello {} and {}": "hallo {1} und {0}"
95
+ "{} invited {}": "{1} wurde von {0} eingeladen",
75
96
  }
76
97
 
77
- const l10n = translation.translate.bind(null, "de-DE")
98
+ const inviter = "Willow"
99
+ const guest = "Jack"
100
+
101
+ console.log(l10n`${inviter} invited ${guest}`.toString())
102
+ // => Jack wurde von Willow eingeladen
103
+ ```
104
+
105
+ ### Nested Translations
78
106
 
79
- const name1 = "willow"
80
- const name2 = "jack"
107
+ ```javascript
108
+ translation.locale = "de-DE"
109
+ translation.templates["de-DE"] = {
110
+ "Bill": "Schmidt",
111
+ "hello {}": "hallo {}",
112
+ }
81
113
 
82
- console.log(l10n`hello ${name1} and ${name2}`)
83
- // => holla jack und willow
114
+ console.log(l10n`hello ${l10n`Bill`}`.toString())
115
+ // => hallo Schmidt
84
116
  ```
85
117
 
86
- ### Nested
118
+ ### Function Slots
119
+
120
+ When a slot is a function, it receives the active locale.
87
121
 
88
122
  ```javascript
123
+ translation.locale = "de-DE"
89
124
  translation.templates["de-DE"] = {
90
- "bill": "schmidt",
91
- "hello {}": "hallo {1}"
125
+ "current locale: {}": "aktuelle Sprache: {}",
92
126
  }
93
127
 
94
- const l10n = translation.translate.bind(null, "de-DE")
128
+ console.log(l10n`current locale: ${locale => locale}`.toString())
129
+ // => aktuelle Sprache: de-DE
130
+ ```
131
+
132
+ ### Call as a Function
133
+
134
+ Tagged templates and function calls use the same placeholder format.
95
135
 
96
- l10n`hello ${l10n`bill`}` // => hallo schmidt
136
+ ```javascript
137
+ translation.locale = "es-ES"
138
+ translation.templates["es-ES"] = {
139
+ "hello {}": "hola {}",
140
+ }
141
+
142
+ console.log(l10n("hello {}", "Willow").toString())
143
+ // => hola Willow
97
144
  ```
98
145
 
99
- ### Function slot
146
+ ### String Mode
100
147
 
101
148
  ```javascript
102
- translation.templates["de-DE"] = {
103
- "bill": "schmidt",
104
- "hello {}": "hallo {1}"
149
+ import { Translation } from "intl-template"
150
+
151
+ const translation = new Translation("es-ES", "string")
152
+
153
+ translation.templates["es-ES"] = {
154
+ "hello {}": "hola {}",
105
155
  }
106
156
 
107
- const l10n = translation.translate.bind(null, "de-DE")
157
+ const t = translation.translate
108
158
 
109
- l10n`hello ${(locale) => 123}` // => hallo 123
159
+ console.log(t`hello ${"Willow"}`)
160
+ // => hola Willow
110
161
  ```
111
162
 
112
- ### Call as function
163
+ ## API
164
+
165
+ ### `translation`
166
+
167
+ The default shared `Translation` instance.
168
+
169
+ ### `l10n`
170
+
171
+ A shorthand for `translation.translate`.
172
+
173
+ ### `new Translation(defaultLocale, mode)`
174
+
175
+ Creates an isolated translation instance.
176
+
177
+ - `defaultLocale`: locale used by this instance.
178
+ - `mode`: `"react"` by default, or `"string"` for plain string output.
179
+
180
+ ### `translation.locale`
181
+
182
+ The active locale used by `translate`.
183
+
184
+ ### `translation.templates`
185
+
186
+ Locale-indexed translation templates.
113
187
 
114
188
  ```javascript
115
- l10n("hello {}", name)
189
+ translation.templates["es-ES"] = {
190
+ "source {}": "translated {}",
191
+ }
116
192
  ```
193
+
194
+ ### `translation.translate(strings, ...parts)`
195
+
196
+ Translates a tagged template or a string with `{}` placeholders.
package/intl.cjs CHANGED
@@ -43,23 +43,43 @@ class Runes extends Array {
43
43
  return this.join("");
44
44
  }
45
45
  }
46
+ var SLOT_RE = /\{(\d*)\}/g;
47
+ var TEMPLATE_KEY_CACHE = new WeakMap;
46
48
  function parseTemplate(templateString) {
47
- const order = [];
48
49
  const template = [];
49
- const parts = templateString.split(/({\d*})/);
50
- parts.forEach((part) => {
51
- if (part.match(/^{\d*}$/)) {
52
- if (part === "{}") {
53
- order.push(order.length);
54
- } else {
55
- order.push(parseInt(part.slice(1, -1), 10));
56
- }
57
- } else {
58
- template.push(part);
59
- }
60
- });
50
+ const order = [];
51
+ let lastIndex = 0;
52
+ let match;
53
+ SLOT_RE.lastIndex = 0;
54
+ while ((match = SLOT_RE.exec(templateString)) !== null) {
55
+ template.push(templateString.slice(lastIndex, match.index));
56
+ order.push(match[1] === "" ? order.length : +match[1]);
57
+ lastIndex = SLOT_RE.lastIndex;
58
+ }
59
+ template.push(templateString.slice(lastIndex));
61
60
  return { template, order };
62
61
  }
62
+ function compileRegionTemplates(regionTemplates) {
63
+ const region = {};
64
+ for (const key in regionTemplates) {
65
+ region[key] = parseTemplate(regionTemplates[key]);
66
+ }
67
+ return region;
68
+ }
69
+ function getTemplateKey(strings) {
70
+ if (!strings.raw) {
71
+ return strings.join("{}");
72
+ }
73
+ let key = TEMPLATE_KEY_CACHE.get(strings);
74
+ if (key === undefined) {
75
+ key = strings.join("{}");
76
+ TEMPLATE_KEY_CACHE.set(strings, key);
77
+ }
78
+ return key;
79
+ }
80
+ function toTemplateString(value) {
81
+ return value == null ? "" : value;
82
+ }
63
83
 
64
84
  class Translation {
65
85
  mode = "react";
@@ -68,23 +88,25 @@ class Translation {
68
88
  this.mode = mode;
69
89
  this.locale = defaultLocale || globalThis?.navigator?.language || "en";
70
90
  }
71
- #templates = new Proxy({}, {
72
- get(templates, locale) {
73
- return new Proxy(templates[locale] || {}, {
74
- set(region, key, value) {
75
- if (typeof value !== "string") {
76
- throw new Error("Template must be a string.");
77
- }
78
- region[key] = parseTemplate(value);
79
- return true;
80
- }
81
- });
91
+ #regions = {};
92
+ #regionProxies = {};
93
+ #templates = new Proxy(this.#regions, {
94
+ get: (regions, locale) => {
95
+ const region = regions[locale];
96
+ if (!region) {
97
+ return new Proxy({}, REGION_HANDLER);
98
+ }
99
+ let proxy = this.#regionProxies[locale];
100
+ if (!proxy) {
101
+ proxy = new Proxy(region, REGION_HANDLER);
102
+ this.#regionProxies[locale] = proxy;
103
+ }
104
+ return proxy;
82
105
  },
83
- set(templates, locale, regionTemplates) {
84
- templates[locale] = Object.entries(regionTemplates).reduce((region, [key, value]) => {
85
- region[key] = parseTemplate(value);
86
- return region;
87
- }, {});
106
+ set: (regions, locale, regionTemplates) => {
107
+ const region = compileRegionTemplates(regionTemplates);
108
+ regions[locale] = region;
109
+ this.#regionProxies[locale] = new Proxy(region, REGION_HANDLER);
88
110
  return true;
89
111
  }
90
112
  });
@@ -92,46 +114,59 @@ class Translation {
92
114
  return this.#templates;
93
115
  }
94
116
  set templates(value) {
95
- Object.entries(value).forEach(([locale, regionTemplates]) => {
96
- this.#templates[locale] = regionTemplates;
97
- });
98
- return true;
117
+ for (const locale in value) {
118
+ this.#templates[locale] = value[locale];
119
+ }
99
120
  }
100
121
  translate = (strings, ...parts) => {
101
122
  const locale = this.locale;
102
- if (typeof strings === "string") {
103
- strings = strings.split("{}");
104
- }
105
- const key = strings.join("{}");
106
- const translation = this.#templates?.[locale];
107
- let { template, order } = translation?.[key] || {};
108
- if (!template) {
109
- console.debug(`[intl-template]not match translate key, ${key}`, { translation, locale, strings, parts });
110
- template = strings.slice();
111
- order = parts.map((_, i) => i);
123
+ const isStringInput = typeof strings === "string";
124
+ const key = isStringInput ? strings : getTemplateKey(strings);
125
+ const compiled = this.#regions[locale]?.[key];
126
+ let template;
127
+ let order;
128
+ if (compiled) {
129
+ template = compiled.template;
130
+ order = compiled.order;
131
+ } else {
132
+ template = isStringInput ? strings.split("{}") : strings.slice();
112
133
  }
113
134
  if (parts.length !== template.length - 1) {
114
135
  throw new Error(`translate template parts length does not match. locale: ${locale}, key: ${key}`);
115
136
  }
116
- const runes = template.reduce((runes2, template2, idx) => {
117
- runes2.push(template2);
118
- const orderIdx = order[idx];
119
- if (orderIdx >= 0) {
120
- const part = parts[orderIdx];
121
- if (typeof part === "function") {
122
- runes2.push(part(locale));
123
- } else {
124
- runes2.push(part);
137
+ const len = template.length;
138
+ if (this.mode !== "react") {
139
+ let result = "";
140
+ for (let idx = 0;idx < len; idx++) {
141
+ result += template[idx];
142
+ if (idx < parts.length) {
143
+ const part = parts[order ? order[idx] : idx];
144
+ result += toTemplateString(typeof part === "function" ? part(locale) : part);
125
145
  }
126
146
  }
127
- return runes2;
128
- }, new Runes);
129
- if (this.mode !== "react") {
130
- return runes.toString();
147
+ return result;
148
+ }
149
+ const runes = new Runes(len + parts.length);
150
+ let runeIdx = 0;
151
+ for (let idx = 0;idx < len; idx++) {
152
+ runes[runeIdx++] = template[idx];
153
+ if (idx < parts.length) {
154
+ const part = parts[order ? order[idx] : idx];
155
+ runes[runeIdx++] = typeof part === "function" ? part(locale) : part;
156
+ }
131
157
  }
132
158
  return runes;
133
159
  };
134
160
  }
161
+ var REGION_HANDLER = {
162
+ set(region, key, value) {
163
+ if (typeof value !== "string") {
164
+ throw new Error("Template must be a string.");
165
+ }
166
+ region[key] = parseTemplate(value);
167
+ return true;
168
+ }
169
+ };
135
170
  var translation = new Translation;
136
171
  var intl_default = translation;
137
172
  var l10n = translation.translate;
package/intl.d.ts CHANGED
@@ -28,19 +28,19 @@ export class Translation {
28
28
  * @type {string}
29
29
  **/
30
30
  locale: string;
31
- set templates(value: Proxy);
32
- get templates(): Proxy;
31
+ set templates(value: any);
32
+ get templates(): any;
33
33
  /**
34
34
  * Translates a string based on the provided locale and strings.
35
35
  *
36
36
  * @param {TemplateStringsArray | string} strings - The string or array of strings to be translated.
37
37
  * @param {...any} parts - The dynamic parts to be inserted into the translated string.
38
- * @returns {Runes} - The translated string with dynamic parts inserted.
38
+ * @returns {Runes | string} - The translated string with dynamic parts inserted.
39
39
  * @throws {Error} - If the length of the template parts does not match the length of the template.
40
40
  */
41
- translate: (strings: TemplateStringsArray | string, ...parts: any[]) => Runes;
41
+ translate: (strings: TemplateStringsArray | string, ...parts: any[]) => Runes | string;
42
42
  #private;
43
43
  }
44
44
  export const translation: Translation;
45
45
  export default translation;
46
- export function l10n(strings: TemplateStringsArray | string, ...parts: any[]): Runes;
46
+ export function l10n(strings: TemplateStringsArray | string, ...parts: any[]): Runes | string;
package/intl.js CHANGED
@@ -4,31 +4,59 @@ export class Runes extends Array {
4
4
  }
5
5
  }
6
6
 
7
+ const SLOT_RE = /\{(\d*)\}/g
8
+ const TEMPLATE_KEY_CACHE = new WeakMap()
9
+
7
10
  /**
8
11
  * Parses a template string and extracts the template parts and order of slots.
9
12
  * @param {string} templateString - The template string to parse.
10
13
  * @returns {{ template: string[], order: number[] }}
11
14
  */
12
15
  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*}$/)) {
18
- if (part === '{}') {
19
- order.push(order.length)
20
- } else {
21
- // slot with order, e.g. `{2}abc{1}def{0}`
22
- order.push(parseInt(part.slice(1, -1), 10))
23
- }
24
- } else {
25
- template.push(part)
26
- }
27
- })
16
+ const template = []
17
+ const order = []
18
+ let lastIndex = 0
19
+ let match
20
+
21
+ SLOT_RE.lastIndex = 0
22
+ while ((match = SLOT_RE.exec(templateString)) !== null) {
23
+ template.push(templateString.slice(lastIndex, match.index))
24
+ // `{}` keeps original auto-numbering: index = current order length
25
+ order.push(match[1] === "" ? order.length : +match[1])
26
+ lastIndex = SLOT_RE.lastIndex
27
+ }
28
+ template.push(templateString.slice(lastIndex))
28
29
 
29
30
  return { template, order }
30
31
  }
31
32
 
33
+ function compileRegionTemplates(regionTemplates) {
34
+ const region = {}
35
+ for (const key in regionTemplates) {
36
+ region[key] = parseTemplate(regionTemplates[key])
37
+ }
38
+
39
+ return region
40
+ }
41
+
42
+ function getTemplateKey(strings) {
43
+ if (!strings.raw) {
44
+ return strings.join("{}")
45
+ }
46
+
47
+ let key = TEMPLATE_KEY_CACHE.get(strings)
48
+ if (key === undefined) {
49
+ key = strings.join("{}")
50
+ TEMPLATE_KEY_CACHE.set(strings, key)
51
+ }
52
+
53
+ return key
54
+ }
55
+
56
+ function toTemplateString(value) {
57
+ return value == null ? "" : value
58
+ }
59
+
32
60
  /**
33
61
  * Represents a Translation object that handles string translation based on locale and templates.
34
62
  */
@@ -55,26 +83,29 @@ export class Translation {
55
83
  * Templates object that stores the translation templates for each locale.
56
84
  * @type {Proxy}
57
85
  */
58
- #templates = new Proxy({}, {
59
- get(templates, locale) {
60
- return new Proxy(templates[locale] || {}, {
61
- set(region, key, value) {
62
- if (typeof value !== "string") {
63
- throw new Error("Template must be a string.")
64
- }
86
+ #regions = {}
65
87
 
66
- region[key] = parseTemplate(value)
88
+ #regionProxies = {}
67
89
 
68
- return true
69
- }
70
- })
71
- },
72
- set(templates, locale, regionTemplates) {
73
- templates[locale] = Object.entries(regionTemplates).reduce((region, [key, value]) => {
74
- region[key] = parseTemplate(value)
90
+ #templates = new Proxy(this.#regions, {
91
+ get: (regions, locale) => {
92
+ const region = regions[locale]
93
+ if (!region) {
94
+ return new Proxy({}, REGION_HANDLER)
95
+ }
75
96
 
76
- return region
77
- }, {})
97
+ let proxy = this.#regionProxies[locale]
98
+ if (!proxy) {
99
+ proxy = new Proxy(region, REGION_HANDLER)
100
+ this.#regionProxies[locale] = proxy
101
+ }
102
+
103
+ return proxy
104
+ },
105
+ set: (regions, locale, regionTemplates) => {
106
+ const region = compileRegionTemplates(regionTemplates)
107
+ regions[locale] = region
108
+ this.#regionProxies[locale] = new Proxy(region, REGION_HANDLER)
78
109
 
79
110
  return true
80
111
  }
@@ -85,11 +116,9 @@ export class Translation {
85
116
  }
86
117
 
87
118
  set templates(value) {
88
- Object.entries(value).forEach(([locale, regionTemplates]) => {
89
- this.#templates[locale] = regionTemplates
90
- })
91
-
92
- return true
119
+ for (const locale in value) {
120
+ this.#templates[locale] = value[locale]
121
+ }
93
122
  }
94
123
 
95
124
  /**
@@ -97,53 +126,66 @@ export class Translation {
97
126
  *
98
127
  * @param {TemplateStringsArray | string} strings - The string or array of strings to be translated.
99
128
  * @param {...any} parts - The dynamic parts to be inserted into the translated string.
100
- * @returns {Runes} - The translated string with dynamic parts inserted.
129
+ * @returns {Runes | string} - The translated string with dynamic parts inserted.
101
130
  * @throws {Error} - If the length of the template parts does not match the length of the template.
102
131
  */
103
132
  translate = (strings, ...parts) => {
104
133
  const locale = this.locale
105
-
106
- if (typeof strings === "string") {
107
- strings = strings.split("{}")
108
- }
109
-
110
- const key = strings.join("{}")
111
- const translation = this.#templates?.[locale]
112
- let { template, order } = translation?.[key] || {}
113
- if (!template) {
114
- console.debug(`[intl-template]not match translate key, ${key}`, { translation, locale, strings, parts })
115
- template = strings.slice()
116
- order = parts.map((_, i) => i)
134
+ const isStringInput = typeof strings === "string"
135
+ const key = isStringInput ? strings : getTemplateKey(strings)
136
+
137
+ const compiled = this.#regions[locale]?.[key]
138
+ let template
139
+ let order
140
+ if (compiled) {
141
+ template = compiled.template
142
+ order = compiled.order
143
+ } else {
144
+ template = isStringInput ? strings.split("{}") : strings.slice()
117
145
  }
118
146
 
119
147
  if (parts.length !== template.length - 1) {
120
148
  throw new Error(`translate template parts length does not match. locale: ${locale}, key: ${key}`)
121
149
  }
122
150
 
123
- const runes = template.reduce((runes, template, idx) => {
124
- runes.push(template)
125
-
126
- const orderIdx = order[idx]
127
- if (orderIdx >= 0) {
128
- const part = parts[orderIdx]
129
- if (typeof part === "function") {
130
- runes.push(part(locale))
131
- } else {
132
- runes.push(part)
151
+ const len = template.length
152
+ if (this.mode !== "react") {
153
+ let result = ""
154
+ for (let idx = 0; idx < len; idx++) {
155
+ result += template[idx]
156
+ if (idx < parts.length) {
157
+ const part = parts[order ? order[idx] : idx]
158
+ result += toTemplateString(typeof part === "function" ? part(locale) : part)
133
159
  }
134
160
  }
135
161
 
136
- return runes
137
- }, new Runes())
162
+ return result
163
+ }
138
164
 
139
- if (this.mode !== "react") {
140
- return runes.toString()
165
+ const runes = new Runes(len + parts.length)
166
+ let runeIdx = 0
167
+ for (let idx = 0; idx < len; idx++) {
168
+ runes[runeIdx++] = template[idx]
169
+ if (idx < parts.length) {
170
+ const part = parts[order ? order[idx] : idx]
171
+ runes[runeIdx++] = typeof part === "function" ? part(locale) : part
172
+ }
141
173
  }
142
174
 
143
175
  return runes
144
176
  }
145
177
  }
146
178
 
179
+ const REGION_HANDLER = {
180
+ set(region, key, value) {
181
+ if (typeof value !== "string") {
182
+ throw new Error("Template must be a string.")
183
+ }
184
+ region[key] = parseTemplate(value)
185
+ return true
186
+ }
187
+ }
188
+
147
189
  export const translation = new Translation()
148
190
 
149
191
  export default translation
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "intl-template",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "description": "l10n tagged template literals",
5
5
  "type": "module",
6
6
  "main": "./intl.cjs",
@@ -15,7 +15,8 @@
15
15
  ],
16
16
  "scripts": {
17
17
  "build": "bun build --outfile intl.cjs --format cjs intl.js",
18
- "postbuild": "rm *.d.ts && tsc --emitDeclarationOnly --allowJs -d --outDir . *.js"
18
+ "perf": "node --test intl.perf.test.js",
19
+ "postbuild": "rm *.d.ts && tsc --emitDeclarationOnly --allowJs -d --outDir . intl.js intl.test.js"
19
20
  },
20
21
  "keywords": [
21
22
  "i18n",