video_translate/src/server/geminiTranslation.ts
2026-03-18 11:42:00 +08:00

72 lines
2.2 KiB
TypeScript

import { GoogleGenAI, Type } from '@google/genai';
import { TranslationSentenceInput, TranslationSentenceResult } from './subtitlePipeline';
export interface TranslateSentencesWithGeminiOptions {
targetLanguage: string;
sentences: TranslationSentenceInput[];
generateContent: (request: any) => Promise<{ text?: string | null }>;
}
const stripJsonFences = (text: string) => text.replace(/```json\n?|\n?```/g, '').trim();
export const translateSentencesWithGemini = async ({
targetLanguage,
sentences,
generateContent,
}: TranslateSentencesWithGeminiOptions): Promise<TranslationSentenceResult[]> => {
const prompt = `Translate the following subtitle segments into ${targetLanguage}.
CRITICAL INSTRUCTIONS:
1. You MUST return a valid JSON array of objects.
2. Keep the EXACT same "id" and "originalText" for each segment.
3. Add a "translatedText" field with the translation.
4. Add a "speaker" field if you can infer a useful display label.
5. DO NOT wrap in markdown blocks. Return ONLY JSON.
Input JSON:
${JSON.stringify(sentences, null, 2)}`;
const response = await generateContent({
model: 'gemini-2.5-flash',
contents: [{ role: 'user', parts: [{ text: prompt }] }],
config: {
responseMimeType: 'application/json',
responseSchema: {
type: Type.ARRAY,
items: {
type: Type.OBJECT,
properties: {
id: { type: Type.STRING },
translatedText: { type: Type.STRING },
speaker: { type: Type.STRING },
},
required: ['id', 'translatedText'],
},
},
},
});
const text = stripJsonFences(response.text || '[]');
return JSON.parse(text) as TranslationSentenceResult[];
};
export const createGeminiSentenceTranslator = ({
apiKey,
}: {
apiKey: string;
}) => {
const ai = new GoogleGenAI({ apiKey });
return async (
sentences: TranslationSentenceInput[],
targetLanguage: string,
): Promise<TranslationSentenceResult[]> =>
translateSentencesWithGemini({
targetLanguage,
sentences,
generateContent: async (request) => {
const response = await ai.models.generateContent(request);
return { text: response.text };
},
});
};