feat: add Legal Translation tool (translate.php)

New dedicated tool for translating Norwegian legal documents (Barnevernet
letters, court decisions, correspondence) into the user's chosen language
with legal-terminology annotations.

- translate.php: new tool page with source/target language selectors,
  4-way UI lang switcher, file upload, doc picker, streaming results
- api/translate.php: NDJSON streaming endpoint; Azure GPT-4o-mini with
  legal-aware prompt that preserves Norwegian statute refs verbatim and
  annotates terms with no target-language equivalent; 2-credit cost
- assets/js/translate.js: form handler, NDJSON stream reader, copy button
- assets/css/tools.css: .lt-* styles for translation result + annotations
- includes/i18n.php: 22 lt_* keys × 4 languages; translate entry in nav
- includes/FreeTier.php: translate → 2 credits
- includes/CaseResults.php + case-result.php: translate in eligible tools,
  toolLabel, toolIcon, deriveTitle, rendering block, rerun map

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-24 09:59:06 +02:00
parent 21c092e0d0
commit effd3289b4
8 changed files with 842 additions and 1 deletions
+4
View File
@@ -34,6 +34,7 @@ final class CaseResults
'redact',
'transcribe',
'legal-analysis',
'translate',
];
/** True when the user is on a tier that gets saved results (Plus, Pro, or active Plus trial). */
@@ -240,6 +241,7 @@ final class CaseResults
'redact' => 'Anonymisering',
'transcribe' => 'Transkripsjon',
'legal-analysis' => 'Juridisk analyse',
'translate' => 'Oversettelse',
][$tool] ?? ucfirst($tool);
}
@@ -258,6 +260,7 @@ final class CaseResults
'redact' => '🖊️',
'transcribe' => '🎙️',
'legal-analysis' => '⚖️🇳🇴',
'translate' => '🌐',
][$tool] ?? '📄';
}
@@ -276,6 +279,7 @@ final class CaseResults
'redact' => [$input['text'] ?? null],
'transcribe' => [$input['filename'] ?? null],
'legal-analysis' => [$input['doc_type'] ?? null, $input['text'] ?? null],
'translate' => [$input['source_lang'] ?? null, $input['target_lang'] ?? null, $input['text'] ?? null],
default => [$input['title'] ?? null, $input['query'] ?? null, $input['text'] ?? null],
};
foreach ($candidates as $c) {