inline-i18n-multi 0.1.2 → 0.2.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 +319 -20
- package/dist/index.js +74 -14
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +74 -14
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -1,55 +1,354 @@
|
|
|
1
|
-
> **Important:** For complete documentation, examples, and best practices, please read the [full documentation on GitHub](https://github.com/exiivy98/inline-i18n-multi).
|
|
2
|
-
|
|
3
1
|
# inline-i18n-multi
|
|
4
2
|
|
|
5
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/inline-i18n-multi)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
**Write translations inline. Find them instantly.**
|
|
7
|
+
|
|
8
|
+
> For complete documentation, examples, and best practices, please read the [full documentation on GitHub](https://github.com/exiivy98/inline-i18n-multi).
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## The Problem
|
|
13
|
+
|
|
14
|
+
Traditional i18n libraries separate translations from code:
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
17
|
+
// Component.tsx
|
|
18
|
+
<p>{t('greeting.hello')}</p>
|
|
19
|
+
|
|
20
|
+
// en.json
|
|
21
|
+
{ "greeting": { "hello": "Hello" } }
|
|
22
|
+
|
|
23
|
+
// ko.json
|
|
24
|
+
{ "greeting": { "hello": "안녕하세요" } }
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
When you see "Hello" in your app and want to find it in the code, you have to:
|
|
28
|
+
1. Search for "Hello" in JSON files
|
|
29
|
+
2. Find the key `greeting.hello`
|
|
30
|
+
3. Search for that key in your code
|
|
31
|
+
4. Finally find `t('greeting.hello')`
|
|
32
|
+
|
|
33
|
+
**This is slow and frustrating.**
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## The Solution
|
|
38
|
+
|
|
39
|
+
With `inline-i18n-multi`, translations live in your code:
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
<p>{it('안녕하세요', 'Hello')}</p>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
See "Hello" in your app? Just search for "Hello" in your codebase. **Done.**
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Features
|
|
50
|
+
|
|
51
|
+
- **Inline translations** - Write translations right where you use them
|
|
52
|
+
- **Instant search** - Find any text in your codebase immediately
|
|
53
|
+
- **Type-safe** - Full TypeScript support with variable type checking
|
|
54
|
+
- **Multiple languages** - Support for any number of locales
|
|
55
|
+
- **i18n compatible** - Support for traditional key-based translations with JSON dictionaries
|
|
56
|
+
- **ICU Message Format** - Plural and select syntax for complex translations
|
|
57
|
+
- **Variable interpolation** - `{name}` syntax for dynamic values
|
|
58
|
+
|
|
59
|
+
---
|
|
6
60
|
|
|
7
61
|
## Installation
|
|
8
62
|
|
|
9
63
|
```bash
|
|
64
|
+
# npm
|
|
10
65
|
npm install inline-i18n-multi
|
|
66
|
+
|
|
67
|
+
# yarn
|
|
68
|
+
yarn add inline-i18n-multi
|
|
69
|
+
|
|
70
|
+
# pnpm
|
|
71
|
+
pnpm add inline-i18n-multi
|
|
11
72
|
```
|
|
12
73
|
|
|
74
|
+
---
|
|
75
|
+
|
|
13
76
|
## Quick Start
|
|
14
77
|
|
|
15
|
-
```
|
|
78
|
+
```typescript
|
|
16
79
|
import { it, setLocale } from 'inline-i18n-multi'
|
|
17
80
|
|
|
18
|
-
|
|
81
|
+
// Set current locale
|
|
82
|
+
setLocale('en')
|
|
19
83
|
|
|
20
|
-
//
|
|
21
|
-
it('안녕하세요', 'Hello') // →
|
|
84
|
+
// Shorthand syntax (Korean + English)
|
|
85
|
+
it('안녕하세요', 'Hello') // → "Hello"
|
|
22
86
|
|
|
23
|
-
// Object syntax
|
|
24
|
-
it({ ko: '
|
|
87
|
+
// Object syntax (multiple languages)
|
|
88
|
+
it({ ko: '안녕하세요', en: 'Hello', ja: 'こんにちは' }) // → "Hello"
|
|
25
89
|
|
|
26
90
|
// With variables
|
|
27
|
-
it('안녕, {name}님', 'Hello, {name}', { name: '
|
|
91
|
+
it('안녕, {name}님', 'Hello, {name}', { name: 'John' }) // → "Hello, John"
|
|
28
92
|
```
|
|
29
93
|
|
|
30
|
-
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Key-Based Translations (i18n Compatible)
|
|
31
97
|
|
|
32
|
-
|
|
33
|
-
import { t, loadDictionaries, setLocale } from 'inline-i18n-multi'
|
|
98
|
+
For projects that already use JSON translation files, or when you need traditional key-based translations:
|
|
34
99
|
|
|
100
|
+
```typescript
|
|
101
|
+
import { t, loadDictionaries } from 'inline-i18n-multi'
|
|
102
|
+
|
|
103
|
+
// Load translation dictionaries
|
|
35
104
|
loadDictionaries({
|
|
36
|
-
en: {
|
|
37
|
-
|
|
105
|
+
en: {
|
|
106
|
+
greeting: { hello: 'Hello', goodbye: 'Goodbye' },
|
|
107
|
+
items: { count_one: '{count} item', count_other: '{count} items' },
|
|
108
|
+
welcome: 'Welcome, {name}!'
|
|
109
|
+
},
|
|
110
|
+
ko: {
|
|
111
|
+
greeting: { hello: '안녕하세요', goodbye: '안녕히 가세요' },
|
|
112
|
+
items: { count_other: '{count}개 항목' },
|
|
113
|
+
welcome: '환영합니다, {name}님!'
|
|
114
|
+
}
|
|
38
115
|
})
|
|
39
116
|
|
|
117
|
+
// Basic key-based translation
|
|
118
|
+
t('greeting.hello') // → "Hello" (when locale is 'en')
|
|
119
|
+
|
|
120
|
+
// With variables
|
|
121
|
+
t('welcome', { name: 'John' }) // → "Welcome, John!"
|
|
122
|
+
|
|
123
|
+
// Plural support (uses Intl.PluralRules)
|
|
124
|
+
t('items.count', { count: 1 }) // → "1 item"
|
|
125
|
+
t('items.count', { count: 5 }) // → "5 items"
|
|
126
|
+
|
|
127
|
+
// Override locale
|
|
128
|
+
t('greeting.hello', undefined, 'ko') // → "안녕하세요"
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Utility Functions
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
import { hasTranslation, getLoadedLocales, getDictionary } from 'inline-i18n-multi'
|
|
137
|
+
|
|
138
|
+
// Check if translation exists
|
|
139
|
+
hasTranslation('greeting.hello') // → true
|
|
140
|
+
hasTranslation('missing.key') // → false
|
|
141
|
+
|
|
142
|
+
// Get loaded locales
|
|
143
|
+
getLoadedLocales() // → ['en', 'ko']
|
|
144
|
+
|
|
145
|
+
// Get dictionary for a locale
|
|
146
|
+
getDictionary('en') // → { greeting: { hello: 'Hello', ... }, ... }
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## ICU Message Format
|
|
152
|
+
|
|
153
|
+
For complex translations with plurals and conditional text:
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
import { it, setLocale } from 'inline-i18n-multi'
|
|
157
|
+
|
|
40
158
|
setLocale('en')
|
|
41
|
-
|
|
42
|
-
|
|
159
|
+
|
|
160
|
+
// Plural
|
|
161
|
+
it({
|
|
162
|
+
ko: '{count, plural, =0 {항목 없음} other {# 개}}',
|
|
163
|
+
en: '{count, plural, =0 {No items} one {# item} other {# items}}'
|
|
164
|
+
}, { count: 0 }) // → "No items"
|
|
165
|
+
|
|
166
|
+
it({
|
|
167
|
+
ko: '{count, plural, =0 {항목 없음} other {# 개}}',
|
|
168
|
+
en: '{count, plural, =0 {No items} one {# item} other {# items}}'
|
|
169
|
+
}, { count: 1 }) // → "1 item"
|
|
170
|
+
|
|
171
|
+
it({
|
|
172
|
+
ko: '{count, plural, =0 {항목 없음} other {# 개}}',
|
|
173
|
+
en: '{count, plural, =0 {No items} one {# item} other {# items}}'
|
|
174
|
+
}, { count: 5 }) // → "5 items"
|
|
175
|
+
|
|
176
|
+
// Select
|
|
177
|
+
it({
|
|
178
|
+
ko: '{gender, select, male {그} female {그녀} other {그들}}',
|
|
179
|
+
en: '{gender, select, male {He} female {She} other {They}}'
|
|
180
|
+
}, { gender: 'female' }) // → "She"
|
|
181
|
+
|
|
182
|
+
// Combined with text
|
|
183
|
+
it({
|
|
184
|
+
ko: '{name}님이 {count, plural, =0 {메시지가 없습니다} other {# 개의 메시지가 있습니다}}',
|
|
185
|
+
en: '{name} has {count, plural, =0 {no messages} one {# message} other {# messages}}'
|
|
186
|
+
}, { name: 'John', count: 3 }) // → "John has 3 messages"
|
|
43
187
|
```
|
|
44
188
|
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## Language Pair Helpers
|
|
192
|
+
|
|
193
|
+
For common language combinations, use the shorthand helpers:
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
import { it_ja, en_zh, ja_es } from 'inline-i18n-multi'
|
|
197
|
+
|
|
198
|
+
// Korean ↔ Japanese
|
|
199
|
+
it_ja('안녕하세요', 'こんにちは')
|
|
200
|
+
|
|
201
|
+
// English ↔ Chinese
|
|
202
|
+
en_zh('Hello', '你好')
|
|
203
|
+
|
|
204
|
+
// Japanese ↔ Spanish
|
|
205
|
+
ja_es('こんにちは', 'Hola')
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Available helpers:
|
|
209
|
+
- `it` (ko↔en), `it_ja`, `it_zh`, `it_es`, `it_fr`, `it_de`
|
|
210
|
+
- `en_ja`, `en_zh`, `en_es`, `en_fr`, `en_de`
|
|
211
|
+
- `ja_zh`, `ja_es`, `zh_es`
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## API Reference
|
|
216
|
+
|
|
217
|
+
### Core Functions
|
|
218
|
+
|
|
219
|
+
| Function | Description |
|
|
220
|
+
|----------|-------------|
|
|
221
|
+
| `it(ko, en, vars?)` | Translate with Korean and English |
|
|
222
|
+
| `it(translations, vars?)` | Translate with object syntax |
|
|
223
|
+
| `setLocale(locale)` | Set current locale |
|
|
224
|
+
| `getLocale()` | Get current locale |
|
|
225
|
+
|
|
226
|
+
### Key-Based Translation
|
|
227
|
+
|
|
228
|
+
| Function | Description |
|
|
229
|
+
|----------|-------------|
|
|
230
|
+
| `t(key, vars?, locale?)` | Key-based translation with optional locale override |
|
|
231
|
+
| `loadDictionaries(dicts)` | Load translation dictionaries for multiple locales |
|
|
232
|
+
| `loadDictionary(locale, dict)` | Load dictionary for a single locale |
|
|
233
|
+
| `hasTranslation(key, locale?)` | Check if translation key exists |
|
|
234
|
+
| `getLoadedLocales()` | Get array of loaded locale codes |
|
|
235
|
+
| `getDictionary(locale)` | Get dictionary for a specific locale |
|
|
236
|
+
|
|
237
|
+
### Types
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
type Locale = string
|
|
241
|
+
type Translations = Record<Locale, string>
|
|
242
|
+
type TranslationVars = Record<string, string | number>
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## Why Inline Translations?
|
|
248
|
+
|
|
249
|
+
### Traditional i18n
|
|
250
|
+
|
|
251
|
+
```
|
|
252
|
+
Code → Key → JSON file → Translation
|
|
253
|
+
↑
|
|
254
|
+
Hard to trace
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### Inline i18n
|
|
258
|
+
|
|
259
|
+
```
|
|
260
|
+
Code ← Translation (same place!)
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
| Aspect | Traditional | Inline |
|
|
264
|
+
|--------|-------------|--------|
|
|
265
|
+
| Finding text in code | Hard (key lookup) | Easy (direct search) |
|
|
266
|
+
| Adding translations | Create key, add to JSON | Write inline |
|
|
267
|
+
| Refactoring | Update key references | Automatic |
|
|
268
|
+
| Code review | Check JSON separately | All visible in diff |
|
|
269
|
+
| Type safety | Limited | Full support |
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
45
273
|
## Framework Integrations
|
|
46
274
|
|
|
47
|
-
|
|
48
|
-
|
|
275
|
+
### React
|
|
276
|
+
|
|
277
|
+
React hooks and components for inline translations. Includes `LocaleProvider` for context management, `useLocale()` hook for locale state, and `T` component for JSX translations. Automatic cookie persistence when locale changes.
|
|
278
|
+
|
|
279
|
+
```bash
|
|
280
|
+
npm install inline-i18n-multi-react
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
[View React package →](https://www.npmjs.com/package/inline-i18n-multi-react)
|
|
284
|
+
|
|
285
|
+
### Next.js
|
|
286
|
+
|
|
287
|
+
Full Next.js App Router integration with SSR/SSG support. Server Components use async `it()`, Client Components use React bindings. Includes SEO utilities: `createMetadata()` for dynamic metadata, `getAlternates()` for hreflang links, and `createI18nMiddleware()` for locale detection.
|
|
288
|
+
|
|
289
|
+
```bash
|
|
290
|
+
npm install inline-i18n-multi-next
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
[View Next.js package →](https://www.npmjs.com/package/inline-i18n-multi-next)
|
|
294
|
+
|
|
295
|
+
---
|
|
296
|
+
|
|
297
|
+
## Build-Time Optimization
|
|
298
|
+
|
|
299
|
+
### Babel Plugin
|
|
300
|
+
|
|
301
|
+
Transform `it()` calls at build time for better performance. Extracts translations for static analysis and enables dead code elimination for unused locales.
|
|
302
|
+
|
|
303
|
+
```bash
|
|
304
|
+
npm install -D @inline-i18n-multi/babel-plugin
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
[View Babel plugin →](https://www.npmjs.com/package/@inline-i18n-multi/babel-plugin)
|
|
308
|
+
|
|
309
|
+
### SWC Plugin
|
|
310
|
+
|
|
311
|
+
SWC plugin for Next.js 13+ projects. Faster than Babel with the same optimization benefits. Configure in `next.config.js` under `experimental.swcPlugins`.
|
|
312
|
+
|
|
313
|
+
```bash
|
|
314
|
+
npm install -D @inline-i18n-multi/swc-plugin
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
[View SWC plugin →](https://www.npmjs.com/package/@inline-i18n-multi/swc-plugin)
|
|
318
|
+
|
|
319
|
+
---
|
|
320
|
+
|
|
321
|
+
## Developer Tools
|
|
322
|
+
|
|
323
|
+
### CLI
|
|
324
|
+
|
|
325
|
+
Command-line tools for translation management. Find translations with `inline-i18n find "text"`, validate consistency with `inline-i18n validate`, and generate coverage reports with `inline-i18n coverage`.
|
|
326
|
+
|
|
327
|
+
```bash
|
|
328
|
+
npm install -D @inline-i18n-multi/cli
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
[View CLI package →](https://www.npmjs.com/package/@inline-i18n-multi/cli)
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
## Requirements
|
|
336
|
+
|
|
337
|
+
- Node.js 18+
|
|
338
|
+
- TypeScript 5.0+ (recommended)
|
|
339
|
+
|
|
340
|
+
---
|
|
49
341
|
|
|
50
342
|
## Documentation
|
|
51
343
|
|
|
52
|
-
**Please read the [full documentation on GitHub](https://github.com/exiivy98/inline-i18n-multi)** for
|
|
344
|
+
**Please read the [full documentation on GitHub](https://github.com/exiivy98/inline-i18n-multi)** for:
|
|
345
|
+
- Complete API reference
|
|
346
|
+
- Framework integrations (React, Next.js)
|
|
347
|
+
- Build-time optimization
|
|
348
|
+
- CLI tools
|
|
349
|
+
- Best practices and examples
|
|
350
|
+
|
|
351
|
+
---
|
|
53
352
|
|
|
54
353
|
## License
|
|
55
354
|
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var icuMessageformatParser = require('@formatjs/icu-messageformat-parser');
|
|
4
|
+
|
|
3
5
|
// src/context.ts
|
|
4
6
|
var currentLocale = "en";
|
|
5
7
|
function setLocale(locale) {
|
|
@@ -8,11 +10,75 @@ function setLocale(locale) {
|
|
|
8
10
|
function getLocale() {
|
|
9
11
|
return currentLocale;
|
|
10
12
|
}
|
|
13
|
+
function interpolateICU(template, vars, locale) {
|
|
14
|
+
const ast = icuMessageformatParser.parse(template);
|
|
15
|
+
return formatElements(ast, vars, locale, null);
|
|
16
|
+
}
|
|
17
|
+
function formatElements(elements, vars, locale, currentPluralValue) {
|
|
18
|
+
return elements.map((el) => formatElement(el, vars, locale, currentPluralValue)).join("");
|
|
19
|
+
}
|
|
20
|
+
function formatElement(el, vars, locale, currentPluralValue) {
|
|
21
|
+
if (icuMessageformatParser.isLiteralElement(el)) {
|
|
22
|
+
return el.value;
|
|
23
|
+
}
|
|
24
|
+
if (icuMessageformatParser.isArgumentElement(el)) {
|
|
25
|
+
const value = vars[el.value];
|
|
26
|
+
return value !== void 0 ? String(value) : `{${el.value}}`;
|
|
27
|
+
}
|
|
28
|
+
if (icuMessageformatParser.isPoundElement(el)) {
|
|
29
|
+
return currentPluralValue !== null ? String(currentPluralValue) : "#";
|
|
30
|
+
}
|
|
31
|
+
if (icuMessageformatParser.isPluralElement(el)) {
|
|
32
|
+
return formatPlural(el, vars, locale);
|
|
33
|
+
}
|
|
34
|
+
if (icuMessageformatParser.isSelectElement(el)) {
|
|
35
|
+
return formatSelect(el, vars, locale);
|
|
36
|
+
}
|
|
37
|
+
return "";
|
|
38
|
+
}
|
|
39
|
+
function formatPlural(el, vars, locale) {
|
|
40
|
+
const value = vars[el.value];
|
|
41
|
+
if (typeof value !== "number") {
|
|
42
|
+
return `{${el.value}}`;
|
|
43
|
+
}
|
|
44
|
+
const adjustedValue = value - el.offset;
|
|
45
|
+
const pluralRules = new Intl.PluralRules(locale, { type: el.pluralType });
|
|
46
|
+
const category = pluralRules.select(adjustedValue);
|
|
47
|
+
const exactKey = `=${value}`;
|
|
48
|
+
if (el.options[exactKey]) {
|
|
49
|
+
return formatElements(el.options[exactKey].value, vars, locale, adjustedValue);
|
|
50
|
+
}
|
|
51
|
+
if (el.options[category]) {
|
|
52
|
+
return formatElements(el.options[category].value, vars, locale, adjustedValue);
|
|
53
|
+
}
|
|
54
|
+
if (el.options.other) {
|
|
55
|
+
return formatElements(el.options.other.value, vars, locale, adjustedValue);
|
|
56
|
+
}
|
|
57
|
+
return `{${el.value}}`;
|
|
58
|
+
}
|
|
59
|
+
function formatSelect(el, vars, locale) {
|
|
60
|
+
const value = vars[el.value];
|
|
61
|
+
const key = String(value);
|
|
62
|
+
if (el.options[key]) {
|
|
63
|
+
return formatElements(el.options[key].value, vars, locale, null);
|
|
64
|
+
}
|
|
65
|
+
if (el.options.other) {
|
|
66
|
+
return formatElements(el.options.other.value, vars, locale, null);
|
|
67
|
+
}
|
|
68
|
+
return `{${el.value}}`;
|
|
69
|
+
}
|
|
70
|
+
var ICU_PATTERN = /\{[^}]+,\s*(plural|select|selectordinal)\s*,/;
|
|
71
|
+
function hasICUPattern(template) {
|
|
72
|
+
return ICU_PATTERN.test(template);
|
|
73
|
+
}
|
|
11
74
|
|
|
12
75
|
// src/interpolation.ts
|
|
13
76
|
var VARIABLE_PATTERN = /\{(\w+)\}/g;
|
|
14
|
-
function interpolate(template, vars) {
|
|
77
|
+
function interpolate(template, vars, locale) {
|
|
15
78
|
if (!vars) return template;
|
|
79
|
+
if (hasICUPattern(template)) {
|
|
80
|
+
return interpolateICU(template, vars, locale || "en");
|
|
81
|
+
}
|
|
16
82
|
return template.replace(VARIABLE_PATTERN, (_, key) => {
|
|
17
83
|
const value = vars[key];
|
|
18
84
|
return value !== void 0 ? String(value) : `{${key}}`;
|
|
@@ -23,9 +89,9 @@ function interpolate(template, vars) {
|
|
|
23
89
|
function resolveTemplate(translations) {
|
|
24
90
|
const locale = getLocale();
|
|
25
91
|
const template = translations[locale];
|
|
26
|
-
if (template) return template;
|
|
92
|
+
if (template) return { template, locale };
|
|
27
93
|
const fallback = translations.en ?? Object.values(translations)[0];
|
|
28
|
-
if (fallback) return fallback;
|
|
94
|
+
if (fallback) return { template: fallback, locale: "en" };
|
|
29
95
|
throw new Error(
|
|
30
96
|
`No translation found for locale "${locale}". Available: ${Object.keys(translations).join(", ")}`
|
|
31
97
|
);
|
|
@@ -34,13 +100,15 @@ function it(first, second, third) {
|
|
|
34
100
|
if (typeof first === "object") {
|
|
35
101
|
const translations2 = first;
|
|
36
102
|
const vars2 = second;
|
|
37
|
-
|
|
103
|
+
const { template: template2, locale: locale2 } = resolveTemplate(translations2);
|
|
104
|
+
return interpolate(template2, vars2, locale2);
|
|
38
105
|
}
|
|
39
106
|
const ko = first;
|
|
40
107
|
const en = second;
|
|
41
108
|
const vars = third;
|
|
42
109
|
const translations = { ko, en };
|
|
43
|
-
|
|
110
|
+
const { template, locale } = resolveTemplate(translations);
|
|
111
|
+
return interpolate(template, vars, locale);
|
|
44
112
|
}
|
|
45
113
|
|
|
46
114
|
// src/runtime.ts
|
|
@@ -87,7 +155,6 @@ var ja_es = createPair("ja", "es");
|
|
|
87
155
|
var zh_es = createPair("zh", "es");
|
|
88
156
|
|
|
89
157
|
// src/dictionary.ts
|
|
90
|
-
var VARIABLE_PATTERN2 = /\{(\w+)\}/g;
|
|
91
158
|
var dictionaries = {};
|
|
92
159
|
function loadDictionaries(dicts) {
|
|
93
160
|
dictionaries = { ...dictionaries, ...dicts };
|
|
@@ -112,13 +179,6 @@ function getNestedValue(dict, key) {
|
|
|
112
179
|
}
|
|
113
180
|
return typeof current === "string" ? current : void 0;
|
|
114
181
|
}
|
|
115
|
-
function interpolate2(template, vars) {
|
|
116
|
-
if (!vars) return template;
|
|
117
|
-
return template.replace(VARIABLE_PATTERN2, (_, key) => {
|
|
118
|
-
const value = vars[key];
|
|
119
|
-
return value !== void 0 ? String(value) : `{${key}}`;
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
182
|
function getPluralCategory(count, locale) {
|
|
123
183
|
const rules = new Intl.PluralRules(locale);
|
|
124
184
|
return rules.select(count);
|
|
@@ -148,7 +208,7 @@ function t(key, vars, locale) {
|
|
|
148
208
|
console.warn(`[inline-i18n] Missing translation: ${key} (${currentLocale2})`);
|
|
149
209
|
return key;
|
|
150
210
|
}
|
|
151
|
-
return
|
|
211
|
+
return interpolate(template, vars, currentLocale2);
|
|
152
212
|
}
|
|
153
213
|
function hasTranslation(key, locale) {
|
|
154
214
|
const currentLocale2 = locale ?? getLocale();
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/context.ts","../src/interpolation.ts","../src/translate.ts","../src/runtime.ts","../src/pairs.ts","../src/dictionary.ts"],"names":["translations","vars","VARIABLE_PATTERN","interpolate","currentLocale"],"mappings":"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","file":"index.js","sourcesContent":["import type { Locale } from './types'\n\nlet currentLocale: Locale = 'en'\n\nexport function setLocale(locale: Locale): void {\n currentLocale = locale\n}\n\nexport function getLocale(): Locale {\n return currentLocale\n}\n","import type { TranslationVars } from './types'\n\nconst VARIABLE_PATTERN = /\\{(\\w+)\\}/g\n\nexport function interpolate(\n template: string,\n vars?: TranslationVars,\n): string {\n if (!vars) return template\n\n return template.replace(VARIABLE_PATTERN, (_, key) => {\n const value = vars[key]\n return value !== undefined ? String(value) : `{${key}}`\n })\n}\n","import type { Translations, TranslationVars } from './types'\nimport { getLocale } from './context'\nimport { interpolate } from './interpolation'\n\nfunction resolveTemplate(translations: Translations): string {\n const locale = getLocale()\n\n const template = translations[locale]\n if (template) return template\n\n // fallback: en -> first available\n const fallback = translations.en ?? Object.values(translations)[0]\n if (fallback) return fallback\n\n throw new Error(\n `No translation found for locale \"${locale}\". Available: ${Object.keys(translations).join(', ')}`\n )\n}\n\n/**\n * Translate with two languages (shorthand)\n * @param ko - Korean text\n * @param en - English text\n * @param vars - Variables for interpolation\n */\nexport function it(ko: string, en: string, vars?: TranslationVars): string\n\n/**\n * Translate with multiple languages (object syntax)\n * @param translations - Translation map with locale keys\n * @param vars - Variables for interpolation\n */\nexport function it(translations: Translations, vars?: TranslationVars): string\n\nexport function it(\n first: string | Translations,\n second?: string | TranslationVars,\n third?: TranslationVars,\n): string {\n // object syntax: it({ ko: '...', en: '...' }, vars?)\n if (typeof first === 'object') {\n const translations = first\n const vars = second as TranslationVars | undefined\n return interpolate(resolveTemplate(translations), vars)\n }\n\n // shorthand syntax: it('한글', 'English', vars?)\n const ko = first\n const en = second as string\n const vars = third\n\n const translations: Translations = { ko, en }\n return interpolate(resolveTemplate(translations), vars)\n}\n","import type { Translations, TranslationVars } from './types'\nimport { getLocale } from './context'\nimport { interpolate } from './interpolation'\n\n/**\n * Runtime lookup function for plugin-transformed code.\n * This is called by code that has been processed by @inline-i18n-multi/babel-plugin\n * or @inline-i18n-multi/swc-plugin.\n *\n * @param _hash - Content hash (for caching/debugging, unused at runtime)\n * @param translations - Translation map with locale keys\n * @param vars - Variables for interpolation\n */\nexport function __i18n_lookup(\n _hash: string,\n translations: Translations,\n vars?: TranslationVars\n): string {\n const locale = getLocale()\n\n const template = translations[locale]\n if (template) {\n return interpolate(template, vars)\n }\n\n // fallback: en -> first available\n const fallback = translations.en ?? Object.values(translations)[0]\n if (fallback) {\n return interpolate(fallback, vars)\n }\n\n throw new Error(\n `No translation found for locale \"${locale}\". Available: ${Object.keys(translations).join(', ')}`\n )\n}\n\n// Register __i18n_lookup globally for plugin transformations\n// This makes it available without explicit import after bundle\nif (typeof globalThis !== 'undefined') {\n (globalThis as Record<string, unknown>).__i18n_lookup = __i18n_lookup\n}\n","import type { Locale, Translations, TranslationVars } from './types'\nimport { it } from './translate'\n\ntype PairFunction = (\n text1: string,\n text2: string,\n vars?: TranslationVars,\n) => string\n\nfunction createPair(lang1: Locale, lang2: Locale): PairFunction {\n return (text1, text2, vars) => {\n const translations: Translations = {\n [lang1]: text1,\n [lang2]: text2,\n }\n return it(translations, vars)\n }\n}\n\n// Korean combinations\nexport const it_ja = createPair('ko', 'ja')\nexport const it_zh = createPair('ko', 'zh')\nexport const it_es = createPair('ko', 'es')\nexport const it_fr = createPair('ko', 'fr')\nexport const it_de = createPair('ko', 'de')\n\n// English combinations\nexport const en_ja = createPair('en', 'ja')\nexport const en_zh = createPair('en', 'zh')\nexport const en_es = createPair('en', 'es')\nexport const en_fr = createPair('en', 'fr')\nexport const en_de = createPair('en', 'de')\n\n// Other combinations\nexport const ja_zh = createPair('ja', 'zh')\nexport const ja_es = createPair('ja', 'es')\nexport const zh_es = createPair('zh', 'es')\n","import { getLocale } from './context'\nimport type { Locale, TranslationVars } from './types'\n\n/**\n * Nested dictionary structure for translations\n * @example { greeting: { hello: \"Hello\", goodbye: \"Goodbye\" } }\n */\nexport type Dictionary = {\n [key: string]: string | Dictionary\n}\n\n/**\n * All loaded dictionaries by locale\n */\nexport type Dictionaries = Record<Locale, Dictionary>\n\n/**\n * Plural rules configuration\n */\nexport interface PluralRules {\n zero?: string\n one?: string\n two?: string\n few?: string\n many?: string\n other: string\n}\n\nconst VARIABLE_PATTERN = /\\{(\\w+)\\}/g\n\n// Global dictionary storage\nlet dictionaries: Dictionaries = {}\n\n/**\n * Load translations from dictionary objects\n * @param dicts - Dictionary objects keyed by locale\n * @example\n * loadDictionaries({\n * en: { greeting: { hello: \"Hello\" } },\n * ko: { greeting: { hello: \"안녕하세요\" } }\n * })\n */\nexport function loadDictionaries(dicts: Dictionaries): void {\n dictionaries = { ...dictionaries, ...dicts }\n}\n\n/**\n * Load a single locale's dictionary\n * @param locale - Locale code\n * @param dict - Dictionary object\n */\nexport function loadDictionary(locale: Locale, dict: Dictionary): void {\n dictionaries[locale] = { ...dictionaries[locale], ...dict }\n}\n\n/**\n * Clear all loaded dictionaries\n */\nexport function clearDictionaries(): void {\n dictionaries = {}\n}\n\n/**\n * Get a nested value from dictionary using dot notation\n * @param dict - Dictionary object\n * @param key - Dot-separated key path\n */\nfunction getNestedValue(dict: Dictionary, key: string): string | undefined {\n const parts = key.split('.')\n let current: string | Dictionary | undefined = dict\n\n for (const part of parts) {\n if (typeof current !== 'object' || current === null) {\n return undefined\n }\n current = current[part]\n if (current === undefined) {\n return undefined\n }\n }\n\n return typeof current === 'string' ? current : undefined\n}\n\n/**\n * Interpolate variables into template string\n */\nfunction interpolate(template: string, vars?: TranslationVars): string {\n if (!vars) return template\n\n return template.replace(VARIABLE_PATTERN, (_, key) => {\n const value = vars[key]\n return value !== undefined ? String(value) : `{${key}}`\n })\n}\n\n/**\n * Get plural category using Intl.PluralRules\n */\nfunction getPluralCategory(count: number, locale: Locale): Intl.LDMLPluralRule {\n const rules = new Intl.PluralRules(locale)\n return rules.select(count)\n}\n\n/**\n * Translate using key-based lookup (i18n compatible)\n * @param key - Dot-separated translation key\n * @param vars - Variables for interpolation (including 'count' for plurals)\n * @param locale - Override locale (optional)\n * @example\n * t('greeting.hello') // \"Hello\"\n * t('items.count', { count: 5 }) // \"5 items\"\n */\nexport function t(\n key: string,\n vars?: TranslationVars,\n locale?: Locale\n): string {\n const currentLocale = locale ?? getLocale()\n const dict = dictionaries[currentLocale]\n\n if (!dict) {\n console.warn(`[inline-i18n] No dictionary loaded for locale: ${currentLocale}`)\n return key\n }\n\n let template = getNestedValue(dict, key)\n\n // Handle plurals if count is provided\n if (vars && typeof vars.count === 'number') {\n const pluralKey = `${key}_${getPluralCategory(vars.count, currentLocale)}`\n const pluralTemplate = getNestedValue(dict, pluralKey)\n if (pluralTemplate) {\n template = pluralTemplate\n }\n }\n\n if (!template) {\n // Try fallback to English\n const fallbackDict = dictionaries['en']\n if (fallbackDict && currentLocale !== 'en') {\n template = getNestedValue(fallbackDict, key)\n }\n }\n\n if (!template) {\n console.warn(`[inline-i18n] Missing translation: ${key} (${currentLocale})`)\n return key\n }\n\n return interpolate(template, vars)\n}\n\n/**\n * Check if a translation key exists\n */\nexport function hasTranslation(key: string, locale?: Locale): boolean {\n const currentLocale = locale ?? getLocale()\n const dict = dictionaries[currentLocale]\n return dict ? getNestedValue(dict, key) !== undefined : false\n}\n\n/**\n * Get all loaded locales\n */\nexport function getLoadedLocales(): Locale[] {\n return Object.keys(dictionaries)\n}\n\n/**\n * Get dictionary for a specific locale\n */\nexport function getDictionary(locale: Locale): Dictionary | undefined {\n return dictionaries[locale]\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/context.ts","../src/icu.ts","../src/interpolation.ts","../src/translate.ts","../src/runtime.ts","../src/pairs.ts","../src/dictionary.ts"],"names":["parse","isLiteralElement","isArgumentElement","isPoundElement","isPluralElement","isSelectElement","translations","vars","template","locale","currentLocale"],"mappings":";;;;;AAEA,IAAI,aAAA,GAAwB,IAAA;AAErB,SAAS,UAAU,MAAA,EAAsB;AAC9C,EAAA,aAAA,GAAgB,MAAA;AAClB;AAEO,SAAS,SAAA,GAAoB;AAClC,EAAA,OAAO,aAAA;AACT;ACQO,SAAS,cAAA,CACd,QAAA,EACA,IAAA,EACA,MAAA,EACQ;AACR,EAAA,MAAM,GAAA,GAAMA,6BAAM,QAAQ,CAAA;AAC1B,EAAA,OAAO,cAAA,CAAe,GAAA,EAAK,IAAA,EAAM,MAAA,EAAQ,IAAI,CAAA;AAC/C;AAEA,SAAS,cAAA,CACP,QAAA,EACA,IAAA,EACA,MAAA,EACA,kBAAA,EACQ;AACR,EAAA,OAAO,QAAA,CACJ,GAAA,CAAI,CAAC,EAAA,KAAO,aAAA,CAAc,EAAA,EAAI,IAAA,EAAM,MAAA,EAAQ,kBAAkB,CAAC,CAAA,CAC/D,IAAA,CAAK,EAAE,CAAA;AACZ;AAEA,SAAS,aAAA,CACP,EAAA,EACA,IAAA,EACA,MAAA,EACA,kBAAA,EACQ;AACR,EAAA,IAAIC,uCAAA,CAAiB,EAAE,CAAA,EAAG;AACxB,IAAA,OAAO,EAAA,CAAG,KAAA;AAAA,EACZ;AAEA,EAAA,IAAIC,wCAAA,CAAkB,EAAE,CAAA,EAAG;AACzB,IAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,EAAA,CAAG,KAAK,CAAA;AAC3B,IAAA,OAAO,UAAU,MAAA,GAAY,MAAA,CAAO,KAAK,CAAA,GAAI,CAAA,CAAA,EAAI,GAAG,KAAK,CAAA,CAAA,CAAA;AAAA,EAC3D;AAEA,EAAA,IAAIC,qCAAA,CAAe,EAAE,CAAA,EAAG;AAEtB,IAAA,OAAO,kBAAA,KAAuB,IAAA,GAAO,MAAA,CAAO,kBAAkB,CAAA,GAAI,GAAA;AAAA,EACpE;AAEA,EAAA,IAAIC,sCAAA,CAAgB,EAAE,CAAA,EAAG;AACvB,IAAA,OAAO,YAAA,CAAa,EAAA,EAAI,IAAA,EAAM,MAAM,CAAA;AAAA,EACtC;AAEA,EAAA,IAAIC,sCAAA,CAAgB,EAAE,CAAA,EAAG;AACvB,IAAA,OAAO,YAAA,CAAa,EAAA,EAAI,IAAA,EAAM,MAAM,CAAA;AAAA,EACtC;AAGA,EAAA,OAAO,EAAA;AACT;AAEA,SAAS,YAAA,CACP,EAAA,EACA,IAAA,EACA,MAAA,EACQ;AACR,EAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,EAAA,CAAG,KAAK,CAAA;AAC3B,EAAA,IAAI,OAAO,UAAU,QAAA,EAAU;AAC7B,IAAA,OAAO,CAAA,CAAA,EAAI,GAAG,KAAK,CAAA,CAAA,CAAA;AAAA,EACrB;AAEA,EAAA,MAAM,aAAA,GAAgB,QAAQ,EAAA,CAAG,MAAA;AACjC,EAAA,MAAM,WAAA,GAAc,IAAI,IAAA,CAAK,WAAA,CAAY,QAAQ,EAAE,IAAA,EAAM,EAAA,CAAG,UAAA,EAAY,CAAA;AACxE,EAAA,MAAM,QAAA,GAAW,WAAA,CAAY,MAAA,CAAO,aAAa,CAAA;AAGjD,EAAA,MAAM,QAAA,GAAW,IAAI,KAAK,CAAA,CAAA;AAC1B,EAAA,IAAI,EAAA,CAAG,OAAA,CAAQ,QAAQ,CAAA,EAAG;AACxB,IAAA,OAAO,cAAA,CAAe,GAAG,OAAA,CAAQ,QAAQ,EAAE,KAAA,EAAO,IAAA,EAAM,QAAQ,aAAa,CAAA;AAAA,EAC/E;AAGA,EAAA,IAAI,EAAA,CAAG,OAAA,CAAQ,QAAQ,CAAA,EAAG;AACxB,IAAA,OAAO,cAAA,CAAe,GAAG,OAAA,CAAQ,QAAQ,EAAE,KAAA,EAAO,IAAA,EAAM,QAAQ,aAAa,CAAA;AAAA,EAC/E;AAGA,EAAA,IAAI,EAAA,CAAG,QAAQ,KAAA,EAAO;AACpB,IAAA,OAAO,eAAe,EAAA,CAAG,OAAA,CAAQ,MAAM,KAAA,EAAO,IAAA,EAAM,QAAQ,aAAa,CAAA;AAAA,EAC3E;AAEA,EAAA,OAAO,CAAA,CAAA,EAAI,GAAG,KAAK,CAAA,CAAA,CAAA;AACrB;AAEA,SAAS,YAAA,CACP,EAAA,EACA,IAAA,EACA,MAAA,EACQ;AACR,EAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,EAAA,CAAG,KAAK,CAAA;AAC3B,EAAA,MAAM,GAAA,GAAM,OAAO,KAAK,CAAA;AAGxB,EAAA,IAAI,EAAA,CAAG,OAAA,CAAQ,GAAG,CAAA,EAAG;AACnB,IAAA,OAAO,cAAA,CAAe,GAAG,OAAA,CAAQ,GAAG,EAAE,KAAA,EAAO,IAAA,EAAM,QAAQ,IAAI,CAAA;AAAA,EACjE;AAGA,EAAA,IAAI,EAAA,CAAG,QAAQ,KAAA,EAAO;AACpB,IAAA,OAAO,eAAe,EAAA,CAAG,OAAA,CAAQ,MAAM,KAAA,EAAO,IAAA,EAAM,QAAQ,IAAI,CAAA;AAAA,EAClE;AAEA,EAAA,OAAO,CAAA,CAAA,EAAI,GAAG,KAAK,CAAA,CAAA,CAAA;AACrB;AAGO,IAAM,WAAA,GAAc,8CAAA;AAKpB,SAAS,cAAc,QAAA,EAA2B;AACvD,EAAA,OAAO,WAAA,CAAY,KAAK,QAAQ,CAAA;AAClC;;;ACjIA,IAAM,gBAAA,GAAmB,YAAA;AAElB,SAAS,WAAA,CACd,QAAA,EACA,IAAA,EACA,MAAA,EACQ;AACR,EAAA,IAAI,CAAC,MAAM,OAAO,QAAA;AAGlB,EAAA,IAAI,aAAA,CAAc,QAAQ,CAAA,EAAG;AAC3B,IAAA,OAAO,cAAA,CAAe,QAAA,EAAU,IAAA,EAAM,MAAA,IAAU,IAAI,CAAA;AAAA,EACtD;AAGA,EAAA,OAAO,QAAA,CAAS,OAAA,CAAQ,gBAAA,EAAkB,CAAC,GAAG,GAAA,KAAQ;AACpD,IAAA,MAAM,KAAA,GAAQ,KAAK,GAAG,CAAA;AACtB,IAAA,OAAO,UAAU,MAAA,GAAY,MAAA,CAAO,KAAK,CAAA,GAAI,IAAI,GAAG,CAAA,CAAA,CAAA;AAAA,EACtD,CAAC,CAAA;AACH;;;ACbA,SAAS,gBAAgB,YAAA,EAA2C;AAClE,EAAA,MAAM,SAAS,SAAA,EAAU;AAEzB,EAAA,MAAM,QAAA,GAAW,aAAa,MAAM,CAAA;AACpC,EAAA,IAAI,QAAA,EAAU,OAAO,EAAE,QAAA,EAAU,MAAA,EAAO;AAGxC,EAAA,MAAM,WAAW,YAAA,CAAa,EAAA,IAAM,OAAO,MAAA,CAAO,YAAY,EAAE,CAAC,CAAA;AACjE,EAAA,IAAI,UAAU,OAAO,EAAE,QAAA,EAAU,QAAA,EAAU,QAAQ,IAAA,EAAK;AAExD,EAAA,MAAM,IAAI,KAAA;AAAA,IACR,CAAA,iCAAA,EAAoC,MAAM,CAAA,cAAA,EAAiB,MAAA,CAAO,KAAK,YAAY,CAAA,CAAE,IAAA,CAAK,IAAI,CAAC,CAAA;AAAA,GACjG;AACF;AAiBO,SAAS,EAAA,CACd,KAAA,EACA,MAAA,EACA,KAAA,EACQ;AAER,EAAA,IAAI,OAAO,UAAU,QAAA,EAAU;AAC7B,IAAA,MAAMC,aAAAA,GAAe,KAAA;AACrB,IAAA,MAAMC,KAAAA,GAAO,MAAA;AACb,IAAA,MAAM,EAAE,QAAA,EAAAC,SAAAA,EAAU,QAAAC,OAAAA,EAAO,GAAI,gBAAgBH,aAAY,CAAA;AACzD,IAAA,OAAO,WAAA,CAAYE,SAAAA,EAAUD,KAAAA,EAAME,OAAM,CAAA;AAAA,EAC3C;AAGA,EAAA,MAAM,EAAA,GAAK,KAAA;AACX,EAAA,MAAM,EAAA,GAAK,MAAA;AACX,EAAA,MAAM,IAAA,GAAO,KAAA;AAEb,EAAA,MAAM,YAAA,GAA6B,EAAE,EAAA,EAAI,EAAA,EAAG;AAC5C,EAAA,MAAM,EAAE,QAAA,EAAU,MAAA,EAAO,GAAI,gBAAgB,YAAY,CAAA;AACzD,EAAA,OAAO,WAAA,CAAY,QAAA,EAAU,IAAA,EAAM,MAAM,CAAA;AAC3C;;;AC/CO,SAAS,aAAA,CACd,KAAA,EACA,YAAA,EACA,IAAA,EACQ;AACR,EAAA,MAAM,SAAS,SAAA,EAAU;AAEzB,EAAA,MAAM,QAAA,GAAW,aAAa,MAAM,CAAA;AACpC,EAAA,IAAI,QAAA,EAAU;AACZ,IAAA,OAAO,WAAA,CAAY,UAAU,IAAI,CAAA;AAAA,EACnC;AAGA,EAAA,MAAM,WAAW,YAAA,CAAa,EAAA,IAAM,OAAO,MAAA,CAAO,YAAY,EAAE,CAAC,CAAA;AACjE,EAAA,IAAI,QAAA,EAAU;AACZ,IAAA,OAAO,WAAA,CAAY,UAAU,IAAI,CAAA;AAAA,EACnC;AAEA,EAAA,MAAM,IAAI,KAAA;AAAA,IACR,CAAA,iCAAA,EAAoC,MAAM,CAAA,cAAA,EAAiB,MAAA,CAAO,KAAK,YAAY,CAAA,CAAE,IAAA,CAAK,IAAI,CAAC,CAAA;AAAA,GACjG;AACF;AAIA,IAAI,OAAO,eAAe,WAAA,EAAa;AACrC,EAAC,WAAuC,aAAA,GAAgB,aAAA;AAC1D;;;AC/BA,SAAS,UAAA,CAAW,OAAe,KAAA,EAA6B;AAC9D,EAAA,OAAO,CAAC,KAAA,EAAO,KAAA,EAAO,IAAA,KAAS;AAC7B,IAAA,MAAM,YAAA,GAA6B;AAAA,MACjC,CAAC,KAAK,GAAG,KAAA;AAAA,MACT,CAAC,KAAK,GAAG;AAAA,KACX;AACA,IAAA,OAAO,EAAA,CAAG,cAAc,IAAI,CAAA;AAAA,EAC9B,CAAA;AACF;AAGO,IAAM,KAAA,GAAQ,UAAA,CAAW,IAAA,EAAM,IAAI;AACnC,IAAM,KAAA,GAAQ,UAAA,CAAW,IAAA,EAAM,IAAI;AACnC,IAAM,KAAA,GAAQ,UAAA,CAAW,IAAA,EAAM,IAAI;AACnC,IAAM,KAAA,GAAQ,UAAA,CAAW,IAAA,EAAM,IAAI;AACnC,IAAM,KAAA,GAAQ,UAAA,CAAW,IAAA,EAAM,IAAI;AAGnC,IAAM,KAAA,GAAQ,UAAA,CAAW,IAAA,EAAM,IAAI;AACnC,IAAM,KAAA,GAAQ,UAAA,CAAW,IAAA,EAAM,IAAI;AACnC,IAAM,KAAA,GAAQ,UAAA,CAAW,IAAA,EAAM,IAAI;AACnC,IAAM,KAAA,GAAQ,UAAA,CAAW,IAAA,EAAM,IAAI;AACnC,IAAM,KAAA,GAAQ,UAAA,CAAW,IAAA,EAAM,IAAI;AAGnC,IAAM,KAAA,GAAQ,UAAA,CAAW,IAAA,EAAM,IAAI;AACnC,IAAM,KAAA,GAAQ,UAAA,CAAW,IAAA,EAAM,IAAI;AACnC,IAAM,KAAA,GAAQ,UAAA,CAAW,IAAA,EAAM,IAAI;;;ACN1C,IAAI,eAA6B,EAAC;AAW3B,SAAS,iBAAiB,KAAA,EAA2B;AAC1D,EAAA,YAAA,GAAe,EAAE,GAAG,YAAA,EAAc,GAAG,KAAA,EAAM;AAC7C;AAOO,SAAS,cAAA,CAAe,QAAgB,IAAA,EAAwB;AACrE,EAAA,YAAA,CAAa,MAAM,IAAI,EAAE,GAAG,aAAa,MAAM,CAAA,EAAG,GAAG,IAAA,EAAK;AAC5D;AAKO,SAAS,iBAAA,GAA0B;AACxC,EAAA,YAAA,GAAe,EAAC;AAClB;AAOA,SAAS,cAAA,CAAe,MAAkB,GAAA,EAAiC;AACzE,EAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,KAAA,CAAM,GAAG,CAAA;AAC3B,EAAA,IAAI,OAAA,GAA2C,IAAA;AAE/C,EAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,IAAA,IAAI,OAAO,OAAA,KAAY,QAAA,IAAY,OAAA,KAAY,IAAA,EAAM;AACnD,MAAA,OAAO,MAAA;AAAA,IACT;AACA,IAAA,OAAA,GAAU,QAAQ,IAAI,CAAA;AACtB,IAAA,IAAI,YAAY,MAAA,EAAW;AACzB,MAAA,OAAO,MAAA;AAAA,IACT;AAAA,EACF;AAEA,EAAA,OAAO,OAAO,OAAA,KAAY,QAAA,GAAW,OAAA,GAAU,MAAA;AACjD;AAKA,SAAS,iBAAA,CAAkB,OAAe,MAAA,EAAqC;AAC7E,EAAA,MAAM,KAAA,GAAQ,IAAI,IAAA,CAAK,WAAA,CAAY,MAAM,CAAA;AACzC,EAAA,OAAO,KAAA,CAAM,OAAO,KAAK,CAAA;AAC3B;AAWO,SAAS,CAAA,CACd,GAAA,EACA,IAAA,EACA,MAAA,EACQ;AACR,EAAA,MAAMC,cAAAA,GAAgB,UAAU,SAAA,EAAU;AAC1C,EAAA,MAAM,IAAA,GAAO,aAAaA,cAAa,CAAA;AAEvC,EAAA,IAAI,CAAC,IAAA,EAAM;AACT,IAAA,OAAA,CAAQ,IAAA,CAAK,CAAA,+CAAA,EAAkDA,cAAa,CAAA,CAAE,CAAA;AAC9E,IAAA,OAAO,GAAA;AAAA,EACT;AAEA,EAAA,IAAI,QAAA,GAAW,cAAA,CAAe,IAAA,EAAM,GAAG,CAAA;AAGvC,EAAA,IAAI,IAAA,IAAQ,OAAO,IAAA,CAAK,KAAA,KAAU,QAAA,EAAU;AAC1C,IAAA,MAAM,SAAA,GAAY,GAAG,GAAG,CAAA,CAAA,EAAI,kBAAkB,IAAA,CAAK,KAAA,EAAOA,cAAa,CAAC,CAAA,CAAA;AACxE,IAAA,MAAM,cAAA,GAAiB,cAAA,CAAe,IAAA,EAAM,SAAS,CAAA;AACrD,IAAA,IAAI,cAAA,EAAgB;AAClB,MAAA,QAAA,GAAW,cAAA;AAAA,IACb;AAAA,EACF;AAEA,EAAA,IAAI,CAAC,QAAA,EAAU;AAEb,IAAA,MAAM,YAAA,GAAe,aAAa,IAAI,CAAA;AACtC,IAAA,IAAI,YAAA,IAAgBA,mBAAkB,IAAA,EAAM;AAC1C,MAAA,QAAA,GAAW,cAAA,CAAe,cAAc,GAAG,CAAA;AAAA,IAC7C;AAAA,EACF;AAEA,EAAA,IAAI,CAAC,QAAA,EAAU;AACb,IAAA,OAAA,CAAQ,IAAA,CAAK,CAAA,mCAAA,EAAsC,GAAG,CAAA,EAAA,EAAKA,cAAa,CAAA,CAAA,CAAG,CAAA;AAC3E,IAAA,OAAO,GAAA;AAAA,EACT;AAEA,EAAA,OAAO,WAAA,CAAY,QAAA,EAAU,IAAA,EAAMA,cAAa,CAAA;AAClD;AAKO,SAAS,cAAA,CAAe,KAAa,MAAA,EAA0B;AACpE,EAAA,MAAMA,cAAAA,GAAgB,UAAU,SAAA,EAAU;AAC1C,EAAA,MAAM,IAAA,GAAO,aAAaA,cAAa,CAAA;AACvC,EAAA,OAAO,IAAA,GAAO,cAAA,CAAe,IAAA,EAAM,GAAG,MAAM,MAAA,GAAY,KAAA;AAC1D;AAKO,SAAS,gBAAA,GAA6B;AAC3C,EAAA,OAAO,MAAA,CAAO,KAAK,YAAY,CAAA;AACjC;AAKO,SAAS,cAAc,MAAA,EAAwC;AACpE,EAAA,OAAO,aAAa,MAAM,CAAA;AAC5B","file":"index.js","sourcesContent":["import type { Locale } from './types'\n\nlet currentLocale: Locale = 'en'\n\nexport function setLocale(locale: Locale): void {\n currentLocale = locale\n}\n\nexport function getLocale(): Locale {\n return currentLocale\n}\n","import {\n parse,\n TYPE,\n type MessageFormatElement,\n type PluralElement,\n type SelectElement,\n isLiteralElement,\n isArgumentElement,\n isPluralElement,\n isSelectElement,\n isPoundElement,\n} from '@formatjs/icu-messageformat-parser'\n\nexport type ICUVars = Record<string, string | number>\n\n/**\n * Parse and format an ICU Message Format string\n */\nexport function interpolateICU(\n template: string,\n vars: ICUVars,\n locale: string\n): string {\n const ast = parse(template)\n return formatElements(ast, vars, locale, null)\n}\n\nfunction formatElements(\n elements: MessageFormatElement[],\n vars: ICUVars,\n locale: string,\n currentPluralValue: number | null\n): string {\n return elements\n .map((el) => formatElement(el, vars, locale, currentPluralValue))\n .join('')\n}\n\nfunction formatElement(\n el: MessageFormatElement,\n vars: ICUVars,\n locale: string,\n currentPluralValue: number | null\n): string {\n if (isLiteralElement(el)) {\n return el.value\n }\n\n if (isArgumentElement(el)) {\n const value = vars[el.value]\n return value !== undefined ? String(value) : `{${el.value}}`\n }\n\n if (isPoundElement(el)) {\n // # is replaced with the current plural value\n return currentPluralValue !== null ? String(currentPluralValue) : '#'\n }\n\n if (isPluralElement(el)) {\n return formatPlural(el, vars, locale)\n }\n\n if (isSelectElement(el)) {\n return formatSelect(el, vars, locale)\n }\n\n // Unsupported types (number, date, time, tag) - return as-is for now\n return ''\n}\n\nfunction formatPlural(\n el: PluralElement,\n vars: ICUVars,\n locale: string\n): string {\n const value = vars[el.value]\n if (typeof value !== 'number') {\n return `{${el.value}}`\n }\n\n const adjustedValue = value - el.offset\n const pluralRules = new Intl.PluralRules(locale, { type: el.pluralType })\n const category = pluralRules.select(adjustedValue)\n\n // Try exact match first (=0, =1, =2, etc.)\n const exactKey = `=${value}`\n if (el.options[exactKey]) {\n return formatElements(el.options[exactKey].value, vars, locale, adjustedValue)\n }\n\n // Then try plural category (zero, one, two, few, many, other)\n if (el.options[category]) {\n return formatElements(el.options[category].value, vars, locale, adjustedValue)\n }\n\n // Fallback to 'other'\n if (el.options.other) {\n return formatElements(el.options.other.value, vars, locale, adjustedValue)\n }\n\n return `{${el.value}}`\n}\n\nfunction formatSelect(\n el: SelectElement,\n vars: ICUVars,\n locale: string\n): string {\n const value = vars[el.value]\n const key = String(value)\n\n // Try exact match\n if (el.options[key]) {\n return formatElements(el.options[key].value, vars, locale, null)\n }\n\n // Fallback to 'other'\n if (el.options.other) {\n return formatElements(el.options.other.value, vars, locale, null)\n }\n\n return `{${el.value}}`\n}\n\n// Pattern to detect ICU format (plural, select, selectordinal)\nexport const ICU_PATTERN = /\\{[^}]+,\\s*(plural|select|selectordinal)\\s*,/\n\n/**\n * Check if a template contains ICU Message Format patterns\n */\nexport function hasICUPattern(template: string): boolean {\n return ICU_PATTERN.test(template)\n}\n","import type { TranslationVars } from './types'\nimport { hasICUPattern, interpolateICU } from './icu'\n\nconst VARIABLE_PATTERN = /\\{(\\w+)\\}/g\n\nexport function interpolate(\n template: string,\n vars?: TranslationVars,\n locale?: string,\n): string {\n if (!vars) return template\n\n // ICU Message Format (plural, select)\n if (hasICUPattern(template)) {\n return interpolateICU(template, vars, locale || 'en')\n }\n\n // Simple variable substitution\n return template.replace(VARIABLE_PATTERN, (_, key) => {\n const value = vars[key]\n return value !== undefined ? String(value) : `{${key}}`\n })\n}\n","import type { Translations, TranslationVars } from './types'\nimport { getLocale } from './context'\nimport { interpolate } from './interpolation'\n\ninterface ResolveResult {\n template: string\n locale: string\n}\n\nfunction resolveTemplate(translations: Translations): ResolveResult {\n const locale = getLocale()\n\n const template = translations[locale]\n if (template) return { template, locale }\n\n // fallback: en -> first available\n const fallback = translations.en ?? Object.values(translations)[0]\n if (fallback) return { template: fallback, locale: 'en' }\n\n throw new Error(\n `No translation found for locale \"${locale}\". Available: ${Object.keys(translations).join(', ')}`\n )\n}\n\n/**\n * Translate with two languages (shorthand)\n * @param ko - Korean text\n * @param en - English text\n * @param vars - Variables for interpolation\n */\nexport function it(ko: string, en: string, vars?: TranslationVars): string\n\n/**\n * Translate with multiple languages (object syntax)\n * @param translations - Translation map with locale keys\n * @param vars - Variables for interpolation\n */\nexport function it(translations: Translations, vars?: TranslationVars): string\n\nexport function it(\n first: string | Translations,\n second?: string | TranslationVars,\n third?: TranslationVars,\n): string {\n // object syntax: it({ ko: '...', en: '...' }, vars?)\n if (typeof first === 'object') {\n const translations = first\n const vars = second as TranslationVars | undefined\n const { template, locale } = resolveTemplate(translations)\n return interpolate(template, vars, locale)\n }\n\n // shorthand syntax: it('한글', 'English', vars?)\n const ko = first\n const en = second as string\n const vars = third\n\n const translations: Translations = { ko, en }\n const { template, locale } = resolveTemplate(translations)\n return interpolate(template, vars, locale)\n}\n","import type { Translations, TranslationVars } from './types'\nimport { getLocale } from './context'\nimport { interpolate } from './interpolation'\n\n/**\n * Runtime lookup function for plugin-transformed code.\n * This is called by code that has been processed by @inline-i18n-multi/babel-plugin\n * or @inline-i18n-multi/swc-plugin.\n *\n * @param _hash - Content hash (for caching/debugging, unused at runtime)\n * @param translations - Translation map with locale keys\n * @param vars - Variables for interpolation\n */\nexport function __i18n_lookup(\n _hash: string,\n translations: Translations,\n vars?: TranslationVars\n): string {\n const locale = getLocale()\n\n const template = translations[locale]\n if (template) {\n return interpolate(template, vars)\n }\n\n // fallback: en -> first available\n const fallback = translations.en ?? Object.values(translations)[0]\n if (fallback) {\n return interpolate(fallback, vars)\n }\n\n throw new Error(\n `No translation found for locale \"${locale}\". Available: ${Object.keys(translations).join(', ')}`\n )\n}\n\n// Register __i18n_lookup globally for plugin transformations\n// This makes it available without explicit import after bundle\nif (typeof globalThis !== 'undefined') {\n (globalThis as Record<string, unknown>).__i18n_lookup = __i18n_lookup\n}\n","import type { Locale, Translations, TranslationVars } from './types'\nimport { it } from './translate'\n\ntype PairFunction = (\n text1: string,\n text2: string,\n vars?: TranslationVars,\n) => string\n\nfunction createPair(lang1: Locale, lang2: Locale): PairFunction {\n return (text1, text2, vars) => {\n const translations: Translations = {\n [lang1]: text1,\n [lang2]: text2,\n }\n return it(translations, vars)\n }\n}\n\n// Korean combinations\nexport const it_ja = createPair('ko', 'ja')\nexport const it_zh = createPair('ko', 'zh')\nexport const it_es = createPair('ko', 'es')\nexport const it_fr = createPair('ko', 'fr')\nexport const it_de = createPair('ko', 'de')\n\n// English combinations\nexport const en_ja = createPair('en', 'ja')\nexport const en_zh = createPair('en', 'zh')\nexport const en_es = createPair('en', 'es')\nexport const en_fr = createPair('en', 'fr')\nexport const en_de = createPair('en', 'de')\n\n// Other combinations\nexport const ja_zh = createPair('ja', 'zh')\nexport const ja_es = createPair('ja', 'es')\nexport const zh_es = createPair('zh', 'es')\n","import { getLocale } from './context'\nimport type { Locale, TranslationVars } from './types'\nimport { interpolate } from './interpolation'\n\n/**\n * Nested dictionary structure for translations\n * @example { greeting: { hello: \"Hello\", goodbye: \"Goodbye\" } }\n */\nexport type Dictionary = {\n [key: string]: string | Dictionary\n}\n\n/**\n * All loaded dictionaries by locale\n */\nexport type Dictionaries = Record<Locale, Dictionary>\n\n/**\n * Plural rules configuration\n */\nexport interface PluralRules {\n zero?: string\n one?: string\n two?: string\n few?: string\n many?: string\n other: string\n}\n\n// Global dictionary storage\nlet dictionaries: Dictionaries = {}\n\n/**\n * Load translations from dictionary objects\n * @param dicts - Dictionary objects keyed by locale\n * @example\n * loadDictionaries({\n * en: { greeting: { hello: \"Hello\" } },\n * ko: { greeting: { hello: \"안녕하세요\" } }\n * })\n */\nexport function loadDictionaries(dicts: Dictionaries): void {\n dictionaries = { ...dictionaries, ...dicts }\n}\n\n/**\n * Load a single locale's dictionary\n * @param locale - Locale code\n * @param dict - Dictionary object\n */\nexport function loadDictionary(locale: Locale, dict: Dictionary): void {\n dictionaries[locale] = { ...dictionaries[locale], ...dict }\n}\n\n/**\n * Clear all loaded dictionaries\n */\nexport function clearDictionaries(): void {\n dictionaries = {}\n}\n\n/**\n * Get a nested value from dictionary using dot notation\n * @param dict - Dictionary object\n * @param key - Dot-separated key path\n */\nfunction getNestedValue(dict: Dictionary, key: string): string | undefined {\n const parts = key.split('.')\n let current: string | Dictionary | undefined = dict\n\n for (const part of parts) {\n if (typeof current !== 'object' || current === null) {\n return undefined\n }\n current = current[part]\n if (current === undefined) {\n return undefined\n }\n }\n\n return typeof current === 'string' ? current : undefined\n}\n\n/**\n * Get plural category using Intl.PluralRules\n */\nfunction getPluralCategory(count: number, locale: Locale): Intl.LDMLPluralRule {\n const rules = new Intl.PluralRules(locale)\n return rules.select(count)\n}\n\n/**\n * Translate using key-based lookup (i18n compatible)\n * @param key - Dot-separated translation key\n * @param vars - Variables for interpolation (including 'count' for plurals)\n * @param locale - Override locale (optional)\n * @example\n * t('greeting.hello') // \"Hello\"\n * t('items.count', { count: 5 }) // \"5 items\"\n */\nexport function t(\n key: string,\n vars?: TranslationVars,\n locale?: Locale\n): string {\n const currentLocale = locale ?? getLocale()\n const dict = dictionaries[currentLocale]\n\n if (!dict) {\n console.warn(`[inline-i18n] No dictionary loaded for locale: ${currentLocale}`)\n return key\n }\n\n let template = getNestedValue(dict, key)\n\n // Handle plurals if count is provided\n if (vars && typeof vars.count === 'number') {\n const pluralKey = `${key}_${getPluralCategory(vars.count, currentLocale)}`\n const pluralTemplate = getNestedValue(dict, pluralKey)\n if (pluralTemplate) {\n template = pluralTemplate\n }\n }\n\n if (!template) {\n // Try fallback to English\n const fallbackDict = dictionaries['en']\n if (fallbackDict && currentLocale !== 'en') {\n template = getNestedValue(fallbackDict, key)\n }\n }\n\n if (!template) {\n console.warn(`[inline-i18n] Missing translation: ${key} (${currentLocale})`)\n return key\n }\n\n return interpolate(template, vars, currentLocale)\n}\n\n/**\n * Check if a translation key exists\n */\nexport function hasTranslation(key: string, locale?: Locale): boolean {\n const currentLocale = locale ?? getLocale()\n const dict = dictionaries[currentLocale]\n return dict ? getNestedValue(dict, key) !== undefined : false\n}\n\n/**\n * Get all loaded locales\n */\nexport function getLoadedLocales(): Locale[] {\n return Object.keys(dictionaries)\n}\n\n/**\n * Get dictionary for a specific locale\n */\nexport function getDictionary(locale: Locale): Dictionary | undefined {\n return dictionaries[locale]\n}\n"]}
|
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { parse, isLiteralElement, isArgumentElement, isPoundElement, isPluralElement, isSelectElement } from '@formatjs/icu-messageformat-parser';
|
|
2
|
+
|
|
1
3
|
// src/context.ts
|
|
2
4
|
var currentLocale = "en";
|
|
3
5
|
function setLocale(locale) {
|
|
@@ -6,11 +8,75 @@ function setLocale(locale) {
|
|
|
6
8
|
function getLocale() {
|
|
7
9
|
return currentLocale;
|
|
8
10
|
}
|
|
11
|
+
function interpolateICU(template, vars, locale) {
|
|
12
|
+
const ast = parse(template);
|
|
13
|
+
return formatElements(ast, vars, locale, null);
|
|
14
|
+
}
|
|
15
|
+
function formatElements(elements, vars, locale, currentPluralValue) {
|
|
16
|
+
return elements.map((el) => formatElement(el, vars, locale, currentPluralValue)).join("");
|
|
17
|
+
}
|
|
18
|
+
function formatElement(el, vars, locale, currentPluralValue) {
|
|
19
|
+
if (isLiteralElement(el)) {
|
|
20
|
+
return el.value;
|
|
21
|
+
}
|
|
22
|
+
if (isArgumentElement(el)) {
|
|
23
|
+
const value = vars[el.value];
|
|
24
|
+
return value !== void 0 ? String(value) : `{${el.value}}`;
|
|
25
|
+
}
|
|
26
|
+
if (isPoundElement(el)) {
|
|
27
|
+
return currentPluralValue !== null ? String(currentPluralValue) : "#";
|
|
28
|
+
}
|
|
29
|
+
if (isPluralElement(el)) {
|
|
30
|
+
return formatPlural(el, vars, locale);
|
|
31
|
+
}
|
|
32
|
+
if (isSelectElement(el)) {
|
|
33
|
+
return formatSelect(el, vars, locale);
|
|
34
|
+
}
|
|
35
|
+
return "";
|
|
36
|
+
}
|
|
37
|
+
function formatPlural(el, vars, locale) {
|
|
38
|
+
const value = vars[el.value];
|
|
39
|
+
if (typeof value !== "number") {
|
|
40
|
+
return `{${el.value}}`;
|
|
41
|
+
}
|
|
42
|
+
const adjustedValue = value - el.offset;
|
|
43
|
+
const pluralRules = new Intl.PluralRules(locale, { type: el.pluralType });
|
|
44
|
+
const category = pluralRules.select(adjustedValue);
|
|
45
|
+
const exactKey = `=${value}`;
|
|
46
|
+
if (el.options[exactKey]) {
|
|
47
|
+
return formatElements(el.options[exactKey].value, vars, locale, adjustedValue);
|
|
48
|
+
}
|
|
49
|
+
if (el.options[category]) {
|
|
50
|
+
return formatElements(el.options[category].value, vars, locale, adjustedValue);
|
|
51
|
+
}
|
|
52
|
+
if (el.options.other) {
|
|
53
|
+
return formatElements(el.options.other.value, vars, locale, adjustedValue);
|
|
54
|
+
}
|
|
55
|
+
return `{${el.value}}`;
|
|
56
|
+
}
|
|
57
|
+
function formatSelect(el, vars, locale) {
|
|
58
|
+
const value = vars[el.value];
|
|
59
|
+
const key = String(value);
|
|
60
|
+
if (el.options[key]) {
|
|
61
|
+
return formatElements(el.options[key].value, vars, locale, null);
|
|
62
|
+
}
|
|
63
|
+
if (el.options.other) {
|
|
64
|
+
return formatElements(el.options.other.value, vars, locale, null);
|
|
65
|
+
}
|
|
66
|
+
return `{${el.value}}`;
|
|
67
|
+
}
|
|
68
|
+
var ICU_PATTERN = /\{[^}]+,\s*(plural|select|selectordinal)\s*,/;
|
|
69
|
+
function hasICUPattern(template) {
|
|
70
|
+
return ICU_PATTERN.test(template);
|
|
71
|
+
}
|
|
9
72
|
|
|
10
73
|
// src/interpolation.ts
|
|
11
74
|
var VARIABLE_PATTERN = /\{(\w+)\}/g;
|
|
12
|
-
function interpolate(template, vars) {
|
|
75
|
+
function interpolate(template, vars, locale) {
|
|
13
76
|
if (!vars) return template;
|
|
77
|
+
if (hasICUPattern(template)) {
|
|
78
|
+
return interpolateICU(template, vars, locale || "en");
|
|
79
|
+
}
|
|
14
80
|
return template.replace(VARIABLE_PATTERN, (_, key) => {
|
|
15
81
|
const value = vars[key];
|
|
16
82
|
return value !== void 0 ? String(value) : `{${key}}`;
|
|
@@ -21,9 +87,9 @@ function interpolate(template, vars) {
|
|
|
21
87
|
function resolveTemplate(translations) {
|
|
22
88
|
const locale = getLocale();
|
|
23
89
|
const template = translations[locale];
|
|
24
|
-
if (template) return template;
|
|
90
|
+
if (template) return { template, locale };
|
|
25
91
|
const fallback = translations.en ?? Object.values(translations)[0];
|
|
26
|
-
if (fallback) return fallback;
|
|
92
|
+
if (fallback) return { template: fallback, locale: "en" };
|
|
27
93
|
throw new Error(
|
|
28
94
|
`No translation found for locale "${locale}". Available: ${Object.keys(translations).join(", ")}`
|
|
29
95
|
);
|
|
@@ -32,13 +98,15 @@ function it(first, second, third) {
|
|
|
32
98
|
if (typeof first === "object") {
|
|
33
99
|
const translations2 = first;
|
|
34
100
|
const vars2 = second;
|
|
35
|
-
|
|
101
|
+
const { template: template2, locale: locale2 } = resolveTemplate(translations2);
|
|
102
|
+
return interpolate(template2, vars2, locale2);
|
|
36
103
|
}
|
|
37
104
|
const ko = first;
|
|
38
105
|
const en = second;
|
|
39
106
|
const vars = third;
|
|
40
107
|
const translations = { ko, en };
|
|
41
|
-
|
|
108
|
+
const { template, locale } = resolveTemplate(translations);
|
|
109
|
+
return interpolate(template, vars, locale);
|
|
42
110
|
}
|
|
43
111
|
|
|
44
112
|
// src/runtime.ts
|
|
@@ -85,7 +153,6 @@ var ja_es = createPair("ja", "es");
|
|
|
85
153
|
var zh_es = createPair("zh", "es");
|
|
86
154
|
|
|
87
155
|
// src/dictionary.ts
|
|
88
|
-
var VARIABLE_PATTERN2 = /\{(\w+)\}/g;
|
|
89
156
|
var dictionaries = {};
|
|
90
157
|
function loadDictionaries(dicts) {
|
|
91
158
|
dictionaries = { ...dictionaries, ...dicts };
|
|
@@ -110,13 +177,6 @@ function getNestedValue(dict, key) {
|
|
|
110
177
|
}
|
|
111
178
|
return typeof current === "string" ? current : void 0;
|
|
112
179
|
}
|
|
113
|
-
function interpolate2(template, vars) {
|
|
114
|
-
if (!vars) return template;
|
|
115
|
-
return template.replace(VARIABLE_PATTERN2, (_, key) => {
|
|
116
|
-
const value = vars[key];
|
|
117
|
-
return value !== void 0 ? String(value) : `{${key}}`;
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
180
|
function getPluralCategory(count, locale) {
|
|
121
181
|
const rules = new Intl.PluralRules(locale);
|
|
122
182
|
return rules.select(count);
|
|
@@ -146,7 +206,7 @@ function t(key, vars, locale) {
|
|
|
146
206
|
console.warn(`[inline-i18n] Missing translation: ${key} (${currentLocale2})`);
|
|
147
207
|
return key;
|
|
148
208
|
}
|
|
149
|
-
return
|
|
209
|
+
return interpolate(template, vars, currentLocale2);
|
|
150
210
|
}
|
|
151
211
|
function hasTranslation(key, locale) {
|
|
152
212
|
const currentLocale2 = locale ?? getLocale();
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/context.ts","../src/interpolation.ts","../src/translate.ts","../src/runtime.ts","../src/pairs.ts","../src/dictionary.ts"],"names":["translations","vars","VARIABLE_PATTERN","interpolate","currentLocale"],"mappings":"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","file":"index.mjs","sourcesContent":["import type { Locale } from './types'\n\nlet currentLocale: Locale = 'en'\n\nexport function setLocale(locale: Locale): void {\n currentLocale = locale\n}\n\nexport function getLocale(): Locale {\n return currentLocale\n}\n","import type { TranslationVars } from './types'\n\nconst VARIABLE_PATTERN = /\\{(\\w+)\\}/g\n\nexport function interpolate(\n template: string,\n vars?: TranslationVars,\n): string {\n if (!vars) return template\n\n return template.replace(VARIABLE_PATTERN, (_, key) => {\n const value = vars[key]\n return value !== undefined ? String(value) : `{${key}}`\n })\n}\n","import type { Translations, TranslationVars } from './types'\nimport { getLocale } from './context'\nimport { interpolate } from './interpolation'\n\nfunction resolveTemplate(translations: Translations): string {\n const locale = getLocale()\n\n const template = translations[locale]\n if (template) return template\n\n // fallback: en -> first available\n const fallback = translations.en ?? Object.values(translations)[0]\n if (fallback) return fallback\n\n throw new Error(\n `No translation found for locale \"${locale}\". Available: ${Object.keys(translations).join(', ')}`\n )\n}\n\n/**\n * Translate with two languages (shorthand)\n * @param ko - Korean text\n * @param en - English text\n * @param vars - Variables for interpolation\n */\nexport function it(ko: string, en: string, vars?: TranslationVars): string\n\n/**\n * Translate with multiple languages (object syntax)\n * @param translations - Translation map with locale keys\n * @param vars - Variables for interpolation\n */\nexport function it(translations: Translations, vars?: TranslationVars): string\n\nexport function it(\n first: string | Translations,\n second?: string | TranslationVars,\n third?: TranslationVars,\n): string {\n // object syntax: it({ ko: '...', en: '...' }, vars?)\n if (typeof first === 'object') {\n const translations = first\n const vars = second as TranslationVars | undefined\n return interpolate(resolveTemplate(translations), vars)\n }\n\n // shorthand syntax: it('한글', 'English', vars?)\n const ko = first\n const en = second as string\n const vars = third\n\n const translations: Translations = { ko, en }\n return interpolate(resolveTemplate(translations), vars)\n}\n","import type { Translations, TranslationVars } from './types'\nimport { getLocale } from './context'\nimport { interpolate } from './interpolation'\n\n/**\n * Runtime lookup function for plugin-transformed code.\n * This is called by code that has been processed by @inline-i18n-multi/babel-plugin\n * or @inline-i18n-multi/swc-plugin.\n *\n * @param _hash - Content hash (for caching/debugging, unused at runtime)\n * @param translations - Translation map with locale keys\n * @param vars - Variables for interpolation\n */\nexport function __i18n_lookup(\n _hash: string,\n translations: Translations,\n vars?: TranslationVars\n): string {\n const locale = getLocale()\n\n const template = translations[locale]\n if (template) {\n return interpolate(template, vars)\n }\n\n // fallback: en -> first available\n const fallback = translations.en ?? Object.values(translations)[0]\n if (fallback) {\n return interpolate(fallback, vars)\n }\n\n throw new Error(\n `No translation found for locale \"${locale}\". Available: ${Object.keys(translations).join(', ')}`\n )\n}\n\n// Register __i18n_lookup globally for plugin transformations\n// This makes it available without explicit import after bundle\nif (typeof globalThis !== 'undefined') {\n (globalThis as Record<string, unknown>).__i18n_lookup = __i18n_lookup\n}\n","import type { Locale, Translations, TranslationVars } from './types'\nimport { it } from './translate'\n\ntype PairFunction = (\n text1: string,\n text2: string,\n vars?: TranslationVars,\n) => string\n\nfunction createPair(lang1: Locale, lang2: Locale): PairFunction {\n return (text1, text2, vars) => {\n const translations: Translations = {\n [lang1]: text1,\n [lang2]: text2,\n }\n return it(translations, vars)\n }\n}\n\n// Korean combinations\nexport const it_ja = createPair('ko', 'ja')\nexport const it_zh = createPair('ko', 'zh')\nexport const it_es = createPair('ko', 'es')\nexport const it_fr = createPair('ko', 'fr')\nexport const it_de = createPair('ko', 'de')\n\n// English combinations\nexport const en_ja = createPair('en', 'ja')\nexport const en_zh = createPair('en', 'zh')\nexport const en_es = createPair('en', 'es')\nexport const en_fr = createPair('en', 'fr')\nexport const en_de = createPair('en', 'de')\n\n// Other combinations\nexport const ja_zh = createPair('ja', 'zh')\nexport const ja_es = createPair('ja', 'es')\nexport const zh_es = createPair('zh', 'es')\n","import { getLocale } from './context'\nimport type { Locale, TranslationVars } from './types'\n\n/**\n * Nested dictionary structure for translations\n * @example { greeting: { hello: \"Hello\", goodbye: \"Goodbye\" } }\n */\nexport type Dictionary = {\n [key: string]: string | Dictionary\n}\n\n/**\n * All loaded dictionaries by locale\n */\nexport type Dictionaries = Record<Locale, Dictionary>\n\n/**\n * Plural rules configuration\n */\nexport interface PluralRules {\n zero?: string\n one?: string\n two?: string\n few?: string\n many?: string\n other: string\n}\n\nconst VARIABLE_PATTERN = /\\{(\\w+)\\}/g\n\n// Global dictionary storage\nlet dictionaries: Dictionaries = {}\n\n/**\n * Load translations from dictionary objects\n * @param dicts - Dictionary objects keyed by locale\n * @example\n * loadDictionaries({\n * en: { greeting: { hello: \"Hello\" } },\n * ko: { greeting: { hello: \"안녕하세요\" } }\n * })\n */\nexport function loadDictionaries(dicts: Dictionaries): void {\n dictionaries = { ...dictionaries, ...dicts }\n}\n\n/**\n * Load a single locale's dictionary\n * @param locale - Locale code\n * @param dict - Dictionary object\n */\nexport function loadDictionary(locale: Locale, dict: Dictionary): void {\n dictionaries[locale] = { ...dictionaries[locale], ...dict }\n}\n\n/**\n * Clear all loaded dictionaries\n */\nexport function clearDictionaries(): void {\n dictionaries = {}\n}\n\n/**\n * Get a nested value from dictionary using dot notation\n * @param dict - Dictionary object\n * @param key - Dot-separated key path\n */\nfunction getNestedValue(dict: Dictionary, key: string): string | undefined {\n const parts = key.split('.')\n let current: string | Dictionary | undefined = dict\n\n for (const part of parts) {\n if (typeof current !== 'object' || current === null) {\n return undefined\n }\n current = current[part]\n if (current === undefined) {\n return undefined\n }\n }\n\n return typeof current === 'string' ? current : undefined\n}\n\n/**\n * Interpolate variables into template string\n */\nfunction interpolate(template: string, vars?: TranslationVars): string {\n if (!vars) return template\n\n return template.replace(VARIABLE_PATTERN, (_, key) => {\n const value = vars[key]\n return value !== undefined ? String(value) : `{${key}}`\n })\n}\n\n/**\n * Get plural category using Intl.PluralRules\n */\nfunction getPluralCategory(count: number, locale: Locale): Intl.LDMLPluralRule {\n const rules = new Intl.PluralRules(locale)\n return rules.select(count)\n}\n\n/**\n * Translate using key-based lookup (i18n compatible)\n * @param key - Dot-separated translation key\n * @param vars - Variables for interpolation (including 'count' for plurals)\n * @param locale - Override locale (optional)\n * @example\n * t('greeting.hello') // \"Hello\"\n * t('items.count', { count: 5 }) // \"5 items\"\n */\nexport function t(\n key: string,\n vars?: TranslationVars,\n locale?: Locale\n): string {\n const currentLocale = locale ?? getLocale()\n const dict = dictionaries[currentLocale]\n\n if (!dict) {\n console.warn(`[inline-i18n] No dictionary loaded for locale: ${currentLocale}`)\n return key\n }\n\n let template = getNestedValue(dict, key)\n\n // Handle plurals if count is provided\n if (vars && typeof vars.count === 'number') {\n const pluralKey = `${key}_${getPluralCategory(vars.count, currentLocale)}`\n const pluralTemplate = getNestedValue(dict, pluralKey)\n if (pluralTemplate) {\n template = pluralTemplate\n }\n }\n\n if (!template) {\n // Try fallback to English\n const fallbackDict = dictionaries['en']\n if (fallbackDict && currentLocale !== 'en') {\n template = getNestedValue(fallbackDict, key)\n }\n }\n\n if (!template) {\n console.warn(`[inline-i18n] Missing translation: ${key} (${currentLocale})`)\n return key\n }\n\n return interpolate(template, vars)\n}\n\n/**\n * Check if a translation key exists\n */\nexport function hasTranslation(key: string, locale?: Locale): boolean {\n const currentLocale = locale ?? getLocale()\n const dict = dictionaries[currentLocale]\n return dict ? getNestedValue(dict, key) !== undefined : false\n}\n\n/**\n * Get all loaded locales\n */\nexport function getLoadedLocales(): Locale[] {\n return Object.keys(dictionaries)\n}\n\n/**\n * Get dictionary for a specific locale\n */\nexport function getDictionary(locale: Locale): Dictionary | undefined {\n return dictionaries[locale]\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/context.ts","../src/icu.ts","../src/interpolation.ts","../src/translate.ts","../src/runtime.ts","../src/pairs.ts","../src/dictionary.ts"],"names":["translations","vars","template","locale","currentLocale"],"mappings":"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","file":"index.mjs","sourcesContent":["import type { Locale } from './types'\n\nlet currentLocale: Locale = 'en'\n\nexport function setLocale(locale: Locale): void {\n currentLocale = locale\n}\n\nexport function getLocale(): Locale {\n return currentLocale\n}\n","import {\n parse,\n TYPE,\n type MessageFormatElement,\n type PluralElement,\n type SelectElement,\n isLiteralElement,\n isArgumentElement,\n isPluralElement,\n isSelectElement,\n isPoundElement,\n} from '@formatjs/icu-messageformat-parser'\n\nexport type ICUVars = Record<string, string | number>\n\n/**\n * Parse and format an ICU Message Format string\n */\nexport function interpolateICU(\n template: string,\n vars: ICUVars,\n locale: string\n): string {\n const ast = parse(template)\n return formatElements(ast, vars, locale, null)\n}\n\nfunction formatElements(\n elements: MessageFormatElement[],\n vars: ICUVars,\n locale: string,\n currentPluralValue: number | null\n): string {\n return elements\n .map((el) => formatElement(el, vars, locale, currentPluralValue))\n .join('')\n}\n\nfunction formatElement(\n el: MessageFormatElement,\n vars: ICUVars,\n locale: string,\n currentPluralValue: number | null\n): string {\n if (isLiteralElement(el)) {\n return el.value\n }\n\n if (isArgumentElement(el)) {\n const value = vars[el.value]\n return value !== undefined ? String(value) : `{${el.value}}`\n }\n\n if (isPoundElement(el)) {\n // # is replaced with the current plural value\n return currentPluralValue !== null ? String(currentPluralValue) : '#'\n }\n\n if (isPluralElement(el)) {\n return formatPlural(el, vars, locale)\n }\n\n if (isSelectElement(el)) {\n return formatSelect(el, vars, locale)\n }\n\n // Unsupported types (number, date, time, tag) - return as-is for now\n return ''\n}\n\nfunction formatPlural(\n el: PluralElement,\n vars: ICUVars,\n locale: string\n): string {\n const value = vars[el.value]\n if (typeof value !== 'number') {\n return `{${el.value}}`\n }\n\n const adjustedValue = value - el.offset\n const pluralRules = new Intl.PluralRules(locale, { type: el.pluralType })\n const category = pluralRules.select(adjustedValue)\n\n // Try exact match first (=0, =1, =2, etc.)\n const exactKey = `=${value}`\n if (el.options[exactKey]) {\n return formatElements(el.options[exactKey].value, vars, locale, adjustedValue)\n }\n\n // Then try plural category (zero, one, two, few, many, other)\n if (el.options[category]) {\n return formatElements(el.options[category].value, vars, locale, adjustedValue)\n }\n\n // Fallback to 'other'\n if (el.options.other) {\n return formatElements(el.options.other.value, vars, locale, adjustedValue)\n }\n\n return `{${el.value}}`\n}\n\nfunction formatSelect(\n el: SelectElement,\n vars: ICUVars,\n locale: string\n): string {\n const value = vars[el.value]\n const key = String(value)\n\n // Try exact match\n if (el.options[key]) {\n return formatElements(el.options[key].value, vars, locale, null)\n }\n\n // Fallback to 'other'\n if (el.options.other) {\n return formatElements(el.options.other.value, vars, locale, null)\n }\n\n return `{${el.value}}`\n}\n\n// Pattern to detect ICU format (plural, select, selectordinal)\nexport const ICU_PATTERN = /\\{[^}]+,\\s*(plural|select|selectordinal)\\s*,/\n\n/**\n * Check if a template contains ICU Message Format patterns\n */\nexport function hasICUPattern(template: string): boolean {\n return ICU_PATTERN.test(template)\n}\n","import type { TranslationVars } from './types'\nimport { hasICUPattern, interpolateICU } from './icu'\n\nconst VARIABLE_PATTERN = /\\{(\\w+)\\}/g\n\nexport function interpolate(\n template: string,\n vars?: TranslationVars,\n locale?: string,\n): string {\n if (!vars) return template\n\n // ICU Message Format (plural, select)\n if (hasICUPattern(template)) {\n return interpolateICU(template, vars, locale || 'en')\n }\n\n // Simple variable substitution\n return template.replace(VARIABLE_PATTERN, (_, key) => {\n const value = vars[key]\n return value !== undefined ? String(value) : `{${key}}`\n })\n}\n","import type { Translations, TranslationVars } from './types'\nimport { getLocale } from './context'\nimport { interpolate } from './interpolation'\n\ninterface ResolveResult {\n template: string\n locale: string\n}\n\nfunction resolveTemplate(translations: Translations): ResolveResult {\n const locale = getLocale()\n\n const template = translations[locale]\n if (template) return { template, locale }\n\n // fallback: en -> first available\n const fallback = translations.en ?? Object.values(translations)[0]\n if (fallback) return { template: fallback, locale: 'en' }\n\n throw new Error(\n `No translation found for locale \"${locale}\". Available: ${Object.keys(translations).join(', ')}`\n )\n}\n\n/**\n * Translate with two languages (shorthand)\n * @param ko - Korean text\n * @param en - English text\n * @param vars - Variables for interpolation\n */\nexport function it(ko: string, en: string, vars?: TranslationVars): string\n\n/**\n * Translate with multiple languages (object syntax)\n * @param translations - Translation map with locale keys\n * @param vars - Variables for interpolation\n */\nexport function it(translations: Translations, vars?: TranslationVars): string\n\nexport function it(\n first: string | Translations,\n second?: string | TranslationVars,\n third?: TranslationVars,\n): string {\n // object syntax: it({ ko: '...', en: '...' }, vars?)\n if (typeof first === 'object') {\n const translations = first\n const vars = second as TranslationVars | undefined\n const { template, locale } = resolveTemplate(translations)\n return interpolate(template, vars, locale)\n }\n\n // shorthand syntax: it('한글', 'English', vars?)\n const ko = first\n const en = second as string\n const vars = third\n\n const translations: Translations = { ko, en }\n const { template, locale } = resolveTemplate(translations)\n return interpolate(template, vars, locale)\n}\n","import type { Translations, TranslationVars } from './types'\nimport { getLocale } from './context'\nimport { interpolate } from './interpolation'\n\n/**\n * Runtime lookup function for plugin-transformed code.\n * This is called by code that has been processed by @inline-i18n-multi/babel-plugin\n * or @inline-i18n-multi/swc-plugin.\n *\n * @param _hash - Content hash (for caching/debugging, unused at runtime)\n * @param translations - Translation map with locale keys\n * @param vars - Variables for interpolation\n */\nexport function __i18n_lookup(\n _hash: string,\n translations: Translations,\n vars?: TranslationVars\n): string {\n const locale = getLocale()\n\n const template = translations[locale]\n if (template) {\n return interpolate(template, vars)\n }\n\n // fallback: en -> first available\n const fallback = translations.en ?? Object.values(translations)[0]\n if (fallback) {\n return interpolate(fallback, vars)\n }\n\n throw new Error(\n `No translation found for locale \"${locale}\". Available: ${Object.keys(translations).join(', ')}`\n )\n}\n\n// Register __i18n_lookup globally for plugin transformations\n// This makes it available without explicit import after bundle\nif (typeof globalThis !== 'undefined') {\n (globalThis as Record<string, unknown>).__i18n_lookup = __i18n_lookup\n}\n","import type { Locale, Translations, TranslationVars } from './types'\nimport { it } from './translate'\n\ntype PairFunction = (\n text1: string,\n text2: string,\n vars?: TranslationVars,\n) => string\n\nfunction createPair(lang1: Locale, lang2: Locale): PairFunction {\n return (text1, text2, vars) => {\n const translations: Translations = {\n [lang1]: text1,\n [lang2]: text2,\n }\n return it(translations, vars)\n }\n}\n\n// Korean combinations\nexport const it_ja = createPair('ko', 'ja')\nexport const it_zh = createPair('ko', 'zh')\nexport const it_es = createPair('ko', 'es')\nexport const it_fr = createPair('ko', 'fr')\nexport const it_de = createPair('ko', 'de')\n\n// English combinations\nexport const en_ja = createPair('en', 'ja')\nexport const en_zh = createPair('en', 'zh')\nexport const en_es = createPair('en', 'es')\nexport const en_fr = createPair('en', 'fr')\nexport const en_de = createPair('en', 'de')\n\n// Other combinations\nexport const ja_zh = createPair('ja', 'zh')\nexport const ja_es = createPair('ja', 'es')\nexport const zh_es = createPair('zh', 'es')\n","import { getLocale } from './context'\nimport type { Locale, TranslationVars } from './types'\nimport { interpolate } from './interpolation'\n\n/**\n * Nested dictionary structure for translations\n * @example { greeting: { hello: \"Hello\", goodbye: \"Goodbye\" } }\n */\nexport type Dictionary = {\n [key: string]: string | Dictionary\n}\n\n/**\n * All loaded dictionaries by locale\n */\nexport type Dictionaries = Record<Locale, Dictionary>\n\n/**\n * Plural rules configuration\n */\nexport interface PluralRules {\n zero?: string\n one?: string\n two?: string\n few?: string\n many?: string\n other: string\n}\n\n// Global dictionary storage\nlet dictionaries: Dictionaries = {}\n\n/**\n * Load translations from dictionary objects\n * @param dicts - Dictionary objects keyed by locale\n * @example\n * loadDictionaries({\n * en: { greeting: { hello: \"Hello\" } },\n * ko: { greeting: { hello: \"안녕하세요\" } }\n * })\n */\nexport function loadDictionaries(dicts: Dictionaries): void {\n dictionaries = { ...dictionaries, ...dicts }\n}\n\n/**\n * Load a single locale's dictionary\n * @param locale - Locale code\n * @param dict - Dictionary object\n */\nexport function loadDictionary(locale: Locale, dict: Dictionary): void {\n dictionaries[locale] = { ...dictionaries[locale], ...dict }\n}\n\n/**\n * Clear all loaded dictionaries\n */\nexport function clearDictionaries(): void {\n dictionaries = {}\n}\n\n/**\n * Get a nested value from dictionary using dot notation\n * @param dict - Dictionary object\n * @param key - Dot-separated key path\n */\nfunction getNestedValue(dict: Dictionary, key: string): string | undefined {\n const parts = key.split('.')\n let current: string | Dictionary | undefined = dict\n\n for (const part of parts) {\n if (typeof current !== 'object' || current === null) {\n return undefined\n }\n current = current[part]\n if (current === undefined) {\n return undefined\n }\n }\n\n return typeof current === 'string' ? current : undefined\n}\n\n/**\n * Get plural category using Intl.PluralRules\n */\nfunction getPluralCategory(count: number, locale: Locale): Intl.LDMLPluralRule {\n const rules = new Intl.PluralRules(locale)\n return rules.select(count)\n}\n\n/**\n * Translate using key-based lookup (i18n compatible)\n * @param key - Dot-separated translation key\n * @param vars - Variables for interpolation (including 'count' for plurals)\n * @param locale - Override locale (optional)\n * @example\n * t('greeting.hello') // \"Hello\"\n * t('items.count', { count: 5 }) // \"5 items\"\n */\nexport function t(\n key: string,\n vars?: TranslationVars,\n locale?: Locale\n): string {\n const currentLocale = locale ?? getLocale()\n const dict = dictionaries[currentLocale]\n\n if (!dict) {\n console.warn(`[inline-i18n] No dictionary loaded for locale: ${currentLocale}`)\n return key\n }\n\n let template = getNestedValue(dict, key)\n\n // Handle plurals if count is provided\n if (vars && typeof vars.count === 'number') {\n const pluralKey = `${key}_${getPluralCategory(vars.count, currentLocale)}`\n const pluralTemplate = getNestedValue(dict, pluralKey)\n if (pluralTemplate) {\n template = pluralTemplate\n }\n }\n\n if (!template) {\n // Try fallback to English\n const fallbackDict = dictionaries['en']\n if (fallbackDict && currentLocale !== 'en') {\n template = getNestedValue(fallbackDict, key)\n }\n }\n\n if (!template) {\n console.warn(`[inline-i18n] Missing translation: ${key} (${currentLocale})`)\n return key\n }\n\n return interpolate(template, vars, currentLocale)\n}\n\n/**\n * Check if a translation key exists\n */\nexport function hasTranslation(key: string, locale?: Locale): boolean {\n const currentLocale = locale ?? getLocale()\n const dict = dictionaries[currentLocale]\n return dict ? getNestedValue(dict, key) !== undefined : false\n}\n\n/**\n * Get all loaded locales\n */\nexport function getLoadedLocales(): Locale[] {\n return Object.keys(dictionaries)\n}\n\n/**\n * Get dictionary for a specific locale\n */\nexport function getDictionary(locale: Locale): Dictionary | undefined {\n return dictionaries[locale]\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "inline-i18n-multi",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Inline i18n - write translations inline, support multiple languages",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -35,6 +35,9 @@
|
|
|
35
35
|
"typescript": "^5.7.2",
|
|
36
36
|
"vitest": "^2.1.8"
|
|
37
37
|
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@formatjs/icu-messageformat-parser": "^3.3.0"
|
|
40
|
+
},
|
|
38
41
|
"scripts": {
|
|
39
42
|
"build": "tsup",
|
|
40
43
|
"dev": "tsup --watch",
|