glotfile 0.4.0 → 0.4.2
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/bin/glotfile.js +0 -0
- package/dist/server/cli.js +14 -12
- package/dist/server/server.js +14 -12
- package/dist/ui/assets/{index-DC5Ppxxa.js → index-CJ_nmOjf.js} +5 -5
- package/dist/ui/assets/index-De8z0F8Y.css +1 -0
- package/dist/ui/index.html +2 -2
- package/package.json +1 -1
- package/dist/ui/assets/index-DVTJ7ZX_.css +0 -1
package/bin/glotfile.js
CHANGED
|
File without changes
|
package/dist/server/cli.js
CHANGED
|
@@ -1590,6 +1590,7 @@ function buildSystemPrompt(hasPluralItems) {
|
|
|
1590
1590
|
function buildBatchPrompt(reqs) {
|
|
1591
1591
|
const targetLocale = reqs[0]?.targetLocale ?? "";
|
|
1592
1592
|
const hasPluralItems = reqs.some((r) => r.plural !== void 0);
|
|
1593
|
+
const hasGlossaryItems = reqs.some((r) => r.glossary !== void 0 && r.glossary.length > 0);
|
|
1593
1594
|
const items = reqs.map((r) => {
|
|
1594
1595
|
const base = {
|
|
1595
1596
|
id: r.id,
|
|
@@ -1599,7 +1600,7 @@ function buildBatchPrompt(reqs) {
|
|
|
1599
1600
|
// Wrap in braces so the model sees "{site}" not "site" — makes the visual
|
|
1600
1601
|
// connection to the source string obvious and reduces rename errors.
|
|
1601
1602
|
placeholders: r.placeholders.map((p) => `{${p}}`),
|
|
1602
|
-
glossary: r.glossary
|
|
1603
|
+
...r.glossary?.length ? { glossary: r.glossary } : {},
|
|
1603
1604
|
hasScreenshot: r.image !== void 0
|
|
1604
1605
|
};
|
|
1605
1606
|
if (r.plural) {
|
|
@@ -1609,7 +1610,7 @@ function buildBatchPrompt(reqs) {
|
|
|
1609
1610
|
});
|
|
1610
1611
|
const returnFormat = hasPluralItems ? 'For a scalar item (has `source`) return {"id","translation"}; for a plural item (has `plural`) return {"id","forms"} with one string per required category.' : 'Return {"id","translation"} for each item.';
|
|
1611
1612
|
return `Translate every item below into the target locale: ${targetLocale}. All items share this one target language.
|
|
1612
|
-
Glossary entries are constraints you MUST apply. Items with hasScreenshot:true have a screenshot supplied as a separate image block above; use it for context. ${returnFormat} Return JSON {"items":[\u2026]}.
|
|
1613
|
+
` + (hasGlossaryItems ? "Glossary entries are constraints you MUST apply. " : "") + `Items with hasScreenshot:true have a screenshot supplied as a separate image block above; use it for context. ${returnFormat} Return JSON {"items":[\u2026]}.
|
|
1613
1614
|
` + JSON.stringify(items, null, 2);
|
|
1614
1615
|
}
|
|
1615
1616
|
function buildTranslateGemmaSystemPrompt(sourceLocale, targetLocale) {
|
|
@@ -2929,7 +2930,7 @@ function selectContextTargets(state, opts, cache2, lastRunAt) {
|
|
|
2929
2930
|
let candidates = [];
|
|
2930
2931
|
for (const key of Object.keys(state.keys).sort()) {
|
|
2931
2932
|
const entry = state.keys[key];
|
|
2932
|
-
if (entry.context) continue;
|
|
2933
|
+
if (entry.context && !opts.force) continue;
|
|
2933
2934
|
if (keySet && !keySet.has(key)) continue;
|
|
2934
2935
|
if (keyRe && !keyRe.test(key)) continue;
|
|
2935
2936
|
if (cutoff) {
|
|
@@ -2977,7 +2978,7 @@ ${s.lines}
|
|
|
2977
2978
|
});
|
|
2978
2979
|
return 'Write a context note for each key. Return JSON {"items":[{"id","context"}]}.\n' + JSON.stringify(items, null, 2);
|
|
2979
2980
|
}
|
|
2980
|
-
function applyContext(state, reqs, results, clock = systemClock) {
|
|
2981
|
+
function applyContext(state, reqs, results, clock = systemClock, force = false) {
|
|
2981
2982
|
const byId = new Map(reqs.map((r) => [r.id, r]));
|
|
2982
2983
|
let written = 0;
|
|
2983
2984
|
const errors = [];
|
|
@@ -2998,7 +2999,7 @@ function applyContext(state, reqs, results, clock = systemClock) {
|
|
|
2998
2999
|
continue;
|
|
2999
3000
|
}
|
|
3000
3001
|
const entry = state.keys[req.key];
|
|
3001
|
-
if (!entry || entry.context) continue;
|
|
3002
|
+
if (!entry || entry.context && !force) continue;
|
|
3002
3003
|
entry.context = context;
|
|
3003
3004
|
entry.contextSource = "ai";
|
|
3004
3005
|
entry.contextAt = clock();
|
|
@@ -4470,7 +4471,7 @@ function createApi(deps) {
|
|
|
4470
4471
|
await stream.writeSSE({ event: "error", data: JSON.stringify({ error: e.message }) });
|
|
4471
4472
|
return;
|
|
4472
4473
|
}
|
|
4473
|
-
const { skipped } = attachScreenshotsForProvider(reqs, s,
|
|
4474
|
+
const { skipped } = attachScreenshotsForProvider(reqs, s, dirname2(resolve9(deps.statePath)), provider.supportsVision());
|
|
4474
4475
|
if (skipped) console.warn(`Model "${aiCfg.model}" has no vision support; ${skipped} screenshot(s) ignored.`);
|
|
4475
4476
|
console.log(`[translate] ${reqs.length} string(s) \u2192 ${aiCfg.model}`);
|
|
4476
4477
|
let totalWritten = 0;
|
|
@@ -4548,7 +4549,7 @@ function createApi(deps) {
|
|
|
4548
4549
|
} catch (e) {
|
|
4549
4550
|
return c.json({ error: e.message }, 400);
|
|
4550
4551
|
}
|
|
4551
|
-
const { skipped } = attachScreenshotsForProvider(toTranslate, s,
|
|
4552
|
+
const { skipped } = attachScreenshotsForProvider(toTranslate, s, dirname2(resolve9(deps.statePath)), provider.supportsVision());
|
|
4552
4553
|
if (skipped) console.warn(`Model "${aiCfg.model}" has no vision support; ${skipped} screenshot(s) ignored.`);
|
|
4553
4554
|
const results = await runLocaleParallel(toTranslate, provider, {}, aiCfg.concurrency);
|
|
4554
4555
|
const latest = load();
|
|
@@ -4643,7 +4644,8 @@ function createApi(deps) {
|
|
|
4643
4644
|
keyGlob: body.keyGlob,
|
|
4644
4645
|
limit: body.limit,
|
|
4645
4646
|
since: body.since,
|
|
4646
|
-
keys: body.keys
|
|
4647
|
+
keys: body.keys,
|
|
4648
|
+
force: body.force
|
|
4647
4649
|
}, cache2, body.lastRunAt);
|
|
4648
4650
|
if (!targets.length) {
|
|
4649
4651
|
await stream.writeSSE({ event: "done", data: JSON.stringify({ requested: 0, written: 0, errors: [] }) });
|
|
@@ -4696,7 +4698,7 @@ function createApi(deps) {
|
|
|
4696
4698
|
if (signal?.aborted) break;
|
|
4697
4699
|
const batch = raw;
|
|
4698
4700
|
const fresh = load();
|
|
4699
|
-
const { written, errors } = applyContext(fresh, chunk2, batch.items ?? []);
|
|
4701
|
+
const { written, errors } = applyContext(fresh, chunk2, batch.items ?? [], void 0, body.force === true);
|
|
4700
4702
|
appendLog(projectRoot, {
|
|
4701
4703
|
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4702
4704
|
kind: "context",
|
|
@@ -4776,12 +4778,12 @@ async function readFileResponse(absPath) {
|
|
|
4776
4778
|
}
|
|
4777
4779
|
function buildApp(opts) {
|
|
4778
4780
|
const app = new Hono2();
|
|
4779
|
-
|
|
4780
|
-
|
|
4781
|
+
const apiDeps = { statePath: opts.statePath, autoExport: true };
|
|
4782
|
+
app.route("/api", createApi(apiDeps));
|
|
4781
4783
|
app.get("/:dir/*", async (c, next) => {
|
|
4782
4784
|
const dirSeg = c.req.param("dir");
|
|
4783
4785
|
if (!dirSeg.endsWith("-screenshots")) return next();
|
|
4784
|
-
const shotsRoot = resolve10(
|
|
4786
|
+
const shotsRoot = resolve10(dirname3(resolve10(apiDeps.statePath)), dirSeg);
|
|
4785
4787
|
const pathname = decodeURIComponent(new URL(c.req.url).pathname);
|
|
4786
4788
|
const rest = pathname.slice(`/${dirSeg}`.length);
|
|
4787
4789
|
const target = resolve10(shotsRoot, "." + rest);
|
package/dist/server/server.js
CHANGED
|
@@ -1129,7 +1129,7 @@ function selectContextTargets(state, opts, cache2, lastRunAt) {
|
|
|
1129
1129
|
let candidates = [];
|
|
1130
1130
|
for (const key of Object.keys(state.keys).sort()) {
|
|
1131
1131
|
const entry = state.keys[key];
|
|
1132
|
-
if (entry.context) continue;
|
|
1132
|
+
if (entry.context && !opts.force) continue;
|
|
1133
1133
|
if (keySet && !keySet.has(key)) continue;
|
|
1134
1134
|
if (keyRe && !keyRe.test(key)) continue;
|
|
1135
1135
|
if (cutoff) {
|
|
@@ -1197,7 +1197,7 @@ var CONTEXT_BATCH_SCHEMA = {
|
|
|
1197
1197
|
required: ["items"],
|
|
1198
1198
|
additionalProperties: false
|
|
1199
1199
|
};
|
|
1200
|
-
function applyContext(state, reqs, results, clock = systemClock) {
|
|
1200
|
+
function applyContext(state, reqs, results, clock = systemClock, force = false) {
|
|
1201
1201
|
const byId = new Map(reqs.map((r) => [r.id, r]));
|
|
1202
1202
|
let written = 0;
|
|
1203
1203
|
const errors = [];
|
|
@@ -1218,7 +1218,7 @@ function applyContext(state, reqs, results, clock = systemClock) {
|
|
|
1218
1218
|
continue;
|
|
1219
1219
|
}
|
|
1220
1220
|
const entry = state.keys[req.key];
|
|
1221
|
-
if (!entry || entry.context) continue;
|
|
1221
|
+
if (!entry || entry.context && !force) continue;
|
|
1222
1222
|
entry.context = context;
|
|
1223
1223
|
entry.contextSource = "ai";
|
|
1224
1224
|
entry.contextAt = clock();
|
|
@@ -2376,6 +2376,7 @@ function buildSystemPrompt(hasPluralItems) {
|
|
|
2376
2376
|
function buildBatchPrompt(reqs) {
|
|
2377
2377
|
const targetLocale = reqs[0]?.targetLocale ?? "";
|
|
2378
2378
|
const hasPluralItems = reqs.some((r) => r.plural !== void 0);
|
|
2379
|
+
const hasGlossaryItems = reqs.some((r) => r.glossary !== void 0 && r.glossary.length > 0);
|
|
2379
2380
|
const items = reqs.map((r) => {
|
|
2380
2381
|
const base = {
|
|
2381
2382
|
id: r.id,
|
|
@@ -2385,7 +2386,7 @@ function buildBatchPrompt(reqs) {
|
|
|
2385
2386
|
// Wrap in braces so the model sees "{site}" not "site" — makes the visual
|
|
2386
2387
|
// connection to the source string obvious and reduces rename errors.
|
|
2387
2388
|
placeholders: r.placeholders.map((p) => `{${p}}`),
|
|
2388
|
-
glossary: r.glossary
|
|
2389
|
+
...r.glossary?.length ? { glossary: r.glossary } : {},
|
|
2389
2390
|
hasScreenshot: r.image !== void 0
|
|
2390
2391
|
};
|
|
2391
2392
|
if (r.plural) {
|
|
@@ -2395,7 +2396,7 @@ function buildBatchPrompt(reqs) {
|
|
|
2395
2396
|
});
|
|
2396
2397
|
const returnFormat = hasPluralItems ? 'For a scalar item (has `source`) return {"id","translation"}; for a plural item (has `plural`) return {"id","forms"} with one string per required category.' : 'Return {"id","translation"} for each item.';
|
|
2397
2398
|
return `Translate every item below into the target locale: ${targetLocale}. All items share this one target language.
|
|
2398
|
-
Glossary entries are constraints you MUST apply. Items with hasScreenshot:true have a screenshot supplied as a separate image block above; use it for context. ${returnFormat} Return JSON {"items":[\u2026]}.
|
|
2399
|
+
` + (hasGlossaryItems ? "Glossary entries are constraints you MUST apply. " : "") + `Items with hasScreenshot:true have a screenshot supplied as a separate image block above; use it for context. ${returnFormat} Return JSON {"items":[\u2026]}.
|
|
2399
2400
|
` + JSON.stringify(items, null, 2);
|
|
2400
2401
|
}
|
|
2401
2402
|
function buildTranslateGemmaSystemPrompt(sourceLocale, targetLocale) {
|
|
@@ -4100,7 +4101,7 @@ function createApi(deps) {
|
|
|
4100
4101
|
await stream.writeSSE({ event: "error", data: JSON.stringify({ error: e.message }) });
|
|
4101
4102
|
return;
|
|
4102
4103
|
}
|
|
4103
|
-
const { skipped } = attachScreenshotsForProvider(reqs, s,
|
|
4104
|
+
const { skipped } = attachScreenshotsForProvider(reqs, s, dirname2(resolve8(deps.statePath)), provider.supportsVision());
|
|
4104
4105
|
if (skipped) console.warn(`Model "${aiCfg.model}" has no vision support; ${skipped} screenshot(s) ignored.`);
|
|
4105
4106
|
console.log(`[translate] ${reqs.length} string(s) \u2192 ${aiCfg.model}`);
|
|
4106
4107
|
let totalWritten = 0;
|
|
@@ -4178,7 +4179,7 @@ function createApi(deps) {
|
|
|
4178
4179
|
} catch (e) {
|
|
4179
4180
|
return c.json({ error: e.message }, 400);
|
|
4180
4181
|
}
|
|
4181
|
-
const { skipped } = attachScreenshotsForProvider(toTranslate, s,
|
|
4182
|
+
const { skipped } = attachScreenshotsForProvider(toTranslate, s, dirname2(resolve8(deps.statePath)), provider.supportsVision());
|
|
4182
4183
|
if (skipped) console.warn(`Model "${aiCfg.model}" has no vision support; ${skipped} screenshot(s) ignored.`);
|
|
4183
4184
|
const results = await runLocaleParallel(toTranslate, provider, {}, aiCfg.concurrency);
|
|
4184
4185
|
const latest = load();
|
|
@@ -4273,7 +4274,8 @@ function createApi(deps) {
|
|
|
4273
4274
|
keyGlob: body.keyGlob,
|
|
4274
4275
|
limit: body.limit,
|
|
4275
4276
|
since: body.since,
|
|
4276
|
-
keys: body.keys
|
|
4277
|
+
keys: body.keys,
|
|
4278
|
+
force: body.force
|
|
4277
4279
|
}, cache2, body.lastRunAt);
|
|
4278
4280
|
if (!targets.length) {
|
|
4279
4281
|
await stream.writeSSE({ event: "done", data: JSON.stringify({ requested: 0, written: 0, errors: [] }) });
|
|
@@ -4326,7 +4328,7 @@ function createApi(deps) {
|
|
|
4326
4328
|
if (signal?.aborted) break;
|
|
4327
4329
|
const batch = raw;
|
|
4328
4330
|
const fresh = load();
|
|
4329
|
-
const { written, errors } = applyContext(fresh, chunk2, batch.items ?? []);
|
|
4331
|
+
const { written, errors } = applyContext(fresh, chunk2, batch.items ?? [], void 0, body.force === true);
|
|
4330
4332
|
appendLog(projectRoot, {
|
|
4331
4333
|
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4332
4334
|
kind: "context",
|
|
@@ -4385,12 +4387,12 @@ async function readFileResponse(absPath) {
|
|
|
4385
4387
|
}
|
|
4386
4388
|
function buildApp(opts) {
|
|
4387
4389
|
const app = new Hono2();
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
+
const apiDeps = { statePath: opts.statePath, autoExport: true };
|
|
4391
|
+
app.route("/api", createApi(apiDeps));
|
|
4390
4392
|
app.get("/:dir/*", async (c, next) => {
|
|
4391
4393
|
const dirSeg = c.req.param("dir");
|
|
4392
4394
|
if (!dirSeg.endsWith("-screenshots")) return next();
|
|
4393
|
-
const shotsRoot = resolve9(
|
|
4395
|
+
const shotsRoot = resolve9(dirname3(resolve9(apiDeps.statePath)), dirSeg);
|
|
4394
4396
|
const pathname = decodeURIComponent(new URL(c.req.url).pathname);
|
|
4395
4397
|
const rest = pathname.slice(`/${dirSeg}`.length);
|
|
4396
4398
|
const target = resolve9(shotsRoot, "." + rest);
|