Legal Analysis: full language follow-through (UI + LLM)
The tool now respects the chosen UI language end-to-end — even if the source document is Norwegian, a user on EN/UK/PL gets the analysis in their language. Norwegian statute references (barnevernsloven § 4-25, EMK Art. 8) and case names (Strand Lobben mot Norge 37283/13) are kept verbatim because they are proper nouns. LLM (LegalAnalysisAgent.php): - extractIssues: prompt asks for question + brief_context in user's language; statute refs preserved - answerIssue: Norwegian core system prompt (keeps fine-tune precision) + language-coercion line for non-NO; localised context/source labels - synthesise: overall_assessment, next_steps, disclaimer in user's language; explicit per-language disclaimer text - runFullAnalysis empty-case fallback also localised - what_to_check translated per language UI: - 40 new la_* translation keys in i18n.php × 4 languages (NO/EN/UK/PL) - legal-analysis.php: 4-way lang switcher, dbnToolsT() for every label, emits window.DBN_LA_I18N for runtime JS strings - legal-analysis.js: t() helper reads from window.DBN_LA_I18N - layout_footer.php: emits window.DBN_CURRENT_LANG + window.DBN_ADDON_I18N so the legal-analysis add-on button works in the page's language no matter which tool it's invoked from - tools.js add-on: reads from DBN_ADDON_I18N, passes DBN_CURRENT_LANG to /api/legal-analysis.php so server responds in same language Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+53
-16
@@ -6,28 +6,58 @@ $toolKind = 'Deep Legal Q&A';
|
||||
$toolBadge = 'Two-pass';
|
||||
$extraScripts = ['assets/js/legal-analysis.js'];
|
||||
require_once __DIR__ . '/includes/layout.php';
|
||||
|
||||
$laLang = dbnToolsCurrentLanguage();
|
||||
$laT = static fn(string $k): string => dbnToolsT($k, $laLang);
|
||||
$laI18n = [
|
||||
'docTypeAuto' => $laT('la_doc_type_auto'),
|
||||
'runButton' => $laT('la_run_button'),
|
||||
'runButtonBusy' => $laT('la_run_button_busy'),
|
||||
'extractingFiles' => $laT('la_extracting_files'),
|
||||
'needInput' => $laT('la_need_input'),
|
||||
'errorPrefix' => $laT('la_error_prefix'),
|
||||
'serverReturned' => $laT('la_server_returned'),
|
||||
'pass1' => $laT('la_pipeline_pass1'),
|
||||
'pass2' => $laT('la_pipeline_pass2'),
|
||||
'pass3' => $laT('la_pipeline_pass3'),
|
||||
'pass1Extracting' => $laT('la_pass1_extracting'),
|
||||
'pass1Found' => $laT('la_pass1_found'),
|
||||
'pass2Asking' => $laT('la_pass2_asking'),
|
||||
'pass2Answered' => $laT('la_pass2_answered'),
|
||||
'pass3Synthesis' => $laT('la_pass3_synthesis'),
|
||||
'waiting' => $laT('la_waiting'),
|
||||
'searchingCorpus' => $laT('la_searching_corpus'),
|
||||
'askingFinetune' => $laT('la_asking_finetune'),
|
||||
'overall' => $laT('la_overall'),
|
||||
'nextSteps' => $laT('la_next_steps'),
|
||||
'answerHeader' => $laT('la_answer_header'),
|
||||
'legalBasis' => $laT('la_legal_basis'),
|
||||
'extractingStatus' => $laT('la_extracting_status'),
|
||||
'synthesisingStatus' => $laT('la_synthesising_status'),
|
||||
'emptyIssues' => $laT('la_empty_issues'),
|
||||
];
|
||||
?>
|
||||
<form id="laForm" class="tool-form" novalidate>
|
||||
|
||||
<div class="lang-switcher" id="laLangSwitcher" role="group" aria-label="UI language">
|
||||
<button type="button" class="lang-btn la-lang-btn is-active" data-lang="no">🇳🇴 NO</button>
|
||||
<button type="button" class="lang-btn la-lang-btn" data-lang="en">🇬🇧 EN</button>
|
||||
<button type="button" class="lang-btn la-lang-btn <?= $laLang === 'en' ? 'is-active' : '' ?>" data-lang="en">🇬🇧 EN</button>
|
||||
<button type="button" class="lang-btn la-lang-btn <?= $laLang === 'no' ? 'is-active' : '' ?>" data-lang="no">🇳🇴 NO</button>
|
||||
<button type="button" class="lang-btn la-lang-btn <?= $laLang === 'uk' ? 'is-active' : '' ?>" data-lang="uk">🇺🇦 UK</button>
|
||||
<button type="button" class="lang-btn la-lang-btn <?= $laLang === 'pl' ? 'is-active' : '' ?>" data-lang="pl">🇵🇱 PL</button>
|
||||
</div>
|
||||
|
||||
<div class="control-row" id="laDocTypeControl">
|
||||
<span class="control-label">Document type</span>
|
||||
<label><input type="radio" name="laDocType" value="auto" checked> Auto-detect</label>
|
||||
<span class="control-label"><?= htmlspecialchars($laT('la_doc_type_label')) ?></span>
|
||||
<label><input type="radio" name="laDocType" value="auto" checked> <?= htmlspecialchars($laT('la_doc_type_auto')) ?></label>
|
||||
<label><input type="radio" name="laDocType" value="barnevernet"> Barnevernet</label>
|
||||
<label><input type="radio" name="laDocType" value="adopsjon"> Adopsjon</label>
|
||||
<label><input type="radio" name="laDocType" value="emergency"> Akutt-plassering</label>
|
||||
<label><input type="radio" name="laDocType" value="samvær"> Samvær</label>
|
||||
<label><input type="radio" name="laDocType" value="fylkesnemnd"> Fylkesnemnd</label>
|
||||
<label><input type="radio" name="laDocType" value="other"> Annet</label>
|
||||
<label><input type="radio" name="laDocType" value="other"> <?= htmlspecialchars($laT('la_doc_type_other')) ?></label>
|
||||
</div>
|
||||
|
||||
<p class="upload-hint">
|
||||
Engine: <strong>dbn-legal-agent-v3</strong> (Norwegian legal fine-tune on GPU). Expect ~30-60 seconds per issue, up to 5 issues per run.
|
||||
</p>
|
||||
<p class="upload-hint"><?= htmlspecialchars($laT('la_engine_hint')) ?></p>
|
||||
|
||||
<div id="docPickerSection" class="doc-picker-section">
|
||||
<button type="button" id="docPickerBtn" class="doc-picker-btn" aria-haspopup="dialog">
|
||||
@@ -51,27 +81,34 @@ require_once __DIR__ . '/includes/layout.php';
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label class="input-label" for="laInput">Pasted text <small class="control-hint">(optional if file or doc selected)</small></label>
|
||||
<textarea id="laInput" name="text" rows="8" placeholder="Paste a case note, court decision, vedtak, brev, or any legal document text. You can also upload a file or select from My Docs above — at least one source is required."></textarea>
|
||||
<label class="input-label" for="laInput"><?= htmlspecialchars($laT('la_input_label')) ?> <small class="control-hint"><?= htmlspecialchars($laT('la_input_hint')) ?></small></label>
|
||||
<textarea id="laInput" name="text" rows="8" placeholder="<?= htmlspecialchars($laT('la_input_placeholder')) ?>"></textarea>
|
||||
|
||||
<div class="form-footer">
|
||||
<p id="laStatus" class="form-status" role="status" aria-live="polite"></p>
|
||||
<button id="laRunButton" type="submit">Run legal analysis</button>
|
||||
<button id="laRunButton" type="submit"><?= htmlspecialchars($laT('la_run_button')) ?></button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<section id="laResults" class="results" aria-live="polite">
|
||||
<div class="empty-state">
|
||||
<h3>Ready</h3>
|
||||
<p>Upload a document or paste text — the tool will extract up to 5 distinct legal issues, then ask the Norwegian-law fine-tune to answer each one with citations.</p>
|
||||
<p class="upload-hint">Pass 1 uses Azure GPT-4o-mini to spot issues. Pass 2 calls the dbn-legal-agent-v3 fine-tune on ocelot for each one. Pass 3 synthesises the overall picture. A typical run takes 2-5 minutes.</p>
|
||||
<h3><?= htmlspecialchars($laT('la_ready_title')) ?></h3>
|
||||
<p><?= htmlspecialchars($laT('la_ready_intro')) ?></p>
|
||||
<p class="upload-hint"><?= htmlspecialchars($laT('la_ready_pipeline')) ?></p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
window.DBN_LA_I18N = <?= json_encode($laI18n, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP) ?>;
|
||||
window.DBN_LA_LANG = <?= json_encode($laLang) ?>;
|
||||
</script>
|
||||
|
||||
<!-- Hidden stubs so tools.js element refs don't crash on this page -->
|
||||
<div class="is-hidden" id="languageControl" aria-hidden="true">
|
||||
<input type="radio" name="language" value="en">
|
||||
<input type="radio" name="language" value="no" checked>
|
||||
<input type="radio" name="language" value="en" <?= $laLang === 'en' ? 'checked' : '' ?>>
|
||||
<input type="radio" name="language" value="no" <?= $laLang === 'no' ? 'checked' : '' ?>>
|
||||
<input type="radio" name="language" value="uk" <?= $laLang === 'uk' ? 'checked' : '' ?>>
|
||||
<input type="radio" name="language" value="pl" <?= $laLang === 'pl' ? 'checked' : '' ?>>
|
||||
</div>
|
||||
<div class="is-hidden" id="redactionControl" aria-hidden="true"></div>
|
||||
<div class="is-hidden" id="audioZone" aria-hidden="true">
|
||||
|
||||
Reference in New Issue
Block a user