const translations = { en: { pageTitle: 'News Verification', loginTitle: 'Sign in', loginSubtitle: 'Use one of the sample accounts to continue.', loginContact: 'Please contact quoc-nguyen@nict.go.jp for login information.', usernameLabel: 'Username', passwordLabel: 'Password', usernamePlaceholder: 'Enter your username', passwordPlaceholder: 'Enter your password', loginButton: 'Continue', loginError: 'Incorrect credentials. Please try again.', fakeAccountsLabel: 'Sample accounts', logoutButton: 'Log out', languageLabel: 'Select Language', languageJapanese: '日本語', languageEnglish: 'English', languageVietnamese: 'Tiếng Việt', uploadMediaLabel: 'Upload media files', uploadHint: 'Click here to upload your files or drag.', supportedFormats: 'Supported Format: PNG, JPG, JPEG, GIF, WEBP, SVG, MOV, MP4', additionalInformation: 'Additional Information', socialMediaPostUrl: 'Social Media Post URL', titleNewsArticle: 'Title of the News Article', locationLabel: 'Location where the news took place', categoryLabel: 'Category of the News (e.g., Politics, Health, Technology)', violenceLevelLabel: 'Violence Level', placeholderAdditionalText: 'Enter text here...', placeholderSocialUrl: 'Enter URL here...', placeholderNewsTitle: 'Enter title here...', placeholderLocation: 'Enter location here...', placeholderCategory: 'Enter category here...', placeholderViolence: 'Enter violence level here...', clearButton: 'Clear', submitButton: 'Submit', processingLabel: 'Processing...', saveButton: 'Save', caseSummaryTab: 'Case Summary', contentClassificationTab: 'Content Classification', verifiedEvidenceTab: 'Verified Evidence', forensicAnalysisTab: 'Forensic Analysis', otherEvidenceTab: 'Other Evidence & Findings', sourceDetailsTab: 'Source Details', whereTab: 'Where', whenTab: 'When', whoTab: 'Who', whyTab: 'Why', verificationPlaceholder: 'Verification result comes here...', uploadRequirement: 'Please upload at least one media file', languageChangeConfirmTitle: 'Change language?', languageChangeConfirmMessage: 'Changing the language will reset the system to its initial state. Do you want to continue?', languageChangeConfirm: 'Confirm', languageChangeCancel: 'Cancel', processingError: 'The server is currently experiencing high traffic. Please try again later.', allowedFormats: 'Only the following formats are accepted: ', noDataToSave: 'No data to save.', downloadPrepareFailedNetwork: 'Unable to prepare the download. Please check your connection and try again.', downloadPrepareFailed: 'Unable to prepare the download. Please try again later.', notificationTitle: 'Notification', movNotSupported: 'The system does not support preview for .mov files.', videoTagNotSupported: 'Your browser does not support the video tag.', previewUnavailable: 'Preview not available for this file type.', imageLabel: 'Image', feedbackHeader: 'Help us improve!', sectionGeneralQuestions: '1. General Questions', q0Title: '1.1 What is your profession?', q0Option1: 'Journalist / Fact Checker', q0Option2: 'Researcher / Student', q0Option3: 'Law Enforcement / OSINT', q0Option4: 'General User', q1Title: '1.2 How accurate was the conclusion?', q1Option1: 'Spot on', q1Option2: 'Mostly correct', q1Option3: 'Mixed results', q1Option4: 'Missed key context', q1Option5: 'Completely wrong', q2Title: '1.3 Did the algorithm correctly classify this content?', q2Option2: 'No - It was actually Real', q2Option3: 'No - It was actually AI-Generated', yesOption: 'Yes', q3Title: '1.4 Were the provided links relevant?', q3Option1: 'All links worked and were relevant', q3Option2: 'Some links were broken/dead', q3Option3: 'Links were unrelated to the topic', q4Title: '1.5 Did the report contain any hallucinations (made-up facts)?', q4Option1: 'No issues', q4Option2: 'Invented dates/times', q4Option3: 'Invented people/names', q4Option4: 'Citations that do not exist', sectionDetailedQuestions: '2. Detailed Questions', q5Title: '2.1 Did the app clearly identify the main people, organizations, or sources involved in the news item?', veryClear: 'Very Clear', clearOption: 'Clear', moderatelyClear: 'Moderately Clear', slightlyClear: 'Slightly Clear', notClear: 'Not Clear', q6Title: '2.2 Did the app correctly identify the central claim or action of the news story?', partially: 'Partially', noOption: 'No', q7Title: '2.3 Did the app clearly show when the original event/information was first published or occurred?', q8Title: '2.4 Was the location of the event ("Where") specified and verified by the app\'s output?', notApplicable: 'Not Applicable', q9Title: '2.5 How well did the app explain the motive or purpose behind the original claim (e.g., persuasion, profit, error)?', clearlyExplained: 'Clearly Explained', wellExplained: 'Well Explained', moderatelyExplained: 'Moderately Explained', poorlyExplained: 'Poorly Explained', notExplained: 'Not Explained', q10Title: '2.6 Help us improve. If the verification failed, what did we miss?', q10Placeholder: 'Share any details we missed...', sendButton: 'Send', sendingLabel: 'Sending...', feedbackSuccess: 'Feedback sent successfully!', liveChatText: 'Send your feedback', statusHeaderCompleted: '{completed}/{total} completed', statusPercent: '{percent}%', stepTitle: 'Step {step}:', waitingLabel: 'Waiting', calculatingLabel: 'Calculating...', minutesRemaining: '{minutes} minutes remaining.', processingEstimate: 'Processing - Estimated time: {minutes} minutes.', beginsAfterStep1: 'Begins after Step 1 is completed (Estimated time: {minutes} minutes).', completedLabel: 'Completed', serviceLabel: 'Service {index}/{total}: {name}', serviceGeolocation: 'Location Analysis', serviceTimestamp: 'Time Analysis', serviceAIGVDetection: 'AI-Generated Content Detection', serviceReport: 'Comprehensive Report', statusProcessing: 'Processing', statusCompleted: 'Completed', statusPending: 'Pending', }, ja: { pageTitle: 'ニュース検証', loginTitle: 'サインイン', loginSubtitle: '以下のサンプルアカウントのいずれかでログインしてください。', loginContact: 'ログイン情報については quoc-nguyen@nict.go.jp までご連絡ください。', usernameLabel: 'ユーザー名またはメール', passwordLabel: 'パスワード', usernamePlaceholder: 'ユーザー名またはメールを入力', passwordPlaceholder: 'パスワードを入力', loginButton: '続行', loginError: '認証情報が正しくありません。もう一度お試しください。', fakeAccountsLabel: 'サンプルアカウント', logoutButton: 'ログアウト', languageLabel: '言語を選択', languageJapanese: '日本語', languageEnglish: 'English', languageVietnamese: 'Tiếng Việt', uploadMediaLabel: 'メディアファイルをアップロード', uploadHint: 'ここをクリックしてファイルをアップロードするか、ドラッグしてください。', supportedFormats: '対応形式: PNG, JPG, JPEG, GIF, WEBP, SVG, MOV, MP4', additionalInformation: '追加情報', socialMediaPostUrl: 'ソーシャルメディア投稿のURL', titleNewsArticle: '記事のタイトル', locationLabel: 'ニュースが起きた場所', categoryLabel: 'ニュースのカテゴリー(例: 政治、健康、テクノロジー)', violenceLevelLabel: '暴力レベル', placeholderAdditionalText: 'ここに入力してください...', placeholderSocialUrl: 'URLを入力してください...', placeholderNewsTitle: 'タイトルを入力してください...', placeholderLocation: '場所を入力してください...', placeholderCategory: 'カテゴリーを入力してください...', placeholderViolence: '暴力レベルを入力してください...', clearButton: 'クリア', submitButton: '送信', processingLabel: '処理中...', saveButton: '保存', caseSummaryTab: '概要', contentClassificationTab: 'コンテンツ分類', verifiedEvidenceTab: '検証済みの証拠', forensicAnalysisTab: 'フォレンジック分析', otherEvidenceTab: 'その他の証拠・所見', sourceDetailsTab: 'ソース詳細', whereTab: 'どこ', whenTab: 'いつ', whoTab: 'だれ', whyTab: 'なぜ', verificationPlaceholder: '検証結果はここに表示されます...', uploadRequirement: '少なくとも1つのファイルをアップロードしてください。', languageChangeConfirmTitle: '言語を変更しますか?', languageChangeConfirmMessage: '言語を変更するとシステムが初期状態にリセットされます。続行しますか?', languageChangeConfirm: '確認', languageChangeCancel: 'キャンセル', processingError: '現在、サーバーが混雑しています。しばらくしてから再度お試しください。', allowedFormats: '利用可能な形式: ', noDataToSave: '保存するデータがありません。', downloadPrepareFailedNetwork: 'ダウンロードの準備に失敗しました。接続を確認して再試行してください。', downloadPrepareFailed: 'ダウンロードの準備に失敗しました。後でもう一度お試しください。', notificationTitle: '通知', movNotSupported: '.mov ファイルのプレビューには対応していません。', videoTagNotSupported: 'お使いのブラウザは video タグをサポートしていません。', previewUnavailable: 'このファイルタイプはプレビューできません。', imageLabel: '画像', feedbackHeader: '改善にご協力ください!', sectionGeneralQuestions: '1. 一般的な質問', q0Title: '1.1 ご職業は何ですか?', q0Option1: 'ジャーナリスト / ファクトチェッカー', q0Option2: '研究者 / 学生', q0Option3: '法執行機関 / OSINT', q0Option4: '一般ユーザー', q1Title: '1.2 結論の正確さはいかがでしたか?', q1Option1: 'ほぼ完璧', q1Option2: 'だいたい正しい', q1Option3: '結果が混在', q1Option4: '重要な文脈を見落とした', q1Option5: 'まったく違う', q2Title: '1.3 アルゴリズムはこのコンテンツを正しく分類しましたか?', q2Option2: 'いいえ - 実際には本物でした', q2Option3: 'いいえ - 実際にはAI生成でした', yesOption: 'はい', q3Title: '1.4 提供されたリンクは有用でしたか?', q3Option1: 'すべてのリンクが有効で関連していた', q3Option2: '一部のリンクが無効/切れていた', q3Option3: 'リンクがトピックと無関係だった', q4Title: '1.5 レポートに事実と異なる内容(幻覚)はありましたか?', q4Option1: '問題なし', q4Option2: '日付/時間の誤り', q4Option3: '人物名の誤り', q4Option4: '存在しない引用/出典', sectionDetailedQuestions: '2. 詳細な質問', q5Title: '2.1 ニュースに関わる主要な人物・組織・情報源を明確に示していましたか?', veryClear: 'とても明確', clearOption: '明確', moderatelyClear: 'やや明確', slightlyClear: '少し明確', notClear: '不明瞭', q6Title: '2.2 ストーリーの中心となる主張や行動を正しく特定していましたか?', partially: '部分的に', noOption: 'いいえ', q7Title: '2.3 元の情報がいつ公開・発生したかを明確に示していましたか?', q8Title: '2.4 イベントの場所(「どこ」)は特定・検証されていましたか?', notApplicable: '該当なし', q9Title: '2.5 もとの主張の動機や目的(説得、利益、誤りなど)をどの程度説明していましたか?', clearlyExplained: 'とても明確に説明した', wellExplained: '十分説明した', moderatelyExplained: 'ある程度説明した', poorlyExplained: '説明が不十分', notExplained: '説明なし', q10Title: '2.6 改善のため、検証が失敗した場合に不足していた点を教えてください。', q10Placeholder: '見落としや改善点を入力してください...', sendButton: '送信', sendingLabel: '送信中...', feedbackSuccess: 'フィードバックを送信しました!', liveChatText: 'フィードバックを送る', statusHeaderCompleted: '{completed}/{total} 完了', statusPercent: '{percent}%', stepTitle: 'ステップ {step}:', waitingLabel: '待機中', calculatingLabel: '計算中...', minutesRemaining: '残り {minutes} 分。', processingEstimate: '処理中 - 推定時間: {minutes} 分。', beginsAfterStep1: 'ステップ1完了後に開始(推定時間: {minutes} 分)。', completedLabel: '完了', serviceLabel: 'サービス {index}/{total}: {name}', serviceGeolocation: '位置情報分析', serviceTimestamp: '時間分析', serviceAIGVDetection: 'AI生成コンテンツ検出', serviceReport: '総合レポート', statusProcessing: '処理中', statusCompleted: '完了', statusPending: '待機中', }, vi: { pageTitle: 'Kiểm chứng tin tức', loginTitle: 'Đăng nhập', loginSubtitle: 'Dùng một trong các tài khoản mẫu để tiếp tục.', loginContact: 'Vui lòng liên hệ quoc-nguyen@nict.go.jp để nhận thông tin đăng nhập.', usernameLabel: 'Tên đăng nhập hoặc email', passwordLabel: 'Mật khẩu', usernamePlaceholder: 'Nhập tên đăng nhập hoặc email', passwordPlaceholder: 'Nhập mật khẩu', loginButton: 'Tiếp tục', loginError: 'Sai thông tin đăng nhập, vui lòng thử lại.', fakeAccountsLabel: 'Tài khoản mẫu', logoutButton: 'Đăng xuất', languageLabel: 'Chọn Ngôn Ngữ', languageJapanese: '日本語', languageEnglish: 'English', languageVietnamese: 'Tiếng Việt', uploadMediaLabel: 'Tải lên tệp phương tiện', uploadHint: 'Nhấn vào đây để tải tệp lên hoặc kéo thả.', supportedFormats: 'Định dạng hỗ trợ: PNG, JPG, JPEG, GIF, WEBP, SVG, MOV, MP4', additionalInformation: 'Thông tin bổ sung', socialMediaPostUrl: 'Đường dẫn bài đăng mạng xã hội', titleNewsArticle: 'Tiêu đề bài báo', locationLabel: 'Địa điểm xảy ra sự kiện', categoryLabel: 'Chủ đề tin tức (ví dụ: Chính trị, Sức khỏe, Công nghệ)', violenceLevelLabel: 'Mức độ bạo lực', placeholderAdditionalText: 'Nhập nội dung tại đây...', placeholderSocialUrl: 'Nhập đường dẫn tại đây...', placeholderNewsTitle: 'Nhập tiêu đề tại đây...', placeholderLocation: 'Nhập địa điểm tại đây...', placeholderCategory: 'Nhập chủ đề tại đây...', placeholderViolence: 'Nhập mức độ bạo lực tại đây...', clearButton: 'Xóa', submitButton: 'Gửi', processingLabel: 'Đang xử lý...', saveButton: 'Lưu', caseSummaryTab: 'Tóm tắt', contentClassificationTab: 'Phân loại nội dung', verifiedEvidenceTab: 'Bằng chứng đã xác minh', forensicAnalysisTab: 'Phân tích pháp chứng', otherEvidenceTab: 'Bằng chứng & phát hiện khác', sourceDetailsTab: 'Chi tiết nguồn', whereTab: 'Ở đâu', whenTab: 'Khi nào', whoTab: 'Ai', whyTab: 'Tại sao', verificationPlaceholder: 'Kết quả kiểm chứng sẽ hiển thị tại đây...', uploadRequirement: 'Vui lòng tải lên ít nhất một tệp.', languageChangeConfirmTitle: 'Đổi ngôn ngữ?', languageChangeConfirmMessage: 'Thay đổi ngôn ngữ sẽ đặt lại hệ thống về trạng thái ban đầu. Bạn có muốn tiếp tục?', languageChangeConfirm: 'Xác nhận', languageChangeCancel: 'Hủy', processingError: 'Máy chủ hiện đang quá tải. Vui lòng thử lại sau.', allowedFormats: 'Chỉ chấp nhận các định dạng: ', noDataToSave: 'Không có dữ liệu để lưu.', downloadPrepareFailedNetwork: 'Không thể chuẩn bị tệp tải xuống. Vui lòng kiểm tra kết nối và thử lại.', downloadPrepareFailed: 'Không thể chuẩn bị tệp tải xuống. Vui lòng thử lại sau.', notificationTitle: 'Thông báo', movNotSupported: 'Hệ thống chưa hỗ trợ xem trước tệp .mov.', videoTagNotSupported: 'Trình duyệt của bạn không hỗ trợ thẻ video.', previewUnavailable: 'Không thể xem trước loại tệp này.', imageLabel: 'Hình', feedbackHeader: 'Giúp chúng tôi cải thiện!', sectionGeneralQuestions: '1. Câu hỏi chung', q0Title: '1.1 Nghề nghiệp của bạn là gì?', q0Option1: 'Nhà báo / Kiểm chứng viên', q0Option2: 'Nhà nghiên cứu / Sinh viên', q0Option3: 'Cơ quan thực thi pháp luật / OSINT', q0Option4: 'Người dùng thông thường', q1Title: '1.2 Mức độ chính xác của kết luận?', q1Option1: 'Rất chính xác', q1Option2: 'Khá chính xác', q1Option3: 'Kết quả lẫn lộn', q1Option4: 'Thiếu ngữ cảnh quan trọng', q1Option5: 'Hoàn toàn sai', q2Title: '1.3 Thuật toán phân loại nội dung này có chính xác không?', q2Option2: 'Không - Thực ra là thật', q2Option3: 'Không - Thực ra là do AI tạo', yesOption: 'Có', q3Title: '1.4 Các liên kết được cung cấp có phù hợp không?', q3Option1: 'Tất cả liên kết hoạt động và phù hợp', q3Option2: 'Một số liên kết bị hỏng/không hoạt động', q3Option3: 'Liên kết không liên quan chủ đề', q4Title: '1.5 Báo cáo có thông tin bịa đặt (hallucination) không?', q4Option1: 'Không có vấn đề', q4Option2: 'Bịa ngày/giờ', q4Option3: 'Bịa tên người', q4Option4: 'Nguồn/citation không tồn tại', sectionDetailedQuestions: '2. Câu hỏi chi tiết', q5Title: '2.1 Ứng dụng có xác định rõ người, tổ chức hoặc nguồn tin chính không?', veryClear: 'Rất rõ ràng', clearOption: 'Rõ ràng', moderatelyClear: 'Khá rõ', slightlyClear: 'Hơi rõ', notClear: 'Không rõ', q6Title: '2.2 Ứng dụng có xác định đúng tuyên bố hoặc hành động chính của câu chuyện không?', partially: 'Một phần', noOption: 'Không', q7Title: '2.3 Ứng dụng có hiển thị rõ thời điểm sự kiện/thông tin gốc được công bố không?', q8Title: '2.4 Địa điểm sự kiện ("Ở đâu") có được chỉ rõ và xác minh không?', notApplicable: 'Không áp dụng', q9Title: '2.5 Ứng dụng giải thích động cơ/mục đích đằng sau tuyên bố gốc tốt thế nào?', clearlyExplained: 'Giải thích rõ ràng', wellExplained: 'Giải thích tốt', moderatelyExplained: 'Giải thích vừa phải', poorlyExplained: 'Giải thích kém', notExplained: 'Không giải thích', q10Title: '2.6 Hãy giúp chúng tôi cải thiện. Nếu kiểm chứng sai, chúng tôi bỏ lỡ điều gì?', q10Placeholder: 'Cho biết điều chúng tôi bỏ sót...', sendButton: 'Gửi', sendingLabel: 'Đang gửi...', feedbackSuccess: 'Gửi phản hồi thành công!', liveChatText: 'Gửi phản hồi của bạn', statusHeaderCompleted: '{completed}/{total} đã xong', statusPercent: '{percent}%', stepTitle: 'Bước {step}:', waitingLabel: 'Đang chờ', calculatingLabel: 'Đang tính toán...', minutesRemaining: 'Còn khoảng {minutes} phút.', processingEstimate: 'Đang xử lý - Thời gian ước tính: {minutes} phút.', beginsAfterStep1: 'Bắt đầu sau khi Bước 1 hoàn tất (Ước tính: {minutes} phút).', completedLabel: 'Hoàn tất', serviceLabel: 'Dịch vụ {index}/{total}: {name}', serviceGeolocation: 'Phân tích vị trí', serviceTimestamp: 'Phân tích thời gian', serviceAIGVDetection: 'Phát hiện nội dung do AI tạo', serviceReport: 'Báo cáo tổng hợp', statusProcessing: 'Đang xử lý', statusCompleted: 'Hoàn tất', statusPending: 'Đang chờ', }, }; let currentLanguage = 'en'; let initialized = false; let languageChangeGuard = null; const listeners = new Set(); export function t(key) { const langPack = translations[currentLanguage] || translations.en; return langPack?.[key] ?? translations.en?.[key] ?? key; } function applyTranslations() { if (typeof document === 'undefined') return; document.documentElement.lang = currentLanguage || 'en'; document.title = t('pageTitle'); const textNodes = document.querySelectorAll('[data-i18n]'); textNodes.forEach((el) => { if (el.dataset.i18nDynamic === 'true') return; const key = el.dataset.i18n; const value = t(key); if (value === undefined) return; if (el.dataset.i18nTarget === 'html') { el.innerHTML = value; } else { el.textContent = value; } }); const placeholders = document.querySelectorAll('[data-i18n-placeholder]'); placeholders.forEach((el) => { const key = el.dataset.i18nPlaceholder; const value = t(key); if (value === undefined) return; if ('placeholder' in el) { el.placeholder = value; } }); const languageSelect = document.getElementById('language-select'); if (languageSelect) { languageSelect.value = currentLanguage; } } export function setLanguage(lang, options = {}) { if (!translations[lang]) return; if ( !options.force && typeof languageChangeGuard === 'function' && languageChangeGuard(lang, currentLanguage) === false ) { return; } currentLanguage = lang; localStorage.setItem('appLanguage', currentLanguage); applyTranslations(); listeners.forEach((cb) => cb(currentLanguage)); } export function setLanguageGuard(fn) { languageChangeGuard = typeof fn === 'function' ? fn : null; } export function getCurrentLanguage() { return currentLanguage; } export function bindLanguageSelector(selector) { const selectElement = typeof selector === 'string' ? document.querySelector(selector) : selector; if (!selectElement) return; selectElement.value = currentLanguage; selectElement.addEventListener('change', (event) => { setLanguage(event.target.value); }); } export function onLanguageChange(callback) { if (typeof callback !== 'function') return () => {}; listeners.add(callback); return () => listeners.delete(callback); } export function initI18n() { if (initialized) { applyTranslations(); return; } initialized = true; const saved = localStorage.getItem('appLanguage'); if (saved && translations[saved]) { currentLanguage = saved; } applyTranslations(); }