text extraction improvements: editable text and source language hint

- Extracted text in the tag panel is now an editable textarea; a Save
  button appears when the content is dirty and persists edits to the DB
- Source language input added next to Re-translate button; when filled,
  the translation prompt uses "translate from X to Y" for more accurate
  results
- New updateExtractedText() helper and PATCH /api/ai-tagging/fields
  endpoint to support saving edited text
- translateItemText/translateText accept optional sourceLanguage param

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Garret Patti
2026-04-13 10:29:47 -04:00
parent c454d020da
commit e31a9667ef
4 changed files with 138 additions and 51 deletions

View File

@@ -623,7 +623,7 @@ export async function extractItemText(itemKey: string): Promise<{ extractedText:
* Translate the extracted_text of an item into the preferred language.
* Returns the translated text or null if no text to translate.
*/
export async function translateItemText(itemKey: string): Promise<string | null> {
export async function translateItemText(itemKey: string, sourceLanguage?: string): Promise<string | null> {
const libraryId = itemKey.split(':')[0]
const config = getEffectiveAiConfig(libraryId)
const translateModel = config.modelTranslate || config.model
@@ -645,7 +645,7 @@ export async function translateItemText(itemKey: string): Promise<string | null>
const preferredLanguage = getPreferredLanguage()
if (!preferredLanguage) return null
const translatedText = await translateText(config.endpoint, translateModel, row.extracted_text, preferredLanguage, config.promptTranslate)
const translatedText = await translateText(config.endpoint, translateModel, row.extracted_text, preferredLanguage, config.promptTranslate, sourceLanguage)
if (translatedText) {
db.prepare('UPDATE media_items SET extracted_text_translated = ? WHERE item_key = ?').run(translatedText, itemKey)
}
@@ -653,6 +653,14 @@ export async function translateItemText(itemKey: string): Promise<string | null>
return translatedText
}
/**
* Update the extracted_text of an item.
*/
export function updateExtractedText(itemKey: string, text: string): void {
const db = getDb()
db.prepare('UPDATE media_items SET extracted_text = ? WHERE item_key = ?').run(text, itemKey)
}
/**
* Translate text to a target language using the chat API.
* Returns null if the text is already in the target language.
@@ -663,16 +671,22 @@ async function translateText(
text: string,
targetLanguage: string,
customInstruction = '',
sourceLanguage?: string,
): Promise<string | null> {
const systemPrompt = `You are a translator. Determine if the following text is already in ${targetLanguage}. If it is, respond with exactly: [ALREADY_TARGET_LANGUAGE]. If it is not, translate it to ${targetLanguage}.${customInstruction ? ' ' + customInstruction : ''}`
let systemPrompt: string
if (sourceLanguage) {
systemPrompt = `You are a translator. Translate the following text from ${sourceLanguage} to ${targetLanguage}.${customInstruction ? ' ' + customInstruction : ''}`
} else {
systemPrompt = `You are a translator. Determine if the following text is already in ${targetLanguage}. If it is, respond with exactly: [ALREADY_TARGET_LANGUAGE]. If it is not, translate it to ${targetLanguage}.${customInstruction ? ' ' + customInstruction : ''}`
}
const result = await callChatApiText(endpoint, model, systemPrompt, text)
if (result === '[ALREADY_TARGET_LANGUAGE]' || !result) {
if (!sourceLanguage && (result === '[ALREADY_TARGET_LANGUAGE]' || !result)) {
return null
}
return result
return result || null
}
/**