attranslate
Version:
Text Translator for Websites and Apps
65 lines (64 loc) • 3.26 kB
JavaScript
;
// AgentTranslation: minimal agent protocol for attranslate
// - First invocation (stdin is TTY): prints missing sources and agent instructions.
// - Second invocation (stdin is piped): reads translations, one per line, in order.
Object.defineProperty(exports, "__esModule", { value: true });
exports.AgentTranslation = void 0;
function printMissingSources(strings, useError = false) {
const log = useError ? console.error : console.log;
log("MISSING TRANSLATIONS:\n");
for (const { key, value } of strings) {
log(`- key: ${key}`);
log(` source: ${value}\n`);
}
}
class AgentTranslation {
async translateStrings(args) {
const results = [];
if (process.stdin.isTTY || process.env.ATTRANSLATE_AGENT_TTY === "1") {
printMissingSources(args.strings);
console.log("INSTRUCTIONS FOR AGENTS:");
console.log("Translate the missing sources listed above, matching the order.");
console.log("Replace <translation1>, <translation2>, ... with your actual translated strings and pipe them into attranslate as follows:");
const cmd = process.argv.slice(2).join(" ");
console.log(`echo -e \"<translation1>\\n<translation2>\\n...\" | attranslate ${cmd}`);
// Non-zero exit code by design: allows CI/CD or tooling to detect "missing translations".
process.exit(2);
}
// Read piped stdin, split into lines, map to keys in order
const stdin = await this.readAllStdin();
const translationCount = args.strings.length;
// Explicitly handle the case where nothing was piped at all.
// (Empty lines are allowed as empty-string translations; this only triggers for truly empty stdin.)
if (stdin.length === 0 && translationCount > 0) {
printMissingSources(args.strings, true);
console.error("ERROR: No translations provided. Pipe one translation per source listed above.");
process.exit(1);
}
// Keep empty lines (empty translations) intact.
// Only trim *trailing* empty lines which can occur due to a trailing newline.
const lines = stdin.replace(/\r/g, "").split("\n");
while (lines.length > translationCount && lines[lines.length - 1] === "") {
lines.pop();
}
// Note: `split("\n")` never returns `[]`, so we don't need a separate `lines.length === 0` check.
if (lines.length !== translationCount) {
printMissingSources(args.strings, true);
console.error(`ERROR: ${lines.length} translations given, ${translationCount} required. Provide exactly one translation per source listed above.`);
process.exit(1);
}
for (let i = 0; i < translationCount; i++) {
results.push({ key: args.strings[i].key, translated: lines[i] });
}
return results;
}
async readAllStdin() {
return new Promise((resolve) => {
let data = "";
process.stdin.setEncoding("utf8");
process.stdin.on("data", (chunk) => (data += chunk));
process.stdin.on("end", () => resolve(data));
});
}
}
exports.AgentTranslation = AgentTranslation;