- translateText/translateTexts call OpenRouter chat-completions; glossary terms are prompt-injected from config/glossaries.ts, generated from the translation repo's TSVs by scripts/build-glossaries.ts - model resolution: per-language override in config/languages.ts → translation.model site setting (new admin field) → TRANSLATION_MODEL env → anthropic/claude-opus-5, at low reasoning effort - blank fragments are held back from the model, which drops them and breaks the one-translation-per-fragment count the caller maps results back on - OpenRouter failures map to a typed error with a human-readable message; an unrecoverable one (no credits, bad key, rejected model) stops the run instead of repeating the same call for every remaining locale - machine translations record source 'auto-translate'; 'deepl' stays readable for snapshots written before the switch
47 lines
1.9 KiB
TypeScript
47 lines
1.9 KiB
TypeScript
/**
|
|
* Generates config/glossaries.ts from the translation repo's glossary TSVs
|
|
* (../translation/deepl-glossaries/*.tsv). The generated module is committed
|
|
* so production builds don't need the translation repo checked out.
|
|
*
|
|
* Run after updating the TSVs: bun scripts/build-glossaries.ts
|
|
*/
|
|
import { readdirSync, readFileSync, writeFileSync } from 'node:fs'
|
|
import { join } from 'node:path'
|
|
|
|
const sourceDir = join(import.meta.dir, '../../translation/deepl-glossaries')
|
|
const outFile = join(import.meta.dir, '../config/glossaries.ts')
|
|
|
|
const glossaries: Record<string, Array<[string, string]>> = {}
|
|
|
|
for (const file of readdirSync(sourceDir).sort()) {
|
|
if (!file.endsWith('.tsv')) continue
|
|
const code = file.replace(/\.tsv$/, '')
|
|
const pairs: Array<[string, string]> = []
|
|
for (const line of readFileSync(join(sourceDir, file), 'utf8').split('\n')) {
|
|
const trimmed = line.trim()
|
|
if (!trimmed) continue
|
|
const [source, target] = trimmed.split('\t')
|
|
if (source && target) pairs.push([source.trim(), target.trim()])
|
|
}
|
|
glossaries[code] = pairs
|
|
}
|
|
|
|
const entries = Object.entries(glossaries)
|
|
.map(([code, pairs]) => {
|
|
const rows = pairs.map(([s, t]) => ` [${JSON.stringify(s)}, ${JSON.stringify(t)}]`).join(',\n')
|
|
return ` ${code}: [\n${rows}\n ]`
|
|
})
|
|
.join(',\n')
|
|
|
|
const output = `// Generated by scripts/build-glossaries.ts from ../translation/deepl-glossaries/*.tsv
|
|
// Do not edit by hand — update the TSVs in the translation repo and re-run:
|
|
// bun scripts/build-glossaries.ts
|
|
|
|
/** English → target-language term pairs, keyed by language code. */
|
|
export const GLOSSARIES: Record<string, ReadonlyArray<readonly [string, string]>> = {
|
|
${entries}
|
|
}
|
|
`
|
|
|
|
writeFileSync(outFile, output)
|
|
console.log(`Wrote ${Object.keys(glossaries).length} glossaries (${Object.values(glossaries).reduce((n, p) => n + p.length, 0)} terms) to config/glossaries.ts`)
|