/**
 * PROJECT JAMES — i18n
 * Default: English. Toggle shows KO | EN both languages.
 */

const TRANSLATIONS = {
  en: {
    'app.name':              'Secure Enterprise Knowledge Operating System',
    'app.subtitle':          'Security-focused Graph-RAG knowledge engine',
    'app.welcome':           'Ask me anything',
    'app.admin':             'ADMIN',

    'common.loading':        'Loading...',
    'common.error':          'Error',
    'common.success':        'Success',
    'common.cancel':         'Cancel',
    'common.confirm':        'Confirm',
    'common.save':           'Save',
    'common.delete':         'Delete',
    'common.close':          'Close',
    'common.edit':           'Edit',
    'common.add':            'Add',
    'common.search':         'Search',
    'common.upload':         'Upload',
    'common.download':       'Download',
    'common.send':           'Send',
    'common.refresh':        'Refresh',
    'common.back':           'Back',
    'common.next':           'Next',
    'common.yes':            'Yes',
    'common.no':             'No',
    'common.empty':          'No data',
    'common.unknown':        'Unknown',
    'common.name':           'Name',
    'common.type':           'Type',
    'common.date':           'Date',
    'common.topic':          'Topic',
    'common.summary':        'Summary',
    'common.actions':        'Actions',
    'common.start':          'Start',
    'common.last':           'Last',

    'chat.title':            'Secure Enterprise Knowledge Operating System — Security Reasoning Engine',
    'chat.placeholder':      'Type a message (Shift+Enter for new line)',
    'chat.example1':         'What is economics?',
    'chat.example2':         'Find documents about John Smith',
    'chat.example3':         'Summarize uploaded documents',
    'chat.example_code':     'Write a Python function',
    'chat.thinking_search':  'Searching internal documents...',
    'chat.thinking_graph':   'Exploring relation graph...',
    'chat.thinking_answer':  'Generating answer...',
    'chat.no_response':      'No response',
    'chat.history_restored': '{count} previous messages restored',
    'chat.clear_history':    'Clear history',
    'chat.session_open':     'Open sessions',
    'chat.session_empty':    'No saved conversations',
    'chat.session_no_title': '(Untitled)',
    'chat.session_turns':    '{count} turns',
    'chat.session_rename':   'Rename',
    'chat.session_delete':   'Delete',
    'chat.session_rename_prompt':  'Session name (max 60 chars):',
    'chat.session_delete_confirm': 'Delete all messages in this session?\n(Cannot be undone)',
    'chat.session_renamed':  'Renamed to: {name}',
    'chat.session_deleted':  'Session deleted',
    'chat.new_session':      '+ New chat',
    'chat.previous':         'Previous chats',

    'badge.source_based':    'Source-based',
    'badge.partial':         'Partial inference',
    'badge.inference_only':  'Inference only',
    'badge.inference_warn':  'Insufficient internal data — answer based on LLM inference. Use as reference only.',

    'toast.web_augmented':   'Answer enhanced via web search',
    // [Axis 6, 2026-05-12 + N-5, 2026-05-13] Web-search chip —
    // three-variant copy per confidence score. See chat.js
    // appendJamesMsg.
    'chat.web_chip_low':     'Search the web for more material',
    'chat.web_chip_mid':     'Partial answer — supplement from the web?',
    'chat.web_chip_high':    'Check the web for newer information',
    // [N-4, 2026-05-13] Header above next-action suggestion chips
    // so the cluster reads as clickable, not as inline prose.
    'chat.suggestions_label': 'Suggestions',
    'toast.longterm_saved':  'Saved to long-term memory',
    'toast.level_up':        'Knowledge +{delta} [{domain}]',
    'toast.no_internal':     'No internal data — inference-based answer',

    'auth.login':            'Login',
    'auth.logout':           'Logout',
    'auth.username':         'Username',
    'auth.password':         'Password',
    'auth.api_key':          'API Key',
    'auth.api_key_hint':     "From server's .env JAMES_API_KEY. Leave blank to reuse stored value.",
    'auth.login_title':      'Login',
    'auth.api_key_prompt':   'Enter your JAMES API Key:',
    'auth.login_success':    '{username} ({role}) logged in',
    'auth.session_expired':  'Session expired. Click to log in again.',
    'auth.auth_expired':     'Authentication expired. Please log in again.',

    'sidebar.mode_upload':   'File upload',
    'sidebar.mode_sessions': 'Chat history',
    'sidebar.mode_recent':   'My files',
    'sidebar.mine_title':    'My files (recent uploads)',
    'sidebar.open_workspace': 'Workspace ↗',
    'sidebar.mode_search':   'Search',
    'sidebar.recent_title':  'Recent conversations',
    'sidebar.recent_hint':   'Recent chat sessions will appear here.\n(Activated after W7 data integration.)',
    'sidebar.search_title':  'Global search',
    'sidebar.search_hint':   'Search across uploaded files · wiki · past conversations.\n(Activated in a follow-up cycle.)',

    'upload.title':          'File Upload',
    'upload.drag':           'drag',
    'upload.drag_label':     'Click or drag to add files',
    'upload.types':          'Image · Video · Audio · PDF · Documents',
    'upload.open':           'Open uploader',
    'upload.folder_hint':    'Save folder (e.g., John Smith folder | default: auto by date)',
    'upload.folder_example': 'e.g., Save to 2026 reports folder',
    'upload.common_label':   'Common save instruction (applied to files without folder)',
    'upload.folder_drag':    'Folder drag also supported',
    'upload.folder_button':  '📁 Choose Folder',
    'mode.auto':                 '🤖 Auto',
    'mode.auto_desc':            'Automatically classify question intent (default)',
    'mode.chat':                 '💬 Chat',
    'mode.chat_desc':            'Direct LLM reply, no retrieval',
    'mode.retrieval':            '🔍 Document Search',
    'mode.retrieval_desc':       'Internal wiki + graph reasoning',
    'mode.meta':                 '📚 Document List',
    'mode.meta_desc':            'Available wiki inventory (no LLM)',
    'mode.coding':               '💻 Coding',
    'mode.coding_desc':          'Coding-specialised model',
    'mode.wiki_edit':            '✏️ Wiki Edit (admin)',
    'mode.wiki_edit_desc':       'Add / modify / delete knowledge',
    'mode.self_evolve':          '🧬 Self-Evolution (admin)',
    'mode.self_evolve_desc':     'Code analysis / self-improvement',
    'mode.not_installed':        '⚠️ not installed',
    'mode.recommend_link':       '🎯 Recommend',
    'mode.recommend_link_title': 'Auto-recommend model for this PC (admin page)',
    'chat.copy_all':             '📋 Copy Conversation',
    'chat.copy_all_title':       'Copy entire current conversation as text',
    'chat.model_picker_title':   'Model used for the selected mode',
    /* v0.4 Sprint 2 #3a — header model indicator chip */
    'model.chip_title':          'Current chat model — click to change',
    'model.chip_title_none':     'No model installed — click to set one up',
    'model.chip_none':           'not installed',
    'model.source.requested':    'using configured model',
    'model.source.preference':   'fallback to preference list',
    'model.source.any':          'fallback to any installed model',
    'model.source.none':         'no model installed',
    /* v0.4 Sprint 2 #3b — picker popover */
    'model.source.override':     'session override (this chat only)',
    'model.popover_title':       'Select model',
    'model.popover_hint':        'Applies to this session',
    'model.popover_empty':       'No models installed. See Admin → LLM install.',
    'model.popover_admin_link':  '⚙ Admin model settings →',
    'chat.mode_install_title':   'Selected mode model not installed — click to ollama pull',
    'chat.mode_recommend_active_title': 'Switch to recommended mode',
    'upload.btn_default':    'Upload & Analyze',
    'upload.btn_count':      'Upload & Analyze ({count})',
    'upload.uploading':      'Uploading...',
    'upload.sending':        'Sending',
    'upload.waiting':        'Waiting',
    'upload.complete':       'Complete',
    'upload.remove':         'Remove',
    'upload.success_count':  '{count} files uploaded',
    'upload.failed':         'Upload failed: {filename}',
    'upload.error_short':    'Failed: {msg}',
    'upload.summary':        'Summary: {text}',
    'upload.category':       'Category: {cat}',
    'upload.sensitivity':    'Security: {level}',
    'upload.complete_msg':   'Upload complete:\n\n{summary}',
    'upload.cancel':         'Cancel',
    'upload.aborted':        'Cancelled',
    'upload.queue_progress': 'Uploading {current}/{total} — {filename}',
    'upload.percent':        '{pct}%',

    'admin.dashboard':       'Dashboard',
    'admin.users':           'Users',
    'admin.entity':          'Entities',
    'admin.memory':          'Memory',
    'admin.patches':         'Patches',
    'admin.tools':           'Tools',
    'admin.persona':         'Persona',
    'admin.section_overview':  'OVERVIEW',
    'admin.section_manage':    'MANAGE',
    'admin.section_security':  'SECURITY',
    'admin.section_evolution': 'EVOLUTION',
    'admin.section_system':    'SYSTEM',
    'admin.audit_log':         'Audit Log',
    'admin.upload_history':    'Upload History',
    'admin.file_mgmt':         'Files',
    'admin.proposal_review':   'Proposals',
    'admin.evo_report':        'Evolution Report',
    'admin.character':         'Character',
    'admin.growth':            'Growth',
    'admin.performance':       'Performance',
    'admin.learning':          'Self-learning',
    'admin.hardware':          'Hardware',
    'admin.settings':          'Settings',
    'admin.chat_link':         'To Chat',

    'dash.page_title':       'Dashboard',
    'users.page_title':      'User Management',
    'entity.page_title':     'Entity Status',
    'memory.page_title':     'Memory Status',
    'hw.page_title':         'Hardware Status',
    'hw.role.compute':                    'Compute',
    'hw.role.memory':                     'Memory',
    'hw.role.ai_acceleration':            'AI Acceleration',
    'hw.role.storage':                    'Storage',
    'hw.cpu.tier_entry':                  'Entry CPU',
    'hw.cpu.tier_mainstream':             'Mainstream CPU',
    'hw.cpu.tier_workstation':            'Workstation CPU',
    'hw.cpu.tier_high_performance':       'High-Performance CPU',
    'hw.cpu.tier_server_grade':           'Server-Grade CPU',
    'hw.cpu.desc_entry':                  'Basic compute — interactive workload',
    'hw.cpu.desc_mainstream':             'Mainstream inference',
    'hw.cpu.desc_workstation':            'Multi-thread parallel workload',
    'hw.cpu.desc_high_performance':       'Heavy reasoning + concurrent sessions',
    'hw.cpu.desc_server_grade':           'Server-class throughput',
    'hw.ram.tier_light':                  'Light Memory',
    'hw.ram.tier_standard':               'Standard Memory',
    'hw.ram.tier_wide_context':           'Wide Context Memory',
    'hw.ram.tier_large_batch':            'Large-Batch Memory',
    'hw.ram.tier_workstation':            'Workstation Memory',
    'hw.ram.desc_light':                  'Single short session',
    'hw.ram.desc_standard':               'Multi-session general use',
    'hw.ram.desc_wide_context':           'Long-context retention',
    'hw.ram.desc_large_batch':            'Multi-document batch',
    'hw.ram.desc_workstation':            'Enterprise-scale concurrency',
    'hw.gpu.tier_cpu_only':               'CPU-only',
    'hw.gpu.tier_entry':                  'Entry Accelerator',
    'hw.gpu.tier_inference':              'Inference-Capable GPU',
    'hw.gpu.tier_production':             'Production GPU',
    'hw.gpu.tier_high_throughput':        'High-Throughput GPU',
    'hw.gpu.tier_datacenter':             'Datacenter GPU',
    'hw.gpu.desc_cpu_only':               'CPU-only inference (slow on large models)',
    'hw.gpu.desc_entry':                  'Basic GPU acceleration',
    'hw.gpu.desc_inference':              'LLM inference capable',
    'hw.gpu.desc_production':             'Production-grade LLM throughput',
    'hw.gpu.desc_high_throughput':        'Multi-model concurrent inference',
    'hw.gpu.desc_datacenter':             'Datacenter-class AI throughput',
    'hw.disk.tier_personal':              'Personal Storage',
    'hw.disk.tier_team':                  'Team Storage',
    'hw.disk.tier_department':            'Department Storage',
    'hw.disk.tier_enterprise':            'Enterprise Storage',
    'hw.disk.tier_archive':               'Archive-Tier Storage',
    'hw.disk.desc_personal':              'Small wiki / personal corpus',
    'hw.disk.desc_team':                  'Mid-size knowledge base',
    'hw.disk.desc_department':            'Department-wide corpus',
    'hw.disk.desc_enterprise':            'Enterprise-scale knowledge base',
    'hw.disk.desc_archive':               'Long-term archive + audit retention',
    'learn.page_title':      'Self-learning',
    'audit.page_title':      'Audit Log',
    'uploads.page_title':    'Upload File History',
    'uploads.search_placeholder': 'Filename search (Enter)',
    'files.page_title':      'File Management',
    'files.search_placeholder': 'Filename search (Enter, case-insensitive)',
    'common.search':         'Search',
    'prop.page_title':       'Proposal Review',
    'evo.page_title':        'Evolution Report',
    'char.page_title':       'Character',
    'growth.page_title':     'Growth',
    'growth.next_level':                'Next:',
    'growth.capability.retrieval':              'Knowledge Retrieval',
    'growth.capability.retrieval_desc':         'Finds accurate information from documents and resources',
    'growth.capability.graph':                  'Relation Reasoning',
    'growth.capability.graph_desc':             'Analyzes connections between people, events, and concepts',
    'growth.capability.security':               'Security Judgment',
    'growth.capability.security_desc':          'Automatically detects sensitive data and dangerous requests',
    'growth.capability.conversation':           'Conversation Understanding',
    'growth.capability.conversation_desc':      'Distinguishes casual chat from professional questions',
    'growth.capability.accuracy':               'Answer Accuracy',
    'growth.capability.accuracy_desc':          'Reduces misinformation and provides evidence-based answers',
    'growth.capability.multimodal':             'Image/Video Analysis',
    'growth.capability.multimodal_desc':        'Recognizes dates, places, and people in photos and videos',
    'growth.capability.evolution':              'Self-Evolution',
    'growth.capability.evolution_desc':         'Generates better answers over time through self-improvement',
    'growth.capability.agent':                  'Agent Capability',
    'growth.capability.agent_desc':             'Plans and executes complex tasks autonomously',
    'growth.domain.security':                   'Security',
    'growth.domain.coding':                     'Coding',
    'growth.domain.business':                   'Business',
    'growth.domain.science':                    'Science/AI',
    'growth.domain.general':                    'General',
    'growth.domain.personal':                   'Personal',
    'perf.page_title':       'Performance',
    'set.page_title':        'Settings',
    'tools.page_title':      'Tools',

    'dash.entity_count':     'Entities',
    'dash.user_count':       'Users',
    'dash.today_queries':    "Today's queries",
    'dash.avg_elapsed':      'Avg response time',
    'dash.blocked_count':    'Blocked requests',
    'dash.memory_count':     'Memory',
    'dash.diag_score':       'Diagnostic score',
    'dash.security_events':  'Security events',
    'dash.recent_logs':      'Recent query log',
    'dash.elapsed_chart':    'Response time trend (last {count})',
    'dash.chart_legend':     'Normal · Caution (10s+) · Warning (20s+)',
    'dash.no_logs':          'No logs',
    'dash.live':             'LIVE',
    'dash.subtext_today':    'Today',
    'dash.subtext_avg':      'Last 200 queries',
    'dash.subtext_blocked':  'Total blocked',
    'dash.subtext_memory':   'Stored memories',
    'dash.subtext_vector':   '{count} vectors indexed',

    'users.title':           'User list',
    'users.add':             'Add user',
    'users.username':        'Username',
    'users.role':            'Role',
    'users.created':         'Created',
    'users.last_login':      'Last login',
    'users.pending_title':   'Pending signups',
    'users.all_title':       'All users',
    'users.empty':           'No users',
    'users.empty_pending':   'No pending signups',
    'users.status':          'Status',
    'users.status_active':   'Active',
    'users.status_pending':  'Pending',
    'users.actions':         'Actions',
    'users.approve_as':      'Approve as',
    'users.approve':         'Approve',
    'users.reject':          'Reject',
    'users.deactivate':      'Deactivate',
    'users.confirm_reject':  'Reject this signup? The row will be deleted.',
    'users.confirm_deactivate': 'Deactivate this user?',
    'users.action_failed':   'Action failed',
    'users.self_deactivate_blocked': 'You cannot deactivate yourself.',
    'users.my_password_title': 'Change my password',
    'users.mypw_old_ph':     'Current password',
    'users.mypw_new_ph':     'New password (8-72 chars, letter + digit)',
    'users.mypw_submit':     'Update',
    'users.mypw_ok':         'Password updated.',
    'users.mypw_empty':      'Fill both fields.',
    'users.issue_token':     'Issue reset token',
    'users.token_issued_title': '🔑 Password reset token issued',
    'users.token_issued_hint':  'This token is shown only once. Hand it to the user, then leave this screen.',
    'users.token_copy':      'Copy token',
    'users.token_copied':    'Copied.',
    'users.my_keys_title':   'My API keys',
    'users.mykey_label_ph':  'Label (optional, e.g. ci-deploy)',
    'users.mykey_issue':     'Issue key',
    'users.mykey_prefix':    'Prefix',
    'users.mykey_label':     'Label',
    'users.mykey_last_used': 'Last used',
    'users.mykey_revoked':   'Revoked',
    'users.mykey_revoke':    'Revoke',
    'users.mykey_empty':     'No API keys yet.',
    'users.mykey_issued_title': '🔑 API key issued',
    'users.mykey_issued_hint':  'This key is shown only once. Store it safely. Lost keys must be re-issued.',
    'users.mykey_copy':      'Copy key',
    'users.mykey_confirm_revoke': 'Revoke this API key? Existing integrations using it will stop working.',

    'entity.list_title':     'Entity list',
    'entity.no_entity':      'No entities',
    'entity.sensitivity':    'Sensitivity',
    'entity.relations':      'Relations',

    'memory.longterm_title': 'Long-term memory (session summaries)',
    'memory.session_list':   'Session list',
    'memory.feedback_title': '📊 User feedback aggregate',
    'mem.feedback_total':    'Total signals',
    'mem.feedback_positive': 'Positive',
    'mem.feedback_negative': 'Negative',
    'mem.feedback_ratio':    'Positive ratio',
    'mem.feedback_tracked':  'Tracked directions',
    'mem.feedback_no_signal': '(no signal yet)',
    // Session list actions (admin Memory tab > session list)
    'mem.delete_only':              'Delete',
    'mem.summarize_delete':         'Summarize & Delete',
    'mem.confirm_delete_only':      'Delete this session? (no summary, no LLM call)',
    'mem.confirm_summarize_delete': 'Summarize and delete? LLM call required (~30-60s on gemma4:e4b). For a no-summary delete, use the "Delete" button instead.',
    'mem.summarizing':              'Summarizing... (LLM call, may take 30-60s)',
    'mem.deleted_no_summary':       'Session deleted (no summary saved)',
    'session.id':            'Session ID',
    'session.turns':         'Turns',

    'know.title':            'Entity list',
    'know.no_entity':        'No entities',
    'know.domain_levels':    'Domain knowledge level',
    'know.coding':           'Coding',
    'know.security':         'Security',
    'know.business':         'Business',
    'know.science':          'Science/AI',
    'know.general':          'General',

    'prop.title':            'Pending proposals',
    'prop.no_pending':       'No pending proposals',
    'prop.detail':           'Details',
    'prop.approve':          'Approve',
    'prop.reject':           'Reject',
    'prop.reject_prompt':    'Rejection reason (will be saved to long-term memory):',
    'prop.web_search':       'Run search',
    'prop.web_confirm':      "Run web search for '{topic}' to enhance long-term knowledge?\n(20-40s)",
    'prop.searching':        'Searching...',
    'prop.completed':        'Completed',
    'prop.rejected':         'Proposal rejected',
    'prop.rejected_saved':   'Rejected + saved to long-term memory',

    'learn.title':           'Recurring error queries',
    'learn.no_errors':       'No recurring errors — Healthy',
    'learn.web_title':       'Web search long-term learning',
    'learn.web_subtitle':    'Search → LLM refine → wiki entity instant save (long-term)',
    'learn.web_topic':       'Search topic',
    'learn.web_label':       'Search web → summarize → save to wiki',
    'learn.web_longterm':    'Saved as long-term knowledge',
    'learn.web_placeholder': 'e.g., quantum computing, generative AI trends',
    'learn.web_btn':         'Search & Learn',
    'learn.web_hint':        'Tip: Asking the same topic twice in chat triggers auto long-term conversion',
    'learn.web_step1':       '1/4 Web search...',
    'learn.web_step2':       '2/4 Fetching URL content...',
    'learn.web_step3':       '3/4 LLM knowledge processing...',
    'learn.web_step4':       '4/4 Saving to wiki...',
    'learn.web_duration':    '(20-40s)',
    'learn.web_done':        'Long-term knowledge saved',
    'learn.web_url_count':   '{count} URL content fetched',
    'learn.web_no_url':      '(snippet only)',
    'learn.llm_title':       'LLM self-learning (internal knowledge)',
    'learn.llm_subtitle':    'Learn via LLM → propose wiki addition',
    'learn.topic_label':     'Learning topic',
    'learn.topic_placeholder': 'e.g., quantum computing, blockchain',
    'learn.btn':             'Learn',
    'learn.from_errors':     'Auto-learn from errors',

    'hw.title':              'Hardware status',
    'hw.measuring':          'Measuring hardware...',
    'hw.sysinfo':            'System info',
    'hw.remeasure':          'Re-measure',
    'hw.llm_title':          'Recommended LLM models for this PC',
    'hw.llm_subtitle':       'Hardware-based recommendation · Ollama auto-install',
    'hw.llm_loading':        'Analyzing recommendations...',
    'hw.llm_installed':      'Installed',
    'hw.llm_install_btn':    'Install ({size}GB)',
    'hw.llm_installing':     'Installing...',
    'hw.llm_failed':         'Failed',
    'hw.llm_install_confirm': "Download '{model}'?\n(May take minutes to tens of minutes)",
    'hw.llm_install_done':   '{model} installed',
    'hw.llm_specs':          'GPU {gpu} · RAM {ram} · Available: {count}',
    'hw.llm_low_specs':      '{count} models below specs',

    'set.title':             'Settings',
    'set.persona_title':     'JAMES persona',
    'set.persona_name':      'Name',
    'set.persona_name_desc': 'How JAMES introduces itself',
    'set.persona_role':      'Personality/Role',
    'set.persona_role_desc': "JAMES's personality and role",
    'set.persona_extra':     'Extra instructions',
    'set.protected':         'Tool access/edit blocked files (checked = protected)',
    'set.reset_default':     'Defaults',
    'set.precision':         'Precision (1=fast, 5=precise)',

    'search.tavily':         'Tavily (AI-focused)',
    'search.duckduckgo':     'DuckDuckGo (fallback)',
    'search.none':           'Not installed',
    'search.tavily_exhausted':'Tavily quota exhausted',
    'search.engine_label':   'Web search engine',

    'patch.no_patches':      'No patches',
    'error.load_failed':     'Load failed: {msg}',
    'error.action_failed':   'Action failed: {msg}',
    'error.measure_failed':  'Measurement failed: {msg}',
    'error.connection':      'Connection error: {msg}',
    'msg.no_response':       'No response',
    'msg.summary_failed':    '[JAMES] Session summary failed: {msg}',
    'msg.summary_done':      '[JAMES] Session summary saved',
    'patch.target':              'Target file',
    'patch.confidence':          'Confidence',
    'prop.generate':             'Analyze Signals → Generate Proposals',
    'prop.type':                 'Type',
    'prop.risk':                 'Risk',
    'prop.content':              'Content',
    'evo.run_date':              'Run date',
    'evo.content':               'Content',
    'evo.result':                'Result',
    'evo.elapsed':               'Elapsed',
    'char.radar':                '▸ Personality Radar',
    'char.summary':              '▸ Character Summary',
    'char.adjust':               '▸ Personality Adjust',
    'char.save':                 'Save Character',
    /* [P2 unified UX 2026-05-10] interactive radar additions */
    'char.intro':                'Drag points on the radar to adjust traits intuitively. Dotted lines show how traits influence each other.',
    'char.drag_hint':            '✋ Drag points inward / outward',
    'char.legend':               '▸ Legend',
    'char.legend_pos':           'Traits that rise together',
    'char.legend_neg':           'One rises → other falls',
    'char.legend_pair':          'Opposing pair: auto-balanced (sum=1.0)',
    'char.connections':          '▸ Influence of Selected Trait',
    'char.connections_empty':    'Click a point on the radar to see which traits it influences.',
    'char.fine_tune':             '▸ Fine Tune',
    'char.note_pair':             '* Groups A~D: opposing (sum=1.0 auto-adjusted)',
    'char.note_corr':             '* Dotted = correlations (damping 0.3 — soft influence)',
    'char.save_ok':               'Character saved.',
    'char.save_warn':             'Some traits failed to save.',
    'char.group_a':               'A · Cognitive (Curiosity ↔ Focus)',
    'char.group_b':               'B · Caution (Cautious ↔ Bold)',
    'char.group_c':               'C · Thinking (Analytical ↔ Intuitive)',
    'char.group_d':               'D · Social (Independent ↔ Collaborative)',
    'char.group_e':               'E · Core Values (independent)',
    'char.group_f':               'F · Expression Style (independent)',
    'char.trait.curiosity':       'Curiosity',
    'char.trait.focus':           'Focus',
    'char.trait.caution':         'Caution',
    'char.trait.boldness':        'Boldness',
    'char.trait.analytical':      'Analytical',
    'char.trait.intuitive':       'Intuitive',
    'char.trait.independent':     'Independent',
    'char.trait.collaborative':   'Collaborative',
    'char.trait.security':        'Security',
    'char.trait.creativity':      'Creativity',
    'char.trait.empathy':         'Empathy',
    'char.trait.conciseness':     'Conciseness',
    'char.trait.directness':      'Directness',
    'char.trait.optimism':        'Optimism',
    'char.trait.risk_tolerance':  'Risk Tolerance',
    'char.trait.patience':        'Patience',
    /* [P3 unified UX 2026-05-10] identity moved from settings to character */
    'char.identity':              '▸ Identity',
    'char.identity_name':         'Name',
    'char.identity_name_desc':    'The name JAMES introduces itself with',
    'char.identity_save':         '💾 Save Name',
    'char.identity_required':     'Please enter a name.',
    'char.identity_saved':        'Name saved.',
    'char.identity_save_fail':    'Failed to save name.',
    /* v0.4 Sprint 2 #6 — character summary + connections panel i18n.
       Previously rendered Korean-only via hardcoded strings in
       admin.js buildCharacterSummary / renderConnectionsPanel. */
    'char.card.core':                 'CORE',
    'char.card.values':               'VALUES',
    'char.card.style':                'STYLE',
    'char.summary.core_balanced':     'Balanced across multiple traits',
    'char.summary.core_prominent':    '{traits}-leaning character',
    'char.summary.values_empty':      'No strong value bias',
    'char.summary.style_balanced':    'Balanced expression style',
    'char.summary.style_suffix':      ' expression style',
    'char.summary.value.security.hi':     'highly security-conscious',
    'char.summary.value.security.lo':     'low security focus',
    'char.summary.value.creativity.hi':   'highly creative',
    'char.summary.value.creativity.lo':   'below-average creativity',
    'char.summary.value.empathy.hi':      'highly empathetic',
    'char.summary.value.empathy.lo':      'reserved empathy',
    'char.summary.style.conciseness.hi':  'concise',
    'char.summary.style.conciseness.lo':  'detailed',
    'char.summary.style.directness.hi':   'direct',
    'char.summary.style.directness.lo':   'indirect',
    'char.summary.style.optimism.hi':     'optimistic',
    'char.summary.style.optimism.lo':     'cautious-toned',
    'char.summary.style.risk_tolerance.hi': 'risk-taking',
    'char.summary.style.risk_tolerance.lo': 'safety-first',
    'char.summary.style.patience.hi':     'patient',
    'char.summary.style.patience.lo':     'quick-to-conclude',
    'char.conn.strength_strong':      'strongly',
    'char.conn.strength_mid':         'moderately',
    'char.conn.strength_weak':        'weakly',
    'char.conn.pair_title':           '↔ Opposing pair (auto sum=1.0)',
    'char.conn.pair_explain':         '{name} moves in the opposite direction automatically.',
    'char.conn.out_title':            '↑ When {name} rises…',
    'char.conn.out_row_pos':          '{name} rises with it',
    'char.conn.out_row_neg':          '{name} weakens',
    'char.conn.in_title':             '↓ What affects {name}',
    'char.conn.in_row_pos':           '{name} rising → this rises',
    'char.conn.in_row_neg':           '{name} rising → this weakens',
    'char.conn.indep_empty':          'Independent trait — no connections with other traits.',
    'set.lang_title':              '▸ Response Language',
    'growth.core_abilities':     '▸ Phase 1~7 Core Abilities',
    'perf.run_now':              'Run Evaluation',
    'perf.history':              '▸ Evaluation History',
    'perf.grade':                'Grade',
    'perf.retrieval':            'Retrieval',
    'perf.speed':                'Speed',
    'perf.issues':               'Issues',
    'learn.query':               'Query',
    'learn.repeat':              'Repeat',
    'hw.description':            'Hardware running JAMES intelligence',
    'set.answer_lang':           'Response Language',
    'set.answer_lang_desc':      'Default response language (includes UI)',
    'set.lang_ko':               '🇰🇷 Korean',
    'set.lang_both':             '🌐 Korean + English (bilingual)',
    'set.lang_en':               '🇺🇸 English',
    'set.persona_extra_desc':    'Additional instructions (free text)',
    'set.persona_extra_placeholder': 'e.g., Always respond concisely.',
    'set.persona_role_placeholder':  'A helpful and security-focused AI assistant',
    'set.save_persona':          'Save Persona',
    'set.server_title':          '▸ Server Settings',
    'set.llm_desc':              'Select the active LLM model',
    'set.model_checking':        'Current model: checking...',
    'set.rag_loop_desc':         'Max RAG reasoning loops',
    'set.current':               'Current',
    'set.speed_fast':            '1=fast',
    'set.speed_precise':         '5=precise',
    'set.protected_label':       'Protected Files',
    'set.save_server':           'Save Server Settings',
    'set.cognitive_title':       '🧠 Cognitive Features',
    'set.cognitive_subtitle':    'Reasoning pipeline toggles',
    'set.cognitive_hint':        'In-process only — container restart reverts to boot .env values. To persist, edit the .env file.',
    'set.cognitive_save':        'Save Cognitive Flags',
    'set.cognitive_saved':       'Cognitive flags saved',
    'set.cognitive_no_changes':  'No changes',
    'set.cognitive_flag_verify':         'Verifier base scan',
    'set.cognitive_flag_fact_check':     'Fact-checker (LLM-driven, runs after verify)',
    'set.cognitive_flag_reflect':        'Reflection loop (draft → critique → revise)',
    'set.cognitive_flag_planner':        'Planner (task decomposition into 2–5 subtasks)',
    'set.cognitive_flag_query_rewrite':  'Query rewriter (LLM-driven expansion)',
    'set.cognitive_flag_rerank':         'Cross-encoder reranker',
    'set.llm_model_label':           'LLM Model',
    'set.max_loop_label':            'Max Loop',
    'set.protected_security_layer':  '🔐 Security Layer',
    'set.protected_auth_module':     '🔑 Auth Module',
    'set.protected_config_file':     '⚙️  Config File',
    'set.protected_fastapi_server':  '🌐 FastAPI Server',
    'set.protected_graph_engine':    '🕸️  Graph Engine',
    'set.protected_reasoning_engine':'🧠 Reasoning Engine',
    'set.protected_vector_store':    '🗄️  Vector Store',
    'set.web_search_title':          '🌐 Web Search',
    'set.web_engine_status':         'Engine Status',
    'set.web_engine_status_hint':    'Active search engine + key configuration status (real-time)',
    'set.web_allowed_roles':         'Allowed Roles',
    'set.web_allowed_roles_hint':    'Roles allowed to trigger web search. Empty is invalid (to disable web search, remove TAVILY_API_KEY from .env).',
    'set.web_relevance_threshold':   'Relevance Threshold',
    'set.web_threshold_hint':        'Web search fires when unified_score falls below this value. Higher (Strong) = web search even with adequate data; lower (Off) = web search only when data is sparse.',
    'set.web_threshold_off':         'Off',
    'set.web_threshold_strong':      'Strong',
    'set.web_save_button':           'Save Web Search Settings',
    'growth.recent_gains':           'Recent gains: ',
    'set.llm_selection_title':       '🤖 LLM Task → Model',
    'set.llm_selection_subtitle':    'Per-task model overrides',
    'set.llm_selection_hint':        'Persistent (workspace/llm_selection.json). Empty selection = config default. Only models installed via ollama are selectable.',
    'set.llm_selection_save':        'Save LLM Selections',
    'set.llm_saved':                 'LLM selections saved',
    'set.llm_no_changes':            'No LLM mapping changes',
    'set.llm_no_models':             'No models installed yet. Use the first-run wizard or "ollama pull <model>".',
    'set.llm_default_option':        '(default — config.GEMMA_MODEL)',
    'set.llm_default_label':         'using config default',
    'set.llm_task_general':          'General reasoning (default chat answer synthesis)',
    'set.llm_task_classify':         'Query classifier (task routing — fast, cheap)',
    'set.llm_task_extract':          'Entity / relation extraction (wiki ingest)',
    'set.llm_task_coding':           'Code generation (qwen2.5-coder family)',
    'set.llm_task_vision':           'Vision / multimodal (llava family)',
    'auth.admin_required':       'Admin login required',
    'auth.login':                'Login',
    'auth.signup_link':          'No account? Sign up',
    'auth.signup_title':         '▸ Signup',
    'auth.signup_hint':          'After submitting, an admin must approve your account before you can log in.',
    'auth.signup_username_ph':   'lowercase letters/digits/_- (3-32 chars)',
    'auth.signup_password_ph':   '8-72 chars, letter + digit',
    'auth.signup_submit':        'Submit',
    'auth.forgot_password':      'Forgot your password?',
    'audit.page_title':          '📋 Audit log',
    'audit.page_hint':           'All auth / user-mgmt / password / API-key events are recorded in audit_log. Filter by category or search event / username / key prefix.',
    'audit.cat_all':             'All',
    'audit.cat_user_mgmt':       'User management',
    'audit.cat_password':        'Password / signup',
    'audit.cat_api_keys':        'API keys',
    'audit.cat_auth':            'Login',
    'audit.cat_query':           'Query / upload',
    'audit.cat_tools':           'Tool calls',
    'audit.cat_attack':          'Attack events',
    'audit.cat_system':          'System log',
    'audit.search_ph':           'Search event / username / key prefix',
    'audit.col_time':            'Time',
    'audit.col_endpoint':        'Endpoint',
    'audit.col_role':            'Role',
    'audit.col_event':           'Event',
    'audit.col_query':           'Target',
    'audit.col_ip':              'IP',
    'audit.empty':                'No matching events.',
    'admin.audit':               'Audit log',
    'admin.policy':              'Policy matrix',
    'auth.logout':               'Logout',
    'workspace.tagline':         'Workspace · Data + jobs',
    'workspace.back_chat':       'Chat',
    'workspace.tab_data':        'My data',
    'workspace.tab_jobs':        'Jobs',
    'workspace.tab_search':      'Search',
    'workspace.data_title':      '📦 Data',
    'workspace.data_hint':       'Uploaded files and their processing status. admin role sees every user’s files; otherwise only your own uploads.',
    'workspace.search_ph':       'Filter by filename',
    'workspace.status_all':      'All status',
    'workspace.col_name':        'Filename',
    'workspace.col_uploader':    'Uploader',
    'workspace.col_uploaded_at': 'Uploaded at',
    'workspace.col_status':      'Status',
    'workspace.col_entities':    'Entities',
    'workspace.linked_entities': 'LINKED ENTITIES',
    'workspace.no_entities':     'No linked entities yet.',
    'workspace.empty':           'No matching artifacts.',
    'workspace.login_to_view':   'Log in to see your uploaded artifacts.',
    'workspace.jobs_title':      'Jobs',
    'workspace.jobs_placeholder': 'Excel build, document combine, entity export — generic job triggers will live here.<br>(Activated in W8.)',
    'workspace.jobs_hint':       'Run generic jobs (Excel / Markdown / JSON) against your entities. Results are downloadable only by the owner.',
    'workspace.jt_excel':        'excel_build · entities → .xlsx',
    'workspace.jt_doc':          'doc_combine · entities → .md',
    'workspace.jt_export':       'entity_export · categories → .json',
    'workspace.refs_ph':         'Comma-separated entity_id (or categories)',
    'workspace.refs_hint_excel': 'e.g. ent_001, ent_042 · empty entries are dropped',
    'workspace.refs_hint_doc':   'e.g. ent_001, ent_042 · each entity body is concatenated',
    'workspace.refs_hint_export':'e.g. concept, document · leave empty for all categories',
    'workspace.run':             'Run',
    'workspace.refresh':         'Refresh',
    'workspace.col_job_type':    'Type',
    'workspace.col_created':     'Created',
    'workspace.col_owner':       'Owner',
    'workspace.col_result':      'Result',
    'workspace.col_actions':     'Actions',
    'workspace.no_jobs':         'No jobs yet — trigger one above.',
    'workspace.search_title':    'Global search',
    'workspace.search_placeholder': 'Cross-search over uploads + wiki + past conversations — activated in a follow-up cycle.',
    'workspace.nav':             'Workspace',
    'workspace.nav_title':       'Open the workspace page (data + jobs)',
    // [v0.2.x CR-C] Change Request panel
    'workspace.tab_cr':                  'Change requests',
    'workspace.cr_title':                'Change requests',
    'workspace.cr_scope_admin':          'ADMIN',
    'workspace.cr_hint':                 'Propose → review → approve flow for writes (wiki entities, job runs, …). Admins see every CR; everyone else sees only their own proposals.',
    'workspace.cr_status_all':           'All status',
    'workspace.cr_status_open':          'open',
    'workspace.cr_status_merged':        'merged',
    'workspace.cr_status_rejected':      'rejected',
    'workspace.cr_status_superseded':    'superseded',
    'workspace.cr_target_all':           'All targets',
    'workspace.cr_target_wiki':          'wiki_entity',
    'workspace.cr_target_jobs':          'run_jobs',
    'workspace.cr_new':                  '+ New proposal',
    'workspace.cr_submit':               'Submit proposal',
    'workspace.cr_form_target_type':     'TARGET TYPE',
    'workspace.cr_form_target_id':       'TARGET ID',
    'workspace.cr_form_target_id_ph':    'entity/prod/concept/your_entity.md',
    'workspace.cr_form_title':           'TITLE',
    'workspace.cr_form_title_ph':        'Short, clear summary of the change',
    'workspace.cr_form_description':     'DESCRIPTION',
    'workspace.cr_form_description_ph':  'Why (optional)',
    'workspace.cr_form_base_hash':       'BASE HASH',
    'workspace.cr_form_body':            'PROPOSED BODY',
    'workspace.cr_form_body_ph':         'New content (wiki_entity → full file body)',
    'workspace.cr_empty':                'No matching change requests.',
    'workspace.cr_no_reviews':           'No reviews yet — leave the first comment.',
    'workspace.cr_col_status':           'Status',
    'workspace.cr_col_target':           'Target',
    'workspace.cr_col_title':            'Title',
    'workspace.cr_col_proposer':         'Proposer',
    'workspace.cr_col_created':          'Created',
    'workspace.cr_detail_target':        'TARGET',
    'workspace.cr_detail_proposer':      'PROPOSER',
    'workspace.cr_detail_created':       'CREATED',
    'workspace.cr_detail_merged':        'MERGED',
    'workspace.cr_detail_reject_reason': 'REJECT REASON',
    'workspace.cr_detail_description':   'DESCRIPTION',
    'workspace.cr_detail_diff':          'PROPOSED DIFF',
    'workspace.cr_detail_reviews':       'REVIEWS',
    'workspace.cr_comment_ph':           'Comment (optional)',
    'workspace.cr_comment_submit':       'Comment',
    'workspace.cr_reject':               'Reject',
    'workspace.cr_approve':              'Approve & apply',
    'common.prev':               '← Prev',
    'common.next':               'Next →',
    'policy.page_title':         '🛡️ Policy matrix',
    'policy.page_hint':          'Configure which role may use each feature. Checked = allowed. Changes apply immediately. "Reset to default" removes every override for that feature.',
    'policy.col_feature':        'Feature',
    'policy.col_action':         'Action',
    'policy.reset_default':      'Reset to default',
    'policy.override_label':     'Override',
    'policy.confirm_reset':      'Reset all overrides for {feature}?',
    'policy.reset_done':         '{n} override(s) cleared.',
    'policy.feature.query_basic':              'JAMES query (/query/)',
    'policy.feature.query_web_search':         'Web search fallback',
    'policy.feature.query_internal_rag':       'Answers grounded in internal data (vector + graph)',
    'policy.feature.upload_file':              'File upload (PDF / document / image)',
    'policy.feature.upload_video':             'Video upload (currently rejected — slot reserved for video-asr)',
    'policy.feature.graph_view':               'Open the Graph page',
    'policy.feature.graph_explore_neighbor':   'Click a node to explore neighbours',
    'policy.feature.password_change_self':     'Change own password',
    'policy.feature.api_keys_issue_self':      'Issue / view / revoke own API keys',
    'policy.feature.admin_users':              'User management (approve / reject / deactivate)',
    'policy.feature.admin_audit_log':          'Audit log view',
    'policy.feature.admin_evolution':          'Self-evolution control (propose / approve / rollback)',
    'policy.feature.admin_policy_matrix':      'Policy matrix management (this screen)',
    'policy.feature.admin_settings':           'System settings (LLM model / persona / web search / model selection)',
    'policy.feature.admin_data':               'Data inspection (entity / memory / files / upload history)',
    'policy.feature.admin_metrics':            'Metrics / dashboard / trace view',
    'policy.feature.admin_character':          'Character profile view / edit',
    'policy.feature.admin_knowledge':          'Knowledge growth view / training',
    'policy.feature.admin_tools':              'Admin tools (e.g., screen-capture analysis)',
    'policy.feature.workspace_view':           'Workspace page (data explorer + job triggers)',
    'policy.feature.workspace_run_jobs':       'Run workspace jobs (Excel / document / Export)',
    'policy.feature.workspace_schedule':       'Schedule workspace jobs (cron) — heavy system impact',
    'policy.feature.data_view_own':            'View own uploads / job results',
    'auth.reset_password_title': '▸ Reset password',
    'auth.reset_password_hint':  'Enter the one-time token issued by your admin. (Valid for 1 hour.)',
    'auth.reset_admin_hint':     '💡 No token? Ask your admin to issue one and send it through a safe channel (messenger, phone).',
    'auth.reset_submit':         'Reset',
    'auth.reset_fill_all':       'Fill in username, token, and new password.',
    'auth.reset_success':        'Password reset. Please log in with the new password.',

    /* ── Reasoning Graph (v0.2 Axis 3 Observability) ── */
    'graph.title':               'Reasoning Graph',
    'graph.back_admin':          'Admin',
    'graph.back_chat':           'Chat',
    'graph.filter.source':       'Source',
    'graph.filter.search':       'Search',
    'graph.search.ph':           'entity name…',
    'graph.legend.title':        'Legend',
    'graph.legend.person':       'person',
    'graph.legend.org':          'org',
    'graph.legend.concept':      'concept',
    'graph.legend.document':     'document',
    'graph.legend.event':        'event',
    'graph.stats.title':         'Snapshot',
    'graph.tip.title':           'Controls',
    'graph.tip.rotate':          'drag — rotate 360°',
    'graph.tip.zoom':            'scroll — zoom',
    'graph.tip.click':           'click node — focus',
    'graph.loading':             'Loading entities...',
    'graph.error':               'Failed to load graph',
    'graph.hint':                'Ask a question below — the path the answer travels will pulse on the graph.',
    'graph.search_toggle_title':       'Entity search (Esc to close)',
    'graph.search_toggle_label':       '🔎 Search ▾',
    'graph.search_node_placeholder':   'Search entity name…',
    'graph.answer.title':        'Answer',
    'graph.query.ph':            'Ask JAMES…',
    'graph.ask':                 'Ask',

    // [Cycle 12 PR-O6b] node attribute edit modal
    'graph.node.edit.button':       'Edit node',
    'graph.node.edit.title':        '▸ Edit node',
    'graph.node.edit.idLabel':      'entity_id',
    'graph.node.edit.name':         'Name',
    'graph.node.edit.entityType':   'Entity type',
    'graph.node.edit.aliases':      'Aliases (one per line)',
    'graph.node.edit.summary':      'Summary',
    'graph.node.edit.sensitivity':  'Sensitivity',
    'graph.node.edit.sensNormal':   'Normal',
    'graph.node.edit.sensSensitive':'Sensitive',
    'graph.node.edit.save':         'Save',
    'graph.node.edit.close':        'Close',
    'graph.node.edit.loading':      'Loading entity…',
    'graph.node.edit.saving':       'Saving…',
    'graph.node.edit.saved':        'Saved',
    'graph.node.edit.fieldsChanged':' field(s) changed',
    'graph.node.edit.noChange':     'Nothing to change.',
    'graph.node.edit.noEntity':     'Entity not loaded.',
    'graph.node.edit.nameRequired': 'Name is required.',
    'graph.node.edit.aliasLimit':   'At most 20 aliases.',
    'graph.node.edit.summaryCap':   'Summary capped at 4000 chars.',
  },

  ko: {
    'app.name':              'Secure Enterprise Knowledge Operating System',
    'app.subtitle':          '보안 중심 Graph-RAG 지식 추론 엔진',
    'app.welcome':           '무엇이든 물어보세요',
    'app.admin':             '관리자',

    'common.loading':        '로딩 중...',
    'common.error':          '오류',
    'common.success':        '성공',
    'common.cancel':         '취소',
    'common.confirm':        '확인',
    'common.save':           '저장',
    'common.delete':         '삭제',
    'common.close':          '닫기',
    'common.edit':           '편집',
    'common.add':            '추가',
    'common.search':         '검색',
    'common.upload':         '업로드',
    'common.download':       '다운로드',
    'common.send':           '전송',
    'common.refresh':        '새로고침',
    'common.back':           '뒤로',
    'common.next':           '다음',
    'common.yes':            '예',
    'common.no':             '아니오',
    'common.empty':          '데이터 없음',
    'common.unknown':        '알 수 없음',
    'common.name':           '이름',
    'common.type':           '타입',
    'common.date':           '날짜',
    'common.topic':          '주제',
    'common.summary':        '요약',
    'common.actions':        '액션',
    'common.start':          '시작',
    'common.last':           '마지막',

    'chat.title':            'Secure Enterprise Knowledge Operating System — 보안 추론 엔진',
    'chat.placeholder':      '메시지를 입력하세요 (Shift+Enter: 줄바꿈)',
    'chat.example1':         '경제학이란 무엇인가?',
    'chat.example2':         '김철수 관련 자료 찾아줘',
    'chat.example3':         '업로드된 문서 요약해줘',
    'chat.example_code':     '파이썬 함수 작성해줘',
    'chat.thinking_search':  '내부 자료 검색 중...',
    'chat.thinking_graph':   '관계 그래프 탐색 중...',
    'chat.thinking_answer':  '답변 생성 중...',
    'chat.no_response':      '응답 없음',
    'chat.history_restored': '이전 대화 {count}개 복원됨',
    'chat.clear_history':    '대화 초기화',
    'chat.session_open':     '세션 열기',
    'chat.session_empty':    '저장된 대화 없음',
    'chat.session_no_title': '(제목 없음)',
    'chat.session_turns':    '{count}턴',
    'chat.session_rename':   '이름 변경',
    'chat.session_delete':   '삭제',
    'chat.session_rename_prompt':  '세션 이름 (60자 이내):',
    'chat.session_delete_confirm': '이 세션의 모든 대화를 삭제하시겠습니까?\n(되돌릴 수 없습니다)',
    'chat.session_renamed':  '이름 변경: {name}',
    'chat.session_deleted':  '세션 삭제 완료',
    'chat.new_session':      '+ 새 대화',
    'chat.previous':         '이전 대화',

    'badge.source_based':    '자료 기반',
    'badge.partial':         '부분 추론',
    'badge.inference_only':  '추론 전용',
    'badge.inference_warn':  '내부 자료가 부족해 LLM 추론으로 답변. 참고용으로만 활용하세요.',

    'toast.web_augmented':   '웹 검색으로 보완된 답변',
    // [Axis 6, 2026-05-12 + N-5, 2026-05-13] 신뢰도 3분기 chip —
    // chat.js 참조. 아이콘은 chat.js variant.icon 으로 별도 prepend.
    'chat.web_chip_low':     '웹 검색으로 자료 수집',
    'chat.web_chip_mid':     '부분 답변 — 웹에서 보완할까요?',
    'chat.web_chip_high':    '최신 정보로 보완 검색해볼까요?',
    // [N-4, 2026-05-13] 다음-행동 제안 chip 클러스터의 헤더 —
    // 본문 산문과 시각적으로 분리해서 클릭 가능 신호를 강화.
    'chat.suggestions_label': '제안',
    'toast.longterm_saved':  '장기 기억으로 저장됨',
    'toast.level_up':        '지식 레벨 +{delta} [{domain}]',
    'toast.no_internal':     '내부 자료 없음 — 추론 기반 답변',

    'auth.login':            '로그인',
    'auth.logout':           '로그아웃',
    'auth.username':         '아이디',
    'auth.password':         '비밀번호',
    'auth.api_key':          'API Key',
    'auth.api_key_hint':     '서버 .env의 JAMES_API_KEY 값. 비워두면 저장된 값 그대로 사용.',
    'auth.login_title':      '로그인',
    'auth.api_key_prompt':   'JAMES API Key를 입력하세요:',
    'auth.login_success':    '{username} ({role}) 로그인 완료',
    'auth.session_expired':  '로그인 세션이 만료됐습니다. 클릭하면 재로그인합니다.',
    'auth.auth_expired':     '인증이 만료됐습니다. 다시 로그인해주세요.',

    'sidebar.mode_upload':   '파일 업로드',
    'sidebar.mode_sessions': '대화 이력',
    'sidebar.mode_recent':   '내 자료',
    'sidebar.mine_title':    '내 자료 (최근 업로드)',
    'sidebar.open_workspace': 'Workspace ↗',
    'sidebar.mode_search':   '검색',
    'sidebar.recent_title':  '최근 챗 기록',
    'sidebar.recent_hint':   '세션별 최근 대화가 여기 표시됩니다.\n(W7 데이터 통합 후 활성화)',
    'sidebar.search_title':  '전역 검색',
    'sidebar.search_hint':   '업로드한 파일·위키·과거 대화 통합 검색.\n(후속 사이클에서 활성화)',

    'upload.title':          '파일 업로드',
    'upload.drag':           '드래그',
    'upload.drag_label':     '클릭하거나 드래그하여 파일 추가',
    'upload.types':          '이미지 · 영상 · 오디오 · PDF · 문서',
    'upload.open':           '업로드 열기',
    'upload.folder_hint':    '저장 폴더 (예: 김철수 폴더에 | 기본: 날짜 자동)',
    'upload.folder_example': '예: 2026년 보고서 폴더에 저장해줘',
    'upload.common_label':   '공통 저장 지시 (폴더 미지정 파일에 적용)',
    'upload.folder_drag':    '폴더 통째로 드래그도 가능',
    'upload.folder_button':  '📁 폴더 선택',
    'mode.auto':                 '🤖 자동',
    'mode.auto_desc':            '질문 의도를 자동 분류 (기본)',
    'mode.chat':                 '💬 일상 대화',
    'mode.chat_desc':            '검색 없이 LLM 직답',
    'mode.retrieval':            '🔍 자료 검색',
    'mode.retrieval_desc':       '내부 wiki + 그래프 추론',
    'mode.meta':                 '📚 자료 목록',
    'mode.meta_desc':            '보유 wiki 인벤토리 (LLM 미사용)',
    'mode.coding':               '💻 코딩',
    'mode.coding_desc':          '코딩 특화 모델',
    'mode.wiki_edit':            '✏️ Wiki 편집 (admin)',
    'mode.wiki_edit_desc':       '지식 추가/수정/삭제',
    'mode.self_evolve':          '🧬 자기진화 (admin)',
    'mode.self_evolve_desc':     '코드 분석 / 자기 개선',
    'mode.not_installed':        '⚠️ 미설치',
    'mode.recommend_link':       '🎯 추천',
    'mode.recommend_link_title': '이 PC에 맞는 모델 자동 추천 (admin 페이지)',
    'chat.copy_all':             '📋 대화 전체 복사',
    'chat.copy_all_title':       '현재 대화 전체를 텍스트로 복사',
    'chat.model_picker_title':   '이 모드에서 사용할 모델 선택',
    /* v0.4 Sprint 2 #3a — header model indicator chip */
    'model.chip_title':          '현재 응답에 사용 중인 모델 — 클릭 시 변경',
    'model.chip_title_none':     '설치된 모델 없음 — 클릭해서 설정',
    'model.chip_none':           '미설치',
    'model.source.requested':    '설정 모델 사용 중',
    'model.source.preference':   'preference list로 폴백',
    'model.source.any':          '설치된 아무 모델로 폴백',
    'model.source.none':         '설치된 모델 없음',
    /* v0.4 Sprint 2 #3b — picker popover */
    'model.source.override':     '세션 override (이 대화에만 적용)',
    'model.popover_title':       '모델 선택',
    'model.popover_hint':        '이 세션에만 적용',
    'model.popover_empty':       '설치된 모델이 없습니다. Admin → LLM 설치 참조.',
    'model.popover_admin_link':  '⚙ Admin 모델 설정 →',
    'chat.mode_install_title':   '선택한 모드의 모델이 미설치 — 클릭하여 ollama pull',
    'chat.mode_recommend_active_title': '권장 모드로 전환',
    'upload.btn_default':    '업로드 및 분석',
    'upload.btn_count':      '업로드 및 분석 ({count}개)',
    'upload.uploading':      '업로드 중...',
    'upload.sending':        '전송 중',
    'upload.waiting':        '대기',
    'upload.complete':       '완료',
    'upload.remove':         '제거',
    'upload.success_count':  '{count}개 파일 업로드 완료',
    'upload.failed':         '업로드 실패: {filename}',
    'upload.error_short':    '실패: {msg}',
    'upload.summary':        '요약: {text}',
    'upload.category':       '분류: {cat}',
    'upload.sensitivity':    '보안: {level}',
    'upload.complete_msg':   '파일 업로드 완료:\n\n{summary}',
    'upload.cancel':         '취소',
    'upload.aborted':        '취소됨',
    'upload.queue_progress': '업로드 중 {current}/{total} — {filename}',
    'upload.percent':        '{pct}%',

    'admin.dashboard':       '대시보드',
    'admin.users':           '사용자',
    'admin.entity':          '엔티티',
    'admin.memory':          '메모리',
    'admin.patches':         '패치',
    'admin.tools':           '도구',
    'admin.persona':         '페르소나',
    'admin.section_overview':  '개요',
    'admin.section_manage':    '관리',
    'admin.section_security':  '보안',
    'admin.section_evolution': '자기진화',
    'admin.section_system':    '시스템',
    'admin.audit_log':         '감사 로그',
    'admin.upload_history':    '업로드 이력',
    'admin.file_mgmt':         '파일 관리',
    'admin.proposal_review':   '제안 검토',
    'admin.evo_report':        '진화 보고서',
    'admin.character':         '성향 캐릭터',
    'admin.growth':            '능력 성장',
    'admin.performance':       '성능 평가',
    'admin.learning':          '자기학습',
    'admin.hardware':          '장비 현황',
    'admin.settings':          '설정',
    'admin.chat_link':         '챗으로',

    'dash.page_title':       '대시보드',
    'users.page_title':      '사용자 관리',
    'entity.page_title':     'Entity 현황',
    'memory.page_title':     'Memory 현황',
    'hw.page_title':         '장비 현황',
    'hw.role.compute':                    '연산',
    'hw.role.memory':                     '메모리',
    'hw.role.ai_acceleration':            'AI 가속',
    'hw.role.storage':                    '저장소',
    'hw.cpu.tier_entry':                  '엔트리 CPU',
    'hw.cpu.tier_mainstream':             '주류 CPU',
    'hw.cpu.tier_workstation':            '워크스테이션 CPU',
    'hw.cpu.tier_high_performance':       '고성능 CPU',
    'hw.cpu.tier_server_grade':           '서버급 CPU',
    'hw.cpu.desc_entry':                  '기본 연산 — 대화형 워크로드',
    'hw.cpu.desc_mainstream':             '주류 추론',
    'hw.cpu.desc_workstation':            '멀티스레드 병렬 워크로드',
    'hw.cpu.desc_high_performance':       '무거운 추론 + 동시 세션',
    'hw.cpu.desc_server_grade':           '서버급 처리량',
    'hw.ram.tier_light':                  '경량 메모리',
    'hw.ram.tier_standard':               '표준 메모리',
    'hw.ram.tier_wide_context':           '광역 컨텍스트 메모리',
    'hw.ram.tier_large_batch':            '대용량 배치 메모리',
    'hw.ram.tier_workstation':            '워크스테이션 메모리',
    'hw.ram.desc_light':                  '단일 짧은 세션',
    'hw.ram.desc_standard':               '멀티세션 일반 사용',
    'hw.ram.desc_wide_context':           '장기 컨텍스트 유지',
    'hw.ram.desc_large_batch':            '다중 문서 배치',
    'hw.ram.desc_workstation':            '엔터프라이즈 동시성',
    'hw.gpu.tier_cpu_only':               'CPU 전용',
    'hw.gpu.tier_entry':                  '엔트리 가속기',
    'hw.gpu.tier_inference':              '추론 가능 GPU',
    'hw.gpu.tier_production':             '프로덕션 GPU',
    'hw.gpu.tier_high_throughput':        '고처리량 GPU',
    'hw.gpu.tier_datacenter':             '데이터센터 GPU',
    'hw.gpu.desc_cpu_only':               'CPU 전용 추론 (대형 모델 느림)',
    'hw.gpu.desc_entry':                  '기본 GPU 가속',
    'hw.gpu.desc_inference':              'LLM 추론 가능',
    'hw.gpu.desc_production':             '프로덕션급 LLM 처리량',
    'hw.gpu.desc_high_throughput':        '다중 모델 동시 추론',
    'hw.gpu.desc_datacenter':             '데이터센터급 AI 처리량',
    'hw.disk.tier_personal':              '개인용 스토리지',
    'hw.disk.tier_team':                  '팀 스토리지',
    'hw.disk.tier_department':            '부서 스토리지',
    'hw.disk.tier_enterprise':            '엔터프라이즈 스토리지',
    'hw.disk.tier_archive':               '아카이브급 스토리지',
    'hw.disk.desc_personal':              '소형 wiki / 개인 코퍼스',
    'hw.disk.desc_team':                  '중형 지식 베이스',
    'hw.disk.desc_department':            '부서 전체 코퍼스',
    'hw.disk.desc_enterprise':            '엔터프라이즈급 지식 베이스',
    'hw.disk.desc_archive':               '장기 아카이브 + 감사 보존',
    'learn.page_title':      '자기학습',
    'audit.page_title':      '감사 로그',
    'uploads.page_title':    '업로드 파일 이력',
    'uploads.search_placeholder': '파일명 검색 (Enter)',
    'files.page_title':      '파일 관리',
    'files.search_placeholder': '파일명 검색 (Enter, 대소문자 무시)',
    'common.search':         '검색',
    'prop.page_title':       '제안 검토',
    'evo.page_title':        '진화 보고서',
    'char.page_title':       '성향 캐릭터',
    'growth.page_title':     '능력 성장',
    'growth.next_level':                '다음까지',
    'growth.capability.retrieval':              '지식 검색',
    'growth.capability.retrieval_desc':         '문서와 자료에서 정확한 정보를 찾음',
    'growth.capability.graph':                  '관계 추론',
    'growth.capability.graph_desc':             '사람·사건·개념 사이의 연결을 분석',
    'growth.capability.security':               '보안 판단',
    'growth.capability.security_desc':          '민감 데이터와 위험 요청을 자동 감지',
    'growth.capability.conversation':           '대화 이해',
    'growth.capability.conversation_desc':      '일상 대화와 전문 질문을 구분',
    'growth.capability.accuracy':               '답변 정확도',
    'growth.capability.accuracy_desc':          '잘못된 정보를 줄이고 근거 기반 답변 제공',
    'growth.capability.multimodal':             '이미지/영상 분석',
    'growth.capability.multimodal_desc':        '사진과 영상에서 날짜·장소·인물 인식',
    'growth.capability.evolution':              '자기진화',
    'growth.capability.evolution_desc':         '자기 개선을 통해 시간이 지날수록 더 나은 답변 생성',
    'growth.capability.agent':                  '에이전트 능력',
    'growth.capability.agent_desc':             '복잡한 작업을 자율적으로 계획·실행',
    'growth.domain.security':                   '보안/정보보호',
    'growth.domain.coding':                     '코딩/개발',
    'growth.domain.business':                   '비즈니스',
    'growth.domain.science':                    '과학/기술',
    'growth.domain.general':                    '일반 상식',
    'growth.domain.personal':                   '개인 맞춤',
    'perf.page_title':       '성능 평가',
    'set.page_title':        '설정',
    'tools.page_title':      'Tool 현황',

    'dash.entity_count':     'ENTITY 수',
    'dash.user_count':       '사용자',
    'dash.today_queries':    '오늘 쿼리',
    'dash.avg_elapsed':      '평균 응답 시간',
    'dash.blocked_count':    '차단 요청',
    'dash.memory_count':     '메모리',
    'dash.diag_score':       '진단 점수',
    'dash.security_events':  '보안 이벤트',
    'dash.recent_logs':      '최근 쿼리 로그',
    'dash.elapsed_chart':    '응답 시간 추이 (최근 {count}회)',
    'dash.chart_legend':     '정상 · 주의(10s+) · 경고(20s+)',
    'dash.no_logs':          '로그 없음',
    'dash.live':             'LIVE',
    'dash.subtext_today':    '금일 기준',
    'dash.subtext_avg':      '최근 200건 기준',
    'dash.subtext_blocked':  '누적 보안 차단',
    'dash.subtext_memory':   '저장된 기억',
    'dash.subtext_vector':   '벡터 {count}개 인덱싱',

    'users.title':           '사용자 목록',
    'users.add':             '사용자 추가',
    'users.username':        '아이디',
    'users.role':            '역할',
    'users.created':         '생성일',
    'users.last_login':      '최근 로그인',
    'users.pending_title':   '가입 신청 대기',
    'users.all_title':       '전체 사용자',
    'users.empty':           '사용자 없음',
    'users.empty_pending':   '대기 중인 가입 신청 없음',
    'users.status':          '상태',
    'users.status_active':   '활성',
    'users.status_pending':  '대기',
    'users.actions':         '액션',
    'users.approve_as':      '승인 역할',
    'users.approve':         '승인',
    'users.reject':          '거부',
    'users.deactivate':      '비활성화',
    'users.confirm_reject':  '이 가입 신청을 거부하시겠습니까? row 가 삭제됩니다.',
    'users.confirm_deactivate': '이 사용자를 비활성화하시겠습니까?',
    'users.action_failed':   '작업 실패',
    'users.self_deactivate_blocked': '자기 자신을 비활성화할 수 없습니다.',
    'users.my_password_title': '내 비밀번호 변경',
    'users.mypw_old_ph':     '현재 비밀번호',
    'users.mypw_new_ph':     '새 비밀번호 (8~72자, 영문+숫자)',
    'users.mypw_submit':     '변경',
    'users.mypw_ok':         '비밀번호가 변경되었습니다.',
    'users.mypw_empty':      '두 필드를 모두 입력하세요.',
    'users.issue_token':     '토큰 발급',
    'users.token_issued_title': '🔑 비밀번호 재설정 토큰 발급',
    'users.token_issued_hint':  '이 토큰은 한 번만 표시됩니다. 사용자에게 전달 후 화면을 떠나세요.',
    'users.token_copy':      '토큰 복사',
    'users.token_copied':    '복사됨.',
    'users.my_keys_title':   '내 API 키',
    'users.mykey_label_ph':  '라벨 (선택, 예: ci-deploy)',
    'users.mykey_issue':     '키 발급',
    'users.mykey_prefix':    'Prefix',
    'users.mykey_label':     '라벨',
    'users.mykey_last_used': '최근 사용',
    'users.mykey_revoked':   '회수됨',
    'users.mykey_revoke':    '회수',
    'users.mykey_empty':     '발급된 API 키가 없습니다.',
    'users.mykey_issued_title': '🔑 API 키 발급 완료',
    'users.mykey_issued_hint':  '이 키는 한 번만 표시됩니다. 안전한 곳에 보관하세요. 분실 시 새로 발급 필요.',
    'users.mykey_copy':      '키 복사',
    'users.mykey_confirm_revoke': '이 API 키를 회수하시겠습니까? 이 키를 사용 중인 통합이 더 이상 작동하지 않습니다.',

    'entity.list_title':     'Entity 목록',
    'entity.no_entity':      'Entity 없음',
    'entity.sensitivity':    '민감도',
    'entity.relations':      'Relations',

    'memory.longterm_title': '장기 기억 (세션 요약)',
    'memory.session_list':   '세션 목록',
    'memory.feedback_title': '📊 사용자 피드백 집계',
    'mem.feedback_total':    '총 신호',
    'mem.feedback_positive': '긍정',
    'mem.feedback_negative': '부정',
    'mem.feedback_ratio':    '긍정 비율',
    'mem.feedback_tracked':  '추적 중인 방향',
    'mem.feedback_no_signal': '(신호 없음)',
    // Session list actions (admin Memory tab > session list)
    'mem.delete_only':              '삭제',
    'mem.summarize_delete':         '요약 후 삭제',
    'mem.confirm_delete_only':      '이 세션을 삭제할까요? (요약 없이, LLM 호출 없음)',
    'mem.confirm_summarize_delete': '요약 후 삭제할까요? LLM 호출 필요 (gemma4:e4b에서 약 30-60초). 요약 없이 삭제하려면 "삭제" 버튼을 사용하세요.',
    'mem.summarizing':              '요약 생성 중... (LLM 호출, 30-60초 소요)',
    'mem.deleted_no_summary':       '세션 삭제 완료 (요약 저장 안 됨)',
    'session.id':            '세션 ID',
    'session.turns':         '대화 수',

    'know.title':            'Entity 목록',
    'know.no_entity':        'Entity 없음',
    'know.domain_levels':    '도메인 지식 레벨',
    'know.coding':           '코딩',
    'know.security':         '보안',
    'know.business':         '비즈니스',
    'know.science':          '과학/AI',
    'know.general':          '일반',

    'prop.title':            '대기 중인 제안',
    'prop.no_pending':       '대기 중인 제안 없음',
    'prop.detail':           '상세',
    'prop.approve':          '승인',
    'prop.reject':           '거부',
    'prop.reject_prompt':    '거부 사유 (입력 시 장기기억에 저장됩니다):',
    'prop.web_search':       '검색 실행',
    'prop.web_confirm':      "'{topic}' 관련 웹 검색을 실행하여 장기 지식을 보강하겠습니까?\n(20~40초 소요)",
    'prop.searching':        '검색 중...',
    'prop.completed':        '완료',
    'prop.rejected':         '제안 거부 완료',
    'prop.rejected_saved':   '거부 완료 + 장기기억 저장됨',

    'learn.title':           '반복 오류 쿼리',
    'learn.no_errors':       '반복 오류 없음 — 양호',
    'learn.web_title':       '웹 검색 장기 학습',
    'learn.web_subtitle':    '검색 → LLM 정제 → wiki entity 즉시 저장 (장기 지식)',
    'learn.web_topic':       '검색 주제',
    'learn.web_label':       '웹에서 검색 → 요약 → wiki 저장',
    'learn.web_longterm':    '장기 지식으로 바로 저장됩니다',
    'learn.web_placeholder': '예: 양자컴퓨팅, 생성형AI 트렌드',
    'learn.web_btn':         '검색 학습',
    'learn.web_hint':        '같은 주제를 챗에서 2번 물으면 자동으로 장기 전환됩니다',
    'learn.web_step1':       '1/4 웹 검색 중...',
    'learn.web_step2':       '2/4 URL 본문 수집 중...',
    'learn.web_step3':       '3/4 LLM 지식 처리 중...',
    'learn.web_step4':       '4/4 Wiki 저장 중...',
    'learn.web_duration':    '(20~40초 소요)',
    'learn.web_done':        '장기 지식 저장 완료',
    'learn.web_url_count':   'URL 본문 {count}개 취득',
    'learn.web_no_url':      '(스니펫만 사용)',
    'learn.llm_title':       'LLM 자기학습 (내부 지식 기반)',
    'learn.llm_subtitle':    'LLM 지식으로 학습 → wiki 추가 제안',
    'learn.topic_label':     '학습 주제',
    'learn.topic_placeholder': '예: 양자컴퓨팅, 블록체인',
    'learn.btn':             '학습',
    'learn.from_errors':     '오류 쿼리 자동 학습',

    'hw.title':              '장비 현황',
    'hw.measuring':          '장비 측정 중...',
    'hw.sysinfo':            '시스템 정보',
    'hw.remeasure':          '다시 측정',
    'hw.llm_title':          '이 PC에 맞는 LLM 모델',
    'hw.llm_subtitle':       '하드웨어 스펙 기반 자동 추천 · Ollama 자동 설치',
    'hw.llm_loading':        '추천 분석 중...',
    'hw.llm_installed':      '설치됨',
    'hw.llm_install_btn':    '설치 ({size}GB)',
    'hw.llm_installing':     '설치 중...',
    'hw.llm_failed':         '실패',
    'hw.llm_install_confirm': "'{model}' 다운로드하시겠습니까?\n(모델 크기에 따라 수분~수십분 소요)",
    'hw.llm_install_done':   '{model} 설치 완료',
    'hw.llm_specs':          'GPU {gpu} · RAM {ram} · 설치 가능: {count}개',
    'hw.llm_low_specs':      '스펙 부족 모델 {count}개 보기',

    'set.title':             '설정',
    'set.persona_title':     'JAMES 페르소나 설정',
    'set.persona_name':      '이름',
    'set.persona_name_desc': 'JAMES가 자신을 소개할 이름',
    'set.persona_role':      '성향/역할',
    'set.persona_role_desc': 'JAMES의 성격과 역할 설명',
    'set.persona_extra':     '추가 지시',
    'set.protected':         'Tool 접근/수정 차단 파일 (체크 = 보호)',
    'set.reset_default':     '기본값',
    'set.precision':         '정밀도 (1=빠름, 5=정밀)',

    'search.tavily':         'Tavily (AI 특화)',
    'search.duckduckgo':     'DuckDuckGo (fallback)',
    'search.none':           '미설치',
    'search.tavily_exhausted':'Tavily 할당량 소진',
    'search.engine_label':   '웹 검색 엔진',

    'patch.no_patches':      'Patch 없음',
    'error.load_failed':     '로드 실패: {msg}',
    'error.action_failed':   '실행 실패: {msg}',
    'error.measure_failed':  '측정 실패: {msg}',
    'error.connection':      '연결 오류: {msg}',
    'msg.no_response':       '응답 없음',
    'msg.summary_failed':    '[JAMES] 세션 요약 실패: {msg}',
    'msg.summary_done':      '[JAMES] 세션 요약 저장 완료',
    'patch.target':              '대상 파일',
    'patch.confidence':          '신뢰도',
    'prop.generate':             '신호 분석 → 제안 생성',
    'prop.type':                 '유형',
    'prop.risk':                 '위험도',
    'prop.content':              '제안 내용',
    'evo.run_date':              '실행일',
    'evo.content':               '내용',
    'evo.result':                '결과',
    'evo.elapsed':               '소요',
    'char.radar':                '▸ 성향 레이더',
    'char.summary':              '▸ 캐릭터 요약',
    'char.adjust':               '▸ 성향 조절',
    'char.save':                 '성향 저장',
    /* [P2 unified UX 2026-05-10] interactive radar additions */
    'char.intro':                '레이더 위 점을 드래그해서 직관적으로 성향을 조절하세요. 점선으로 연결된 성향들은 서로 영향을 줍니다.',
    'char.drag_hint':            '✋ 점을 안쪽/바깥쪽으로 드래그',
    'char.legend':               '▸ 범례',
    'char.legend_pos':           '함께 증가하는 성향',
    'char.legend_neg':           '한쪽이 증가하면 다른쪽 감소',
    'char.legend_pair':          '짝(상충): 자동 1.0-합 유지',
    'char.connections':          '▸ 선택한 성향의 영향력',
    'char.connections_empty':    '레이더의 점을 클릭하면 그 성향과 연결된 다른 성향들이 표시됩니다.',
    'char.fine_tune':             '▸ 정밀 조정',
    'char.note_pair':             '* Groups A~D: 상충 (합=1.0 자동조정)',
    'char.note_corr':             '* 점선 = 상관관계 (damping 0.3로 부드럽게 영향)',
    'char.save_ok':               '성향 저장 완료',
    'char.save_warn':             '일부 성향 저장 실패',
    'char.group_a':               'A · 인지 (탐구 ↔ 집중)',
    'char.group_b':               'B · 신중성 (신중 ↔ 과감)',
    'char.group_c':               'C · 사고 (분석 ↔ 직관)',
    'char.group_d':               'D · 사회성 (독립 ↔ 협력)',
    'char.group_e':               'E · 핵심 가치 (독립)',
    'char.group_f':               'F · 표현 스타일 (독립)',
    'char.trait.curiosity':       '탐구심',
    'char.trait.focus':           '집중력',
    'char.trait.caution':         '신중함',
    'char.trait.boldness':        '과감함',
    'char.trait.analytical':      '분석력',
    'char.trait.intuitive':       '직관력',
    'char.trait.independent':     '독립성',
    'char.trait.collaborative':   '협력성',
    'char.trait.security':        '보안의식',
    'char.trait.creativity':      '창의성',
    'char.trait.empathy':         '공감능력',
    'char.trait.conciseness':     '간결성',
    'char.trait.directness':      '직설성',
    'char.trait.optimism':        '낙관성',
    'char.trait.risk_tolerance':  '위험감수',
    'char.trait.patience':        '인내심',
    /* [P3 unified UX 2026-05-10] identity moved from settings to character */
    'char.identity':              '▸ 신원',
    'char.identity_name':         '이름',
    'char.identity_name_desc':    'JAMES가 자신을 소개할 이름',
    'char.identity_save':         '💾 이름 저장',
    'char.identity_required':     '이름을 입력하세요.',
    'char.identity_saved':        '이름 저장 완료',
    'char.identity_save_fail':    '이름 저장 실패',
    /* v0.4 Sprint 2 #6 — character summary + connections panel i18n */
    'char.card.core':                 '핵심',
    'char.card.values':               '가치',
    'char.card.style':                '스타일',
    'char.summary.core_balanced':     '여러 성향이 균형을 이룬 성격',
    'char.summary.core_prominent':    '{traits}이 두드러진 성격',
    'char.summary.values_empty':      '특정 가치 편향이 두드러지지 않음',
    'char.summary.style_balanced':    '균형 잡힌 표현 스타일',
    'char.summary.style_suffix':      ' 표현 스타일',
    'char.summary.value.security.hi':     '보안에 매우 민감함',
    'char.summary.value.security.lo':     '보안의식이 약함',
    'char.summary.value.creativity.hi':   '창의성이 풍부함',
    'char.summary.value.creativity.lo':   '창의성은 보통 이하',
    'char.summary.value.empathy.hi':      '공감능력이 높음',
    'char.summary.value.empathy.lo':      '공감 표현이 절제됨',
    'char.summary.style.conciseness.hi':  '간결한',
    'char.summary.style.conciseness.lo':  '설명이 풍부한',
    'char.summary.style.directness.hi':   '직설적인',
    'char.summary.style.directness.lo':   '우회적인',
    'char.summary.style.optimism.hi':     '낙관적인',
    'char.summary.style.optimism.lo':     '신중한 톤의',
    'char.summary.style.risk_tolerance.hi': '위험을 감수하는',
    'char.summary.style.risk_tolerance.lo': '안전 우선의',
    'char.summary.style.patience.hi':     '인내심 있는',
    'char.summary.style.patience.lo':     '결론이 빠른',
    'char.conn.strength_strong':      '강하게',
    'char.conn.strength_mid':         '중간 정도로',
    'char.conn.strength_weak':        '약하게',
    'char.conn.pair_title':           '↔ 짝 (자동 1.0 합)',
    'char.conn.pair_explain':         '{name}이/가 자동으로 반대 방향으로 함께 움직입니다.',
    'char.conn.out_title':            '↑ {name}이/가 강해지면…',
    'char.conn.out_row_pos':          '{name}이/가 함께 강해집니다',
    'char.conn.out_row_neg':          '{name}이/가 약해집니다',
    'char.conn.in_title':             '↓ {name}에 영향을 주는 것',
    'char.conn.in_row_pos':           '{name}이/가 강해지면 함께 강해집니다',
    'char.conn.in_row_neg':           '{name}이/가 강해지면 약해집니다',
    'char.conn.indep_empty':          '독립 성향 — 다른 성향과 연결되지 않습니다.',
    'set.lang_title':              '▸ 답변 언어',
    'growth.core_abilities':     '▸ Phase 1~7 핵심 능력',
    'perf.run_now':              '지금 채점',
    'perf.history':              '▸ 평가 이력',
    'perf.grade':                '등급',
    'perf.retrieval':            '검색',
    'perf.speed':                '속도',
    'perf.issues':               '문제점',
    'learn.query':               '쿼리',
    'learn.repeat':              '반복',
    'hw.description':            '자메스의 두뇌를 실행하는 물리 인프라',
    'set.answer_lang':           '답변 언어',
    'set.answer_lang_desc':      '기본 답변 언어 (UI 전환 포함)',
    'set.lang_ko':               '🇰🇷 한국어',
    'set.lang_both':             '🌐 한국어 + English (동시)',
    'set.lang_en':               '🇺🇸 English',
    'set.persona_extra_desc':    '자유롭게 추가할 지시사항',
    'set.persona_extra_placeholder': '예: 항상 간결하게 답변하세요.',
    'set.persona_role_placeholder':  '친절하고 보안을 중시하는 AI 어시스턴트',
    'set.save_persona':          '페르소나 저장',
    'set.server_title':          '▸ 서버 설정',
    'set.llm_desc':              '현재 사용 중인 LLM 모델 선택',
    'set.model_checking':        '현재 로드된 모델: 확인 중...',
    'set.rag_loop_desc':         'RAG 추론 루프 최대 횟수',
    'set.current':               '현재',
    'set.speed_fast':            '1=빠름',
    'set.speed_precise':         '5=정밀',
    'set.protected_label':       '보호 파일',
    'set.save_server':           '서버 설정 저장',
    'set.cognitive_title':       '🧠 인지 기능',
    'set.cognitive_subtitle':    '추론 파이프라인 토글',
    'set.cognitive_hint':        '프로세스 메모리에만 저장됩니다 — 재시작 시 .env 의 부트 값으로 복귀. 영구 적용은 .env 파일 직접 수정.',
    'set.cognitive_save':        '인지 기능 저장',
    'set.cognitive_saved':       '인지 기능 저장 완료',
    'set.cognitive_no_changes':  '변경 없음',
    'set.cognitive_flag_verify':         '검증자 기본 스캔',
    'set.cognitive_flag_fact_check':     '팩트 체커 (LLM 기반, 검증 후 실행)',
    'set.cognitive_flag_reflect':        '반영 루프 (초안 → 비평 → 수정)',
    'set.cognitive_flag_planner':        '플래너 (작업을 2–5 하위 작업으로 분해)',
    'set.cognitive_flag_query_rewrite':  '쿼리 재작성기 (LLM 기반 확장)',
    'set.cognitive_flag_rerank':         '교차 인코더 리랭커',
    'set.llm_model_label':           'LLM 모델',
    'set.max_loop_label':            '최대 루프',
    'set.protected_security_layer':  '🔐 보안 계층',
    'set.protected_auth_module':     '🔑 인증 모듈',
    'set.protected_config_file':     '⚙️  설정 파일',
    'set.protected_fastapi_server':  '🌐 FastAPI 서버',
    'set.protected_graph_engine':    '🕸️  그래프 엔진',
    'set.protected_reasoning_engine':'🧠 추론 엔진',
    'set.protected_vector_store':    '🗄️  벡터 저장소',
    'set.web_search_title':          '🌐 웹 검색',
    'set.web_engine_status':         '엔진 상태',
    'set.web_engine_status_hint':    '현재 활성 검색 엔진 + 키 설정 상태 (실시간)',
    'set.web_allowed_roles':         '허용된 역할',
    'set.web_allowed_roles_hint':    '웹 검색을 트리거할 수 있는 role. 비어있으면 안됨 (web search 비활성화는 .env에서 TAVILY_API_KEY 제거).',
    'set.web_relevance_threshold':   '연관성 임계값',
    'set.web_threshold_hint':        'unified_score 가 이 값 미만이면 웹 검색 트리거. 높이면 (강력) 자료 부족해도 자주 웹 검색, 낮추면 (안함) 자료 풍부할 때만 웹 검색.',
    'set.web_threshold_off':         '안함',
    'set.web_threshold_strong':      '강력',
    'set.web_save_button':           '웹 검색 설정 저장',
    'growth.recent_gains':           '최근 성장: ',
    'set.llm_selection_title':       '🤖 작업별 LLM 매핑',
    'set.llm_selection_subtitle':    '작업별 모델 오버라이드',
    'set.llm_selection_hint':        '영구 저장 (workspace/llm_selection.json). 빈 선택 = config 기본값. ollama 에 설치된 모델만 선택 가능.',
    'set.llm_selection_save':        '작업별 모델 저장',
    'set.llm_saved':                 '작업별 모델 매핑 저장 완료',
    'set.llm_no_changes':            'LLM 매핑 변경 없음',
    'set.llm_no_models':             '설치된 모델 없음. 첫 실행 마법사 또는 "ollama pull <model>" 사용.',
    'set.llm_default_option':        '(기본 — config.GEMMA_MODEL)',
    'set.llm_default_label':         'config 기본값 사용',
    'set.llm_task_general':          '일반 추론 (채팅 응답 합성 기본)',
    'set.llm_task_classify':         '쿼리 분류 (작업 라우팅 — 빠르고 가벼움)',
    'set.llm_task_extract':          '엔티티/관계 추출 (위키 ingest)',
    'set.llm_task_coding':           '코드 생성 (qwen2.5-coder 계열)',
    'set.llm_task_vision':           '비전/멀티모달 (llava 계열)',
    'auth.admin_required':       '관리자 로그인이 필요합니다',
    'auth.login':                '로그인',
    'auth.signup_link':          '계정이 없으신가요? 회원가입',
    'auth.signup_title':         '▸ 회원가입',
    'auth.signup_hint':          '가입 신청 후 관리자 승인을 거쳐야 로그인할 수 있습니다.',
    'auth.signup_username_ph':   '영문 소문자/숫자/_- (3~32자)',
    'auth.signup_password_ph':   '8~72자, 영문 + 숫자',
    'auth.signup_submit':        '가입 신청',
    'auth.forgot_password':      '비밀번호를 잊으셨나요?',
    'audit.page_title':          '📋 감사 로그',
    'audit.page_hint':           '모든 인증/사용자 관리/비밀번호/API 키 이벤트가 audit_log 에 기록됩니다. 카테고리 필터로 좁히거나 이벤트·사용자명·키 prefix 로 검색할 수 있습니다.',
    'audit.cat_all':             '전체',
    'audit.cat_user_mgmt':       '사용자 관리',
    'audit.cat_password':        '비밀번호/가입',
    'audit.cat_api_keys':        'API 키',
    'audit.cat_auth':            '로그인',
    'audit.cat_query':           '질의/업로드',
    'audit.cat_tools':           '도구 호출',
    'audit.cat_attack':          '공격 이벤트',
    'audit.cat_system':          '시스템 로그',
    'audit.search_ph':           '이벤트 / username / 키 prefix 검색',
    'audit.col_time':            '시각',
    'audit.col_endpoint':        '엔드포인트',
    'audit.col_role':            'role',
    'audit.col_event':           '이벤트',
    'audit.col_query':           '대상',
    'audit.col_ip':              'IP',
    'audit.empty':                '조건에 맞는 이벤트가 없습니다.',
    'admin.audit':               '감사 로그',
    'admin.policy':              '권한 매트릭스',
    'auth.logout':               '로그아웃',
    'workspace.tagline':         'Workspace · 데이터 + 작업',
    'workspace.back_chat':       '챗',
    'workspace.tab_data':        '내 데이터',
    'workspace.tab_jobs':        '작업',
    'workspace.tab_search':      '검색',
    'workspace.data_title':      '📦 데이터',
    'workspace.data_hint':       '업로드한 파일과 처리 상태를 조회합니다. admin role 은 모든 사용자의 파일을, 그 외는 본인 업로드만 표시합니다.',
    'workspace.search_ph':       '파일명 검색',
    'workspace.status_all':      '전체 상태',
    'workspace.col_name':        '파일명',
    'workspace.col_uploader':    '업로더',
    'workspace.col_uploaded_at': '업로드 시각',
    'workspace.col_status':      '상태',
    'workspace.col_entities':    '엔터티',
    'workspace.linked_entities': '연결된 엔터티',
    'workspace.no_entities':     '연결된 엔터티가 아직 없습니다.',
    'workspace.empty':           '조건에 맞는 아티팩트가 없습니다.',
    'workspace.login_to_view':   '로그인하면 본인 업로드 파일을 조회할 수 있습니다.',
    'workspace.jobs_title':      '작업',
    'workspace.jobs_placeholder': 'Excel 합성 · 문서 합성 · entity export 같은 generic 작업 트리거가 곧 이 영역에서 동작합니다.<br>(W8 사이클에서 활성화)',
    'workspace.jobs_hint':       '엔티티를 Excel/문서/JSON 으로 합성하는 generic 작업을 실행합니다. 결과물은 본인만 다운로드 가능합니다.',
    'workspace.jt_excel':        'excel_build · 엔티티 → .xlsx',
    'workspace.jt_doc':          'doc_combine · 엔티티 → .md',
    'workspace.jt_export':       'entity_export · 카테고리 → .json',
    'workspace.refs_ph':         '쉼표로 구분된 entity_id (또는 카테고리)',
    'workspace.refs_hint_excel': '예: ent_001, ent_042 · 빈 항목은 자동으로 생략됩니다.',
    'workspace.refs_hint_doc':   '예: ent_001, ent_042 · 각 entity 의 markdown 본문이 연결됩니다.',
    'workspace.refs_hint_export':'예: concept, document · 빈 칸이면 모든 카테고리를 내보냅니다.',
    'workspace.run':             '실행',
    'workspace.refresh':         '새로고침',
    'workspace.col_job_type':    '타입',
    'workspace.col_created':     '생성',
    'workspace.col_owner':       '소유자',
    'workspace.col_result':      '결과',
    'workspace.col_actions':     '액션',
    'workspace.no_jobs':         '작업 이력이 없습니다. 위에서 실행해 보세요.',
    'workspace.search_title':    '전역 검색',
    'workspace.search_placeholder': '업로드한 파일 · 위키 entity · 과거 대화 통합 검색이 후속 사이클에서 활성화됩니다.',
    'workspace.nav':             'Workspace',
    'workspace.nav_title':       '워크스페이스 페이지 열기 (데이터 + 작업)',
    // [v0.2.x CR-C] 변경 요청 패널
    'workspace.tab_cr':                  '변경 요청',
    'workspace.cr_title':                '변경 요청',
    'workspace.cr_scope_admin':          'ADMIN',
    'workspace.cr_hint':                 '위키 엔티티 / 잡 실행 등 쓰기 작업의 제안 → 검토 → 결재 흐름입니다. admin 은 모든 제안을 보고 결재할 수 있고, 그 외 사용자는 본인 제안만 보입니다.',
    'workspace.cr_status_all':           '전체 상태',
    'workspace.cr_status_open':          'open',
    'workspace.cr_status_merged':        'merged',
    'workspace.cr_status_rejected':      'rejected',
    'workspace.cr_status_superseded':    'superseded',
    'workspace.cr_target_all':           '전체 대상',
    'workspace.cr_target_wiki':          'wiki_entity',
    'workspace.cr_target_jobs':          'run_jobs',
    'workspace.cr_new':                  '+ 새 제안',
    'workspace.cr_submit':               '제안 제출',
    'workspace.cr_form_target_type':     'TARGET TYPE',
    'workspace.cr_form_target_id':       'TARGET ID',
    'workspace.cr_form_target_id_ph':    'entity/prod/concept/your_entity.md',
    'workspace.cr_form_title':           'TITLE',
    'workspace.cr_form_title_ph':        '짧고 명확한 변경 요약',
    'workspace.cr_form_description':     'DESCRIPTION',
    'workspace.cr_form_description_ph':  '배경 / 이유 (선택)',
    'workspace.cr_form_base_hash':       'BASE HASH',
    'workspace.cr_form_body':            'PROPOSED BODY',
    'workspace.cr_form_body_ph':         '새 본문 (wiki_entity → 전체 파일 내용)',
    'workspace.cr_empty':                '조건에 맞는 변경 요청이 없습니다.',
    'workspace.cr_no_reviews':           '아직 코멘트가 없습니다 — 첫 코멘트를 남겨보세요.',
    'workspace.cr_col_status':           '상태',
    'workspace.cr_col_target':           '대상',
    'workspace.cr_col_title':            '제목',
    'workspace.cr_col_proposer':         '제안자',
    'workspace.cr_col_created':          '생성',
    'workspace.cr_detail_target':        '대상',
    'workspace.cr_detail_proposer':      '제안자',
    'workspace.cr_detail_created':       '생성 시각',
    'workspace.cr_detail_merged':        '머지',
    'workspace.cr_detail_reject_reason': '거절 사유',
    'workspace.cr_detail_description':   '설명',
    'workspace.cr_detail_diff':          '제안된 변경',
    'workspace.cr_detail_reviews':       '리뷰',
    'workspace.cr_comment_ph':           '코멘트 (선택)',
    'workspace.cr_comment_submit':       '코멘트',
    'workspace.cr_reject':               '거절',
    'workspace.cr_approve':              '승인 + 적용',
    'common.prev':               '← 이전',
    'common.next':               '다음 →',
    'policy.page_title':         '🛡️ 권한 매트릭스',
    'policy.page_hint':          '각 기능을 어느 role 이 사용할 수 있는지 설정합니다. 체크 = 허용. 변경하면 즉시 반영. "기본값 복원" 으로 해당 기능의 모든 override 를 제거합니다.',
    'policy.col_feature':        '기능',
    'policy.col_action':         '액션',
    'policy.reset_default':      '기본값 복원',
    'policy.override_label':     'Override 적용',
    'policy.confirm_reset':      '{feature} 의 모든 override 를 제거하시겠습니까?',
    'policy.reset_done':         '{n} 개 override 가 제거되었습니다.',
    'policy.feature.query_basic':              '자메스 질의 (/query/)',
    'policy.feature.query_web_search':         '웹 검색 fallback',
    'policy.feature.query_internal_rag':       '내부 자료(vector+graph) 기반 답변',
    'policy.feature.upload_file':              '파일 업로드 (PDF/문서/이미지)',
    'policy.feature.upload_video':             '영상 업로드 (현재 거부, video-asr 대비 슬롯)',
    'policy.feature.graph_view':               '그래프 페이지 진입',
    'policy.feature.graph_explore_neighbor':   '노드 클릭 이웃 탐색',
    'policy.feature.password_change_self':     '본인 비밀번호 변경',
    'policy.feature.api_keys_issue_self':      '본인 API 키 발급/조회/회수',
    'policy.feature.admin_users':              '사용자 관리 (승인/거부/비활성화)',
    'policy.feature.admin_audit_log':          '감사 로그 조회',
    'policy.feature.admin_evolution':          'self-evolution 제어 (제안/승인/롤백)',
    'policy.feature.admin_policy_matrix':      '권한 매트릭스 관리 (이 화면 자체)',
    'policy.feature.admin_settings':           '시스템 설정 (LLM 모델 / persona / 웹 검색 / model selection)',
    'policy.feature.admin_data':               '데이터 인스펙션 (entity / memory / files / 업로드 이력)',
    'policy.feature.admin_metrics':            '메트릭·대시보드·trace 조회',
    'policy.feature.admin_character':          '성향(character) 조회·설정',
    'policy.feature.admin_knowledge':          '능력 성장(knowledge) 조회·학습',
    'policy.feature.admin_tools':              '관리자 도구 (화면 캡처 분석 등)',
    'policy.feature.workspace_view':           '워크스페이스 페이지 진입 (데이터 explorer + job 트리거)',
    'policy.feature.workspace_run_jobs':       '워크스페이스 job 실행 (Excel/문서/Export)',
    'policy.feature.workspace_schedule':       '워크스페이스 job 예약 (cron) — 시스템 영향 큼',
    'policy.feature.data_view_own':            '자기 업로드 파일 / job 결과 조회',
    'auth.reset_password_title': '▸ 비밀번호 재설정',
    'auth.reset_password_hint':  '관리자가 발급한 일회용 토큰을 입력하세요. (1시간 유효)',
    'auth.reset_admin_hint':     '💡 토큰이 없다면 관리자에게 "비밀번호 재설정 토큰을 발급해 주세요" 라고 요청하세요. 관리자가 사용자 관리 페이지에서 토큰을 발급해 안전한 경로(메신저·전화 등)로 전달합니다.',
    'auth.reset_submit':         '재설정',
    'auth.reset_fill_all':       '아이디, 토큰, 새 비밀번호를 모두 입력하세요.',
    'auth.reset_success':        '비밀번호가 재설정되었습니다. 새 비밀번호로 로그인하세요.',

    /* ── 추론 그래프 (v0.2 Axis 3 옵저버빌리티) ── */
    'graph.title':               '추론 그래프',
    'graph.back_admin':          '관리자',
    'graph.back_chat':           '챗',
    'graph.filter.source':       '소스',
    'graph.filter.search':       '검색',
    'graph.search.ph':           '엔티티 이름…',
    'graph.legend.title':        '범례',
    'graph.legend.person':       '인물',
    'graph.legend.org':          '조직',
    'graph.legend.concept':      '개념',
    'graph.legend.document':     '문서',
    'graph.legend.event':        '사건',
    'graph.stats.title':         '스냅샷',
    'graph.tip.title':           '조작',
    'graph.tip.rotate':          '드래그 — 360° 회전',
    'graph.tip.zoom':            '스크롤 — 확대/축소',
    'graph.tip.click':           '노드 클릭 — 포커스',
    'graph.loading':             '엔티티 불러오는 중...',
    'graph.error':               '그래프 로드 실패',
    'graph.hint':                '아래에 질문하세요 — 답이 흐르는 경로가 그래프 위에서 반짝입니다.',
    'graph.search_toggle_title':       '엔티티 검색 (Esc로 닫기)',
    'graph.search_toggle_label':       '🔎 검색 ▾',
    'graph.search_node_placeholder':   '엔티티 이름 검색…',
    'graph.answer.title':        '답변',
    'graph.query.ph':            'JAMES에게 질문…',
    'graph.ask':                 '질문',

    // [Cycle 12 PR-O6b] 노드 속성 편집 modal
    'graph.node.edit.button':       '노드 편집',
    'graph.node.edit.title':        '▸ 노드 편집',
    'graph.node.edit.idLabel':      '엔티티 ID',
    'graph.node.edit.name':         '이름',
    'graph.node.edit.entityType':   '엔티티 종류',
    'graph.node.edit.aliases':      '별칭 (한 줄에 하나씩)',
    'graph.node.edit.summary':      '요약',
    'graph.node.edit.sensitivity':  '민감도',
    'graph.node.edit.sensNormal':   '보통',
    'graph.node.edit.sensSensitive':'민감',
    'graph.node.edit.save':         '저장',
    'graph.node.edit.close':        '닫기',
    'graph.node.edit.loading':      '엔티티 정보 불러오는 중…',
    'graph.node.edit.saving':       '저장 중…',
    'graph.node.edit.saved':        '저장 완료',
    'graph.node.edit.fieldsChanged':'개 필드 변경',
    'graph.node.edit.noChange':     '변경된 내용이 없습니다.',
    'graph.node.edit.noEntity':     '엔티티가 로드되지 않았습니다.',
    'graph.node.edit.nameRequired': '이름은 비울 수 없습니다.',
    'graph.node.edit.aliasLimit':   '별칭은 20개까지 가능합니다.',
    'graph.node.edit.summaryCap':   '요약은 4000자까지 가능합니다.',
  }
};

