Files
DemonGatanjieu-Anomalous_Mo…/web/modules/prompt_gloss.js
T
DemonGatanjieuandClaude Opus 5.5 d00882925c Prompt Studio: meanings in a chosen language, any language to English
Tag meanings are brighter and slightly larger, and the top bar picks
their language (Simplified/Traditional Chinese, Japanese, Korean,
Russian, Spanish, French, German, Portuguese, or none) for every
interface language; the old Chinese switch carries over. Typed text in
any language that is not plain English is translated to English, and
translating a box's foreign tags drops one whose English is already
there. The backend skips DeepL for targets it does not take instead of
falling back to English, and lets MyMemory detect other sources.
Unused prompt helpers in translation_service.js are removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 11:24:04 +08:00

68 lines
2.8 KiB
JavaScript

/**
* The meaning under each tag in Prompt Studio, in a language the user picks. Tags without one
* yet are sent to the translation service (translation_service.js) a line each, in batches of
* a few hundred characters; a batch whose answer does not come back line for line asks tag by
* tag. Meanings are kept for the session, per language. Tags that are not English have none.
* No DOM here.
*/
import { needsEnglish, translatePromptText } from './translation_service.js';
import { tagWeight } from './prompt_tags.js';
/** Languages a meaning can be shown in: the translation code and the language's own name. */
export const GLOSS_LANGUAGES = [
['zh-CN', '简体中文'], ['zh-TW', '繁體中文'], ['ja', '日本語'], ['ko', '한국어'], ['ru', 'Русский'],
['es', 'Español'], ['fr', 'Français'], ['de', 'Deutsch'], ['pt', 'Português'],
];
const BATCH_CHARS = 800;
const MAX_KEPT = 3000;
const meanings = new Map(); // `${lang}:${tag words, lower case}` -> meaning
const coreOf = tag => tagWeight(tag).core.trim();
const keyOf = (core, lang) => `${lang}:${core.toLowerCase()}`;
/** The meaning of `tag` in `lang`: a string, '' when it needs none, undefined when not known yet. */
export function glossOf(tag, lang) {
const core = coreOf(tag);
if (!core || needsEnglish(core)) return '';
return meanings.get(keyOf(core, lang));
}
function keep(core, lang, meaning) {
if (meanings.size >= MAX_KEPT) meanings.delete(meanings.keys().next().value);
meanings.set(keyOf(core, lang), meaning.trim());
}
async function askOneByOne(cores, lang, signal) {
for (const core of cores) {
const result = await translatePromptText(core, { targetLang: lang, signal });
if (!result.ok) return false;
keep(core, lang, result.translated);
}
return true;
}
/** Looks up the tags with no meaning in `lang` yet; false when the service could not be reached. */
export async function fetchGlosses(tags, lang, signal) {
const wanted = [...new Set(tags.map(coreOf))].filter(core => glossOf(core, lang) === undefined);
let batch = [];
let size = 0;
const flush = async () => {
if (!batch.length) return true;
const cores = batch;
batch = [];
size = 0;
const result = await translatePromptText(cores.join('\n'), { targetLang: lang, signal });
if (!result.ok) return false;
const lines = result.translated.split(/\r?\n/);
if (lines.length !== cores.length) return askOneByOne(cores, lang, signal);
cores.forEach((core, index) => keep(core, lang, lines[index]));
return true;
};
for (const core of wanted) {
if (size + core.length > BATCH_CHARS && !await flush()) return false;
batch.push(core);
size += core.length + 1;
}
return flush();
}