transl8-srt 1.0.1 → 1.0.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/README.md +17 -0
- package/dist/index.cjs +29 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -3
- package/dist/index.d.ts +20 -3
- package/dist/index.js +29 -12
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -39,8 +39,20 @@ interface TranslateOptions {
|
|
|
39
39
|
batchSize?: number;
|
|
40
40
|
/** Context overlap: N sentences from the prev batch prepended (default 4) */
|
|
41
41
|
contextOverlap?: number;
|
|
42
|
-
/**
|
|
43
|
-
|
|
42
|
+
/**
|
|
43
|
+
* Model for translation batches.
|
|
44
|
+
* OpenAI users: defaults to "gpt-4o" for complex scripts, "gpt-4o-mini" otherwise.
|
|
45
|
+
* Custom client users (DeepSeek, Groq, etc.): set this to your provider's model name,
|
|
46
|
+
* e.g. "deepseek-chat". Auto-selection only works for OpenAI model names.
|
|
47
|
+
*/
|
|
48
|
+
model?: "gpt-4o" | "gpt-4o-mini" | (string & {});
|
|
49
|
+
/**
|
|
50
|
+
* Fallback model used when auto-selection would normally pick gpt-4o-mini
|
|
51
|
+
* but you're on a custom provider. Only needed if you want different models
|
|
52
|
+
* for different language complexity tiers on a non-OpenAI backend.
|
|
53
|
+
* Most custom client users should just set `model` and leave this unset.
|
|
54
|
+
*/
|
|
55
|
+
modelFallback?: string;
|
|
44
56
|
/** Max concurrent batch requests (default 8) */
|
|
45
57
|
concurrency?: number;
|
|
46
58
|
/** Enable AI primer call — for general/unknown content only */
|
|
@@ -89,7 +101,12 @@ declare class SubtitleTranslationError extends Error {
|
|
|
89
101
|
declare function translate(
|
|
90
102
|
/** Raw SRT or VTT file content */
|
|
91
103
|
fileContent: string, opts: TranslateOptions,
|
|
92
|
-
/**
|
|
104
|
+
/**
|
|
105
|
+
* Pass your own OpenAI-compatible client.
|
|
106
|
+
* For DeepSeek: new OpenAI({ baseURL: "https://api.deepseek.com", apiKey: "..." })
|
|
107
|
+
* For Groq: new OpenAI({ baseURL: "https://api.groq.com/openai/v1", apiKey: "..." })
|
|
108
|
+
* Remember to also set opts.model to your provider's model name.
|
|
109
|
+
*/
|
|
93
110
|
client?: OpenAI): Promise<TranslationResult>;
|
|
94
111
|
declare function translateFile(inputPath: string, outputPath: string, opts: TranslateOptions, client?: OpenAI): Promise<TranslationResult>;
|
|
95
112
|
|
package/dist/index.d.ts
CHANGED
|
@@ -39,8 +39,20 @@ interface TranslateOptions {
|
|
|
39
39
|
batchSize?: number;
|
|
40
40
|
/** Context overlap: N sentences from the prev batch prepended (default 4) */
|
|
41
41
|
contextOverlap?: number;
|
|
42
|
-
/**
|
|
43
|
-
|
|
42
|
+
/**
|
|
43
|
+
* Model for translation batches.
|
|
44
|
+
* OpenAI users: defaults to "gpt-4o" for complex scripts, "gpt-4o-mini" otherwise.
|
|
45
|
+
* Custom client users (DeepSeek, Groq, etc.): set this to your provider's model name,
|
|
46
|
+
* e.g. "deepseek-chat". Auto-selection only works for OpenAI model names.
|
|
47
|
+
*/
|
|
48
|
+
model?: "gpt-4o" | "gpt-4o-mini" | (string & {});
|
|
49
|
+
/**
|
|
50
|
+
* Fallback model used when auto-selection would normally pick gpt-4o-mini
|
|
51
|
+
* but you're on a custom provider. Only needed if you want different models
|
|
52
|
+
* for different language complexity tiers on a non-OpenAI backend.
|
|
53
|
+
* Most custom client users should just set `model` and leave this unset.
|
|
54
|
+
*/
|
|
55
|
+
modelFallback?: string;
|
|
44
56
|
/** Max concurrent batch requests (default 8) */
|
|
45
57
|
concurrency?: number;
|
|
46
58
|
/** Enable AI primer call — for general/unknown content only */
|
|
@@ -89,7 +101,12 @@ declare class SubtitleTranslationError extends Error {
|
|
|
89
101
|
declare function translate(
|
|
90
102
|
/** Raw SRT or VTT file content */
|
|
91
103
|
fileContent: string, opts: TranslateOptions,
|
|
92
|
-
/**
|
|
104
|
+
/**
|
|
105
|
+
* Pass your own OpenAI-compatible client.
|
|
106
|
+
* For DeepSeek: new OpenAI({ baseURL: "https://api.deepseek.com", apiKey: "..." })
|
|
107
|
+
* For Groq: new OpenAI({ baseURL: "https://api.groq.com/openai/v1", apiKey: "..." })
|
|
108
|
+
* Remember to also set opts.model to your provider's model name.
|
|
109
|
+
*/
|
|
93
110
|
client?: OpenAI): Promise<TranslationResult>;
|
|
94
111
|
declare function translateFile(inputPath: string, outputPath: string, opts: TranslateOptions, client?: OpenAI): Promise<TranslationResult>;
|
|
95
112
|
|
package/dist/index.js
CHANGED
|
@@ -7126,11 +7126,27 @@ Do not explain metaphors or idioms \u2014 translate them faithfully.`,
|
|
|
7126
7126
|
Preserve the speaker's natural register and sentence rhythm.
|
|
7127
7127
|
Do not add formality that isn't in the source.`
|
|
7128
7128
|
};
|
|
7129
|
-
var
|
|
7130
|
-
|
|
7131
|
-
|
|
7132
|
-
|
|
7133
|
-
|
|
7129
|
+
var NEEDS_FULL_MODEL = /* @__PURE__ */ new Set([
|
|
7130
|
+
"ar",
|
|
7131
|
+
"am",
|
|
7132
|
+
"zh",
|
|
7133
|
+
"ja",
|
|
7134
|
+
"ko",
|
|
7135
|
+
"hi",
|
|
7136
|
+
"ru",
|
|
7137
|
+
"uk",
|
|
7138
|
+
"el",
|
|
7139
|
+
"yo",
|
|
7140
|
+
"ig",
|
|
7141
|
+
"ha",
|
|
7142
|
+
"sw",
|
|
7143
|
+
"zu"
|
|
7144
|
+
]);
|
|
7145
|
+
function pickModel(opts) {
|
|
7146
|
+
if (opts.model) return opts.model;
|
|
7147
|
+
const needsFull = NEEDS_FULL_MODEL.has(opts.targetLang);
|
|
7148
|
+
if (needsFull) return "gpt-4o";
|
|
7149
|
+
return opts.modelFallback ?? "gpt-4o-mini";
|
|
7134
7150
|
}
|
|
7135
7151
|
function buildGlossaryBlock(meta) {
|
|
7136
7152
|
const lines = [];
|
|
@@ -7145,7 +7161,7 @@ function buildGlossaryBlock(meta) {
|
|
|
7145
7161
|
}
|
|
7146
7162
|
if (meta.glossary) {
|
|
7147
7163
|
for (const [src, tgt] of Object.entries(meta.glossary)) {
|
|
7148
|
-
lines.push(`"${src}"
|
|
7164
|
+
lines.push(`"${src}" -> "${tgt}"`);
|
|
7149
7165
|
}
|
|
7150
7166
|
}
|
|
7151
7167
|
return lines.length ? `GLOSSARY (must follow exactly):
|
|
@@ -7161,7 +7177,7 @@ function buildSystemPrompt(opts) {
|
|
|
7161
7177
|
domainBlock,
|
|
7162
7178
|
glossaryBlock,
|
|
7163
7179
|
`RULES:
|
|
7164
|
-
- Preserve ALL-CAPS emphasis, ellipses (...), dashes
|
|
7180
|
+
- Preserve ALL-CAPS emphasis, ellipses (...), dashes, and line-ending punctuation exactly.
|
|
7165
7181
|
- Never merge or split sentences \u2014 one input item = one output item.
|
|
7166
7182
|
- Never add explanatory text, parentheticals, or translator notes.
|
|
7167
7183
|
- Output ONLY valid JSON. No markdown, no code fences, no preamble.`,
|
|
@@ -7276,7 +7292,7 @@ async function executeTranslation(client, sentences, opts) {
|
|
|
7276
7292
|
const batchSize = opts.batchSize ?? 25;
|
|
7277
7293
|
const overlap = opts.contextOverlap ?? 4;
|
|
7278
7294
|
const concurrency = opts.concurrency ?? 8;
|
|
7279
|
-
const model = pickModel(opts
|
|
7295
|
+
const model = pickModel(opts);
|
|
7280
7296
|
const systemPrompt = buildSystemPrompt(opts);
|
|
7281
7297
|
const payloads = buildBatches(sentences, batchSize, overlap);
|
|
7282
7298
|
const tasks = payloads.map(
|
|
@@ -7389,12 +7405,13 @@ function emitVTT(cues) {
|
|
|
7389
7405
|
// src/index.ts
|
|
7390
7406
|
import { readFile, writeFile } from "fs/promises";
|
|
7391
7407
|
import { extname } from "path";
|
|
7392
|
-
var
|
|
7408
|
+
var KNOWN_COSTS = {
|
|
7393
7409
|
"gpt-4o": { input: 2.5, output: 10 },
|
|
7394
7410
|
"gpt-4o-mini": { input: 0.15, output: 0.6 }
|
|
7395
7411
|
};
|
|
7396
7412
|
function estimateCost(model, inputTokens, outputTokens) {
|
|
7397
|
-
const rates =
|
|
7413
|
+
const rates = KNOWN_COSTS[model];
|
|
7414
|
+
if (!rates) return 0;
|
|
7398
7415
|
return inputTokens / 1e6 * rates.input + outputTokens / 1e6 * rates.output;
|
|
7399
7416
|
}
|
|
7400
7417
|
async function translate(fileContent, opts, client) {
|
|
@@ -7405,14 +7422,14 @@ async function translate(fileContent, opts, client) {
|
|
|
7405
7422
|
const sentences = reconstructSentences(cues);
|
|
7406
7423
|
const { translations, totalInputTokens, totalOutputTokens, totalBatches } = await executeTranslation(oi, sentences, opts);
|
|
7407
7424
|
const translatedCues = reanchorCues(cues, sentences, translations);
|
|
7408
|
-
const
|
|
7425
|
+
const resolvedModel = pickModel(opts);
|
|
7409
7426
|
const stats = {
|
|
7410
7427
|
totalCues: cues.length,
|
|
7411
7428
|
totalSentences: sentences.length,
|
|
7412
7429
|
totalBatches,
|
|
7413
7430
|
inputTokens: totalInputTokens,
|
|
7414
7431
|
outputTokens: totalOutputTokens,
|
|
7415
|
-
estimatedCostUsd: estimateCost(
|
|
7432
|
+
estimatedCostUsd: estimateCost(resolvedModel, totalInputTokens, totalOutputTokens),
|
|
7416
7433
|
durationMs: Date.now() - startTime
|
|
7417
7434
|
};
|
|
7418
7435
|
return {
|