@astroscope/i18n 0.2.3 → 0.3.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 +144 -16
- package/dist/chunk-33JVAFGQ.js +44 -0
- package/dist/{chunk-4IUQAJ6R.js → chunk-EXMM4JID.js} +58 -15
- package/dist/{chunk-ZKXMIGSF.js → chunk-MSJHXRJB.js} +1 -1
- package/dist/chunk-VGPVG5JP.js +73 -0
- package/dist/client/translate.d.ts +41 -0
- package/dist/client/{t.js → translate.js} +14 -5
- package/dist/extraction/index.d.ts +2 -2
- package/dist/extraction/index.js +1 -1
- package/dist/{i18n-BBAAVYz_.d.ts → i18n-DaLnNvss.d.ts} +2 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +4 -3
- package/dist/internal.d.ts +3 -3
- package/dist/internal.js +2 -2
- package/dist/rich-CQUhdwBc.d.ts +15 -0
- package/dist/server/translate.d.ts +38 -0
- package/dist/server/{t.js → translate.js} +17 -6
- package/dist/{types-DTUho6gi.d.ts → types-moTgocy7.d.ts} +3 -3
- package/package.json +13 -11
- package/dist/chunk-EMM6LRO2.js +0 -8
- package/dist/chunk-KNDYKTHE.js +0 -31
- package/dist/client/t.d.ts +0 -20
- package/dist/server/t.d.ts +0 -17
- /package/src/{components → astro}/I18nScript.astro +0 -0
- /package/src/{components → astro}/index.ts +0 -0
package/README.md
CHANGED
|
@@ -21,11 +21,12 @@ i18n for Astro + React islands — automatic tree-shaking, parallel loading, any
|
|
|
21
21
|
## Features
|
|
22
22
|
|
|
23
23
|
- **Per-chunk translation loading** — each island gets only its translations
|
|
24
|
-
- **
|
|
24
|
+
- **Unicode MessageFormat 2** (MF2) support via `messageformat` v4
|
|
25
|
+
- **Built-in formatters** — `:number`, `:integer`, `:percent`, `:currency`, `:date`, `:time`, `:datetime`, `:unit`
|
|
25
26
|
- **Babel-based extraction** — robust AST parsing, source maps, production stripping
|
|
26
27
|
- **Manifest fallbacks** — missing translations automatically use extracted fallbacks
|
|
27
28
|
- **Full TypeScript support**
|
|
28
|
-
- **Tiny client runtime** —
|
|
29
|
+
- **Tiny client runtime** — ~8KB gzipped for translations
|
|
29
30
|
|
|
30
31
|
## Installation
|
|
31
32
|
|
|
@@ -123,7 +124,7 @@ Inject translations into the page for hydrated components:
|
|
|
123
124
|
|
|
124
125
|
```astro
|
|
125
126
|
---
|
|
126
|
-
import { I18nScript } from '@astroscope/i18n/
|
|
127
|
+
import { I18nScript } from '@astroscope/i18n/astro';
|
|
127
128
|
---
|
|
128
129
|
<html>
|
|
129
130
|
<head>
|
|
@@ -140,25 +141,27 @@ import { I18nScript } from '@astroscope/i18n/components';
|
|
|
140
141
|
```astro
|
|
141
142
|
---
|
|
142
143
|
// In .astro files
|
|
143
|
-
import { t } from '@astroscope/i18n/
|
|
144
|
+
import { t } from '@astroscope/i18n/translate';
|
|
144
145
|
---
|
|
145
146
|
<h1>{t('checkout.title', 'Order Summary')}</h1>
|
|
146
147
|
```
|
|
147
148
|
|
|
148
149
|
```tsx
|
|
149
150
|
// In React components
|
|
150
|
-
import { t } from '@astroscope/i18n/
|
|
151
|
+
import { t } from '@astroscope/i18n/translate';
|
|
151
152
|
|
|
152
153
|
export function CheckoutSummary() {
|
|
153
154
|
return (
|
|
154
155
|
<div>
|
|
155
156
|
<h1>{t('checkout.title', 'Order Summary')}</h1>
|
|
156
|
-
<p>{t('checkout.tax', 'Includes {tax} VAT', { tax: '19%' })}</p>
|
|
157
|
+
<p>{t('checkout.tax', 'Includes {$tax} VAT', { tax: '19%' })}</p>
|
|
157
158
|
</div>
|
|
158
159
|
);
|
|
159
160
|
}
|
|
160
161
|
```
|
|
161
162
|
|
|
163
|
+
> **Note:** Variables use `{$name}` syntax (with `$` prefix) per MessageFormat 2 specification.
|
|
164
|
+
|
|
162
165
|
### 6. Use i18n-aware client directives
|
|
163
166
|
|
|
164
167
|
**The problem:** With standard `client:*` directives, the translation chunk loads *after* the component module. This delays hydration while translations are fetched sequentially.
|
|
@@ -179,28 +182,90 @@ import Cart from '../components/Cart';
|
|
|
179
182
|
|
|
180
183
|
### `t(key, fallback, values?)`
|
|
181
184
|
|
|
182
|
-
Translate a key with optional interpolation values.
|
|
185
|
+
Translate a key with optional interpolation values. The fallback is used when a translation is missing and also serves as an example for translators. Uses [MessageFormat 2](https://github.com/unicode-org/message-format-wg) syntax.
|
|
183
186
|
|
|
184
187
|
```ts
|
|
185
|
-
// simple
|
|
188
|
+
// simple text
|
|
186
189
|
t('checkout.title', 'Order Summary')
|
|
187
190
|
|
|
188
|
-
// with variables
|
|
189
|
-
t('checkout.tax', 'Includes {tax} VAT', { tax: '19%' })
|
|
191
|
+
// with variables (note the $ prefix)
|
|
192
|
+
t('checkout.tax', 'Includes {$tax} VAT', { tax: '19%' })
|
|
193
|
+
|
|
194
|
+
// with pluralization (MF2 syntax)
|
|
195
|
+
t('cart.items', `.input {$count :number}
|
|
196
|
+
.match $count
|
|
197
|
+
one {{{$count} item}}
|
|
198
|
+
* {{{$count} items}}`, { count: 5 })
|
|
199
|
+
|
|
200
|
+
// with number formatting
|
|
201
|
+
t('stats.value', '{$value :number minimumFractionDigits=2}', { value: 1234.5 })
|
|
202
|
+
|
|
203
|
+
// with percentage
|
|
204
|
+
t('stats.ratio', '{$value :percent}', { value: 0.856 })
|
|
190
205
|
|
|
191
|
-
// with
|
|
192
|
-
t('
|
|
206
|
+
// with date/time formatting
|
|
207
|
+
t('event.date', '{$date :date style=long}', { date: new Date() })
|
|
208
|
+
t('event.time', '{$time :time style=short}', { time: new Date() })
|
|
209
|
+
|
|
210
|
+
// with currency (translator controls currency)
|
|
211
|
+
t('product.price', '{$price :currency currency=EUR}', { price: 99.99 })
|
|
212
|
+
|
|
213
|
+
// with currency (code controls currency via wrapped value)
|
|
214
|
+
t('product.price', '{$price :currency}', {
|
|
215
|
+
price: { valueOf: () => 99.99, options: { currency: 'EUR' } }
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
// with units
|
|
219
|
+
t('distance', '{$value :unit unit=kilometer}', { value: 42 })
|
|
193
220
|
|
|
194
221
|
// with metadata object (for extraction tooling)
|
|
195
222
|
t('cart.total', {
|
|
196
|
-
|
|
223
|
+
example: 'Total: {$amount}',
|
|
197
224
|
description: 'Cart total price',
|
|
198
225
|
variables: {
|
|
199
|
-
amount: {
|
|
226
|
+
amount: { example: '$0.00', description: 'Formatted price' }
|
|
200
227
|
}
|
|
201
228
|
}, { amount: '$49.99' })
|
|
202
229
|
```
|
|
203
230
|
|
|
231
|
+
### `rich(key, fallback, components, values?)`
|
|
232
|
+
|
|
233
|
+
Translate with embedded components using MF2 markup syntax. Returns an array of strings and JSX elements that can be rendered directly.
|
|
234
|
+
|
|
235
|
+
```tsx
|
|
236
|
+
import { rich } from '@astroscope/i18n/translate';
|
|
237
|
+
|
|
238
|
+
// basic link
|
|
239
|
+
rich('tos', 'Read our {#link}Terms of Service{/link}', {
|
|
240
|
+
link: (children) => <a href="/tos">{children}</a>
|
|
241
|
+
})
|
|
242
|
+
// Returns: ['Read our ', <a href="/tos">Terms of Service</a>]
|
|
243
|
+
|
|
244
|
+
// multiple tags
|
|
245
|
+
rich('legal', 'Read our {#tos}Terms{/tos} and {#privacy}Privacy Policy{/privacy}', {
|
|
246
|
+
tos: (children) => <a href="/tos">{children}</a>,
|
|
247
|
+
privacy: (children) => <a href="/privacy">{children}</a>,
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
// with variables
|
|
251
|
+
rich('greeting', 'Hello {$name}, check your {#inbox}messages{/inbox}', {
|
|
252
|
+
inbox: (children) => <a href="/inbox">{children}</a>,
|
|
253
|
+
}, { name: 'Alice' })
|
|
254
|
+
|
|
255
|
+
// nested tags
|
|
256
|
+
rich('highlight', 'This is {#bold}very {#em}important{/em}{/bold}', {
|
|
257
|
+
bold: (children) => <strong>{children}</strong>,
|
|
258
|
+
em: (children) => <em>{children}</em>,
|
|
259
|
+
})
|
|
260
|
+
|
|
261
|
+
// standalone (self-closing) tags
|
|
262
|
+
rich('install', 'Click {#icon/} to install', {
|
|
263
|
+
icon: () => <DownloadIcon />,
|
|
264
|
+
})
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
The same code works in both Astro templates and React islands — `rich()` is JSX-runtime agnostic.
|
|
268
|
+
|
|
204
269
|
### `i18n` singleton
|
|
205
270
|
|
|
206
271
|
```ts
|
|
@@ -256,11 +321,74 @@ export function App() {
|
|
|
256
321
|
3. **SSR** — Middleware provides translations to `t()`, merging manifest fallbacks for missing keys
|
|
257
322
|
4. **Client** — Custom directives load only the translations needed by each chunk
|
|
258
323
|
|
|
259
|
-
The same `import { t } from '@astroscope/i18n/
|
|
324
|
+
The same `import { t } from '@astroscope/i18n/translate'` works everywhere — bundler picks the correct implementation via conditional exports (`browser` vs `default`).
|
|
260
325
|
|
|
261
326
|
### Client bundle
|
|
262
327
|
|
|
263
|
-
Translation chunks are served as raw
|
|
328
|
+
Translation chunks are served as raw MessageFormat 2 strings and compiled on the browser on first use. This keeps chunk sizes minimal — the `messageformat` runtime is ~8KB gzipped. Compiled messages are cached for subsequent renders.
|
|
329
|
+
|
|
330
|
+
> **Future:** Once browsers ship native `Intl.MessageFormat`, this 8KB runtime will be replaced by the built-in API with zero bundle cost.
|
|
331
|
+
|
|
332
|
+
## MessageFormat 2 Syntax
|
|
333
|
+
|
|
334
|
+
This library uses [Unicode MessageFormat 2](https://github.com/unicode-org/message-format-wg) (MF2), the modern standard for internationalization.
|
|
335
|
+
|
|
336
|
+
### Basic syntax
|
|
337
|
+
|
|
338
|
+
```
|
|
339
|
+
Simple text
|
|
340
|
+
Hello {$name}
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
### Pluralization
|
|
344
|
+
|
|
345
|
+
```
|
|
346
|
+
.input {$count :number}
|
|
347
|
+
.match $count
|
|
348
|
+
one {{{$count} item}}
|
|
349
|
+
* {{{$count} items}}
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
### Selection (gender, etc.)
|
|
353
|
+
|
|
354
|
+
```
|
|
355
|
+
.input {$gender :string}
|
|
356
|
+
.match $gender
|
|
357
|
+
male {{He liked it}}
|
|
358
|
+
female {{She liked it}}
|
|
359
|
+
* {{They liked it}}
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
### Built-in formatters
|
|
363
|
+
|
|
364
|
+
| Formatter | Description | Example |
|
|
365
|
+
|-----------|-------------|---------|
|
|
366
|
+
| `:number` | Locale-aware number | `{$n :number}` → "1,234.56" |
|
|
367
|
+
| `:integer` | Integer (no decimals) | `{$n :integer}` → "1,235" |
|
|
368
|
+
| `:percent` | Percentage | `{$n :percent}` → "85.6%" |
|
|
369
|
+
| `:currency` | Currency | `{$n :currency currency=EUR}` → "€99.99" |
|
|
370
|
+
| `:date` | Date | `{$d :date style=long}` → "January 26, 2026" |
|
|
371
|
+
| `:time` | Time | `{$d :time style=short}` → "3:45 PM" |
|
|
372
|
+
| `:datetime` | Date + time | `{$d :datetime dateStyle=medium timeStyle=short}` |
|
|
373
|
+
| `:unit` | Units | `{$n :unit unit=kilometer}` → "42 km" |
|
|
374
|
+
|
|
375
|
+
### Currency and unit options
|
|
376
|
+
|
|
377
|
+
For `:currency` and `:unit`, the required option (`currency` or `unit`) can be:
|
|
378
|
+
|
|
379
|
+
1. **Hardcoded in translation** (translator controls):
|
|
380
|
+
```
|
|
381
|
+
{$price :currency currency=EUR}
|
|
382
|
+
```
|
|
383
|
+
Code passes plain number: `{ price: 99.99 }`
|
|
384
|
+
|
|
385
|
+
2. **Provided by code** (for dynamic currency/unit):
|
|
386
|
+
```
|
|
387
|
+
{$price :currency}
|
|
388
|
+
```
|
|
389
|
+
Code passes wrapped value: `{ price: { valueOf: () => 99.99, options: { currency: 'EUR' } } }`
|
|
390
|
+
|
|
391
|
+
> **Note:** If both translation and code specify the option, **translation wins**.
|
|
264
392
|
|
|
265
393
|
## Configuration
|
|
266
394
|
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// src/shared/compiler.ts
|
|
2
|
+
import { MessageFormat } from "messageformat";
|
|
3
|
+
import { DraftFunctions } from "messageformat/functions";
|
|
4
|
+
var mfOptions = { functions: DraftFunctions };
|
|
5
|
+
var cache = /* @__PURE__ */ new Map();
|
|
6
|
+
function getOrCreateMessageFormat(locale, message) {
|
|
7
|
+
const cacheKey = `${locale}:${message}`;
|
|
8
|
+
const cached = cache.get(cacheKey);
|
|
9
|
+
if (cached) {
|
|
10
|
+
return cached;
|
|
11
|
+
}
|
|
12
|
+
const mf = new MessageFormat(locale, message, mfOptions);
|
|
13
|
+
cache.set(cacheKey, mf);
|
|
14
|
+
return mf;
|
|
15
|
+
}
|
|
16
|
+
function compileMessage(locale, message) {
|
|
17
|
+
try {
|
|
18
|
+
const mf = getOrCreateMessageFormat(locale, message);
|
|
19
|
+
return (values) => mf.format(values);
|
|
20
|
+
} catch {
|
|
21
|
+
return () => message;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function formatMessageToParts(locale, message, values) {
|
|
25
|
+
try {
|
|
26
|
+
const mf = getOrCreateMessageFormat(locale, message);
|
|
27
|
+
return mf.formatToParts(values);
|
|
28
|
+
} catch {
|
|
29
|
+
return [{ type: "literal", value: message }];
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function compileTranslations(locale, raw) {
|
|
33
|
+
const compiled = {};
|
|
34
|
+
for (const [key, message] of Object.entries(raw)) {
|
|
35
|
+
compiled[key] = compileMessage(locale, message);
|
|
36
|
+
}
|
|
37
|
+
return compiled;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export {
|
|
41
|
+
compileMessage,
|
|
42
|
+
formatMessageToParts,
|
|
43
|
+
compileTranslations
|
|
44
|
+
};
|
|
@@ -23,7 +23,12 @@ function getManifest() {
|
|
|
23
23
|
...key,
|
|
24
24
|
// make file paths relative to project root for better readability
|
|
25
25
|
// and avoiding leaking absolute paths from dev or ci environments
|
|
26
|
-
|
|
26
|
+
files: key.files.map((fileLocation) => {
|
|
27
|
+
const [filePath, line] = fileLocation.split(":");
|
|
28
|
+
if (!filePath) return fileLocation;
|
|
29
|
+
const relativePath = state.projectRoot ? path.relative(state.projectRoot, filePath) : filePath;
|
|
30
|
+
return line ? `${relativePath}:${line}` : relativePath;
|
|
31
|
+
})
|
|
27
32
|
}));
|
|
28
33
|
return { keys, chunks: state.chunkManifest, imports: state.importsManifest };
|
|
29
34
|
}
|
|
@@ -83,18 +88,21 @@ var KeyStore = class {
|
|
|
83
88
|
constructor(logger) {
|
|
84
89
|
this.logger = logger;
|
|
85
90
|
}
|
|
86
|
-
|
|
91
|
+
/** All key occurrences (may have duplicates) */
|
|
92
|
+
occurrences = [];
|
|
87
93
|
fileToKeys = /* @__PURE__ */ new Map();
|
|
88
94
|
filesWithI18n = /* @__PURE__ */ new Set();
|
|
89
95
|
/**
|
|
90
|
-
* Add
|
|
96
|
+
* Add key occurrences for a file, replacing any existing occurrences for that file.
|
|
91
97
|
*/
|
|
92
98
|
addFileKeys(filename, keys) {
|
|
93
|
-
const oldKeys = this.fileToKeys.get(filename);
|
|
94
99
|
const newKeyStrings = keys.map((k) => k.key);
|
|
100
|
+
const oldKeys = this.fileToKeys.get(filename);
|
|
95
101
|
if (oldKeys) {
|
|
96
|
-
const
|
|
97
|
-
const
|
|
102
|
+
const uniqueNew = [...new Set(newKeyStrings)];
|
|
103
|
+
const uniqueOld = [...new Set(oldKeys)];
|
|
104
|
+
const added = uniqueNew.filter((k) => !uniqueOld.includes(k));
|
|
105
|
+
const removed = uniqueOld.filter((k) => !uniqueNew.includes(k));
|
|
98
106
|
const file = filename.split("/").pop();
|
|
99
107
|
if (added.length > 0 || removed.length > 0) {
|
|
100
108
|
const parts = [];
|
|
@@ -104,23 +112,47 @@ var KeyStore = class {
|
|
|
104
112
|
}
|
|
105
113
|
}
|
|
106
114
|
if (oldKeys) {
|
|
107
|
-
for (let i = this.
|
|
108
|
-
if (this.
|
|
109
|
-
this.
|
|
115
|
+
for (let i = this.occurrences.length - 1; i >= 0; i--) {
|
|
116
|
+
if (this.occurrences[i]?.file === filename) {
|
|
117
|
+
this.occurrences.splice(i, 1);
|
|
110
118
|
}
|
|
111
119
|
}
|
|
112
120
|
}
|
|
113
121
|
this.filesWithI18n.add(filename);
|
|
114
122
|
if (keys.length > 0) {
|
|
115
|
-
this.
|
|
123
|
+
this.occurrences.push(...keys);
|
|
116
124
|
}
|
|
117
125
|
this.fileToKeys.set(filename, newKeyStrings);
|
|
118
126
|
}
|
|
127
|
+
/**
|
|
128
|
+
* Get deduplicated keys with all file locations merged.
|
|
129
|
+
* Last occurrence's meta wins for each key.
|
|
130
|
+
*/
|
|
131
|
+
get extractedKeys() {
|
|
132
|
+
const keyMap = /* @__PURE__ */ new Map();
|
|
133
|
+
for (const occurrence of this.occurrences) {
|
|
134
|
+
const existing = keyMap.get(occurrence.key);
|
|
135
|
+
const fileLocation = `${occurrence.file}:${occurrence.line}`;
|
|
136
|
+
if (existing) {
|
|
137
|
+
if (!existing.files.includes(fileLocation)) {
|
|
138
|
+
existing.files.push(fileLocation);
|
|
139
|
+
}
|
|
140
|
+
existing.meta = occurrence.meta;
|
|
141
|
+
} else {
|
|
142
|
+
keyMap.set(occurrence.key, {
|
|
143
|
+
key: occurrence.key,
|
|
144
|
+
meta: occurrence.meta,
|
|
145
|
+
files: [fileLocation]
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return Array.from(keyMap.values());
|
|
150
|
+
}
|
|
119
151
|
/**
|
|
120
152
|
* Get count of unique keys.
|
|
121
153
|
*/
|
|
122
154
|
get uniqueKeyCount() {
|
|
123
|
-
return new Set(this.
|
|
155
|
+
return new Set(this.occurrences.map((k) => k.key)).size;
|
|
124
156
|
}
|
|
125
157
|
/**
|
|
126
158
|
* Merge another store's data into this one.
|
|
@@ -132,7 +164,13 @@ var KeyStore = class {
|
|
|
132
164
|
for (const file of other.filesWithI18n) {
|
|
133
165
|
this.filesWithI18n.add(file);
|
|
134
166
|
}
|
|
135
|
-
this.
|
|
167
|
+
this.occurrences.push(...other.occurrences);
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Internal access to occurrences (for merge)
|
|
171
|
+
*/
|
|
172
|
+
get occurrencesList() {
|
|
173
|
+
return this.occurrences;
|
|
136
174
|
}
|
|
137
175
|
};
|
|
138
176
|
|
|
@@ -159,7 +197,7 @@ async function scan(projectRoot, logger) {
|
|
|
159
197
|
const results = await Promise.all(
|
|
160
198
|
files.map(async (file) => {
|
|
161
199
|
let code = await fs.promises.readFile(file, "utf-8");
|
|
162
|
-
if (!code.includes("@astroscope/i18n/
|
|
200
|
+
if (!code.includes("@astroscope/i18n/translate")) {
|
|
163
201
|
return { file, keys: null };
|
|
164
202
|
}
|
|
165
203
|
if (file.endsWith(".astro")) {
|
|
@@ -195,6 +233,10 @@ var MANIFEST_FILE_NAME = "i18n-manifest.json";
|
|
|
195
233
|
function i18nVitePlugin(options) {
|
|
196
234
|
const { logger } = options;
|
|
197
235
|
const store = new KeyStore(logger);
|
|
236
|
+
const syncGlobalState = () => {
|
|
237
|
+
const state = getGlobalState();
|
|
238
|
+
state.extractedKeys = store.extractedKeys;
|
|
239
|
+
};
|
|
198
240
|
let isBuild = false;
|
|
199
241
|
let isSSR = false;
|
|
200
242
|
let projectRoot = "";
|
|
@@ -204,7 +246,6 @@ function i18nVitePlugin(options) {
|
|
|
204
246
|
configResolved(config) {
|
|
205
247
|
const state = getGlobalState();
|
|
206
248
|
state.projectRoot = config.root;
|
|
207
|
-
state.extractedKeys = store.extractedKeys;
|
|
208
249
|
isBuild = config.command === "build";
|
|
209
250
|
isSSR = !!config.build.ssr;
|
|
210
251
|
projectRoot = config.root;
|
|
@@ -215,6 +256,7 @@ function i18nVitePlugin(options) {
|
|
|
215
256
|
}
|
|
216
257
|
const result = await scan(projectRoot, logger);
|
|
217
258
|
store.merge(result);
|
|
259
|
+
syncGlobalState();
|
|
218
260
|
logger.info(`dev mode: scanned ${result.fileToKeys.size} files, found ${result.uniqueKeyCount} keys`);
|
|
219
261
|
},
|
|
220
262
|
resolveId(id) {
|
|
@@ -251,7 +293,7 @@ export function getManifest() { return _getManifest(); }
|
|
|
251
293
|
if (filename.includes("node_modules")) {
|
|
252
294
|
return null;
|
|
253
295
|
}
|
|
254
|
-
if (!code.includes("@astroscope/i18n/
|
|
296
|
+
if (!code.includes("@astroscope/i18n/translate")) {
|
|
255
297
|
return null;
|
|
256
298
|
}
|
|
257
299
|
const result = await extractKeysFromFile({
|
|
@@ -261,6 +303,7 @@ export function getManifest() { return _getManifest(); }
|
|
|
261
303
|
stripFallbacks: isBuild
|
|
262
304
|
});
|
|
263
305
|
store.addFileKeys(filename, result.keys);
|
|
306
|
+
syncGlobalState();
|
|
264
307
|
if (isBuild && result.code) {
|
|
265
308
|
return { code: result.code, map: result.map };
|
|
266
309
|
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// src/shared/meta.ts
|
|
2
|
+
function normalizeMeta(meta) {
|
|
3
|
+
return typeof meta === "string" ? { fallback: meta } : meta;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
// src/shared/rich.ts
|
|
7
|
+
var REACT_ELEMENT_TYPE = /* @__PURE__ */ Symbol.for("react.element");
|
|
8
|
+
var REACT_TRANSITIONAL_ELEMENT_TYPE = /* @__PURE__ */ Symbol.for("react.transitional.element");
|
|
9
|
+
function isReactElement(value) {
|
|
10
|
+
if (typeof value !== "object" || value === null || !("$$typeof" in value)) {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
const type = value.$$typeof;
|
|
14
|
+
return type === REACT_ELEMENT_TYPE || type === REACT_TRANSITIONAL_ELEMENT_TYPE;
|
|
15
|
+
}
|
|
16
|
+
function addKeyIfNeeded(element, key) {
|
|
17
|
+
if (isReactElement(element) && element.key === null) {
|
|
18
|
+
return { ...element, key };
|
|
19
|
+
}
|
|
20
|
+
return element;
|
|
21
|
+
}
|
|
22
|
+
function isMarkupPart(part) {
|
|
23
|
+
return part.type === "markup";
|
|
24
|
+
}
|
|
25
|
+
function partsToNodes(parts, components) {
|
|
26
|
+
const result = [];
|
|
27
|
+
const stack = [];
|
|
28
|
+
let keyIndex = 0;
|
|
29
|
+
for (const part of parts) {
|
|
30
|
+
const target = stack.length > 0 ? stack[stack.length - 1].children : result;
|
|
31
|
+
if (isMarkupPart(part)) {
|
|
32
|
+
if (part.kind === "open") {
|
|
33
|
+
stack.push({ name: part.name, children: [] });
|
|
34
|
+
} else if (part.kind === "close") {
|
|
35
|
+
const frame = stack.pop();
|
|
36
|
+
if (!frame) {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
const component = components[frame.name];
|
|
40
|
+
const parent = stack.length > 0 ? stack[stack.length - 1].children : result;
|
|
41
|
+
if (component) {
|
|
42
|
+
parent.push(addKeyIfNeeded(component(frame.children), `rich-${keyIndex++}`));
|
|
43
|
+
} else {
|
|
44
|
+
parent.push(...frame.children);
|
|
45
|
+
}
|
|
46
|
+
} else if (part.kind === "standalone") {
|
|
47
|
+
const component = components[part.name];
|
|
48
|
+
if (component) {
|
|
49
|
+
target.push(addKeyIfNeeded(component([]), `rich-${keyIndex++}`));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
} else if (part.type === "text") {
|
|
53
|
+
target.push(part.value);
|
|
54
|
+
} else if (part.type === "bidiIsolation") {
|
|
55
|
+
} else {
|
|
56
|
+
if ("value" in part && part.value !== void 0 && part.value !== null) {
|
|
57
|
+
const stringValue = String(part.value);
|
|
58
|
+
target.push(stringValue);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
while (stack.length > 0) {
|
|
63
|
+
const frame = stack.pop();
|
|
64
|
+
const parent = stack.length > 0 ? stack[stack.length - 1].children : result;
|
|
65
|
+
parent.push(...frame.children);
|
|
66
|
+
}
|
|
67
|
+
return result;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export {
|
|
71
|
+
normalizeMeta,
|
|
72
|
+
partsToNodes
|
|
73
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { R as RichComponents } from '../rich-CQUhdwBc.js';
|
|
2
|
+
import { a as TranslateFunction, T as TranslationMeta } from '../types-DNRFj1XA.js';
|
|
3
|
+
import '../types-BeDFfFIL.js';
|
|
4
|
+
|
|
5
|
+
declare function getLocale(): string;
|
|
6
|
+
/**
|
|
7
|
+
* Client-side translate function
|
|
8
|
+
*
|
|
9
|
+
* Reads from window.__i18n__.translations (injected per-chunk).
|
|
10
|
+
* In production, translations are always loaded before component code runs
|
|
11
|
+
* via the chunk loader injected by the Vite plugin.
|
|
12
|
+
*
|
|
13
|
+
* Call patterns:
|
|
14
|
+
* - Development: t('key', 'fallback') or t('key', 'fallback', values)
|
|
15
|
+
* - Production: t('key') or t('key', undefined, values)
|
|
16
|
+
*
|
|
17
|
+
* Falls back to meta.fallback (dev) or key (prod) when translation is missing.
|
|
18
|
+
*/
|
|
19
|
+
declare const t: TranslateFunction;
|
|
20
|
+
/**
|
|
21
|
+
* Client-side rich text translation function
|
|
22
|
+
*
|
|
23
|
+
* Parses MF2 markup syntax and applies component callbacks to wrap content.
|
|
24
|
+
* Works with any JSX runtime (React, Preact, etc.).
|
|
25
|
+
*
|
|
26
|
+
* @param key - Translation key
|
|
27
|
+
* @param meta - Fallback string or meta object with fallback (used as message template)
|
|
28
|
+
* @param components - Map of tag names to component callbacks
|
|
29
|
+
* @param values - Runtime interpolation values (ICU MessageFormat)
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```tsx
|
|
33
|
+
* rich('tos', 'Read our {#link}Terms{/link}', {
|
|
34
|
+
* link: (children) => <a href="/tos">{children}</a>
|
|
35
|
+
* })
|
|
36
|
+
* // Returns: ['Read our ', <a href="/tos">Terms</a>]
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
declare function rich<T = unknown>(key: string, meta?: TranslationMeta | string | undefined, components?: RichComponents<T> | undefined, values?: Record<string, unknown> | undefined): (string | T)[];
|
|
40
|
+
|
|
41
|
+
export { getLocale, rich, t };
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
|
-
normalizeMeta
|
|
3
|
-
|
|
2
|
+
normalizeMeta,
|
|
3
|
+
partsToNodes
|
|
4
|
+
} from "../chunk-VGPVG5JP.js";
|
|
4
5
|
import {
|
|
5
|
-
compileMessage
|
|
6
|
-
|
|
6
|
+
compileMessage,
|
|
7
|
+
formatMessageToParts
|
|
8
|
+
} from "../chunk-33JVAFGQ.js";
|
|
7
9
|
|
|
8
|
-
// src/client/
|
|
10
|
+
// src/client/translate.ts
|
|
9
11
|
var cache = /* @__PURE__ */ new Map();
|
|
10
12
|
function getLocale() {
|
|
11
13
|
return window.__i18n__.locale;
|
|
@@ -31,7 +33,14 @@ var t = ((key, meta, values) => {
|
|
|
31
33
|
}
|
|
32
34
|
return compiled(values);
|
|
33
35
|
});
|
|
36
|
+
function rich(key, meta, components, values) {
|
|
37
|
+
const normalizedMeta = meta ? normalizeMeta(meta) : { fallback: "" };
|
|
38
|
+
const raw = window.__i18n__.translations[key] ?? normalizedMeta.fallback ?? key;
|
|
39
|
+
const parts = formatMessageToParts(window.__i18n__.locale, raw, values);
|
|
40
|
+
return partsToNodes(parts, components ?? {});
|
|
41
|
+
}
|
|
34
42
|
export {
|
|
35
43
|
getLocale,
|
|
44
|
+
rich,
|
|
36
45
|
t
|
|
37
46
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as ExtractionManifest } from '../types-
|
|
2
|
-
export { C as ChunkManifest, E as ExtractedKey } from '../types-
|
|
1
|
+
import { a as ExtractionManifest } from '../types-moTgocy7.js';
|
|
2
|
+
export { C as ChunkManifest, E as ExtractedKey } from '../types-moTgocy7.js';
|
|
3
3
|
import { AstroIntegrationLogger } from 'astro';
|
|
4
4
|
import { Plugin } from 'vite';
|
|
5
5
|
import '../types-DNRFj1XA.js';
|
package/dist/extraction/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as ExtractionManifest } from './types-
|
|
1
|
+
import { a as ExtractionManifest } from './types-moTgocy7.js';
|
|
2
2
|
import { T as TranslationMeta, C as CompiledTranslations, R as RawTranslations } from './types-DNRFj1XA.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -11,6 +11,7 @@ type FallbackBehavior = 'key' | 'fallback' | 'throw' | ((key: string, meta?: Tra
|
|
|
11
11
|
type I18nContext = {
|
|
12
12
|
locale: string;
|
|
13
13
|
translations: CompiledTranslations;
|
|
14
|
+
rawTranslations: RawTranslations;
|
|
14
15
|
fallback: FallbackBehavior;
|
|
15
16
|
};
|
|
16
17
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { AstroIntegration, MiddlewareHandler, APIContext } from 'astro';
|
|
2
|
-
export { F as FallbackBehavior, I as I18nConfig, i as i18n } from './i18n-
|
|
2
|
+
export { F as FallbackBehavior, I as I18nConfig, i as i18n } from './i18n-DaLnNvss.js';
|
|
3
3
|
import { ExcludePattern } from '@astroscope/excludes';
|
|
4
4
|
export { R as RawTranslations, T as TranslationMeta } from './types-DNRFj1XA.js';
|
|
5
|
-
export { E as ExtractedKey, a as ExtractionManifest } from './types-
|
|
5
|
+
export { E as ExtractedKey, a as ExtractionManifest } from './types-moTgocy7.js';
|
|
6
6
|
|
|
7
7
|
interface I18nOptions {
|
|
8
8
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
i18nVitePlugin
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-EXMM4JID.js";
|
|
4
4
|
import {
|
|
5
5
|
i18n,
|
|
6
6
|
runWithContext
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-
|
|
7
|
+
} from "./chunk-MSJHXRJB.js";
|
|
8
|
+
import "./chunk-33JVAFGQ.js";
|
|
9
9
|
import "./chunk-4A5BKHFN.js";
|
|
10
10
|
|
|
11
11
|
// src/integration/integration.ts
|
|
@@ -112,6 +112,7 @@ function createI18nMiddleware(options) {
|
|
|
112
112
|
const context = {
|
|
113
113
|
locale,
|
|
114
114
|
translations: i18n.getCompiledTranslations(locale),
|
|
115
|
+
rawTranslations: i18n.getTranslations(locale),
|
|
115
116
|
fallback: i18n.getConfig().fallback
|
|
116
117
|
};
|
|
117
118
|
return runWithContext(context, () => next());
|
package/dist/internal.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { a as I18nContext } from './i18n-
|
|
2
|
-
export { i as i18n } from './i18n-
|
|
1
|
+
import { a as I18nContext } from './i18n-DaLnNvss.js';
|
|
2
|
+
export { i as i18n } from './i18n-DaLnNvss.js';
|
|
3
3
|
export { I as I18nClientState } from './types-DNRFj1XA.js';
|
|
4
|
-
import './types-
|
|
4
|
+
import './types-moTgocy7.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Get the current i18n context
|
package/dist/internal.js
CHANGED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Component callbacks for rich text rendering.
|
|
3
|
+
* Each key maps to a function that wraps children in a component.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```tsx
|
|
7
|
+
* const components: RichComponents = {
|
|
8
|
+
* link: (children) => <a href="/tos">{children}</a>,
|
|
9
|
+
* bold: (children) => <strong>{children}</strong>,
|
|
10
|
+
* };
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
type RichComponents<T = unknown> = Record<string, (children: (string | T)[]) => T>;
|
|
14
|
+
|
|
15
|
+
export type { RichComponents as R };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { R as RichComponents } from '../rich-CQUhdwBc.js';
|
|
2
|
+
import { a as TranslateFunction, T as TranslationMeta } from '../types-DNRFj1XA.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Get the current locale from request context.
|
|
6
|
+
* Returns defaultLocale if called outside a request (e.g. during build).
|
|
7
|
+
*/
|
|
8
|
+
declare function getLocale(): string;
|
|
9
|
+
/**
|
|
10
|
+
* Server-side translate function
|
|
11
|
+
*
|
|
12
|
+
* Call patterns:
|
|
13
|
+
* - Development: t('key', 'fallback') or t('key', 'fallback', values)
|
|
14
|
+
* - Production: t('key') or t('key', undefined, values)
|
|
15
|
+
*/
|
|
16
|
+
declare const t: TranslateFunction;
|
|
17
|
+
/**
|
|
18
|
+
* Server-side rich text translation function
|
|
19
|
+
*
|
|
20
|
+
* Parses MF2 markup syntax and applies component callbacks to wrap content.
|
|
21
|
+
* Works with any JSX runtime (Astro, React during SSR, etc.).
|
|
22
|
+
*
|
|
23
|
+
* @param key - Translation key
|
|
24
|
+
* @param meta - Fallback string or meta object with fallback (used as message template)
|
|
25
|
+
* @param components - Map of tag names to component callbacks
|
|
26
|
+
* @param values - Runtime interpolation values (ICU MessageFormat)
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```tsx
|
|
30
|
+
* rich('tos', 'Read our {#link}Terms{/link}', {
|
|
31
|
+
* link: (children) => <a href="/tos">{children}</a>
|
|
32
|
+
* })
|
|
33
|
+
* // Returns: ['Read our ', <a href="/tos">Terms</a>]
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
declare function rich<T = unknown>(key: string, meta?: TranslationMeta | string | undefined, components?: RichComponents<T> | undefined, values?: Record<string, unknown> | undefined): (string | T)[];
|
|
37
|
+
|
|
38
|
+
export { getLocale, rich, t };
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getContext,
|
|
3
3
|
i18n
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-MSJHXRJB.js";
|
|
5
5
|
import {
|
|
6
|
-
normalizeMeta
|
|
7
|
-
|
|
6
|
+
normalizeMeta,
|
|
7
|
+
partsToNodes
|
|
8
|
+
} from "../chunk-VGPVG5JP.js";
|
|
8
9
|
import {
|
|
9
|
-
compileMessage
|
|
10
|
-
|
|
10
|
+
compileMessage,
|
|
11
|
+
formatMessageToParts
|
|
12
|
+
} from "../chunk-33JVAFGQ.js";
|
|
11
13
|
|
|
12
|
-
// src/server/
|
|
14
|
+
// src/server/translate.ts
|
|
13
15
|
var fallbackCache = /* @__PURE__ */ new Map();
|
|
14
16
|
function applyFallback(key, meta, fallback) {
|
|
15
17
|
if (typeof fallback === "function") {
|
|
@@ -53,7 +55,16 @@ var t = ((key, meta, values) => {
|
|
|
53
55
|
}
|
|
54
56
|
return compiled(values);
|
|
55
57
|
});
|
|
58
|
+
function rich(key, meta, components, values) {
|
|
59
|
+
const normalizedMeta = meta ? normalizeMeta(meta) : { fallback: "" };
|
|
60
|
+
const ctx = getContext();
|
|
61
|
+
const locale = ctx?.locale ?? i18n.getConfig().defaultLocale;
|
|
62
|
+
const raw = ctx?.rawTranslations[key] ?? normalizedMeta.fallback ?? key;
|
|
63
|
+
const parts = formatMessageToParts(locale, raw, values);
|
|
64
|
+
return partsToNodes(parts, components ?? {});
|
|
65
|
+
}
|
|
56
66
|
export {
|
|
57
67
|
getLocale,
|
|
68
|
+
rich,
|
|
58
69
|
t
|
|
59
70
|
};
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { T as TranslationMeta } from './types-DNRFj1XA.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* Deduplicated translation key with all file locations (used in manifest)
|
|
5
5
|
*/
|
|
6
6
|
type ExtractedKey = {
|
|
7
7
|
key: string;
|
|
8
8
|
meta: TranslationMeta;
|
|
9
|
-
file:
|
|
10
|
-
|
|
9
|
+
/** All locations where this key is used (file:line format) */
|
|
10
|
+
files: string[];
|
|
11
11
|
};
|
|
12
12
|
/**
|
|
13
13
|
* Chunk-to-keys mapping produced by Vite plugin
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astroscope/i18n",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "i18n for Astro + React islands — automatic tree-shaking, parallel loading, any translation source",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "i18n for Astro + React islands — MessageFormat 2, automatic tree-shaking, parallel loading, any translation source",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
@@ -10,10 +10,10 @@
|
|
|
10
10
|
"types": "./dist/index.d.ts",
|
|
11
11
|
"import": "./dist/index.js"
|
|
12
12
|
},
|
|
13
|
-
"./
|
|
14
|
-
"types": "./dist/client/
|
|
15
|
-
"browser": "./dist/client/
|
|
16
|
-
"default": "./dist/server/
|
|
13
|
+
"./translate": {
|
|
14
|
+
"types": "./dist/client/translate.d.ts",
|
|
15
|
+
"browser": "./dist/client/translate.js",
|
|
16
|
+
"default": "./dist/server/translate.js"
|
|
17
17
|
},
|
|
18
18
|
"./client-directives/load.js": {
|
|
19
19
|
"types": "./dist/client/directives/load.d.ts",
|
|
@@ -35,8 +35,8 @@
|
|
|
35
35
|
"types": "./dist/client/directives/only.d.ts",
|
|
36
36
|
"import": "./dist/client/directives/only.js"
|
|
37
37
|
},
|
|
38
|
-
"./
|
|
39
|
-
"import": "./src/
|
|
38
|
+
"./astro": {
|
|
39
|
+
"import": "./src/astro/index.ts"
|
|
40
40
|
},
|
|
41
41
|
"./internal": {
|
|
42
42
|
"types": "./dist/internal.d.ts",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
},
|
|
50
50
|
"files": [
|
|
51
51
|
"dist",
|
|
52
|
-
"src/
|
|
52
|
+
"src/astro"
|
|
53
53
|
],
|
|
54
54
|
"sideEffects": [
|
|
55
55
|
"./dist/client/directives/*.js",
|
|
@@ -69,6 +69,8 @@
|
|
|
69
69
|
"i18n",
|
|
70
70
|
"internationalization",
|
|
71
71
|
"translations",
|
|
72
|
+
"messageformat",
|
|
73
|
+
"mf2",
|
|
72
74
|
"react",
|
|
73
75
|
"islands",
|
|
74
76
|
"ssr"
|
|
@@ -80,7 +82,7 @@
|
|
|
80
82
|
},
|
|
81
83
|
"homepage": "https://github.com/smnbbrv/astroscope/tree/main/packages/i18n#readme",
|
|
82
84
|
"scripts": {
|
|
83
|
-
"build": "tsup src/index.ts src/internal.ts src/extraction/index.ts src/server/
|
|
85
|
+
"build": "tsup src/index.ts src/internal.ts src/extraction/index.ts src/server/translate.ts src/client/translate.ts src/client/directives/load.ts src/client/directives/idle.ts src/client/directives/visible.ts src/client/directives/media.ts src/client/directives/only.ts src/client/directives/utils.ts --format esm --dts --external astro --external astro/client/load.js --external astro/client/idle.js --external astro/client/visible.js --external astro/client/media.js --external astro/client/only.js --external virtual:@astroscope/i18n/manifest --external @babel/core",
|
|
84
86
|
"typecheck": "tsc --noEmit",
|
|
85
87
|
"lint": "eslint 'src/**/*.ts'",
|
|
86
88
|
"lint:fix": "eslint 'src/**/*.ts' --fix"
|
|
@@ -89,7 +91,7 @@
|
|
|
89
91
|
"@astrojs/compiler": "^2.12.2",
|
|
90
92
|
"@babel/core": "^7.28.6",
|
|
91
93
|
"@babel/preset-typescript": "^7.28.5",
|
|
92
|
-
"
|
|
94
|
+
"messageformat": "^4.0.0",
|
|
93
95
|
"glob": "^11.0.0",
|
|
94
96
|
"magic-string": "^0.30.21"
|
|
95
97
|
},
|
package/dist/chunk-EMM6LRO2.js
DELETED
package/dist/chunk-KNDYKTHE.js
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
// src/shared/compiler.ts
|
|
2
|
-
import MessageFormat from "@messageformat/core";
|
|
3
|
-
var cache = /* @__PURE__ */ new Map();
|
|
4
|
-
function getMessageFormat(locale) {
|
|
5
|
-
let mf = cache.get(locale);
|
|
6
|
-
if (!mf) {
|
|
7
|
-
mf = new MessageFormat(locale);
|
|
8
|
-
cache.set(locale, mf);
|
|
9
|
-
}
|
|
10
|
-
return mf;
|
|
11
|
-
}
|
|
12
|
-
function compileMessage(locale, message) {
|
|
13
|
-
const mf = getMessageFormat(locale);
|
|
14
|
-
try {
|
|
15
|
-
return mf.compile(message);
|
|
16
|
-
} catch {
|
|
17
|
-
return () => message;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
function compileTranslations(locale, raw) {
|
|
21
|
-
const compiled = {};
|
|
22
|
-
for (const [key, message] of Object.entries(raw)) {
|
|
23
|
-
compiled[key] = compileMessage(locale, message);
|
|
24
|
-
}
|
|
25
|
-
return compiled;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export {
|
|
29
|
-
compileMessage,
|
|
30
|
-
compileTranslations
|
|
31
|
-
};
|
package/dist/client/t.d.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { a as TranslateFunction } from '../types-DNRFj1XA.js';
|
|
2
|
-
import '../types-BeDFfFIL.js';
|
|
3
|
-
|
|
4
|
-
declare function getLocale(): string;
|
|
5
|
-
/**
|
|
6
|
-
* Client-side translate function
|
|
7
|
-
*
|
|
8
|
-
* Reads from window.__i18n__.translations (injected per-chunk).
|
|
9
|
-
* In production, translations are always loaded before component code runs
|
|
10
|
-
* via the chunk loader injected by the Vite plugin.
|
|
11
|
-
*
|
|
12
|
-
* Call patterns:
|
|
13
|
-
* - Development: t('key', 'fallback') or t('key', 'fallback', values)
|
|
14
|
-
* - Production: t('key') or t('key', undefined, values)
|
|
15
|
-
*
|
|
16
|
-
* Falls back to meta.fallback (dev) or key (prod) when translation is missing.
|
|
17
|
-
*/
|
|
18
|
-
declare const t: TranslateFunction;
|
|
19
|
-
|
|
20
|
-
export { getLocale, t };
|
package/dist/server/t.d.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { a as TranslateFunction } from '../types-DNRFj1XA.js';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Get the current locale from request context.
|
|
5
|
-
* Returns defaultLocale if called outside a request (e.g. during build).
|
|
6
|
-
*/
|
|
7
|
-
declare function getLocale(): string;
|
|
8
|
-
/**
|
|
9
|
-
* Server-side translate function
|
|
10
|
-
*
|
|
11
|
-
* Call patterns:
|
|
12
|
-
* - Development: t('key', 'fallback') or t('key', 'fallback', values)
|
|
13
|
-
* - Production: t('key') or t('key', undefined, values)
|
|
14
|
-
*/
|
|
15
|
-
declare const t: TranslateFunction;
|
|
16
|
-
|
|
17
|
-
export { getLocale, t };
|
|
File without changes
|
|
File without changes
|