// Default: English
let currentLang = localStorage.getItem('james_lang') || 'en';

function t(key, vars) {
  vars = vars || {};
  var dict = TRANSLATIONS[currentLang] || TRANSLATIONS.en;
  var text = dict[key] || TRANSLATIONS.en[key] || key;
  for (var k in vars) {
    text = text.replace(new RegExp('\\{' + k + '\\}', 'g'), vars[k]);
  }
  return text;
}

function setLang(lang) {
  if (!TRANSLATIONS[lang]) return;
  currentLang = lang;
  localStorage.setItem('james_lang', lang);
  applyTranslations();
  if (typeof onLangChange === 'function') onLangChange(lang);
}

function getLang() {
  return currentLang;
}

function toggleLang() {
  setLang(currentLang === 'en' ? 'ko' : 'en');
}

// Toggle button display: KO | EN both visible, active highlighted
function updateLangToggleDisplay() {
  var btns = document.querySelectorAll('[data-lang-toggle]');
  for (var i = 0; i < btns.length; i++) {
    var koActive = currentLang === 'ko';
    btns[i].innerHTML =
      '<span style="opacity:' + (koActive ? 1 : 0.4) + ';font-weight:' + (koActive ? 700 : 400) + '">KO</span>' +
      '<span style="opacity:0.5;margin:0 4px">|</span>' +
      '<span style="opacity:' + (!koActive ? 1 : 0.4) + ';font-weight:' + (!koActive ? 700 : 400) + '">EN</span>';
  }
}

function applyTranslations() {
  var els = document.querySelectorAll('[data-i18n]');
  for (var i = 0; i < els.length; i++) {
    var el = els[i];
    var key = el.getAttribute('data-i18n');
    if (key) {
      if (el.hasAttribute('data-i18n-html')) {
        el.innerHTML = t(key);
      } else {
        el.textContent = t(key);
      }
    }
  }
  var phs = document.querySelectorAll('[data-i18n-placeholder]');
  for (var i = 0; i < phs.length; i++) {
    var key = phs[i].getAttribute('data-i18n-placeholder');
    if (key) phs[i].placeholder = t(key);
  }
  var titles = document.querySelectorAll('[data-i18n-title]');
  for (var i = 0; i < titles.length; i++) {
    var key = titles[i].getAttribute('data-i18n-title');
    if (key) titles[i].title = t(key);
  }
  updateLangToggleDisplay();
}

if (typeof document !== 'undefined') {
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', applyTranslations);
  } else {
    applyTranslations();
  }
}
