@astroscope/i18n 0.2.1 → 0.2.3
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/dist/{babel-plugin-K2K4ZPM5.js → babel-plugin-GOB6FMZ2.js} +3 -2
- package/dist/{chunk-EO4FUQXR.js → chunk-4IUQAJ6R.js} +180 -47
- package/dist/{chunk-RHWEV4L2.js → chunk-ZKXMIGSF.js} +39 -0
- package/dist/client/directives/visible.js +1 -3
- package/dist/extraction/index.js +1 -1
- package/dist/{i18n-mTHTcD8K.d.ts → i18n-BBAAVYz_.d.ts} +7 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +14 -20
- package/dist/internal.d.ts +2 -2
- package/dist/internal.js +1 -1
- package/dist/server/t.js +1 -1
- package/package.json +3 -1
|
@@ -103,10 +103,11 @@ function i18nExtractPlugin({ types: t }) {
|
|
|
103
103
|
const meta = args.length >= 2 && t.isExpression(args[1]) ? extractMeta(t, args[1], warn) : { fallback: "" };
|
|
104
104
|
state.opts.onKeyExtracted({ key, meta, file, line });
|
|
105
105
|
if (state.opts.stripFallbacks && args.length >= 2) {
|
|
106
|
+
const firstArg = args[0];
|
|
106
107
|
if (args.length === 2) {
|
|
107
|
-
path.node.arguments = [
|
|
108
|
+
path.node.arguments = [firstArg];
|
|
108
109
|
} else {
|
|
109
|
-
path.node.arguments = [
|
|
110
|
+
path.node.arguments = [firstArg, t.identifier("undefined"), args[2]];
|
|
110
111
|
}
|
|
111
112
|
}
|
|
112
113
|
}
|
|
@@ -29,31 +29,193 @@ function getManifest() {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
// src/extraction/vite-plugin.ts
|
|
32
|
-
import
|
|
32
|
+
import fs2 from "fs";
|
|
33
33
|
import path2 from "path";
|
|
34
34
|
import MagicString from "magic-string";
|
|
35
|
+
|
|
36
|
+
// src/extraction/extract.ts
|
|
37
|
+
var TS_EXTENSIONS = [".ts", ".tsx"];
|
|
38
|
+
var JSX_EXTENSIONS = [".jsx", ".tsx"];
|
|
39
|
+
var JS_EXTENSIONS = [".js", ".jsx"];
|
|
40
|
+
var BABEL_EXTENSIONS = [...TS_EXTENSIONS, ...JS_EXTENSIONS];
|
|
41
|
+
var ALL_EXTENSIONS = [...BABEL_EXTENSIONS, ".astro"];
|
|
42
|
+
async function extractKeysFromFile(options) {
|
|
43
|
+
const { filename, code, logger, stripFallbacks = false, onKeyExtracted } = options;
|
|
44
|
+
const keys = [];
|
|
45
|
+
const [{ transformAsync }, { i18nExtractPlugin }] = await Promise.all([
|
|
46
|
+
import("@babel/core"),
|
|
47
|
+
import("./babel-plugin-GOB6FMZ2.js")
|
|
48
|
+
]);
|
|
49
|
+
const isTypeScript = TS_EXTENSIONS.some((ext) => filename.endsWith(ext)) || filename.endsWith(".astro");
|
|
50
|
+
const isJSX = JSX_EXTENSIONS.some((ext) => filename.endsWith(ext));
|
|
51
|
+
const result = await transformAsync(code, {
|
|
52
|
+
filename,
|
|
53
|
+
sourceMaps: stripFallbacks,
|
|
54
|
+
// only need source maps when transforming
|
|
55
|
+
babelrc: false,
|
|
56
|
+
configFile: false,
|
|
57
|
+
parserOpts: {
|
|
58
|
+
plugins: [...isTypeScript ? ["typescript"] : [], ...isJSX ? ["jsx"] : []]
|
|
59
|
+
},
|
|
60
|
+
plugins: [
|
|
61
|
+
[
|
|
62
|
+
i18nExtractPlugin,
|
|
63
|
+
{
|
|
64
|
+
stripFallbacks,
|
|
65
|
+
onKeyExtracted: (key) => {
|
|
66
|
+
keys.push(key);
|
|
67
|
+
onKeyExtracted?.(key);
|
|
68
|
+
},
|
|
69
|
+
logger
|
|
70
|
+
}
|
|
71
|
+
]
|
|
72
|
+
]
|
|
73
|
+
});
|
|
74
|
+
return {
|
|
75
|
+
keys,
|
|
76
|
+
code: result?.code ?? null,
|
|
77
|
+
map: result?.map
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// src/extraction/key-store.ts
|
|
82
|
+
var KeyStore = class {
|
|
83
|
+
constructor(logger) {
|
|
84
|
+
this.logger = logger;
|
|
85
|
+
}
|
|
86
|
+
extractedKeys = [];
|
|
87
|
+
fileToKeys = /* @__PURE__ */ new Map();
|
|
88
|
+
filesWithI18n = /* @__PURE__ */ new Set();
|
|
89
|
+
/**
|
|
90
|
+
* Add keys for a file, replacing any existing keys for that file.
|
|
91
|
+
*/
|
|
92
|
+
addFileKeys(filename, keys) {
|
|
93
|
+
const oldKeys = this.fileToKeys.get(filename);
|
|
94
|
+
const newKeyStrings = keys.map((k) => k.key);
|
|
95
|
+
if (oldKeys) {
|
|
96
|
+
const added = newKeyStrings.filter((k) => !oldKeys.includes(k));
|
|
97
|
+
const removed = oldKeys.filter((k) => !newKeyStrings.includes(k));
|
|
98
|
+
const file = filename.split("/").pop();
|
|
99
|
+
if (added.length > 0 || removed.length > 0) {
|
|
100
|
+
const parts = [];
|
|
101
|
+
if (added.length > 0) parts.push(`+${added.length}`);
|
|
102
|
+
if (removed.length > 0) parts.push(`-${removed.length}`);
|
|
103
|
+
this.logger.info(`hmr: ${parts.join(" ")} key(s) in ${file}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (oldKeys) {
|
|
107
|
+
for (let i = this.extractedKeys.length - 1; i >= 0; i--) {
|
|
108
|
+
if (this.extractedKeys[i]?.file === filename) {
|
|
109
|
+
this.extractedKeys.splice(i, 1);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
this.filesWithI18n.add(filename);
|
|
114
|
+
if (keys.length > 0) {
|
|
115
|
+
this.extractedKeys.push(...keys);
|
|
116
|
+
}
|
|
117
|
+
this.fileToKeys.set(filename, newKeyStrings);
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Get count of unique keys.
|
|
121
|
+
*/
|
|
122
|
+
get uniqueKeyCount() {
|
|
123
|
+
return new Set(this.extractedKeys.map((k) => k.key)).size;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Merge another store's data into this one.
|
|
127
|
+
*/
|
|
128
|
+
merge(other) {
|
|
129
|
+
for (const [file, keys] of other.fileToKeys) {
|
|
130
|
+
this.fileToKeys.set(file, keys);
|
|
131
|
+
}
|
|
132
|
+
for (const file of other.filesWithI18n) {
|
|
133
|
+
this.filesWithI18n.add(file);
|
|
134
|
+
}
|
|
135
|
+
this.extractedKeys.push(...other.extractedKeys);
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
// src/extraction/scan.ts
|
|
140
|
+
import fs from "fs";
|
|
141
|
+
import { glob } from "glob";
|
|
142
|
+
var GLOB_PATTERN = `src/**/*.{${ALL_EXTENSIONS.map((e) => e.slice(1)).join(",")}}`;
|
|
143
|
+
async function compileAstro(code, filename) {
|
|
144
|
+
try {
|
|
145
|
+
const { transform } = await import("@astrojs/compiler");
|
|
146
|
+
const result = await transform(code, { filename });
|
|
147
|
+
return result.code;
|
|
148
|
+
} catch {
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
async function scan(projectRoot, logger) {
|
|
153
|
+
const store = new KeyStore(logger);
|
|
154
|
+
const files = await glob(GLOB_PATTERN, {
|
|
155
|
+
cwd: projectRoot,
|
|
156
|
+
absolute: true,
|
|
157
|
+
ignore: ["**/node_modules/**"]
|
|
158
|
+
});
|
|
159
|
+
const results = await Promise.all(
|
|
160
|
+
files.map(async (file) => {
|
|
161
|
+
let code = await fs.promises.readFile(file, "utf-8");
|
|
162
|
+
if (!code.includes("@astroscope/i18n/t")) {
|
|
163
|
+
return { file, keys: null };
|
|
164
|
+
}
|
|
165
|
+
if (file.endsWith(".astro")) {
|
|
166
|
+
const compiled = await compileAstro(code, file);
|
|
167
|
+
if (!compiled) {
|
|
168
|
+
return { file, keys: null };
|
|
169
|
+
}
|
|
170
|
+
code = compiled;
|
|
171
|
+
}
|
|
172
|
+
const result = await extractKeysFromFile({
|
|
173
|
+
filename: file,
|
|
174
|
+
code,
|
|
175
|
+
logger,
|
|
176
|
+
stripFallbacks: false
|
|
177
|
+
});
|
|
178
|
+
return { file, keys: result.keys.length > 0 ? result.keys : null };
|
|
179
|
+
})
|
|
180
|
+
);
|
|
181
|
+
for (const { file, keys } of results) {
|
|
182
|
+
if (keys) {
|
|
183
|
+
store.addFileKeys(file, keys);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return store;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// src/extraction/vite-plugin.ts
|
|
35
190
|
var VIRTUAL_MODULE_ID = "virtual:@astroscope/i18n/manifest";
|
|
36
191
|
var isVirtualModuleId = (id) => id === VIRTUAL_MODULE_ID || id.startsWith(`${VIRTUAL_MODULE_ID}?`);
|
|
37
192
|
var RESOLVED_VIRTUAL_MODULE_ID = `\0${VIRTUAL_MODULE_ID}`;
|
|
38
193
|
var isResolvedVirtualModuleId = (id) => id === RESOLVED_VIRTUAL_MODULE_ID || id.startsWith(`\0${VIRTUAL_MODULE_ID}?`);
|
|
39
194
|
var MANIFEST_FILE_NAME = "i18n-manifest.json";
|
|
40
|
-
var INCLUDE_EXTENSIONS = [".ts", ".tsx", ".js", ".jsx", ".astro"];
|
|
41
195
|
function i18nVitePlugin(options) {
|
|
42
196
|
const { logger } = options;
|
|
43
|
-
const
|
|
44
|
-
const fileToKeys = /* @__PURE__ */ new Map();
|
|
45
|
-
const filesWithI18n = /* @__PURE__ */ new Set();
|
|
197
|
+
const store = new KeyStore(logger);
|
|
46
198
|
let isBuild = false;
|
|
47
199
|
let isSSR = false;
|
|
200
|
+
let projectRoot = "";
|
|
48
201
|
return {
|
|
49
202
|
name: "@astroscope/i18n/extract",
|
|
50
203
|
enforce: "pre",
|
|
51
204
|
configResolved(config) {
|
|
52
205
|
const state = getGlobalState();
|
|
53
206
|
state.projectRoot = config.root;
|
|
54
|
-
state.extractedKeys = extractedKeys;
|
|
207
|
+
state.extractedKeys = store.extractedKeys;
|
|
55
208
|
isBuild = config.command === "build";
|
|
56
209
|
isSSR = !!config.build.ssr;
|
|
210
|
+
projectRoot = config.root;
|
|
211
|
+
},
|
|
212
|
+
async configureServer(server) {
|
|
213
|
+
if (server.config.isProduction || isBuild) {
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
const result = await scan(projectRoot, logger);
|
|
217
|
+
store.merge(result);
|
|
218
|
+
logger.info(`dev mode: scanned ${result.fileToKeys.size} files, found ${result.uniqueKeyCount} keys`);
|
|
57
219
|
},
|
|
58
220
|
resolveId(id) {
|
|
59
221
|
if (isVirtualModuleId(id)) {
|
|
@@ -83,7 +245,7 @@ export function getManifest() { return _getManifest(); }
|
|
|
83
245
|
}
|
|
84
246
|
},
|
|
85
247
|
async transform(code, filename) {
|
|
86
|
-
if (!
|
|
248
|
+
if (!ALL_EXTENSIONS.some((ext) => filename.endsWith(ext))) {
|
|
87
249
|
return null;
|
|
88
250
|
}
|
|
89
251
|
if (filename.includes("node_modules")) {
|
|
@@ -92,43 +254,14 @@ export function getManifest() { return _getManifest(); }
|
|
|
92
254
|
if (!code.includes("@astroscope/i18n/t")) {
|
|
93
255
|
return null;
|
|
94
256
|
}
|
|
95
|
-
|
|
96
|
-
const fileKeys = [];
|
|
97
|
-
const [{ transformAsync }, { i18nExtractPlugin }] = await Promise.all([
|
|
98
|
-
import("@babel/core"),
|
|
99
|
-
import("./babel-plugin-K2K4ZPM5.js")
|
|
100
|
-
]);
|
|
101
|
-
const result = await transformAsync(code, {
|
|
257
|
+
const result = await extractKeysFromFile({
|
|
102
258
|
filename,
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
parserOpts: {
|
|
107
|
-
plugins: [
|
|
108
|
-
...filename.endsWith(".ts") || filename.endsWith(".tsx") ? ["typescript"] : [],
|
|
109
|
-
...filename.endsWith(".jsx") || filename.endsWith(".tsx") ? ["jsx"] : []
|
|
110
|
-
]
|
|
111
|
-
},
|
|
112
|
-
plugins: [
|
|
113
|
-
[
|
|
114
|
-
i18nExtractPlugin,
|
|
115
|
-
{
|
|
116
|
-
stripFallbacks: isBuild,
|
|
117
|
-
// only strip in production
|
|
118
|
-
onKeyExtracted: (key) => fileKeys.push(key),
|
|
119
|
-
logger
|
|
120
|
-
}
|
|
121
|
-
]
|
|
122
|
-
]
|
|
259
|
+
code,
|
|
260
|
+
logger,
|
|
261
|
+
stripFallbacks: isBuild
|
|
123
262
|
});
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
fileToKeys.set(
|
|
127
|
-
filename,
|
|
128
|
-
fileKeys.map((k) => k.key)
|
|
129
|
-
);
|
|
130
|
-
}
|
|
131
|
-
if (isBuild && result?.code) {
|
|
263
|
+
store.addFileKeys(filename, result.keys);
|
|
264
|
+
if (isBuild && result.code) {
|
|
132
265
|
return { code: result.code, map: result.map };
|
|
133
266
|
}
|
|
134
267
|
return null;
|
|
@@ -145,9 +278,9 @@ export function getManifest() { return _getManifest(); }
|
|
|
145
278
|
const keys = /* @__PURE__ */ new Set();
|
|
146
279
|
let hasI18nModule = false;
|
|
147
280
|
for (const moduleId of chunk.moduleIds) {
|
|
148
|
-
const moduleKeys = fileToKeys.get(moduleId);
|
|
281
|
+
const moduleKeys = store.fileToKeys.get(moduleId);
|
|
149
282
|
moduleKeys?.forEach((k) => keys.add(k));
|
|
150
|
-
if (filesWithI18n.has(moduleId)) {
|
|
283
|
+
if (store.filesWithI18n.has(moduleId)) {
|
|
151
284
|
hasI18nModule = true;
|
|
152
285
|
}
|
|
153
286
|
}
|
|
@@ -202,14 +335,14 @@ export function getManifest() { return _getManifest(); }
|
|
|
202
335
|
chunk.map = s.generateMap({ hires: true });
|
|
203
336
|
}
|
|
204
337
|
}
|
|
205
|
-
logger.info(`manifest: ${Object.keys(state.chunkManifest).length} chunks, ${
|
|
338
|
+
logger.info(`manifest: ${Object.keys(state.chunkManifest).length} chunks, ${store.uniqueKeyCount} keys`);
|
|
206
339
|
},
|
|
207
340
|
writeBundle(outputOptions) {
|
|
208
341
|
if (!isSSR && outputOptions.dir) {
|
|
209
342
|
const chunksDir = path2.resolve(outputOptions.dir, "..", "server", "chunks");
|
|
210
343
|
const manifestPath = path2.join(chunksDir, MANIFEST_FILE_NAME);
|
|
211
|
-
if (
|
|
212
|
-
|
|
344
|
+
if (fs2.existsSync(chunksDir)) {
|
|
345
|
+
fs2.writeFileSync(manifestPath, JSON.stringify(getManifest()));
|
|
213
346
|
} else {
|
|
214
347
|
logger.error(`server chunks directory not found, cannot write manifest to ${manifestPath}`);
|
|
215
348
|
}
|
|
@@ -53,6 +53,8 @@ var I18nSingleton = class {
|
|
|
53
53
|
hashCache = /* @__PURE__ */ new Map();
|
|
54
54
|
// locale -> inline script for I18nScript component
|
|
55
55
|
scriptCache = /* @__PURE__ */ new Map();
|
|
56
|
+
// "locale:chunkName" -> encoded chunk response body
|
|
57
|
+
chunkCache = /* @__PURE__ */ new Map();
|
|
56
58
|
// manifest getter (set by init module, provides live data in dev mode)
|
|
57
59
|
manifestGetter = null;
|
|
58
60
|
async configure(config) {
|
|
@@ -153,6 +155,38 @@ var I18nSingleton = class {
|
|
|
153
155
|
getHashes(locale) {
|
|
154
156
|
return this.hashCache.get(locale) ?? {};
|
|
155
157
|
}
|
|
158
|
+
/**
|
|
159
|
+
* Get the encoded chunk response body for a locale/chunk combination.
|
|
160
|
+
* Cached to avoid repeated JSON.stringify and encoding on each request.
|
|
161
|
+
* Returns undefined if chunk not found in manifest.
|
|
162
|
+
*/
|
|
163
|
+
getChunkBody(locale, chunkName) {
|
|
164
|
+
const keys = this.getManifest().chunks[chunkName];
|
|
165
|
+
if (!keys) {
|
|
166
|
+
return void 0;
|
|
167
|
+
}
|
|
168
|
+
const cacheKey = `${locale}:${chunkName}`;
|
|
169
|
+
const cached = this.chunkCache.get(cacheKey);
|
|
170
|
+
if (cached) {
|
|
171
|
+
return cached;
|
|
172
|
+
}
|
|
173
|
+
const translations = this.getTranslations(locale);
|
|
174
|
+
const chunkTranslations = {};
|
|
175
|
+
for (const key of keys) {
|
|
176
|
+
if (translations[key]) {
|
|
177
|
+
chunkTranslations[key] = translations[key];
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
const js = `/* @astroscope/i18n chunk: ${chunkName} */
|
|
181
|
+
(function() {
|
|
182
|
+
var i = window.__i18n__;
|
|
183
|
+
if (i) Object.assign(i.translations, ${JSON.stringify(chunkTranslations)});
|
|
184
|
+
})();
|
|
185
|
+
`;
|
|
186
|
+
const body = new TextEncoder().encode(js);
|
|
187
|
+
this.chunkCache.set(cacheKey, body);
|
|
188
|
+
return body;
|
|
189
|
+
}
|
|
156
190
|
/**
|
|
157
191
|
* Get the inline script for I18nScript component.
|
|
158
192
|
* Cached per locale, invalidated when translations change.
|
|
@@ -203,6 +237,11 @@ var I18nSingleton = class {
|
|
|
203
237
|
this.compiledCache.delete(locale);
|
|
204
238
|
this.hashCache.delete(locale);
|
|
205
239
|
this.scriptCache.delete(locale);
|
|
240
|
+
for (const key of this.chunkCache.keys()) {
|
|
241
|
+
if (key.startsWith(`${locale}:`)) {
|
|
242
|
+
this.chunkCache.delete(key);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
206
245
|
}
|
|
207
246
|
}
|
|
208
247
|
resolveLocales(locales) {
|
|
@@ -11,9 +11,7 @@ var visibleDirective = (load, options, el) => {
|
|
|
11
11
|
await hydrate();
|
|
12
12
|
};
|
|
13
13
|
const rawOptions = typeof options.value === "object" ? options.value : void 0;
|
|
14
|
-
const ioOptions = {
|
|
15
|
-
rootMargin: rawOptions?.rootMargin
|
|
16
|
-
};
|
|
14
|
+
const ioOptions = rawOptions?.rootMargin ? { rootMargin: rawOptions.rootMargin } : {};
|
|
17
15
|
const io = new IntersectionObserver((entries) => {
|
|
18
16
|
for (const entry of entries) {
|
|
19
17
|
if (!entry.isIntersecting) continue;
|
package/dist/extraction/index.js
CHANGED
|
@@ -31,6 +31,7 @@ declare class I18nSingleton {
|
|
|
31
31
|
private compiledCache;
|
|
32
32
|
private hashCache;
|
|
33
33
|
private scriptCache;
|
|
34
|
+
private chunkCache;
|
|
34
35
|
private manifestGetter;
|
|
35
36
|
configure(config: I18nConfig): Promise<void>;
|
|
36
37
|
isConfigured(): boolean;
|
|
@@ -45,6 +46,12 @@ declare class I18nSingleton {
|
|
|
45
46
|
*/
|
|
46
47
|
getManifest(): ExtractionManifest;
|
|
47
48
|
getHashes(locale: string): Record<string, string>;
|
|
49
|
+
/**
|
|
50
|
+
* Get the encoded chunk response body for a locale/chunk combination.
|
|
51
|
+
* Cached to avoid repeated JSON.stringify and encoding on each request.
|
|
52
|
+
* Returns undefined if chunk not found in manifest.
|
|
53
|
+
*/
|
|
54
|
+
getChunkBody(locale: string, chunkName: string): Uint8Array | undefined;
|
|
48
55
|
/**
|
|
49
56
|
* Get the inline script for I18nScript component.
|
|
50
57
|
* Cached per locale, invalidated when translations change.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
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-BBAAVYz_.js';
|
|
3
3
|
import { ExcludePattern } from '@astroscope/excludes';
|
|
4
4
|
export { R as RawTranslations, T as TranslationMeta } from './types-DNRFj1XA.js';
|
|
5
5
|
export { E as ExtractedKey, a as ExtractionManifest } from './types-DTUho6gi.js';
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
i18nVitePlugin
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-4IUQAJ6R.js";
|
|
4
4
|
import {
|
|
5
5
|
i18n,
|
|
6
6
|
runWithContext
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-ZKXMIGSF.js";
|
|
8
8
|
import "./chunk-KNDYKTHE.js";
|
|
9
9
|
import "./chunk-4A5BKHFN.js";
|
|
10
10
|
|
|
@@ -69,6 +69,13 @@ function createI18nChunkMiddleware() {
|
|
|
69
69
|
return next();
|
|
70
70
|
}
|
|
71
71
|
const locale = path.slice(0, slashIdx);
|
|
72
|
+
const config = i18n.getConfig();
|
|
73
|
+
if (!config.locales.includes(locale)) {
|
|
74
|
+
return new Response("/* unknown locale */", {
|
|
75
|
+
status: 404,
|
|
76
|
+
headers: { "Content-Type": "application/javascript" }
|
|
77
|
+
});
|
|
78
|
+
}
|
|
72
79
|
const rest = path.slice(slashIdx + 1);
|
|
73
80
|
if (!rest.endsWith(".js")) {
|
|
74
81
|
return next();
|
|
@@ -79,27 +86,13 @@ function createI18nChunkMiddleware() {
|
|
|
79
86
|
return next();
|
|
80
87
|
}
|
|
81
88
|
const chunkName = withoutJs.slice(0, lastDotIdx);
|
|
82
|
-
const
|
|
83
|
-
if (!
|
|
89
|
+
const body = i18n.getChunkBody(locale, chunkName);
|
|
90
|
+
if (!body) {
|
|
84
91
|
return new Response(`/* chunk not found: ${chunkName} */`, {
|
|
85
92
|
status: 404,
|
|
86
93
|
headers: { "Content-Type": "application/javascript" }
|
|
87
94
|
});
|
|
88
95
|
}
|
|
89
|
-
const translations = i18n.getTranslations(locale);
|
|
90
|
-
const chunkTranslations = {};
|
|
91
|
-
for (const key of keys) {
|
|
92
|
-
if (translations[key]) {
|
|
93
|
-
chunkTranslations[key] = translations[key];
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
const js = `/* @astroscope/i18n chunk: ${chunkName} */
|
|
97
|
-
(function() {
|
|
98
|
-
var i = window.__i18n__;
|
|
99
|
-
if (i) Object.assign(i.translations, ${JSON.stringify(chunkTranslations)});
|
|
100
|
-
})();
|
|
101
|
-
`;
|
|
102
|
-
const body = new TextEncoder().encode(js);
|
|
103
96
|
return new Response(body, {
|
|
104
97
|
status: 200,
|
|
105
98
|
headers: {
|
|
@@ -134,11 +127,12 @@ function detectLocale(request) {
|
|
|
134
127
|
if (!acceptLanguage) return void 0;
|
|
135
128
|
const preferred = acceptLanguage.split(",").map((part) => {
|
|
136
129
|
const [lang, q = "q=1"] = part.trim().split(";");
|
|
130
|
+
const baseLang = lang?.split("-")[0]?.toLowerCase();
|
|
137
131
|
return {
|
|
138
|
-
lang:
|
|
132
|
+
lang: baseLang,
|
|
139
133
|
q: parseFloat(q.replace("q=", ""))
|
|
140
134
|
};
|
|
141
|
-
}).sort((a, b) => b.q - a.q);
|
|
135
|
+
}).filter((p) => !!p.lang).sort((a, b) => b.q - a.q);
|
|
142
136
|
for (const { lang } of preferred) {
|
|
143
137
|
if (locales.includes(lang)) {
|
|
144
138
|
return lang;
|
package/dist/internal.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as I18nContext } from './i18n-
|
|
2
|
-
export { i as i18n } from './i18n-
|
|
1
|
+
import { a as I18nContext } from './i18n-BBAAVYz_.js';
|
|
2
|
+
export { i as i18n } from './i18n-BBAAVYz_.js';
|
|
3
3
|
export { I as I18nClientState } from './types-DNRFj1XA.js';
|
|
4
4
|
import './types-DTUho6gi.js';
|
|
5
5
|
|
package/dist/internal.js
CHANGED
package/dist/server/t.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astroscope/i18n",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "i18n for Astro + React islands — automatic tree-shaking, parallel loading, any translation source",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -86,9 +86,11 @@
|
|
|
86
86
|
"lint:fix": "eslint 'src/**/*.ts' --fix"
|
|
87
87
|
},
|
|
88
88
|
"dependencies": {
|
|
89
|
+
"@astrojs/compiler": "^2.12.2",
|
|
89
90
|
"@babel/core": "^7.28.6",
|
|
90
91
|
"@babel/preset-typescript": "^7.28.5",
|
|
91
92
|
"@messageformat/core": "^3.4.0",
|
|
93
|
+
"glob": "^11.0.0",
|
|
92
94
|
"magic-string": "^0.30.21"
|
|
93
95
|
},
|
|
94
96
|
"devDependencies": {
|