export type Language = "vi" | "en"; const languageStorageKey = "carepath-demo-language"; export const copy = { vi: { title: "CarePath | Phiên dịch khám bệnh trực tiếp", breadcrumb: "Đường dẫn sản phẩm", status: "Bản mô phỏng tương tác", allProducts: "Tất cả chức năng", language: "Ngôn ngữ thanh sản phẩm", productName: "Phiên dịch khám bệnh trực tiếp", consent: { eyebrow: "Phiên dịch khám bệnh trực tiếp", heading: "Phiên dịch khám bệnh trực tiếp", description: "Dịch hai chiều giữa bác sĩ tiếng Việt và bệnh nhân tiếng Anh trong lúc khám.", steps: "Cách phiên dịch diễn ra", doctorSpeaks: "Bác sĩ nói tiếng Việt", aiToPatient: "AI dịch sang tiếng Anh cho bệnh nhân", patientSpeaks: "Bệnh nhân trả lời bằng tiếng Anh", aiToDoctor: "AI dịch lại sang tiếng Việt cho bác sĩ", limitation: "CarePath chỉ hỗ trợ phiên dịch, không đưa ra chẩn đoán hoặc tư vấn điều trị.", acknowledgements: "Xác nhận trước khi bắt đầu", aiDisclosure: "Tôi đã được giải thích rằng bản dịch do AI tạo ra có thể có lỗi và cần được bác sĩ kiểm tra trước khi sử dụng.", interpreterRight: "Tôi đã được giải thích rằng có thể yêu cầu phiên dịch viên trực tiếp bất cứ lúc nào.", start: "Bắt đầu phiên dịch", starting: "Đang bắt đầu…", retry: "Thử lại để bắt đầu phiên dịch", startError: "Không thể bắt đầu phiên dịch. Vui lòng kiểm tra kết nối máy chủ và thử lại.", startHint: "Đánh dấu cả hai xác nhận để bắt đầu.", startReadyHint: "Đã đủ xác nhận. Bạn có thể bắt đầu phiên dịch.", }, deviceCheck: { title: "Kiểm tra micrô trước khi bắt đầu", body: "Kiểm tra micrô để dùng chế độ nói, hoặc tiếp tục bằng văn bản.", microphone: "Micrô", test: "Kiểm tra micrô", retry: "Thử lại", idle: "Chưa kiểm tra micrô.", ready: "Micrô đã sẵn sàng.", level: "Mức âm thanh", scaleLow: "Nhỏ", scaleHigh: "To", continueVoice: "Tiếp tục bằng micrô", continueTyped: "Tiếp tục bằng văn bản", unavailable: "Không tìm thấy micrô. Bạn có thể thử lại hoặc tiếp tục bằng văn bản.", denied: "Không thể dùng micrô. Hãy cấp quyền rồi thử lại, hoặc tiếp tục bằng văn bản.", }, workspace: { title: "Phiên dịch khám bệnh trực tiếp", mockDisclaimer: "Bản mô phỏng này không dùng cho chăm sóc lâm sàng thực tế.", requestInterpreter: "Yêu cầu phiên dịch viên trực tiếp", escalationTitle: "Đã yêu cầu phiên dịch viên trực tiếp", escalationBody: "Tạm dừng phát bản dịch AI và kết nối phiên dịch viên đủ điều kiện trước khi tiếp tục.", controls: "Điều khiển nói", holdToTalk: "Nhấn giữ để nói", doctor: "Bác sĩ", patient: "Bệnh nhân", vietnamese: "Tiếng Việt", english: "Tiếng Anh", speaker: "Người nói", regionInactiveHint: "Chọn khu vực này để nói hoặc nhập cho người này.", typedFallback: "Nhập văn bản thay thế", typedPlaceholder: "Nhập nội dung cần dịch", send: "Gửi", connecting: "Đang kết nối", connected: "Đã kết nối", disconnected: "Đã ngắt kết nối", inputReady: "Sẵn sàng", inputProcessing: "Đang xử lý", inputDelivered: "Đã chuyển bản dịch", inputReviewRequired: "Cần bác sĩ xác nhận", recording: "Đang ghi lời nói của {speaker}", connectionNotReady: "Kết nối chưa sẵn sàng.", connectionNotReadyType: "Kết nối chưa sẵn sàng. Hãy nhập văn bản thay thế.", microphoneUnavailable: "Không thể dùng micrô. Hãy nhập văn bản thay thế.", microphoneDenied: "Micrô chưa được cấp quyền. Hãy nhập văn bản thay thế.", lowConfidence: "Độ tin cậy thấp. Hãy nói lại hoặc nhập văn bản.", repeatTurn: "Nói lại", typeTurn: "Nhập văn bản", confirmationRequired: "Cần bác sĩ xác nhận trước khi phát cho bệnh nhân.", confirmFailed: "Không thể xác nhận lượt dịch.", escalationFailed: "Không thể đánh dấu đã yêu cầu phiên dịch viên.", playbackSuppressed: "Đã tạm dừng phát tự động. Chỉ tiếp tục sau khi bác sĩ xác nhận phát cục bộ.", resumePlayback: "Tiếp tục phát tự động trên thiết bị này", turnError: "Lượt dịch gặp lỗi. Hãy thử lại hoặc nhập văn bản.", pipelineFailureTitle: "Lượt dịch đã được chặn vì xử lý thất bại", sourceUnavailable: "Chưa thể tạo văn bản nguồn từ âm thanh.", translationUnavailable: "Chưa có bản dịch để phát cho người bệnh.", pipelineFailureAction: "Kiểm tra nội dung nguồn nếu có, thử lại hoặc yêu cầu phiên dịch viên trực tiếp.", lifecycle: "Kết thúc phiên", endSession: "Kết thúc phiên", withdrawConsent: "Rút lại xác nhận", deleteSession: "Xóa dữ liệu phiên", deleteConfirm: "Xóa toàn bộ dữ liệu của phiên này?", lifecycleFailed: "Không thể hoàn tất thao tác. Phiên vẫn được giữ ở trạng thái an toàn.", retry: "Thử lại", confirmations: "Xác nhận của bác sĩ", confirmationRequiredLabel: "Cần bác sĩ xác nhận", interpreterRecommended: "Khuyến nghị phiên dịch viên trực tiếp", readback: "Đọc lại để xác nhận", entity: "Thông tin", source: "Bản gốc", translation: "Bản dịch", flags: "Lưu ý", editTranslation: "Sửa bản dịch", confirm: "Xác nhận", saveEdit: "Lưu chỉnh sửa", escalate: "Yêu cầu phiên dịch viên", transcript: "Nội dung trao đổi", turns: "lượt", noTurns: "Chưa có lượt trao đổi.", sourceLanguage: "Ngôn ngữ gốc", targetLanguage: "Ngôn ngữ dịch", blocked: "Đã chặn, chờ bác sĩ xác nhận.", patientSafeMask: "Nội dung đang chờ bác sĩ xem xét.", openReview: "Mở bản xem xét", reviewOpened: "Đã mở bản xem xét dành cho bác sĩ.", draftTranslation: "Bản dịch nháp", riskReason: "Lý do cần xem xét", criticalEntities: "Thông tin cần kiểm tra", confirmAndPlay: "Xác nhận và phát", saveEditAndPlay: "Lưu chỉnh sửa và phát", editRequired: "Bản dịch chỉnh sửa không được để trống.", riskSpans: "Thông tin cần kiểm tra", feedback: "Báo lỗi bản dịch", feedbackSaved: "Đã lưu phản hồi.", reason: "Lý do", comment: "Ghi chú", submitFeedback: "Gửi phản hồi", risk: { low: "Thấp", medium: "Trung bình", high: "Cao", critical: "Nghiêm trọng", other: "Cần kiểm tra" }, status: { delivered: "Đã chuyển", awaiting_confirm: "Chờ xác nhận", confirmed: "Đã xác nhận", blocked: "Đã chặn", other: "Đang xử lý" }, feedbackReason: { wrong_term: "Sai thuật ngữ", wrong_meaning: "Sai nghĩa", missing: "Thiếu nội dung", other: "Khác" }, riskKind: { dose_number: "Liều lượng", dose: "Liều lượng", other: "Thông tin cần kiểm tra" }, }, demo: { eyebrow: "Mô phỏng trước khi xác nhận", heading: "Xem cách phiên dịch được xử lý", start: "Xem mô phỏng", next: "Xem tình huống tiếp theo", restart: "Xem lại mô phỏng", step: "Tình huống {current}/3", normal: "Bản dịch được chuyển", lowConfidence: "Độ tin cậy thấp", blocked: "Đã chặn, chờ bác sĩ xác nhận", normalSource: "Tôi bị đau đầu từ sáng nay.", normalTranslation: "I have had a headache since this morning.", lowSource: "Tôi uống một viên... hay nửa viên?", lowTranslation: "I took one tablet... or half a tablet?", blockedSource: "Uống 500 mg mỗi ngày.", blockedTranslation: "Take 500 mg every day.", }, intent: { moment: "Trong buổi khám", roleQuestion: "Hôm nay bạn là ai?", doctor: "Bác sĩ", clinicStaff: "Nhân viên phòng khám", directionQuestion: "Bạn cần dịch theo chiều nào?", viToEn: "Tiếng Việt → Tiếng Anh", enToVi: "Tiếng Anh → Tiếng Việt", continue: "Tiếp tục", skip: "Bỏ qua và dùng lựa chọn đề xuất", recommended: "Đề xuất", }, stepper: { label: "Tiến trình bắt đầu phiên dịch", language: "Ngôn ngữ", about: "Về bạn", consent: "Xác nhận", device: "Kiểm tra thiết bị", start: "Bắt đầu", complete: "Đã hoàn thành", current: "Đang thực hiện", upcoming: "Tiếp theo", }, checklist: { title: "Danh sách an toàn phiên đầu", delivered: "Đã chuyển lượt đầu tiên", confirmed: "Đã hoàn tất xác nhận", escalation: "Đã xác định nút yêu cầu phiên dịch viên", dismiss: "Ẩn danh sách" }, }, en: { title: "CarePath | Medical Interpreter", breadcrumb: "Product breadcrumb", status: "Interactive mock simulation", allProducts: "All products", language: "Product bar language", productName: "Medical Interpreter", consent: { eyebrow: "Medical Interpreter", heading: "Live medical interpretation", description: "Two-way interpretation between a Vietnamese-speaking clinician and an English-speaking patient during a visit.", steps: "How interpretation works", doctorSpeaks: "The clinician speaks Vietnamese", aiToPatient: "AI translates to English for the patient", patientSpeaks: "The patient replies in English", aiToDoctor: "AI translates back to Vietnamese for the clinician", limitation: "CarePath provides translation support only; it does not provide diagnoses or treatment advice.", acknowledgements: "Confirm before starting", aiDisclosure: "I have been told that AI-generated translations can contain errors and must be reviewed by the clinician before use.", interpreterRight: "I have been told that a human interpreter can be requested at any time.", start: "Start interpreting", starting: "Starting…", retry: "Try starting interpretation again", startError: "Unable to start interpretation. Check the server connection and try again.", startHint: "Check both confirmations to start.", startReadyHint: "Both confirmations complete. You can start interpreting.", }, deviceCheck: { title: "Check the microphone before starting", body: "Test a microphone for voice mode, or continue with typed turns.", microphone: "Microphone", test: "Test microphone", retry: "Try again", idle: "Microphone not tested yet.", ready: "Microphone is ready.", level: "Sound level", scaleLow: "Quiet", scaleHigh: "Loud", continueVoice: "Continue with microphone", continueTyped: "Continue with typed turns", unavailable: "No microphone was found. Try again or continue with typed turns.", denied: "The microphone is unavailable. Grant permission and try again, or continue with typed turns.", }, workspace: { title: "Live medical interpretation", mockDisclaimer: "This simulation is not for real clinical care.", requestInterpreter: "Request a human interpreter", escalationTitle: "Human interpreter requested", escalationBody: "Pause AI playback and connect a qualified interpreter before continuing.", controls: "Speaking controls", holdToTalk: "Hold to talk", doctor: "Clinician", patient: "Patient", vietnamese: "Vietnamese", english: "English", speaker: "Speaker", regionInactiveHint: "Select this area to speak or type for this person.", typedFallback: "Typed fallback", typedPlaceholder: "Type a turn to interpret", send: "Send", connecting: "Connecting", connected: "Connected", disconnected: "Disconnected", inputReady: "Ready", inputProcessing: "Processing", inputDelivered: "Translation delivered", inputReviewRequired: "Clinician review required", recording: "Recording {speaker}", connectionNotReady: "Connection is not ready.", connectionNotReadyType: "Connection is not ready. Type the turn instead.", microphoneUnavailable: "Microphone is unavailable. Type the turn instead.", microphoneDenied: "Microphone permission denied. Type the turn instead.", lowConfidence: "Low confidence. Please repeat or type the turn.", repeatTurn: "Repeat", typeTurn: "Type text", confirmationRequired: "Clinician confirmation is required before patient playback.", confirmFailed: "Could not confirm the turn.", escalationFailed: "Could not mark the session escalated.", playbackSuppressed: "Automatic playback is paused. Resume only after the clinician confirms local playback.", resumePlayback: "Resume automatic playback on this device", turnError: "The turn could not be processed. Please try again or type it.", pipelineFailureTitle: "Turn blocked because processing failed", sourceUnavailable: "No source text could be produced from the audio.", translationUnavailable: "No translation is available for patient playback.", pipelineFailureAction: "Check the source when available, retry, or request a human interpreter.", lifecycle: "Session actions", endSession: "End session", withdrawConsent: "Withdraw consent", deleteSession: "Delete session data", deleteConfirm: "Delete all data for this session?", lifecycleFailed: "The action could not be completed. The session remains safely stopped.", retry: "Retry", confirmations: "Clinician confirmation", confirmationRequiredLabel: "Clinician confirmation required", interpreterRecommended: "Human interpreter recommended", readback: "Read-back", entity: "Item", source: "Source", translation: "Translation", flags: "Notes", editTranslation: "Edit translation", confirm: "Confirm", saveEdit: "Save edit", escalate: "Request interpreter", transcript: "Conversation", turns: "turns", noTurns: "No turns yet.", sourceLanguage: "Source language", targetLanguage: "Translation language", blocked: "Blocked pending clinician confirmation.", patientSafeMask: "This content is awaiting clinician review.", openReview: "Open review", reviewOpened: "Clinician review opened.", draftTranslation: "Draft translation", riskReason: "Reason for review", criticalEntities: "Critical information to check", confirmAndPlay: "Confirm and play", saveEditAndPlay: "Save edit and play", editRequired: "An edited translation cannot be empty.", riskSpans: "Items to check", feedback: "Flag translation", feedbackSaved: "Feedback saved.", reason: "Reason", comment: "Comment", submitFeedback: "Submit feedback", risk: { low: "Low", medium: "Medium", high: "High", critical: "Critical", other: "Needs review" }, status: { delivered: "Delivered", awaiting_confirm: "Awaiting confirmation", confirmed: "Confirmed", blocked: "Blocked", other: "Processing" }, feedbackReason: { wrong_term: "Wrong term", wrong_meaning: "Wrong meaning", missing: "Missing content", other: "Other" }, riskKind: { dose_number: "Dose", dose: "Dose", other: "Item to check" }, }, demo: { eyebrow: "Pre-consent simulation", heading: "See how interpretation is handled", start: "View simulation", next: "View next situation", restart: "Replay simulation", step: "Situation {current}/3", normal: "Translation delivered", lowConfidence: "Low confidence", blocked: "Blocked pending clinician confirmation", normalSource: "I have had a headache since this morning.", normalTranslation: "Tôi bị đau đầu từ sáng nay.", lowSource: "I took one tablet... or half a tablet?", lowTranslation: "Tôi uống một viên... hay nửa viên?", blockedSource: "Take 500 mg every day.", blockedTranslation: "Uống 500 mg mỗi ngày.", }, intent: { moment: "During the visit", roleQuestion: "Who are you today?", doctor: "Clinician", clinicStaff: "Clinic staff", directionQuestion: "Which direction do you need?", viToEn: "Vietnamese → English", enToVi: "English → Vietnamese", continue: "Continue", skip: "Skip and use recommended choices", recommended: "Recommended", }, stepper: { label: "Interpreter setup progress", language: "Language", about: "About you", consent: "Confirmation", device: "Device check", start: "Start", complete: "Complete", current: "Current step", upcoming: "Upcoming", }, checklist: { title: "First-session safety checklist", delivered: "First turn delivered", confirmed: "Confirmation completed", escalation: "Human-interpreter control located", dismiss: "Dismiss checklist" }, }, } as const; export function initialLanguage(): Language { const value = new URLSearchParams(window.location.search).get("lang"); if (value !== null) { return value === "en" || value === "vi" ? value : "vi"; } return localStorage.getItem(languageStorageKey) === "en" ? "en" : "vi"; } export function persistLanguage(language: Language) { localStorage.setItem(languageStorageKey, language); }