@astroscope/i18n 2.1.2 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -31
- package/dist/{babel-plugin-DJZS9L_W.js → babel-plugin-CRgz4mri.js} +2 -3
- package/dist/{babel-plugin-DJZS9L_W.js.map → babel-plugin-CRgz4mri.js.map} +1 -1
- package/dist/client/translate.d.ts +2 -4
- package/dist/client/translate.d.ts.map +1 -1
- package/dist/client/translate.js +5 -7
- package/dist/client/translate.js.map +1 -1
- package/dist/{compiler-CkOVGWU5.js → compiler-K3uHKoFs.js} +2 -3
- package/dist/{compiler-CkOVGWU5.js.map → compiler-K3uHKoFs.js.map} +1 -1
- package/dist/context-CJvTEa-I.js +18 -0
- package/dist/context-CJvTEa-I.js.map +1 -0
- package/dist/extraction/index.d.ts +5 -6
- package/dist/extraction/index.d.ts.map +1 -1
- package/dist/extraction/index.js +2 -2
- package/dist/{i18n-Ca5mVXlq.d.ts → i18n-7dnqpAwm.d.ts} +16 -8
- package/dist/i18n-7dnqpAwm.d.ts.map +1 -0
- package/dist/{context-Cq9-KsBw.js → i18n-f7qdMTwm.js} +8 -25
- package/dist/i18n-f7qdMTwm.js.map +1 -0
- package/dist/index.d.ts +14 -64
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20 -15
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +2 -4
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +3 -1
- package/dist/{manifest-X-UhW6tH.js → manifest-DK513NbQ.js} +2 -3
- package/dist/{manifest-X-UhW6tH.js.map → manifest-DK513NbQ.js.map} +1 -1
- package/dist/{middleware-SkXWqtl3.js → middleware-CNXfXefr.js} +23 -29
- package/dist/middleware-CNXfXefr.js.map +1 -0
- package/dist/{rich-BDqjhv2h.js → rich-BbDoHrd9.js} +3 -5
- package/dist/{rich-BDqjhv2h.js.map → rich-BbDoHrd9.js.map} +1 -1
- package/dist/{rich-BWszYAzS.d.ts → rich-CavnJlKg.d.ts} +1 -3
- package/dist/rich-CavnJlKg.d.ts.map +1 -0
- package/dist/server/chunk-middleware-entrypoint.d.ts +0 -2
- package/dist/server/chunk-middleware-entrypoint.d.ts.map +1 -1
- package/dist/server/chunk-middleware-entrypoint.js +2 -3
- package/dist/server/locale-middleware-entrypoint.d.ts +2 -0
- package/dist/server/locale-middleware-entrypoint.d.ts.map +1 -0
- package/dist/server/locale-middleware-entrypoint.js +7 -0
- package/dist/server/locale-middleware-entrypoint.js.map +1 -0
- package/dist/server/translate.d.ts +2 -4
- package/dist/server/translate.d.ts.map +1 -1
- package/dist/server/translate.js +5 -5
- package/dist/server/translate.js.map +1 -1
- package/dist/{types-BjTD5hUT.d.ts → types-DYnR9xP3.d.ts} +1 -3
- package/dist/types-DYnR9xP3.d.ts.map +1 -0
- package/dist/{types-BC-tQ30r.d.ts → types-DwM0qsLj.d.ts} +2 -4
- package/dist/types-DwM0qsLj.d.ts.map +1 -0
- package/dist/{types-Bo4jMK8g.d.ts → types-ioAI5pu0.d.ts} +7 -3
- package/dist/types-ioAI5pu0.d.ts.map +1 -0
- package/dist/{url-CifeaDzl.js → url-6HS_IPPA.js} +2 -3
- package/dist/{url-CifeaDzl.js.map → url-6HS_IPPA.js.map} +1 -1
- package/dist/{vite-plugin-BPYFFBQp.js → vite-plugin-Yc5NP6bs.js} +17 -16
- package/dist/vite-plugin-Yc5NP6bs.js.map +1 -0
- package/package.json +15 -11
- package/dist/context-Cq9-KsBw.js.map +0 -1
- package/dist/i18n-Ca5mVXlq.d.ts.map +0 -1
- package/dist/middleware-SkXWqtl3.js.map +0 -1
- package/dist/rich-BWszYAzS.d.ts.map +0 -1
- package/dist/types-BC-tQ30r.d.ts.map +0 -1
- package/dist/types-BjTD5hUT.d.ts.map +0 -1
- package/dist/types-Bo4jMK8g.d.ts.map +0 -1
- package/dist/vite-plugin-BPYFFBQp.js.map +0 -1
package/README.md
CHANGED
|
@@ -62,6 +62,22 @@ i18n({
|
|
|
62
62
|
- **`'warn'`** — Log a warning but continue
|
|
63
63
|
- **`'off'`** — Disable consistency checking
|
|
64
64
|
|
|
65
|
+
#### Middleware
|
|
66
|
+
|
|
67
|
+
The integration injects two middlewares: one serving the `/_i18n/` translation chunks ahead of your own middleware, and the locale middleware after it — it resolves the request's locale and provides the request context for `t()`. Running after your middleware means the locale resolver sees what a session middleware put on `locals`.
|
|
68
|
+
|
|
69
|
+
Both middlewares are injected in the order of your `integrations` array.
|
|
70
|
+
|
|
71
|
+
By default, `RECOMMENDED_EXCLUDES` (static assets like `/_astro/`) are excluded from locale context setup. To customize:
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
import { RECOMMENDED_EXCLUDES } from '@astroscope/node/excludes';
|
|
75
|
+
|
|
76
|
+
i18n({
|
|
77
|
+
exclude: [...RECOMMENDED_EXCLUDES, { exact: '/health' }],
|
|
78
|
+
})
|
|
79
|
+
```
|
|
80
|
+
|
|
65
81
|
### 2. Configure i18n in your boot file
|
|
66
82
|
|
|
67
83
|
> **Note:** `i18n.configure` must be awaited during boot, before any requests are handled.
|
|
@@ -80,6 +96,9 @@ export async function onStartup() {
|
|
|
80
96
|
await i18n.configure({
|
|
81
97
|
locales: ['en', 'de'],
|
|
82
98
|
defaultLocale: 'en', // optional, defaults to first locale
|
|
99
|
+
// optional, per request: a cookie, the session on `locals`, the url, `detectLocale(request)`
|
|
100
|
+
// for Accept-Language — `undefined` falls back to defaultLocale
|
|
101
|
+
locale: (ctx) => ctx.cookies.get('locale')?.value,
|
|
83
102
|
});
|
|
84
103
|
|
|
85
104
|
// load translations for all locales
|
|
@@ -93,36 +112,7 @@ export async function onStartup() {
|
|
|
93
112
|
}
|
|
94
113
|
```
|
|
95
114
|
|
|
96
|
-
### 3.
|
|
97
|
-
|
|
98
|
-
```ts
|
|
99
|
-
// src/middleware.ts
|
|
100
|
-
import { createI18nMiddleware, i18n } from '@astroscope/i18n';
|
|
101
|
-
|
|
102
|
-
export const onRequest = createI18nMiddleware({
|
|
103
|
-
locale: (ctx) =>
|
|
104
|
-
ctx.cookies.get('locale')?.value ??
|
|
105
|
-
i18n.getConfig().defaultLocale,
|
|
106
|
-
});
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
The middleware serving `/_i18n/` translation chunks is injected automatically by the integration.
|
|
110
|
-
|
|
111
|
-
By default, `RECOMMENDED_EXCLUDES` (static assets like `/_astro/`) are excluded from locale context setup. To customize:
|
|
112
|
-
|
|
113
|
-
```ts
|
|
114
|
-
import { createI18nMiddleware, i18n } from '@astroscope/i18n';
|
|
115
|
-
import { RECOMMENDED_EXCLUDES } from '@astroscope/node/excludes';
|
|
116
|
-
|
|
117
|
-
export const onRequest = createI18nMiddleware({
|
|
118
|
-
locale: (ctx) =>
|
|
119
|
-
ctx.cookies.get('locale')?.value ??
|
|
120
|
-
i18n.getConfig().defaultLocale,
|
|
121
|
-
exclude: [...RECOMMENDED_EXCLUDES, { exact: '/health' }],
|
|
122
|
-
});
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
### 4. Use `t()` in your components
|
|
115
|
+
### 3. Use `t()` in your components
|
|
126
116
|
|
|
127
117
|
```astro
|
|
128
118
|
---
|
|
@@ -290,7 +280,7 @@ export function App() {
|
|
|
290
280
|
|
|
291
281
|
1. **Build time** — all `t()` calls are extracted, mapped to chunks, and fallbacks are stripped from production bundles
|
|
292
282
|
2. **Manifest** — Extracted keys with fallbacks are written to `i18n-manifest.json`
|
|
293
|
-
3. **SSR** —
|
|
283
|
+
3. **SSR** — the locale middleware provides translations to `t()`, merging manifest fallbacks for missing keys
|
|
294
284
|
4. **Client** — each island preloads only the translation chunks its code needs, in parallel with the component code
|
|
295
285
|
|
|
296
286
|
The same `import { t } from '@astroscope/i18n/translate'` works everywhere — bundler picks the correct implementation via conditional exports (`browser` vs `default`).
|
|
@@ -378,6 +368,7 @@ For `:currency` and `:unit`, the required option (`currency` or `unit`) can be:
|
|
|
378
368
|
| `locales` | `string[]` | required | Supported locales |
|
|
379
369
|
| `defaultLocale` | `string` | first locale | Default/fallback locale |
|
|
380
370
|
| `fallback` | `FallbackBehavior` | `'fallback'` | Behavior when translation missing |
|
|
371
|
+
| `locale` | `(ctx: APIContext) => string \| undefined` | `defaultLocale` for every request | Per-request locale resolver, run by the locale middleware |
|
|
381
372
|
|
|
382
373
|
### FallbackBehavior
|
|
383
374
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/extraction/babel-plugin.ts
|
|
2
2
|
/**
|
|
3
3
|
* Extract string value from AST node
|
|
4
4
|
*/
|
|
@@ -119,7 +119,6 @@ function i18nExtractPlugin({ types: t }) {
|
|
|
119
119
|
} }
|
|
120
120
|
};
|
|
121
121
|
}
|
|
122
|
-
//#endregion
|
|
123
122
|
export { i18nExtractPlugin };
|
|
124
123
|
|
|
125
|
-
//# sourceMappingURL=babel-plugin-
|
|
124
|
+
//# sourceMappingURL=babel-plugin-CRgz4mri.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"babel-plugin-
|
|
1
|
+
{"version":3,"file":"babel-plugin-CRgz4mri.js","names":[],"sources":["../src/extraction/babel-plugin.ts"],"sourcesContent":["import type { NodePath, PluginObject, PluginPass } from '@babel/core';\nimport type * as BabelTypes from '@babel/types';\nimport type { Expression, Node, ObjectExpression } from '@babel/types';\nimport type { TranslationMeta, VariableDef } from '../shared/types.js';\nimport type { ExtractedKeyOccurrence, ExtractionError } from './types.js';\n\nexport type I18nExtractPluginOpts = {\n onKeyExtracted: (key: ExtractedKeyOccurrence) => void;\n onExtractionError: (error: ExtractionError) => void;\n stripFallbacks: boolean;\n};\n\ntype PluginState = PluginPass & { opts: I18nExtractPluginOpts };\n\ntype ReportFn = (reason: string) => void;\n\n/**\n * Extract string value from AST node\n */\nfunction getStringValue(t: typeof BabelTypes, node: Node): string | null {\n if (t.isStringLiteral(node)) {\n return node.value;\n }\n\n if (t.isTemplateLiteral(node) && node.quasis.length === 1 && node.expressions.length === 0) {\n return node.quasis[0]?.value.cooked ?? null;\n }\n\n return null;\n}\n\n/**\n * Extract TranslationMeta from the second argument of t()\n */\nfunction extractMeta(t: typeof BabelTypes, node: Expression, report: ReportFn): TranslationMeta {\n // string shorthand: t('key', 'fallback')\n const stringValue = getStringValue(t, node);\n\n if (stringValue !== null) {\n return { fallback: stringValue };\n }\n\n // dynamic template literal: t('key', `Hello ${name}`)\n if (t.isTemplateLiteral(node) && node.expressions.length > 0) {\n report('the fallback is a template literal with expressions');\n\n return { fallback: '' };\n }\n\n // object form: t('key', { fallback: '...', description: '...', variables: {...} })\n if (t.isObjectExpression(node)) {\n const meta: TranslationMeta = { fallback: '' };\n\n for (const prop of node.properties) {\n // spread elements cannot be extracted statically\n if (t.isSpreadElement(prop)) {\n report('the meta object contains a spread element');\n\n continue;\n }\n\n if (!t.isObjectProperty(prop)) continue;\n if (!t.isIdentifier(prop.key)) continue;\n\n const name = prop.key.name;\n\n if (name === 'fallback') {\n const value = getStringValue(t, prop.value as Node);\n\n if (value === null) {\n report('the fallback is not a static string');\n } else {\n meta.fallback = value;\n }\n } else if (name === 'description') {\n const value = getStringValue(t, prop.value as Node);\n\n if (value === null) {\n report('the description is not a static string');\n } else {\n meta.description = value;\n }\n } else if (name === 'variables' && t.isObjectExpression(prop.value)) {\n meta.variables = extractVariables(t, prop.value, report);\n }\n }\n\n return meta;\n }\n\n // dynamic meta (e.g. variable reference)\n report('the meta is neither a static string nor an object literal');\n\n return { fallback: '' };\n}\n\n/**\n * Extract variables definition from object expression\n */\nfunction extractVariables(\n t: typeof BabelTypes,\n obj: ObjectExpression,\n report: ReportFn,\n): Record<string, VariableDef> | undefined {\n const result: Record<string, VariableDef> = {};\n\n for (const prop of obj.properties) {\n if (t.isSpreadElement(prop)) {\n report('the variables object contains a spread element');\n continue;\n }\n\n if (!t.isObjectProperty(prop)) continue;\n if (!t.isIdentifier(prop.key)) continue;\n\n const varName = prop.key.name;\n\n if (!t.isObjectExpression(prop.value)) continue;\n\n const varDef: VariableDef = {};\n\n for (const varProp of prop.value.properties) {\n if (t.isSpreadElement(varProp)) {\n report(`variable \"${varName}\" contains a spread element`);\n continue;\n }\n\n if (!t.isObjectProperty(varProp)) continue;\n if (!t.isIdentifier(varProp.key)) continue;\n\n const varPropName = varProp.key.name;\n const value = getStringValue(t, varProp.value as Node);\n\n if (value !== null) {\n if (varPropName === 'fallback') {\n varDef.fallback = value;\n } else if (varPropName === 'description') {\n varDef.description = value;\n }\n } else if (varPropName === 'fallback' || varPropName === 'description') {\n report(`variable \"${varName}.${varPropName}\" is not a static string`);\n }\n }\n\n result[varName] = varDef;\n }\n\n return Object.keys(result).length > 0 ? result : undefined;\n}\n\n/**\n * Babel plugin for i18n extraction and transformation\n *\n * - Extracts t() calls with key and metadata\n * - Strips fallback argument in production builds\n */\nexport function i18nExtractPlugin({ types: t }: { types: typeof BabelTypes }): PluginObject<PluginState> {\n return {\n name: '@astroscope/i18n/extract',\n visitor: {\n CallExpression(path: NodePath<BabelTypes.CallExpression>, state: PluginState) {\n // matched by name only — an aliased or reassigned `t` is invisible here\n if (!path.get('callee').isIdentifier({ name: 't' })) return;\n\n const args = path.node.arguments;\n const keyArg = args[0];\n\n if (!t.isExpression(keyArg)) return;\n\n const key = getStringValue(t, keyArg);\n\n if (key === null) return; // first arg must be string\n\n const file = state.filename ?? '';\n const line = path.node.loc?.start.line ?? 0;\n const column = path.node.loc?.start.column ?? 0;\n const report: ReportFn = (reason) => state.opts.onExtractionError({ key, reason, file, line, column });\n\n // second arg is always meta (fallback string or object)\n // may not exist if we're processing already-transformed code\n const meta = args.length >= 2 && t.isExpression(args[1]) ? extractMeta(t, args[1], report) : { fallback: '' };\n\n state.opts.onKeyExtracted({ key, meta, file, line });\n\n // strip fallback in production\n // t('key', 'fallback') → t('key')\n // t('key', 'fallback', values) → t('key', undefined, values)\n if (!state.opts.stripFallbacks || args.length < 2) return;\n\n const firstArg = args[0]!;\n\n if (args.length === 2) {\n path.node.arguments = [firstArg];\n } else {\n path.node.arguments = [firstArg, t.identifier('undefined'), args[2]!];\n }\n },\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { t as RichComponents } from "../rich-
|
|
2
|
-
import { a as TranslationMeta, i as TranslateFunction } from "../types-
|
|
3
|
-
//#region src/client/translate.d.ts
|
|
1
|
+
import { t as RichComponents } from "../rich-CavnJlKg.js";
|
|
2
|
+
import { a as TranslationMeta, i as TranslateFunction } from "../types-DYnR9xP3.js";
|
|
4
3
|
export declare function getLocale(): string;
|
|
5
4
|
/**
|
|
6
5
|
* Client-side translate function
|
|
@@ -36,5 +35,4 @@ export declare const t: TranslateFunction;
|
|
|
36
35
|
* ```
|
|
37
36
|
*/
|
|
38
37
|
export declare function rich<T = unknown>(key: string, meta?: TranslationMeta | string | undefined, components?: RichComponents<T> | undefined, values?: Record<string, unknown> | undefined): (string | T)[];
|
|
39
|
-
//#endregion
|
|
40
38
|
//# sourceMappingURL=translate.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"translate.d.ts","names":[],"sources":["../../src/client/translate.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"translate.d.ts","names":[],"sources":["../../src/client/translate.ts"],"mappings":";;wBAQgB;;;;;;;;;;;;;;qBAiBH,GAAG;;;;;;;;;;;;;;;;;;;;wBAyDA,KAAK,aACnB,aACA,OAAO,sCACP,aAAa,eAAe,gBAC5B,SAAS,gDACE"}
|
package/dist/client/translate.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { r as formatMessageToParts, t as compileMessage } from "../compiler-
|
|
2
|
-
import { n as normalizeMeta, t as partsToNodes } from "../rich-
|
|
3
|
-
|
|
1
|
+
import { r as formatMessageToParts, t as compileMessage } from "../compiler-K3uHKoFs.js";
|
|
2
|
+
import { n as normalizeMeta, t as partsToNodes } from "../rich-BbDoHrd9.js";
|
|
3
|
+
// src/client/state.ts
|
|
4
4
|
let warnedMissingState = false;
|
|
5
5
|
/**
|
|
6
6
|
* Read the client i18n state injected by the i18n middleware and islands emitter.
|
|
@@ -12,12 +12,11 @@ function getI18nState() {
|
|
|
12
12
|
const state = window.__i18n__;
|
|
13
13
|
if (!state && !warnedMissingState) {
|
|
14
14
|
warnedMissingState = true;
|
|
15
|
-
console.error("[@astroscope/i18n] window.__i18n__ is not defined — translations will not work on the client. Make sure
|
|
15
|
+
console.error("[@astroscope/i18n] window.__i18n__ is not defined — translations will not work on the client. Make sure the @astroscope/i18n integration is set up and the page is not in its `exclude` list (the locale middleware injects the state) and, for per-island hashes, that the site uses the @astroscope/node adapter.");
|
|
16
16
|
}
|
|
17
17
|
return state;
|
|
18
18
|
}
|
|
19
|
-
|
|
20
|
-
//#region src/client/translate.ts
|
|
19
|
+
// src/client/translate.ts
|
|
21
20
|
const cache = /* @__PURE__ */ new Map();
|
|
22
21
|
function getLocale() {
|
|
23
22
|
return getI18nState()?.locale ?? "";
|
|
@@ -84,7 +83,6 @@ function rich(key, meta, components, values) {
|
|
|
84
83
|
const parts = formatMessageToParts(state?.locale ?? "", raw, values);
|
|
85
84
|
return partsToNodes(parts, components ?? {});
|
|
86
85
|
}
|
|
87
|
-
//#endregion
|
|
88
86
|
export { getLocale, rich, t };
|
|
89
87
|
|
|
90
88
|
//# sourceMappingURL=translate.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"translate.js","names":[],"sources":["../../src/client/state.ts","../../src/client/translate.ts"],"sourcesContent":["import type { I18nClientState } from '../shared/types.js';\nimport './types.js';\n\nlet warnedMissingState = false;\n\n/**\n * Read the client i18n state injected by the i18n middleware and islands emitter.\n *\n * Returns undefined and logs an actionable error (once) when the state was not\n * injected — without it window.__i18n__ is undefined and translations cannot load.\n */\nexport function getI18nState(): I18nClientState | undefined {\n const state = window.__i18n__;\n\n if (!state && !warnedMissingState) {\n warnedMissingState = true;\n\n console.error(\n '[@astroscope/i18n] window.__i18n__ is not defined — translations will not work on the client. ' +\n 'Make sure
|
|
1
|
+
{"version":3,"file":"translate.js","names":[],"sources":["../../src/client/state.ts","../../src/client/translate.ts"],"sourcesContent":["import type { I18nClientState } from '../shared/types.js';\nimport './types.js';\n\nlet warnedMissingState = false;\n\n/**\n * Read the client i18n state injected by the i18n middleware and islands emitter.\n *\n * Returns undefined and logs an actionable error (once) when the state was not\n * injected — without it window.__i18n__ is undefined and translations cannot load.\n */\nexport function getI18nState(): I18nClientState | undefined {\n const state = window.__i18n__;\n\n if (!state && !warnedMissingState) {\n warnedMissingState = true;\n\n console.error(\n '[@astroscope/i18n] window.__i18n__ is not defined — translations will not work on the client. ' +\n 'Make sure the @astroscope/i18n integration is set up and the page is not in its `exclude` list ' +\n '(the locale middleware injects the state) and, for per-island hashes, that the site uses the ' +\n '@astroscope/node adapter.',\n );\n }\n\n return state;\n}\n","import { compileMessage, formatMessageToParts } from '../shared/compiler.js';\nimport { normalizeMeta } from '../shared/meta.js';\nimport { type RichComponents, partsToNodes } from '../shared/rich.js';\nimport type { CompiledTranslation, TranslateFunction, TranslationMeta } from '../shared/types.js';\nimport { getI18nState } from './state.js';\n\nconst cache = new Map<string, CompiledTranslation>();\n\nexport function getLocale(): string {\n return getI18nState()?.locale ?? '';\n}\n\n/**\n * Client-side translate function\n *\n * Reads from window.__i18n__.translations (injected per-chunk).\n * In production, translations are always loaded before component code runs\n * via the chunk loader injected by the Vite plugin.\n *\n * Call patterns:\n * - Development: t('key', 'fallback') or t('key', 'fallback', values)\n * - Production: t('key') or t('key', undefined, values)\n *\n * Falls back to meta.fallback (dev) or key (prod) when translation is missing.\n */\nexport const t: TranslateFunction = ((\n key: string,\n meta?: TranslationMeta | string,\n values?: Record<string, unknown>,\n): string => {\n const normalizedMeta: TranslationMeta = meta ? normalizeMeta(meta) : { fallback: '' };\n\n const state = getI18nState();\n const locale = state?.locale ?? '';\n\n const raw = state?.translations[key];\n\n if (!raw) {\n // translation missing: use fallback (dev) or key (prod)\n if (normalizedMeta.fallback) {\n let compiled = cache.get(`__fallback__${key}`);\n\n if (!compiled) {\n compiled = compileMessage(locale, normalizedMeta.fallback);\n cache.set(`__fallback__${key}`, compiled);\n }\n\n return compiled(values);\n }\n\n return key;\n }\n\n let compiled = cache.get(key);\n\n if (!compiled) {\n compiled = compileMessage(locale, raw);\n cache.set(key, compiled);\n }\n\n return compiled(values);\n}) as TranslateFunction;\n\n/**\n * Client-side rich text translation function\n *\n * Parses MF2 markup syntax and applies component callbacks to wrap content.\n * Works with any JSX runtime (React, Preact, etc.).\n *\n * @param key - Translation key\n * @param meta - Fallback string or meta object with fallback (used as message template)\n * @param components - Map of tag names to component callbacks\n * @param values - Runtime interpolation values (MessageFormat 2)\n *\n * @example\n * ```tsx\n * rich('tos', 'Read our {#link}Terms{/link}', {\n * link: (children) => <a href=\"/tos\">{children}</a>\n * })\n * // Returns: ['Read our ', <a href=\"/tos\">Terms</a>]\n * ```\n */\nexport function rich<T = unknown>(\n key: string,\n meta?: TranslationMeta | string | undefined,\n components?: RichComponents<T> | undefined,\n values?: Record<string, unknown> | undefined,\n): (string | T)[] {\n const normalizedMeta: TranslationMeta = meta ? normalizeMeta(meta) : { fallback: '' };\n\n const state = getI18nState();\n\n const raw = state?.translations[key] || normalizedMeta.fallback || key;\n\n const parts = formatMessageToParts(state?.locale ?? '', raw, values);\n\n return partsToNodes(parts, components ?? {});\n}\n"],"mappings":";;;AAGA,IAAI,qBAAqB;;;;;;;AAQzB,SAAgB,eAA4C;CAC1D,MAAM,QAAQ,OAAO;CAErB,IAAI,CAAC,SAAS,CAAC,oBAAoB;EACjC,qBAAqB;EAErB,QAAQ,MACN,qTAIF;CACF;CAEA,OAAO;AACT;;ACpBA,MAAM,wBAAQ,IAAI,IAAiC;AAEnD,SAAgB,YAAoB;CAClC,OAAO,aAAa,CAAC,EAAE,UAAU;AACnC;;;;;;;;;;;;;;AAeA,MAAa,MACX,KACA,MACA,WACW;CACX,MAAM,iBAAkC,OAAO,cAAc,IAAI,IAAI,EAAE,UAAU,GAAG;CAEpF,MAAM,QAAQ,aAAa;CAC3B,MAAM,SAAS,OAAO,UAAU;CAEhC,MAAM,MAAM,OAAO,aAAa;CAEhC,IAAI,CAAC,KAAK;EAER,IAAI,eAAe,UAAU;GAC3B,IAAI,WAAW,MAAM,IAAI,eAAe,KAAK;GAE7C,IAAI,CAAC,UAAU;IACb,WAAW,eAAe,QAAQ,eAAe,QAAQ;IACzD,MAAM,IAAI,eAAe,OAAO,QAAQ;GAC1C;GAEA,OAAO,SAAS,MAAM;EACxB;EAEA,OAAO;CACT;CAEA,IAAI,WAAW,MAAM,IAAI,GAAG;CAE5B,IAAI,CAAC,UAAU;EACb,WAAW,eAAe,QAAQ,GAAG;EACrC,MAAM,IAAI,KAAK,QAAQ;CACzB;CAEA,OAAO,SAAS,MAAM;AACxB;;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,KACd,KACA,MACA,YACA,QACgB;CAChB,MAAM,iBAAkC,OAAO,cAAc,IAAI,IAAI,EAAE,UAAU,GAAG;CAEpF,MAAM,QAAQ,aAAa;CAE3B,MAAM,MAAM,OAAO,aAAa,QAAQ,eAAe,YAAY;CAEnE,MAAM,QAAQ,qBAAqB,OAAO,UAAU,IAAI,KAAK,MAAM;CAEnE,OAAO,aAAa,OAAO,cAAc,CAAC,CAAC;AAC7C"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { MessageFormat } from "messageformat";
|
|
2
2
|
import { DraftFunctions } from "messageformat/functions";
|
|
3
|
-
|
|
3
|
+
// src/shared/compiler.ts
|
|
4
4
|
const mfOptions = {
|
|
5
5
|
functions: DraftFunctions,
|
|
6
6
|
bidiIsolation: "none"
|
|
@@ -42,7 +42,6 @@ function compileTranslations(locale, raw) {
|
|
|
42
42
|
for (const [key, message] of Object.entries(raw)) compiled[key] = compileMessage(locale, message);
|
|
43
43
|
return compiled;
|
|
44
44
|
}
|
|
45
|
-
//#endregion
|
|
46
45
|
export { compileTranslations as n, formatMessageToParts as r, compileMessage as t };
|
|
47
46
|
|
|
48
|
-
//# sourceMappingURL=compiler-
|
|
47
|
+
//# sourceMappingURL=compiler-K3uHKoFs.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compiler-
|
|
1
|
+
{"version":3,"file":"compiler-K3uHKoFs.js","names":[],"sources":["../src/shared/compiler.ts"],"sourcesContent":["import { MessageFormat, type MessagePart } from 'messageformat';\nimport { DraftFunctions } from 'messageformat/functions';\n\nimport type { CompiledTranslation, CompiledTranslations, RawTranslations } from './types.js';\n\n// enable draft functions for :currency, :date, :datetime, :time, :percent, :unit\n// bidi isolation is disabled: the default wraps string placeholders in invisible\n// U+2068/U+2069 characters that end up in the DOM and copy-paste\nconst mfOptions = { functions: DraftFunctions, bidiIsolation: 'none' as const };\n\nconst cache = new Map<string, MessageFormat<string, string>>();\n\nfunction getOrCreateMessageFormat(locale: string, message: string): MessageFormat<string, string> {\n const cacheKey = `${locale}:${message}`;\n const cached = cache.get(cacheKey);\n\n if (cached) {\n return cached;\n }\n\n const mf = new MessageFormat(locale, message, mfOptions);\n\n cache.set(cacheKey, mf);\n\n return mf;\n}\n\nexport function compileMessage(locale: string, message: string): CompiledTranslation {\n try {\n const mf = getOrCreateMessageFormat(locale, message);\n\n return (values) => mf.format(values);\n } catch {\n // if MF2 parsing fails, return raw message so the problem is visible to the user\n return () => message;\n }\n}\n\n/**\n * Format a message to parts for rich text rendering.\n * Returns an array of MessagePart objects that can be processed\n * to build component trees with markup elements.\n */\nexport function formatMessageToParts(\n locale: string,\n message: string,\n values?: Record<string, unknown> | undefined,\n): MessagePart<string>[] {\n try {\n const mf = getOrCreateMessageFormat(locale, message);\n\n return mf.formatToParts(values);\n } catch {\n // if MF2 parsing fails, return a single literal part with the raw message\n return [{ type: 'literal', value: message }];\n }\n}\n\nexport function compileTranslations(locale: string, raw: RawTranslations): CompiledTranslations {\n const compiled: CompiledTranslations = {};\n\n for (const [key, message] of Object.entries(raw)) {\n compiled[key] = compileMessage(locale, message);\n }\n\n return compiled;\n}\n"],"mappings":";;;AAQA,MAAM,YAAY;CAAE,WAAW;CAAgB,eAAe;AAAgB;AAE9E,MAAM,wBAAQ,IAAI,IAA2C;AAE7D,SAAS,yBAAyB,QAAgB,SAAgD;CAChG,MAAM,WAAW,GAAG,OAAO,GAAG;CAC9B,MAAM,SAAS,MAAM,IAAI,QAAQ;CAEjC,IAAI,QACF,OAAO;CAGT,MAAM,KAAK,IAAI,cAAc,QAAQ,SAAS,SAAS;CAEvD,MAAM,IAAI,UAAU,EAAE;CAEtB,OAAO;AACT;AAEA,SAAgB,eAAe,QAAgB,SAAsC;CACnF,IAAI;EACF,MAAM,KAAK,yBAAyB,QAAQ,OAAO;EAEnD,QAAQ,WAAW,GAAG,OAAO,MAAM;CACrC,QAAQ;EAEN,aAAa;CACf;AACF;;;;;;AAOA,SAAgB,qBACd,QACA,SACA,QACuB;CACvB,IAAI;EAGF,OAFW,yBAAyB,QAAQ,OAEpC,CAAC,CAAC,cAAc,MAAM;CAChC,QAAQ;EAEN,OAAO,CAAC;GAAE,MAAM;GAAW,OAAO;EAAQ,CAAC;CAC7C;AACF;AAEA,SAAgB,oBAAoB,QAAgB,KAA4C;CAC9F,MAAM,WAAiC,CAAC;CAExC,KAAK,MAAM,CAAC,KAAK,YAAY,OAAO,QAAQ,GAAG,GAC7C,SAAS,OAAO,eAAe,QAAQ,OAAO;CAGhD,OAAO;AACT"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
// src/server/context.ts
|
|
3
|
+
const als = new AsyncLocalStorage();
|
|
4
|
+
/**
|
|
5
|
+
* Run a function with i18n context (called by middleware)
|
|
6
|
+
*/
|
|
7
|
+
function runWithContext(context, fn) {
|
|
8
|
+
return als.run(context, fn);
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Get the current i18n context
|
|
12
|
+
*/
|
|
13
|
+
function getContext() {
|
|
14
|
+
return als.getStore() ?? null;
|
|
15
|
+
}
|
|
16
|
+
export { runWithContext as n, getContext as t };
|
|
17
|
+
|
|
18
|
+
//# sourceMappingURL=context-CJvTEa-I.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-CJvTEa-I.js","names":[],"sources":["../src/server/context.ts"],"sourcesContent":["import { AsyncLocalStorage } from 'node:async_hooks';\nimport type { I18nContext } from './types.js';\n\nconst als = new AsyncLocalStorage<I18nContext>();\n\n/**\n * Run a function with i18n context (called by middleware)\n */\nexport function runWithContext<T>(context: I18nContext, fn: () => T): T {\n return als.run(context, fn);\n}\n\n/**\n * Get the current i18n context\n */\nexport function getContext(): I18nContext | null {\n return als.getStore() ?? null;\n}\n"],"mappings":";;AAGA,MAAM,MAAM,IAAI,kBAA+B;;;;AAK/C,SAAgB,eAAkB,SAAsB,IAAgB;CACtE,OAAO,IAAI,IAAI,SAAS,EAAE;AAC5B;;;;AAKA,SAAgB,aAAiC;CAC/C,OAAO,IAAI,SAAS,KAAK;AAC3B"}
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import { n as ExtractedKey, r as ExtractionManifest, t as ChunkManifest } from "../types-
|
|
2
|
-
import { t as ConsistencyCheckLevel } from "../types-
|
|
1
|
+
import { n as ExtractedKey, r as ExtractionManifest, t as ChunkManifest } from "../types-DwM0qsLj.js";
|
|
2
|
+
import { t as ConsistencyCheckLevel } from "../types-ioAI5pu0.js";
|
|
3
|
+
import { ExcludePattern } from "@astroscope/node/excludes";
|
|
3
4
|
import { AstroIntegrationLogger } from "astro";
|
|
4
5
|
import { Plugin } from "vite";
|
|
5
|
-
//#region src/extraction/manifest.d.ts
|
|
6
6
|
/**
|
|
7
7
|
* Get the current extraction manifest (for dev mode live access).
|
|
8
8
|
* This bypasses Vite's module caching to return fresh data.
|
|
9
9
|
*/
|
|
10
10
|
export declare function getManifest(): ExtractionManifest;
|
|
11
|
-
//#endregion
|
|
12
|
-
//#region src/extraction/vite-plugin.d.ts
|
|
13
11
|
type I18nVitePluginOptions = {
|
|
14
12
|
logger: AstroIntegrationLogger;
|
|
15
13
|
consistency: ConsistencyCheckLevel;
|
|
14
|
+
/** the locale middleware's exclude patterns, null for the default */
|
|
15
|
+
exclude: ExcludePattern[] | null;
|
|
16
16
|
};
|
|
17
17
|
/**
|
|
18
18
|
* Vite plugin for i18n extraction, chunk mapping, and loader injection
|
|
@@ -25,6 +25,5 @@ type I18nVitePluginOptions = {
|
|
|
25
25
|
* 5. Injects translation loader into chunks that use t()
|
|
26
26
|
*/
|
|
27
27
|
export declare function i18nVitePlugin(options: I18nVitePluginOptions): Plugin;
|
|
28
|
-
//#endregion
|
|
29
28
|
export type { ChunkManifest, ExtractedKey, ExtractionManifest, I18nVitePluginOptions };
|
|
30
29
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/extraction/manifest.ts","../../src/extraction/vite-plugin.ts"],"mappings":";;;;;;;;;wBA4CgB,eAAe
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/extraction/manifest.ts","../../src/extraction/vite-plugin.ts"],"mappings":";;;;;;;;;wBA4CgB,eAAe;KChBnB;EACV,QAAQ;EACR,aAAa;;EAEb,SAAS;;;;;;;;;;;;wBAuCK,eAAe,SAAS,wBAAwB"}
|
package/dist/extraction/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { n as getManifest } from "../manifest-DK513NbQ.js";
|
|
2
|
+
import { t as i18nVitePlugin } from "../vite-plugin-Yc5NP6bs.js";
|
|
3
3
|
export { getManifest, i18nVitePlugin };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { r as
|
|
3
|
-
|
|
1
|
+
import { r as ExtractionManifest } from "./types-DwM0qsLj.js";
|
|
2
|
+
import { a as TranslationMeta, r as RawTranslations, t as CompiledTranslations } from "./types-DYnR9xP3.js";
|
|
3
|
+
import { APIContext } from "astro";
|
|
4
4
|
/**
|
|
5
5
|
* Fallback behavior when translation is missing
|
|
6
6
|
*/
|
|
@@ -14,17 +14,26 @@ type I18nContext = {
|
|
|
14
14
|
rawTranslations: RawTranslations;
|
|
15
15
|
fallback: FallbackBehavior;
|
|
16
16
|
};
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Resolves a request's locale — from a cookie, the session on `locals`, the url or
|
|
19
|
+
* `detectLocale(request)`. `undefined` falls back to the default locale.
|
|
20
|
+
*/
|
|
21
|
+
type LocaleResolver = (ctx: APIContext) => string | undefined;
|
|
19
22
|
type I18nConfig = {
|
|
20
23
|
locales: string[];
|
|
21
24
|
defaultLocale?: string | undefined;
|
|
22
25
|
fallback?: FallbackBehavior | undefined;
|
|
26
|
+
/**
|
|
27
|
+
* Called by the locale middleware for every request it does not exclude. Without
|
|
28
|
+
* a resolver every request renders in the default locale.
|
|
29
|
+
*/
|
|
30
|
+
locale?: LocaleResolver | undefined;
|
|
23
31
|
};
|
|
24
32
|
type NormalizedConfig = {
|
|
25
33
|
locales: string[];
|
|
26
34
|
defaultLocale: string;
|
|
27
35
|
fallback: FallbackBehavior;
|
|
36
|
+
locale: LocaleResolver | undefined;
|
|
28
37
|
};
|
|
29
38
|
declare class I18nSingleton {
|
|
30
39
|
private config;
|
|
@@ -76,6 +85,5 @@ declare class I18nSingleton {
|
|
|
76
85
|
}[];
|
|
77
86
|
}
|
|
78
87
|
declare const i18n: I18nSingleton;
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
//# sourceMappingURL=i18n-Ca5mVXlq.d.ts.map
|
|
88
|
+
export { I18nContext as a, FallbackBehavior as i, LocaleResolver as n, i18n as r, I18nConfig as t };
|
|
89
|
+
//# sourceMappingURL=i18n-7dnqpAwm.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"i18n-7dnqpAwm.d.ts","names":[],"sources":["../src/server/types.ts","../src/server/i18n.ts"],"mappings":";;;;;;KAKY,oDACuB,aAAa,OAAO;;;;KAK3C;EACV;EAEA,cAAc;EAEd,iBAAiB;EACjB,UAAU;;;;;;KCJA,kBAAkB,KAAK;KAEvB;EACV;EACA;EACA,WAAW;;;;;EAKX,SAAS;;KAGN;EACH;EACA;EACA,UAAU;EACV,QAAQ;;cAGJ;UACI;UAGA;UAGA;UAGA;UAGA;UAGA;UAGA;UAGA;UAGA;;UAGA;UAaA;EAYF,UAAU,QAAQ,aAAa;EAkDrC;EAIA,aAAa;EAQb,gBAAgB,gBAAgB,KAAK;EAqBrC,gBAAgB,iBAAiB;EA8BjC,wBAAwB,iBAAiB;;;;;;EAmBzC,eAAe;EAQf,UAAU,iBAAiB;;;;;;EAS3B,aAAa,gBAAgB,oBAAoB;;;;;;EA4CjD,qBAAqB,iBAAiB;;;;;EAoBtC,MAAM;;EAmBN,mBAAmB;IAAqB;IAAgB;;;cAK7C,MAAI"}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { t as getGlobalState } from "./manifest-
|
|
2
|
-
import { n as compileTranslations } from "./compiler-
|
|
1
|
+
import { t as getGlobalState } from "./manifest-DK513NbQ.js";
|
|
2
|
+
import { n as compileTranslations } from "./compiler-K3uHKoFs.js";
|
|
3
3
|
import { createObservableGauge } from "@astroscope/node/telemetry";
|
|
4
4
|
import { createHash } from "node:crypto";
|
|
5
|
-
|
|
6
|
-
//#region src/extraction/hash.ts
|
|
5
|
+
// src/extraction/hash.ts
|
|
7
6
|
/**
|
|
8
7
|
* Hash a string to a short hash (for cache busting)
|
|
9
8
|
*/
|
|
@@ -31,8 +30,7 @@ function computeAllChunkHashes(translations, manifest) {
|
|
|
31
30
|
for (const [chunkId, keys] of Object.entries(manifest)) hashes[chunkId] = computeChunkHash(translations, keys);
|
|
32
31
|
return hashes;
|
|
33
32
|
}
|
|
34
|
-
|
|
35
|
-
//#region src/server/i18n.ts
|
|
33
|
+
// src/server/i18n.ts
|
|
36
34
|
var I18nSingleton = class {
|
|
37
35
|
config = null;
|
|
38
36
|
rawCache = /* @__PURE__ */ new Map();
|
|
@@ -72,7 +70,8 @@ var I18nSingleton = class {
|
|
|
72
70
|
this.config = {
|
|
73
71
|
locales: config.locales,
|
|
74
72
|
defaultLocale: config.defaultLocale ?? config.locales[0],
|
|
75
|
-
fallback: config.fallback ?? "fallback"
|
|
73
|
+
fallback: config.fallback ?? "fallback",
|
|
74
|
+
locale: config.locale
|
|
76
75
|
};
|
|
77
76
|
if (this.manifestGetter) return;
|
|
78
77
|
const m = await import("virtual:@astroscope/i18n/manifest");
|
|
@@ -203,22 +202,6 @@ createObservableGauge("astro.i18n.translations.age", {
|
|
|
203
202
|
}, (result) => {
|
|
204
203
|
for (const { locale, seconds } of i18n.getTranslationsAge()) result.observe(seconds, { "astro.i18n.locale": locale });
|
|
205
204
|
});
|
|
206
|
-
|
|
207
|
-
//#region src/server/context.ts
|
|
208
|
-
const als = new AsyncLocalStorage();
|
|
209
|
-
/**
|
|
210
|
-
* Run a function with i18n context (called by middleware)
|
|
211
|
-
*/
|
|
212
|
-
function runWithContext(context, fn) {
|
|
213
|
-
return als.run(context, fn);
|
|
214
|
-
}
|
|
215
|
-
/**
|
|
216
|
-
* Get the current i18n context
|
|
217
|
-
*/
|
|
218
|
-
function getContext() {
|
|
219
|
-
return als.getStore() ?? null;
|
|
220
|
-
}
|
|
221
|
-
//#endregion
|
|
222
|
-
export { runWithContext as n, i18n as r, getContext as t };
|
|
205
|
+
export { i18n as t };
|
|
223
206
|
|
|
224
|
-
//# sourceMappingURL=
|
|
207
|
+
//# sourceMappingURL=i18n-f7qdMTwm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"i18n-f7qdMTwm.js","names":[],"sources":["../src/extraction/hash.ts","../src/server/i18n.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport type { RawTranslations } from '../shared/types.js';\nimport type { ChunkManifest } from './types.js';\n\n/**\n * Hash a string to a short hash (for cache busting)\n */\nfunction hashString(str: string): string {\n return createHash('sha256').update(str).digest('hex').slice(0, 8);\n}\n\n// code-unit order (what the default sort does): the order feeds the hash, so it must not depend on icu locale data\nfunction compareKeys(a: string, b: string): number {\n if (a === b) return 0;\n\n return a < b ? -1 : 1;\n}\n\n/**\n * Compute hash for chunk translations\n */\nexport function computeChunkHash(translations: RawTranslations, keys: string[]): string {\n const relevantTranslations: RawTranslations = {};\n const sortedKeys = keys.toSorted(compareKeys);\n\n for (const key of sortedKeys) {\n if (translations[key] !== undefined) {\n relevantTranslations[key] = translations[key];\n }\n }\n\n return hashString(JSON.stringify(relevantTranslations));\n}\n\n/**\n * Compute hashes for all chunks in manifest\n */\nexport function computeAllChunkHashes(translations: RawTranslations, manifest: ChunkManifest): Record<string, string> {\n const hashes: Record<string, string> = {};\n\n for (const [chunkId, keys] of Object.entries(manifest)) {\n hashes[chunkId] = computeChunkHash(translations, keys);\n }\n\n return hashes;\n}\n","import { createObservableGauge } from '@astroscope/node/telemetry';\nimport type { APIContext } from 'astro';\nimport { computeAllChunkHashes } from '../extraction/hash.js';\nimport { getGlobalState } from '../extraction/manifest.js';\nimport type { ExtractionManifest } from '../extraction/types.js';\nimport { compileTranslations } from '../shared/compiler.js';\nimport type { CompiledTranslations, RawTranslations } from '../shared/types.js';\nimport type { FallbackBehavior } from './types.js';\n\n/**\n * Resolves a request's locale — from a cookie, the session on `locals`, the url or\n * `detectLocale(request)`. `undefined` falls back to the default locale.\n */\nexport type LocaleResolver = (ctx: APIContext) => string | undefined;\n\nexport type I18nConfig = {\n locales: string[];\n defaultLocale?: string | undefined;\n fallback?: FallbackBehavior | undefined;\n /**\n * Called by the locale middleware for every request it does not exclude. Without\n * a resolver every request renders in the default locale.\n */\n locale?: LocaleResolver | undefined;\n};\n\ntype NormalizedConfig = {\n locales: string[];\n defaultLocale: string;\n fallback: FallbackBehavior;\n locale: LocaleResolver | undefined;\n};\n\nclass I18nSingleton {\n private config: NormalizedConfig | null = null;\n\n // locale -> raw translation strings (before MessageFormat compilation)\n private rawCache = new Map<string, RawTranslations>();\n\n // locale -> raw translations merged with manifest fallbacks\n private mergedCache = new Map<string, RawTranslations>();\n\n // locale -> compiled MessageFormat 2 functions\n private compiledCache = new Map<string, CompiledTranslations>();\n\n // locale -> chunk name -> content hash (for cache busting)\n private hashCache = new Map<string, Record<string, string>>();\n\n // \"locale:chunkName\" -> encoded chunk response body\n private chunkCache = new Map<string, Uint8Array>();\n\n // locale -> epoch ms of the last setTranslations, for the age gauge\n private updatedAt = new Map<string, number>();\n\n // manifest getter, set on configure() via the virtual module (live data in dev mode)\n private manifestGetter: (() => ExtractionManifest) | null = null;\n\n // tracks the manifest version to detect when derived caches need invalidation\n private manifestVersion = 0;\n\n /** Invalidate derived caches when the extraction manifest changed (dev HMR). */\n private invalidateIfManifestChanged(): void {\n const { version } = getGlobalState();\n\n if (version === this.manifestVersion) {\n return;\n }\n\n this.manifestVersion = version;\n this.mergedCache.clear();\n this.compiledCache.clear();\n this.chunkCache.clear();\n }\n\n private resolveLocales(locales?: string | string[] | undefined): string[] {\n if (!locales) {\n return this.config?.locales ?? [];\n }\n\n if (typeof locales === 'string') {\n return [locales];\n }\n\n return locales;\n }\n\n async configure(config: I18nConfig): Promise<void> {\n if (!config.locales?.length) {\n throw new Error('i18n.configure(): locales array is required and must not be empty');\n }\n\n for (const locale of config.locales) {\n if (!locale) {\n throw new Error('i18n.configure(): locale cannot be empty');\n }\n\n if (locale !== locale.trim()) {\n throw new Error(`i18n.configure(): locale \"${locale}\" has leading or trailing whitespace`);\n }\n }\n\n if (new Set(config.locales).size !== config.locales.length) {\n throw new Error('i18n.configure(): locales array contains duplicates');\n }\n\n if (config.defaultLocale !== undefined) {\n if (!config.defaultLocale) {\n throw new Error('i18n.configure(): defaultLocale cannot be empty');\n }\n\n if (config.defaultLocale !== config.defaultLocale.trim()) {\n throw new Error(`i18n.configure(): defaultLocale \"${config.defaultLocale}\" has leading or trailing whitespace`);\n }\n\n if (!config.locales.includes(config.defaultLocale)) {\n throw new Error(`i18n.configure(): defaultLocale \"${config.defaultLocale}\" is not in locales array`);\n }\n }\n\n this.config = {\n locales: config.locales,\n defaultLocale: config.defaultLocale ?? config.locales[0]!,\n fallback: config.fallback ?? 'fallback',\n locale: config.locale,\n };\n\n // resolved lazily so importing the singleton needs no virtual module\n if (this.manifestGetter) {\n return;\n }\n\n const m = await import('virtual:@astroscope/i18n/manifest');\n\n this.manifestGetter = m.getManifest;\n }\n\n isConfigured(): boolean {\n return this.config !== null;\n }\n\n getConfig(): NormalizedConfig {\n if (!this.config) {\n throw new Error('i18n not configured. Call i18n.configure() first.');\n }\n\n return this.config;\n }\n\n setTranslations(locale: string, raw: RawTranslations): void {\n this.rawCache.set(locale, raw);\n this.updatedAt.set(locale, Date.now());\n this.mergedCache.delete(locale);\n this.compiledCache.delete(locale);\n this.hashCache.delete(locale);\n\n for (const key of this.chunkCache.keys()) {\n if (key.startsWith(`${locale}:`)) {\n this.chunkCache.delete(key);\n }\n }\n\n // recompute hashes if chunk manifest exists (production only)\n const { chunks } = this.getManifest();\n\n if (Object.keys(chunks).length > 0) {\n this.hashCache.set(locale, computeAllChunkHashes(raw, chunks));\n }\n }\n\n getTranslations(locale: string): RawTranslations {\n this.invalidateIfManifestChanged();\n\n const cached = this.mergedCache.get(locale);\n\n if (cached) {\n return cached;\n }\n\n const raw = this.rawCache.get(locale) ?? {};\n const manifest = this.manifestGetter?.() ?? { keys: [], chunks: {}, scripts: [] };\n\n if (manifest.keys.length === 0) {\n return raw;\n }\n\n // merge fallbacks for keys that don't have translations\n const withFallbacks: RawTranslations = { ...raw };\n\n for (const { key, meta } of manifest.keys) {\n if (!Object.hasOwn(withFallbacks, key) && meta.fallback) {\n withFallbacks[key] = meta.fallback;\n }\n }\n\n this.mergedCache.set(locale, withFallbacks);\n\n return withFallbacks;\n }\n\n getCompiledTranslations(locale: string): CompiledTranslations {\n const cached = this.compiledCache.get(locale);\n\n if (cached) {\n return cached;\n }\n\n const compiled = compileTranslations(locale, this.getTranslations(locale));\n\n this.compiledCache.set(locale, compiled);\n\n return compiled;\n }\n\n /**\n * Get the extraction manifest (keys + chunks).\n * - keys: translation keys extracted from codebase with metadata\n * - chunks: mapping of chunk names to their translation keys\n */\n getManifest(): ExtractionManifest {\n if (!this.manifestGetter) {\n throw new Error('i18n manifest not initialized. Call i18n.configure() first.');\n }\n\n return this.manifestGetter();\n }\n\n getHashes(locale: string): Record<string, string> {\n return this.hashCache.get(locale) ?? {};\n }\n\n /**\n * Get the encoded chunk response body for a locale/chunk combination.\n * Cached to avoid repeated JSON.stringify and encoding on each request.\n * Returns undefined if chunk not found in manifest.\n */\n getChunkBody(locale: string, chunkName: string): Uint8Array | undefined {\n const keys = this.getManifest().chunks[chunkName];\n\n if (!keys) {\n return undefined;\n }\n\n const cacheKey = `${locale}:${chunkName}`;\n const cached = this.chunkCache.get(cacheKey);\n\n if (cached) {\n return cached;\n }\n\n const translations = this.getTranslations(locale);\n const chunkTranslations: RawTranslations = {};\n\n for (const key of keys) {\n const translation = translations[key];\n\n if (translation) {\n chunkTranslations[key] = translation;\n }\n }\n\n const js = `/* @astroscope/i18n chunk: ${chunkName} */\n(function() {\n var i = window.__i18n__;\n if (i) Object.assign(i.translations, ${JSON.stringify(chunkTranslations)});\n})();\n`;\n\n const body = new TextEncoder().encode(js);\n\n this.chunkCache.set(cacheKey, body);\n\n return body;\n }\n\n /**\n * Hashes for chunks reachable from astro `<script>` entries. Script modules are\n * not islands, so the islands emitter never covers them — the middleware\n * bootstraps these few into the client state on every html response.\n */\n getScriptChunkHashes(locale: string): Record<string, string> {\n const { scripts } = this.getManifest();\n const hashes = this.getHashes(locale);\n const result: Record<string, string> = {};\n\n for (const chunk of scripts) {\n const hash = hashes[chunk];\n\n if (hash) {\n result[chunk] = hash;\n }\n }\n\n return result;\n }\n\n /**\n * Clear translations for specified locale(s).\n * @param locales - Locale(s) to clear, or all if not specified\n */\n clear(locales?: string | string[] | undefined): void {\n const targets = this.resolveLocales(locales);\n\n for (const locale of targets) {\n this.rawCache.delete(locale);\n this.updatedAt.delete(locale);\n this.mergedCache.delete(locale);\n this.compiledCache.delete(locale);\n this.hashCache.delete(locale);\n\n for (const key of this.chunkCache.keys()) {\n if (key.startsWith(`${locale}:`)) {\n this.chunkCache.delete(key);\n }\n }\n }\n }\n\n /** seconds since each locale's translations were last set */\n getTranslationsAge(now = Date.now()): { locale: string; seconds: number }[] {\n return [...this.updatedAt].map(([locale, at]) => ({ locale, seconds: (now - at) / 1000 }));\n }\n}\n\nexport const i18n = new I18nSingleton();\n\n// the one signal for a stalled refresh loop: an app that reloads translations\n// periodically shows a sawtooth here, a stuck one a line going up\ncreateObservableGauge(\n 'astro.i18n.translations.age',\n { scope: '@astroscope/i18n', description: 'Seconds since the translations of a locale were last set', unit: 's' },\n (result) => {\n for (const { locale, seconds } of i18n.getTranslationsAge()) {\n result.observe(seconds, { 'astro.i18n.locale': locale });\n }\n },\n);\n"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,76 +1,26 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as
|
|
3
|
-
import {
|
|
4
|
-
import { n as
|
|
5
|
-
import {
|
|
6
|
-
import { APIContext, AstroIntegration, MiddlewareHandler } from "astro";
|
|
7
|
-
//#region src/integration/integration.d.ts
|
|
1
|
+
import { n as I18nOptions } from "./types-ioAI5pu0.js";
|
|
2
|
+
import { i as FallbackBehavior, n as LocaleResolver, r as i18n, t as I18nConfig } from "./i18n-7dnqpAwm.js";
|
|
3
|
+
import { a as TranslationMeta, r as RawTranslations } from "./types-DYnR9xP3.js";
|
|
4
|
+
import { n as ExtractedKey, r as ExtractionManifest } from "./types-DwM0qsLj.js";
|
|
5
|
+
import { AstroIntegration } from "astro";
|
|
8
6
|
/**
|
|
9
7
|
* Astro integration for i18n with automatic per-chunk translation loading.
|
|
10
8
|
*
|
|
11
9
|
* Registers the vite plugin for translation key extraction, chunk mapping and
|
|
12
|
-
* loader injection, and injects the chunk middleware serving
|
|
13
|
-
* translation chunks
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
10
|
+
* loader injection, and injects both middlewares: the chunk middleware serving
|
|
11
|
+
* `/_i18n/` translation chunks ahead of the project's own middleware, and the
|
|
12
|
+
* locale middleware after it, so the locale resolver from `i18n.configure` sees
|
|
13
|
+
* what a session middleware put on `locals`. Client state delivery and
|
|
14
|
+
* translation preloading happen at runtime: the locale middleware bootstraps
|
|
15
|
+
* `window.__i18n__`, and the islands emitter (via `@astroscope/node`) slices
|
|
16
|
+
* hashes and preloads translation chunks per island — plain `client:*`
|
|
17
|
+
* directives, no forks.
|
|
17
18
|
*/
|
|
18
19
|
export default function i18nIntegration(options?: I18nOptions): AstroIntegration;
|
|
19
|
-
//#endregion
|
|
20
|
-
//#region src/server/middleware.d.ts
|
|
21
|
-
type I18nMiddlewareOptions = {
|
|
22
|
-
locale: (ctx: APIContext) => string;
|
|
23
|
-
/**
|
|
24
|
-
* Patterns to exclude from locale context setup.
|
|
25
|
-
* Defaults to RECOMMENDED_EXCLUDES if not provided.
|
|
26
|
-
*
|
|
27
|
-
* @example
|
|
28
|
-
* ```ts
|
|
29
|
-
* import { RECOMMENDED_EXCLUDES } from '@astroscope/node/excludes';
|
|
30
|
-
*
|
|
31
|
-
* // extend defaults
|
|
32
|
-
* createI18nMiddleware({
|
|
33
|
-
* locale: (ctx) => ctx.locals.session?.locale ?? 'en',
|
|
34
|
-
* exclude: [...RECOMMENDED_EXCLUDES, { exact: '/health' }],
|
|
35
|
-
* })
|
|
36
|
-
*
|
|
37
|
-
* // disable excludes entirely
|
|
38
|
-
* createI18nMiddleware({
|
|
39
|
-
* locale: (ctx) => ctx.locals.session?.locale ?? 'en',
|
|
40
|
-
* exclude: [],
|
|
41
|
-
* })
|
|
42
|
-
* ```
|
|
43
|
-
*/
|
|
44
|
-
exclude?: ExcludePattern[] | ((context: APIContext) => boolean) | undefined;
|
|
45
|
-
};
|
|
46
|
-
/**
|
|
47
|
-
* Create the i18n locale middleware.
|
|
48
|
-
*
|
|
49
|
-
* Sets up the request context with locale and translations for `t()`, injects
|
|
50
|
-
* the client i18n state into html responses, and records the locale for the
|
|
51
|
-
* islands emitter. Place this after session middleware if your locale detection
|
|
52
|
-
* depends on session/cookies.
|
|
53
|
-
*
|
|
54
|
-
* @example
|
|
55
|
-
* ```typescript
|
|
56
|
-
* import { createI18nMiddleware, detectLocale, i18n } from '@astroscope/i18n';
|
|
57
|
-
*
|
|
58
|
-
* export const i18nMiddleware = createI18nMiddleware({
|
|
59
|
-
* locale: (ctx) =>
|
|
60
|
-
* ctx.cookies.get('locale')?.value ??
|
|
61
|
-
* detectLocale(ctx.request) ??
|
|
62
|
-
* i18n.getConfig().defaultLocale,
|
|
63
|
-
* });
|
|
64
|
-
* ```
|
|
65
|
-
*/
|
|
66
|
-
export declare function createI18nMiddleware(options: I18nMiddlewareOptions): MiddlewareHandler;
|
|
67
|
-
//#endregion
|
|
68
|
-
//#region src/server/locale.d.ts
|
|
69
20
|
/**
|
|
70
21
|
* Detect locale from Accept-Language header.
|
|
71
22
|
* Returns best match from configured locales, or undefined if none match.
|
|
72
23
|
*/
|
|
73
24
|
export declare function detectLocale(request: Request): string | undefined;
|
|
74
|
-
|
|
75
|
-
export { type ExtractedKey, type ExtractionManifest, type FallbackBehavior, type I18nConfig, type I18nMiddlewareOptions, type RawTranslations, type TranslationMeta, i18n };
|
|
25
|
+
export { type ExtractedKey, type ExtractionManifest, type FallbackBehavior, type I18nConfig, type I18nOptions, type LocaleResolver, type RawTranslations, type TranslationMeta, i18n };
|
|
76
26
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/integration/integration.ts","../src/server/
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/integration/integration.ts","../src/server/locale.ts"],"mappings":";;;;;;;;;;;;;;;;;;wBAiBwB,gBAAgB,UAAS,cAAmB;;;;;wBCXpD,aAAa,SAAS"}
|