/**
 * OmicVerse Single Cell Analysis — Internationalization & Language Support
 */

Object.assign(SingleCellAnalysis.prototype, {

    setupLanguageToggle() {
        const stored = localStorage.getItem('omicverse.lang');
        this.currentLang = stored || 'en';
        const toggle = document.getElementById('lang-toggle');
        if (toggle) {
            toggle.addEventListener('click', () => {
                this.currentLang = this.currentLang === 'en' ? 'zh' : 'en';
                localStorage.setItem('omicverse.lang', this.currentLang);
                this.applyLanguage(this.currentLang);
            });
        }
        this.applyLanguage(this.currentLang);
    },

    t(key) {
        const dict = {
            en: {
                'lang.toggle': '中文',
                'nav.singleCell': 'Single-cell Analysis',
                'nav.preprocessing': 'Preprocessing',
                'nav.normalization': 'Normalization',
                'nav.qc': 'Quality Control',
                'nav.featureSelection': 'Feature Selection',
                'nav.dimReduction': 'Visualization',
                'nav.dimReductionSub': 'Dimensionality Reduction',
                'nav.linearDR': 'Linear DR',
                'nav.nonlinearDR': 'Nonlinear DR',
                'nav.visualization': 'Visualization',
                'nav.clustering': 'Basic Analysis',
                'nav.clusteringSub': 'Clustering',
                'nav.communityDetection': 'Community Detection',
                'nav.cellTypeId': 'Cell Type ID',
                'nav.clusterValidation': 'Cluster Validation',
                'nav.omicverse': 'Advanced Analysis',
                'nav.cellAnnotation': 'Cell Annotation',
                'nav.trajectory': 'Trajectory Analysis',
                'nav.envSection': 'Environment',
                'nav.envInstall': 'Python Package Install',
                'nav.envInfo': 'Python Env Info',
                'envInfo.title': 'Python Environment Info',
                'envInfo.loading': 'Loading environment info...',
                'envInfo.system': 'System',
                'envInfo.python': 'Python Environment',
                'envInfo.gpu': 'GPU / Accelerator',
                'envInfo.noGpu': 'No GPU / accelerator detected',
                'envInfo.keyPkgs': 'Key Package Versions',
                'envInfo.pkgName': 'Package',
                'envInfo.pkgVersion': 'Version',
                'envInfo.pkgStatus': 'Status',
                'envInfo.notInstalled': 'Not installed',
                'envInfo.allPkgs': 'All Installed Packages',
                'envInfo.filter': 'Filter packages...',
                'nav.diffAnalysis': 'Differential Analysis',
                'env.title': 'Python Environment Manager',
                'env.pkgPlaceholder': 'e.g. sctour, scvi-tools, harmonypy',
                'env.search': 'Search',
                'env.foundOnPypi': 'Found on PyPI',
                'env.notFound': 'Package not found on PyPI.',
                'env.tabPip': 'uv pip',
                'env.tabConda': 'mamba / conda',
                'env.mirror': 'Mirror:',
                'env.testMirrors': 'Test Speed',
                'env.testingMirrors': 'Testing mirror latency...',
                'env.extraArgs': 'Extra args:',
                'env.channels': 'Channels:',
                'env.customChannel': 'custom-channel',
                'env.addChannel': '+ Add',
                'env.install': 'Install',
                'env.output': 'Output',
                'env.clear': 'Clear',
                'env.consolePlaceholder': 'Ready.',
                'env.installSuccess': '✓ Installation completed successfully.',
                'env.installFailed': '✗ Installation failed',
                'env.enterPkg': 'Please enter a package name first.',
                'nav.enrichment': 'Functional Enrichment',
                'search.placeholder': 'Search genes, cell types...',
                'search.hint': 'Searching...',
                'search.genes': 'Genes',
                'search.cellTypes': 'Cell Types',
                'search.pathways': 'Pathways',
                'search.analysis': 'Analysis',
                'view.visualization': 'Visualization',
                'view.codeEditor': 'Code Editor',
                'view.skills': 'Skills',
                'view.terminal': 'Terminal',
                'view.skillsTitle': 'Skill Store',
                'terminal.label': 'Terminal',
                'terminal.panelTitle': 'Terminals',
                'terminal.newSession': 'New Terminal',
                'breadcrumb.home': 'Home',
                'breadcrumb.title': 'Single-cell Analysis',
                'upload.title': 'Upload H5AD File',
                'upload.subtitle': 'Drag & drop or click to select a file',
                'upload.button': 'Select File',
                'upload.analysisMode': 'Analysis Mode',
                'upload.analysisModeDesc': 'Load data fully into memory — all analysis tools enabled',
                'upload.previewMode': 'Preview Mode',
                'upload.previewModeDesc': 'Low-memory read (backed=\'r\') — analysis tools disabled',
                'upload.previewButton': 'Preview File',
                'upload.previewUploading': 'Opening in preview mode…',
                'upload.previewStart': 'Preview read',
                'upload.previewSuccess': 'Preview mode loaded',
                'upload.invalidFormat': 'Please upload a .h5ad file',
                'upload.uploading': 'Uploading file...',
                'upload.start': 'Starting upload',
                'upload.htmlError': 'Server returned HTML error page',
                'upload.invalidResponse': 'Server returned non-JSON response',
                'upload.failed': 'Upload failed',
                'upload.success': 'Upload successful',
                'upload.successDetail': 'Upload successful',
                'preview.bannerTitle': 'Preview Mode',
                'preview.bannerDesc': ' — analysis tools are disabled; data is loaded with low memory usage',
                'preview.switchBtn': 'Switch to Analysis Mode',
                'preview.switchConfirm': 'Switching to Analysis Mode will fully load data into memory, which may take a moment. Continue?',
                'preview.switchHint': 'Please re-upload the file using the "Analysis Mode" drop zone on the left to fully load data.',
                'preview.modeBadge': 'Preview',
                'preview.toolDisabledTip': 'Analysis is disabled in Preview Mode. Switch to "Analysis Mode" to run analyses.',
                'preview.toolDisabledAlert': '⚠️ Analysis tools are disabled in Preview Mode.\n\nTo run analyses, click "Switch to Analysis Mode" in the data status bar and re-upload the file.',
                'preview.saveDisabled': 'Cannot save in Preview Mode',
                'memory.title': 'Memory Usage',
                'memory.process': 'This App',
                'memory.other': 'System Used',
                'status.filename': 'Filename',
                'status.cells': 'cells',
                'status.genes': 'genes',
                'status.save': 'Save',
                'status.reset': 'Reset',
                'status.dsMax': 'max',
                'status.dsInt': 'int',
                'status.dsFloat': 'float',
                'status.dsNorm': 'normalized',
                'status.dsNormTip': 'Row sums are uniform — data appears normalized',
                'status.dsRaw': 'raw counts',
                'status.dsRawTip': 'Row sums vary significantly — data appears to be raw counts',
                'status.dsLog1pTip': 'log1p transformation has been applied',
                'status.dsScaled': 'scaled',
                'status.dsScaledTip': 'Data has negative values — sc.pp.scale may have been applied',
                'status.dsTargetTip': 'Estimated normalization target_sum',
                'controls.embedding': 'Embedding',
                'controls.embeddingPlaceholder': 'Select embedding',
                'controls.customBadge': 'Custom',
                'controls.customAxes': 'Custom X/Y',
                'controls.xAxis': 'X Axis',
                'controls.yAxis': 'Y Axis',
                'controls.randomEmbedding': '🎲 Random',
                'controls.colorBy': 'Color by',
                'controls.colorNone': 'None',
                'controls.gene': 'Gene Expression',
                'controls.genePlaceholder': 'Enter gene name',
                'controls.paletteContinuous': 'Palette (continuous)',
                'controls.paletteDefault': 'Default',
                'controls.paletteDiverging': 'RdBu (diverging)',
                'controls.paletteSpectral': 'Spectral (diverging)',
                'controls.categoryPalette': 'Categorical palette',
                'controls.ovDefault': 'OmicVerse (28 colors)',
                'controls.ov56': 'OmicVerse 56',
                'controls.ov112': 'OmicVerse 112',
                'controls.vibrant': 'Vibrant (24)',
                'controls.paletteDefaultScanpy': 'Default (Scanpy)',
                'controls.tab10': 'Tab10 (10)',
                'controls.tab20': 'Tab20 (20)',
                'controls.set1': 'Set1 (9)',
                'controls.set2': 'Set2 (8)',
                'controls.set3': 'Set3 (12)',
                'controls.paired': 'Paired (12)',
                'controls.vmin': 'Min (vmin)',
                'controls.vmax': 'Max (vmax)',
                'controls.auto': 'Auto',
                'controls.apply': 'Apply',
                'controls.pointSize': 'Point size',
                'controls.opacity': 'Opacity',
                'controls.densityEnable': 'Enable density adjust',
                'controls.densityAdjust': 'Density adjust',
                'controls.densityDisabledByToggle': 'Density adjust is off',
                'controls.densityDisabledNoFeature': 'Select a feature to enable density adjust',
                'controls.densityPendingType': 'Checking feature type...',
                'controls.resetStyle': 'Reset style',
                'controls.renderer': 'Renderer',
                'controls.rendererAuto': 'Auto',
                'controls.rendererHint': 'WebGL: GPU-accelerated, hover & animations at any scale',
                'loading.processing': 'Processing data...',
                'panel.parameters': 'Parameters',
                'panel.selectAnalysis': 'Select an analysis type from the left menu',
                'panel.adataStatus': 'adata Status',
                'panel.analysisStatus': 'Analysis Status',
                'panel.waitingUpload': 'Waiting for data upload...',
                'toolbar.kernel': 'Kernel',
                'toolbar.runAll': 'Run all',
                'toolbar.addCell': 'Add cell',
                'toolbar.save': 'Save',
                'toolbar.insertTemplate': 'Insert template',
                'toolbar.clearAll': 'Clear all',
                'toolbar.fontDown': 'Decrease font',
                'toolbar.fontUp': 'Increase font',
                'toolbar.interrupt': 'Interrupt',
                'file.browser': 'File Browser',
                'file.root': 'Working Directory',
                'file.empty': 'Empty folder',
                'skills.title': 'Skill Store',
                'skills.subtitle': 'Browse local skills, import new ones, and open any skill for editing.',
                'skills.searchPlaceholder': 'Search skills by name or description',
                'skills.import': 'Import Skill',
                'skills.create': 'New Skill',
                'skills.emptyTitle': 'No skills yet',
                'skills.emptyBody': 'Create or import a skill to populate the store.',
                'skills.openEditor': 'Edit',
                'skills.openViewer': 'Open',
                'skills.modifySkill': 'Modify Skill',
                'skills.modifyReference': 'Modify Reference',
                'skills.viewSkill': 'View Skill',
                'skills.viewReference': 'View Reference',
                'skills.viewDetail': 'View Detail',
                'skills.openHomepage': 'Homepage',
                'skills.location': 'Location',
                'skills.local': 'Local',
                'skills.online': 'Online',
                'skills.version': 'Version',
                'skills.path': 'Path',
                'skills.author': 'Author',
                'skills.tags': 'Tags',
                'skills.package': 'Package',
                'skills.sources': 'Sources',
                'skills.all': 'All',
                'skills.reference': 'Reference',
                'skills.referenceEmpty': 'No reference yet. You can leave it empty or add extra guidance later.',
                'skills.readOnly': 'Read only',
                'skills.cardView': 'Cards',
                'skills.listView': 'List',
                'skills.createPrompt': 'Enter a skill name',
                'skills.descriptionPrompt': 'Enter a short description',
                'skills.importExists': 'A skill with the same slug already exists.',
                'skills.createFailed': 'Create skill failed',
                'skills.loadFailed': 'Load skills failed',
                'skills.onlineLoadFailed': 'Online skills unavailable',
                'kernel.monitor': 'Kernel Monitor',
                'kernel.memory': 'Memory Usage',
                'kernel.topVars': 'Top Variables',
                'kernel.vars': 'Variable Viewer',
                'kernel.varName': 'Variable',
                'kernel.varType': 'Type',
                'kernel.varPreview': 'Preview',
                'kernel.switching': 'Switching kernel to',
                'kernel.switched': 'Kernel switched:',
                'kernel.switchFailed': 'Kernel switch failed:',
                'common.refresh': 'Refresh',
                'common.loading': 'Loading...',
                'common.noData': 'No data',
                'common.collapse': 'Collapse',
                'common.expand': 'Expand',
                'common.close': 'Close',
                'common.cancel': 'Cancel',
                'common.run': 'Run',
                'common.failed': 'Failed',
                'common.error': 'Error',
                'common.unknownError': 'Unknown error',
                'agent.configTitle': 'Agent Configuration',
                'agent.apiBase': 'API Base URL',
                'agent.apiKey': 'API Key',
                'agent.model': 'Model',
                'agent.modelPlaceholder': 'Model name',
                'agent.temperature': 'Temperature',
                'agent.topP': 'Top P',
                'agent.maxTokens': 'Max Tokens',
                'agent.timeout': 'Timeout (s)',
                'agent.systemPrompt': 'System Prompt',
                'agent.systemPromptPlaceholder': 'You are a single-cell analysis assistant...',
                'agent.save': 'Save',
                'agent.reset': 'Reset',
                'agent.localNotice': 'Saved in browser storage',
                'agent.title': 'Single-cell Agent',
                'agent.configButton': 'Configure',
                'agent.greeting': 'Hi, I can help analyze single-cell data. Tell me what you want to do.',
                'agent.inputPlaceholder': 'Ask a question, e.g. suggest cell type annotation',
                'agent.send': 'Send',
                'agent.stop': 'Stop',
                'agent.newChat': 'New Chat',
                'agent.analyzing': 'Analyzing...',
                'agent.done': 'Analysis completed.',
                'agent.stopped': 'Generation stopped by user.',
                'agent.dataUpdated': 'Data updated',
                'agent.reconnectFailed': 'Reconnection failed',
                'agent.sessionId': 'Session',
                'agent.cancelled': 'Cancelled by server',
                'agent.rememberKey': 'Remember key across sessions',
                'agent.keyNotice': 'API key is stored in session memory only. Check "Remember" to persist.',
                'status.ready': 'Ready',
                'status.agentSaved': 'Agent settings saved',
                'status.agentReset': 'Agent settings reset',
                'status.downloadingData': 'Downloading processed data...',
                'status.downloadStart': 'Starting download...',
                'status.saveFailed': 'Save failed',
                'status.dataSaved': 'Data saved',
                'status.resetConfirm': 'Reset all data? Unsaved results will be lost.',
                'status.importFailed': 'Import failed',
                'status.openFailed': 'Open failed',
                'status.saveSuccess': 'Saved successfully',
                'status.uploadFirst': 'Please upload data first',
                'status.executing': 'Running...',
                'status.noOutput': '(Success, no output)',
                'status.beforeLeave': 'You have unsaved data. Leave the page?',
                'status.backendUnavailable': 'Cannot reach backend. Ensure the server is running.',
                'status.createFailed': 'Create failed',
                'status.deleteFailed': 'Delete failed',
                'status.renameFailed': 'Rename failed',
                'status.pasteFailed': 'Paste failed',
                'status.moveFailed': 'Move failed',
                'prompt.newFile': 'New file name',
                'prompt.newFolder': 'New folder name',
                'prompt.deleteConfirm': 'Delete',
                'prompt.renameTo': 'Rename to',
                'prompt.moveTo': 'Move to (relative path)',
                'common.comingSoon': 'Coming soon',
                'tools.codeRef': 'Code Reference',
                'tools.backToList': 'Back',
                'tools.codeRefSubtitle': 'adata is the current AnnData object',
                'tools.codeTabLabel': 'Code',
                'tools.paramsTabLabel': 'Parameters',
                'tools.copyCode': 'Copy Code',
                'tools.codeCopied': 'Copied!',
                'tools.paramName': 'Parameter',
                'tools.paramType': 'Type',
                'tools.paramDefault': 'Default',
                'tools.paramDesc': 'Description',
                'panel.categorySelected': 'Selected category:',
                'tools.normalize': 'Normalize',
                'tools.normalizeDesc': 'Total-count normalization',
                'tools.log1p': 'Log transform',
                'tools.log1pDesc': 'Natural log1p',
                'tools.scale': 'Scale',
                'tools.scaleDesc': 'Z-score scaling',
                'tools.filterCells': 'Filter cells',
                'tools.filterCellsDesc': 'Filter by UMI/gene thresholds',
                'tools.filterGenes': 'Filter genes',
                'tools.filterGenesDesc': 'Filter by expressing cells/UMI thresholds',
                'tools.filterOutliers': 'Filter outliers',
                'tools.filterOutliersDesc': 'QC then filter by mitochondrial ratios, etc.',
                'tools.doublets': 'Remove doublets',
                'tools.doubletsDesc': 'Detect potential doublets (Scrublet)',
                'tools.hvg': 'Highly variable genes',
                'tools.hvgDesc': 'Select HVGs',
                'tools.pca': 'PCA',
                'tools.pcaDesc': 'Principal component analysis',
                'tools.umap': 'UMAP',
                'tools.umapDesc': 'Uniform Manifold Approximation',
                'tools.tsne': 't-SNE',
                'tools.tsneDesc': 't-distributed Stochastic Neighbor Embedding',
                'tools.neighbors': 'Neighbors',
                'tools.neighborsDesc': 'KNN graph construction',
                'tools.leiden': 'Leiden',
                'tools.leidenDesc': 'Community detection',
                'tools.louvain': 'Louvain',
                'tools.louvainDesc': 'Community detection',
                'tools.cellAnnotation': 'Cell annotation',
                'tools.cellAnnotationDesc': 'Automatic cell type annotation',
                'tools.trajectory': 'Trajectory',
                'tools.trajectoryDesc': 'Cell developmental trajectory',
                'tools.diff': 'Differential analysis',
                'tools.diffDesc': 'Differential expression genes',
                'tools.enrichment': 'Enrichment',
                'tools.enrichmentDesc': 'GO/KEGG enrichment',
                'tools.celltypist': 'CellTypist',
                'tools.celltypistDesc': 'Logistic regression-based cell type annotation',
                'tools.gpt4celltype': 'GPT4Celltype',
                'tools.gpt4celltypeDesc': 'LLM-based cell type annotation',
                'tools.scsa': 'SCSA',
                'tools.scsaDesc': 'Score-based cell type annotation',
                'tools.diffusionmap': 'Diffusion Map',
                'tools.diffusionmapDesc': 'Diffusion map trajectory + DPT pseudotime',
                'tools.slingshot': 'Slingshot',
                'tools.slingshotDesc': 'Cell lineage and pseudotime inference',
                'tools.palantir': 'Palantir',
                'tools.palantirDesc': 'Cell fate prediction via diffusion',
                'tools.paga': 'PAGA',
                'tools.pagaDesc': 'Partition-based graph abstraction visualization',
                'tools.sctour': 'scTour',
                'tools.sctourDesc': 'Deep learning-based trajectory analysis',
                'gene.error': 'Gene expression error',
                'gene.notFound': 'Gene not found',
                'gene.showing': 'Showing gene expression',
                'gene.loaded': 'Gene expression loaded',
                'gene.loadFailed': 'Failed to load gene expression',
                'gene.updating': 'Updating gene expression...',
                'gene.loading': 'Loading gene expression...',
                'plot.generating': 'Generating plot...',
                'plot.errorPrefix': 'Plot error',
                'plot.failedPrefix': 'Plot failed',
                'plot.done': 'Plot generated',
                'plot.switchEmbedding': 'Switching embedding...',
                'plot.updateColor': 'Updating colors...',
                'plot.colorUpdated': 'Color updated',
                'plot.embeddingSwitched': 'Embedding switched',
                'tool.running': 'Running',
                'tool.start': 'Started',
                'tool.failed': 'failed',
                'tool.completed': 'completed',
                'tool.execFailed': 'failed to run',
                'traj.title': 'Trajectory Visualization',
                'traj.embTitle': 'Pseudotime Embedding',
                'traj.hmTitle': 'Gene Trend Heatmap',
                'traj.generate': 'Generate',
                'traj.clickGenerate': 'Click Generate to render',
                'traj.hmHint': 'Enter genes then click Generate',
                'traj.pseudotimeCol': 'Pseudotime column',
                'traj.autoDetect': 'Auto-detect',
                'traj.basis': 'Embedding',
                'traj.colormap': 'Colormap',
                'traj.pointSize': 'Point size',
                'traj.pagaOverlay': 'Overlay PAGA graph',
                'traj.pagaGroups': 'Cluster column (groups)',
                'traj.selectCol': 'Select',
                'traj.minEdge': 'Min edge width',
                'traj.nodeScale': 'Node size scale',
                'traj.genes': 'Genes (comma or newline separated)',
                'traj.layer': 'Data layer',
                'traj.bins': 'Pseudotime bins',
                'traj.hmColormap': 'Colormap',
                'nav.deg': 'Differential Expression',
                'nav.dct': 'Differential Cell Type',
                // DCT analysis strings
                'dct.title': 'Differential Cell Type Composition',
                'dct.method': 'Analysis Method',
                'dct.conditionCol': 'Condition Column',
                'dct.ctrlGroup': 'Control Group',
                'dct.testGroup': 'Test Group',
                'dct.celltypeKey': 'Cell Type Column',
                'dct.sampleKey': 'Sample Key',
                'dct.sampleKeyOptional': '-- Optional for scCODA --',
                'dct.sampleKeySelect':   '-- Required for milopy --',
                'dct.sampleKeyHintSccoda': 'Column in obs with sample info (recommended for scCODA)',
                'dct.sampleKeyHintMilopy': 'Column in obs with sample info (required for milopy)',
                'dct.useRep': 'Embedding (for milopy)',
                'dct.estFdr': 'Estimated FDR',
                'dct.run': 'Run DCT Analysis',
                'dct.methodScocodaDesc': 'Bayesian hierarchical model, statistically rigorous',
                'dct.methodMilopyDesc': 'Milo neighborhood analysis, captures continuous changes',
                'dct.running': 'Running DCT analysis...',
                'dct.done': 'DCT analysis complete',
                'dct.selectCol': '-- Select --',
                'dct.totalCellTypes': 'Cell types:',
                'dct.credible': 'Credible:',
                'dct.totalNhoods': 'Neighborhoods:',
                'dct.sigUp': 'Sig. Up:',
                'dct.sigDown': 'Sig. Down:',
                'dct.compositionTitle': 'Cell Type Composition',
                'dct.effectsTitle': 'scCODA Effects',
                'dct.beeswarmTitle': 'DA Beeswarm (milopy)',
                'dct.generate': 'Generate',
                'dct.compositionHint': 'Run DCT analysis first, then click Generate',
                'dct.effectsHint': 'Run DCT analysis first, then click Generate',
                'dct.resultsTitle': 'DCT Results',
                'dct.tableEmpty': 'Run DCT analysis to see results',
                'dct.col.celltype': 'Cell Type',
                'dct.col.effect': 'Effect',
                'dct.col.log2fc': 'log₂FC',
                'dct.col.logfc': 'logFC',
                'dct.col.inclProb': 'Incl. Prob.',
                'dct.col.sig': 'Significant',
                'dct.col.nhoodSize': 'Nhood Size',
                'deg.title': 'Differential Expression Analysis',
                'deg.setupTitle': 'Run DEG Analysis',
                'deg.volcanoTitle': 'Volcano Plot',
                'deg.run': 'Run',
                'deg.methodWilcoxonDesc': 'Wilcoxon rank-sum test, recommended for most cases',
                'deg.methodTtestDesc': 'Student\'s t-test, faster computation',
                'deg.generate': 'Generate',
                'deg.conditionCol': 'Condition column',
                'deg.ctrlGroup': 'Control group',
                'deg.testGroup': 'Test group',
                'deg.celltypeKey': 'Cell type column',
                'deg.celltypeGroup': 'Cell types to analyze',
                'deg.allTypes': 'All cell types',
                'deg.celltypeHint': 'Hold Ctrl/Cmd to select multiple. Deselect all = use all.',
                'deg.method': 'Method',
                'deg.maxCells': 'Max cells',
                'deg.selectCol': '-- Select --',
                'deg.volcanoHint': 'Run DEG analysis first, then click Generate',
                'deg.fcThresh': 'FC threshold',
                'deg.padjThresh': 'padj threshold',
                'deg.pointSize': 'Point size',
                'deg.labelTop': 'Label top N genes',
                'deg.total': 'Total genes:',
                'deg.sigUp': 'Up:',
                'deg.sigDown': 'Down:',
                'deg.topGenes': 'Top significant genes',
                'deg.geneCol': 'Gene',
                'deg.log2fcCol': 'log2FC',
                'deg.padjCol': 'padj',
                'deg.running': 'Running DEG analysis...',
                'deg.done': 'DEG analysis completed',
                'deg.violinTitle': 'Violin Plot',
                'deg.violinHint': 'Click a gene in the results table, or type gene names below',
                'deg.violinGenes': 'Genes (comma separated)',
                'deg.violinGroupby': 'Group by',
                'deg.violinLayer': 'Data layer',
                'deg.resultsTitle': 'DEG Results',
                'deg.filterFC': '|log₂FC| ≥',
                'deg.filterPadj': 'padj ≤',
                'deg.filterPct': 'Expr% ≥',
                'deg.filterDir': 'Direction',
                'deg.dirAll': 'All',
                'deg.dirUp': 'Up',
                'deg.dirDown': 'Down',
                'deg.filterSearch': 'Search gene',
                'deg.pctCtrlCol': 'Expr%(ctrl)',
                'deg.pctTestCol': 'Expr%(test)',
                'deg.tableEmpty': 'Run DEG analysis to see results',
                'deg.tableClickHint': 'Click a gene row to add it to the Violin Plot',
                'deg.noResults': 'No genes match current filters',
                'notebook.selectIpynb': 'Please select a .ipynb file',
                'notebook.importConfirm': 'Importing a notebook will replace current cells. Continue?',
                'notebook.openConfirm': 'Opening a notebook will replace current cells. Continue?',
                'file.noSaveContent': 'Nothing to save',
                'cell.placeholderMarkdown': 'Enter Markdown...',
                'cell.placeholderRaw': 'Enter raw text...',
                'cell.deleteConfirm': 'Delete this code cell?',
                'cell.clearConfirm': 'Clear all code cells?',
                'template.chooseTitle': 'Choose a template:',
                'template.chooseIndex': 'Enter a number',
                'var.preview50': 'Preview 50x50',
                'var.dataframeLabel': 'DataFrame',
                'var.anndataLabel': 'AnnData',
                'var.loadToViz': 'Load to Visualization',
                'parameter.none': 'No parameters required.',
                'view.agentTitle': 'Agent Chat',
                'view.accountTitle': 'Personal Center',
                'view.gatewayTitle': 'Gateway',
                'view.codeTitle': 'Python Code Editor',
                'breadcrumb.agent': 'Agent Chat',
                'breadcrumb.account': 'Personal Center',
                'breadcrumb.gateway': 'Gateway',
                'breadcrumb.code': 'Python Code Editor',
                'code.placeholder': '# Enter Python code (variables: adata, sc, pd, np)\n# Shift+Enter to run',
                'cell.typeCode': 'Code',
                'cell.typeMarkdown': 'Markdown',
                'cell.typeRaw': 'Raw',
                'cell.moveUp': 'Move up',
                'cell.moveDown': 'Move down',
                'cell.run': 'Run (Shift+Enter)',
                'cell.toggleOutput': 'Toggle output',
                'cell.hideOutput': 'Hide output',
                'cell.clearOutput': 'Clear output',
                'cell.delete': 'Delete'
                , 'cell.outputHidden': 'Output hidden'
                , 'context.newFile': 'New File'
                , 'context.newFolder': 'New Folder'
                , 'context.rename': 'Rename'
                , 'context.copy': 'Copy'
                , 'context.paste': 'Paste'
                , 'context.move': 'Move'
                , 'context.delete': 'Delete'
                , 'notify.title': 'Analysis Notifications'
                , 'notify.markRead': 'Mark as read'
                , 'notify.preprocessing': 'Preprocessing'
                , 'notify.preprocessingDone': 'normalization completed'
                , 'notify.timeAgo': '2 minutes ago'
                , 'notify.empty': 'No notifications'
                , 'notify.all': 'All notifications'
                , 'user.menu': 'User Menu'
                , 'user.profile': 'Profile'
                , 'user.settings': 'Settings'
                , 'user.help': 'Help'
                , 'user.logout': 'Sign out'
                , 'account.center': 'Personal Center'
                , 'account.guest': 'Guest'
                , 'account.guestHint': 'Sign in to manage your profile.'
                , 'account.serverOffline': 'Account server unavailable'
                , 'account.signIn': 'Sign in'
                , 'account.register': 'Register'
                , 'account.profile': 'Profile'
                , 'account.settings': 'Settings'
                , 'account.help': 'Help'
                , 'account.logout': 'Sign out'
                , 'account.email': 'Email'
                , 'account.emailPlaceholder': 'name@lab.org'
                , 'account.password': 'Password'
                , 'account.passwordPlaceholder': 'At least 8 characters'
                , 'account.displayName': 'Display Name'
                , 'account.displayNamePlaceholder': 'How your name appears in the UI'
                , 'account.fullName': 'Full Name'
                , 'account.fullNamePlaceholder': 'Your real name'
                , 'account.fullNameHint': 'Used for account identity and collaboration records.'
                , 'account.institution': 'Institution'
                , 'account.institutionPlaceholder': 'University, lab, company, or hospital'
                , 'account.researchArea': 'Research Area'
                , 'account.researchAreaPlaceholder': 'e.g. single-cell atlas, spatial transcriptomics'
                , 'account.usagePurpose': 'Usage Purpose'
                , 'account.usagePurposePlaceholder': 'Tell us what you plan to do with OmicVerse or OmicClaw'
                , 'account.brandWorkspaceTitle': 'Research account workspace'
                , 'account.brandWorkspaceSubtitle': 'Sign in to sync your profile, unlock online skills, and keep a persistent identity across sessions.'
                , 'account.brandWorkspaceTitleClaw': 'Agent account workspace'
                , 'account.brandWorkspaceSubtitleClaw': 'Sign in to sync your agent identity, online skill access, and persistent profile details.'
                , 'account.signInTitle': 'Welcome back'
                , 'account.signInBody': 'Sign in to restore your profile and continue with your saved account identity.'
                , 'account.registerTitle': 'Create your research account'
                , 'account.registerBody': 'Tell us who you are so we can keep your workspace profile complete from day one.'
                , 'account.authPanelSubtitle': 'Use the same account for profile management and online features.'
                , 'account.registerNote': 'These details are stored with your account and can be updated later in your personal center.'
                , 'account.resendActivation': 'Resend activation email'
                , 'account.resendActivationCountdown': 'Resend activation email ({seconds}s)'
                , 'account.activationEmailPrompt': 'Please enter your email address first.'
                , 'account.activationEmailSent': 'Activation email sent. Please check your inbox.'
                , 'account.activationResendFailed': 'Failed to resend. Please try again later.'
                , 'account.activationCheckEmail': 'Please check your email to activate your account.'
                , 'account.featureProfileTitle': 'Persistent profile'
                , 'account.featureProfileBody': 'Keep your account identity, organization, and research context in one place.'
                , 'account.featureStoreTitle': 'Online skill access'
                , 'account.featureStoreBody': 'Use your account to access authenticated features in the online skill store.'
                , 'account.featurePrivacyTitle': 'Minimal data collection'
                , 'account.featurePrivacyBody': 'We only collect the profile information needed to manage your workspace and service access.'
                , 'account.saveProfile': 'Save Profile'
                , 'account.loginSuccess': 'Signed in successfully'
                , 'account.registerSuccess': 'Registered successfully'
                , 'account.logoutSuccess': 'Signed out'
                , 'account.loginFailed': 'Sign in failed'
                , 'account.registerFailed': 'Registration failed'
                , 'account.profileSaved': 'Profile updated'
                , 'account.profileSaveFailed': 'Profile update failed'
                , 'account.profileHintReadonly': 'Profile information is read only in this view.'
                , 'account.profileHintEditable': 'You can update your account profile details here.'
                , 'account.profileIntro': 'Manage the information shown in the personal center.'
                , 'account.loggedInAs': 'Signed in as'
                , 'account.memberSince': 'Member since'
                , 'account.status': 'Status'
                , 'account.active': 'Active'
                , 'account.editing': 'Editing profile'
                , 'account.editProfile': 'Edit Profile'
                , 'account.helpText': 'This account only affects the top-right personal center. Analysis, upload, notebooks, and tools remain available without login.'
                , 'gateway.navTitle': 'Gateway'
                , 'gateway.overview': 'Overview'
                , 'gateway.channels': 'Channels'
                , 'gateway.sessions': 'Sessions'
                , 'gateway.memory': 'Memory'
                , 'gateway.actions': 'Actions'
                , 'gateway.activeChannels': 'Active Channels'
                , 'gateway.messages24h': 'Messages (24 h)'
                , 'gateway.memoryDocs': 'Memory Docs'
                , 'gateway.statusTitle': 'Gateway Status'
                , 'gateway.webOnlyMode': 'Gateway API not available — running in web-only mode.'
                , 'gateway.noChannelsConnected': 'No channels connected.'
                , 'gateway.noChannelsHint': 'Configured channels will appear here after the gateway starts.'
                , 'gateway.sessionsShort': 'sess.'
                , 'gateway.sessionsLower': 'sessions'
                , 'gateway.noActiveSessions': 'No active sessions.'
                , 'gateway.sessionId': 'Session ID'
                , 'gateway.channel': 'Channel'
                , 'gateway.messages': 'Messages'
                , 'gateway.lastActive': 'Last Active'
                , 'gateway.allDocuments': 'All Documents'
                , 'gateway.noDocumentsYet': 'No documents yet.'
                , 'gateway.editDocument': 'Edit Document'
                , 'gateway.newMemoryDocument': 'New Memory Document'
                , 'gateway.titleRequired': 'Title is required'
                , 'gateway.deleteDocumentConfirm': 'Delete this document?'
                , 'gateway.nameRequired': 'Name required'
                , 'gateway.loadingConfigError': 'Could not load config. Ensure the gateway backend is running.'
                , 'gateway.llmModel': 'LLM Model'
                , 'gateway.model': 'Model'
                , 'gateway.apiKey': 'API Key'
                , 'gateway.apiEndpoint': 'API Endpoint'
                , 'gateway.optional': 'optional'
                , 'gateway.activeSessionsTitle': 'Active Sessions'
                , 'gateway.memoryStore': 'Memory Store'
                , 'gateway.searchMemory': 'Search memory...'
                , 'gateway.newDoc': 'New Doc'
                , 'gateway.folders': 'Folders'
                , 'gateway.newFolder': 'New Folder'
                , 'gateway.folderName': 'Folder Name'
                , 'gateway.folderNamePlaceholder': 'My folder'
                , 'gateway.documentTitle': 'Title'
                , 'gateway.documentTitlePlaceholder': 'Document title'
                , 'gateway.folder': 'Folder'
                , 'gateway.root': '- root -'
                , 'gateway.tags': 'Tags (comma-separated)'
                , 'gateway.tagsPlaceholder': 'tag1, tag2'
                , 'gateway.content': 'Content'
                , 'gateway.contentPlaceholder': 'Markdown content...'
                , 'gateway.status.running': 'running'
                , 'gateway.status.failed': 'failed'
                , 'gateway.status.starting': 'starting'
                , 'gateway.status.stopped': 'stopped'
                , 'gateway.status.notConfigured': 'not configured'
                , 'gateway.button.running': 'Running'
                , 'gateway.button.stopped': 'Stopped'
                , 'gateway.button.notConfigured': 'Not configured'
                , 'gateway.button.stop': 'Stop'
                , 'gateway.button.logs': 'Logs'
                , 'gateway.button.test': 'Test'
                , 'gateway.button.testConnection': 'Test Connection'
                , 'gateway.button.save': 'Save'
                , 'gateway.noOutputYet': 'No output yet.'
                , 'gateway.secret.set': 'set'
                , 'gateway.secret.notSet': 'not set'
                , 'gateway.secret.keepExisting': '(already set — leave blank to keep)'
                , 'gateway.secret.enterValue': 'Enter value'
                , 'gateway.telegram.token': 'Bot Token'
                , 'gateway.telegram.tokenHint': 'Get from @BotFather — set TELEGRAM_BOT_TOKEN env var or enter here'
                , 'gateway.telegram.allowedUsers': 'Allowed Users <span class="text-muted fw-normal">(comma-sep usernames/IDs)</span>'
                , 'gateway.telegram.allowedUsersPlaceholder': '@username or 123456789'
                , 'gateway.telegram.allowedUsersHint': 'Leave empty to allow all users'
                , 'gateway.feishu.appId': 'App ID'
                , 'gateway.feishu.appSecret': 'App Secret'
                , 'gateway.feishu.connectionMode': 'Connection Mode'
                , 'gateway.feishu.verificationToken': 'Verification Token'
                , 'gateway.feishu.verificationTokenHint': 'Webhook only — optional'
                , 'gateway.feishu.encryptKey': 'Encrypt Key'
                , 'gateway.feishu.encryptKeyHint': 'Optional'
                , 'gateway.feishu.webhookHost': 'Webhook Host'
                , 'gateway.feishu.webhookHostHint': 'webhook mode only'
                , 'gateway.feishu.webhookPort': 'Webhook Port'
                , 'gateway.feishu.webhookPath': 'Webhook Path'
                , 'gateway.qq.appId': 'App ID'
                , 'gateway.qq.appIdHint': 'From QQ Open Platform → My Apps'
                , 'gateway.qq.clientSecret': 'Client Secret'
                , 'gateway.qq.clientSecretHint': 'Set QQ_CLIENT_SECRET env var or enter here'
                , 'gateway.qq.imageHost': 'Image Host <span class="text-muted fw-normal">(for figures)</span>'
                , 'gateway.qq.imageHostHint': 'Required to send charts; leave empty to skip'
                , 'gateway.qq.imageServerPort': 'Image Server Port'
                , 'gateway.qq.markdown': 'Enable Markdown replies (msg_type=2, requires QQ Open Platform permission)'
                , 'gateway.imessage.cliPath': 'imsg CLI Path'
                , 'gateway.imessage.cliPathHint': 'Path to the imsg binary'
                , 'gateway.imessage.dbPath': 'chat.db Path'
                , 'gateway.imessage.includeAttachments': 'Include attachment metadata'
                , 'gateway.savedTo': 'Saved to'
                , 'gateway.saveFailed': 'Save failed'
                , 'gateway.deleteFailed': 'Delete failed'
                , 'gateway.testing': 'Testing...'
                , 'gateway.startingEllipsis': 'Starting...'
                , 'gateway.started': 'Started'
                , 'gateway.startFailed': 'Start failed'
                , 'gateway.logUnavailable': 'Log endpoint not available.'
                , 'gateway.processNotRunning': 'Process not running. Click Start to launch, then open Logs.'
                , 'gateway.waitingOutput': '(no output yet — waiting...)'
                , 'gateway.deleteSessionConfirm': 'Delete session'
                , 'gateway.loadingSessions': 'Loading sessions...'
                , 'gateway.loadingDocuments': 'Loading documents...'
                , 'gateway.apiKeyKeepPlaceholder': 'sk-... (leave empty to keep existing)'
                , 'gateway.apiKeySetPrefix': 'set: '
                , 'gateway.channel.telegram': 'Telegram'
                , 'gateway.channel.feishu': 'Feishu / Lark'
                , 'gateway.channel.qq': 'QQ Bot'
                , 'gateway.channel.imessage': 'iMessage'
            },
            zh: {
                'lang.toggle': 'EN',
                'nav.singleCell': '单细胞分析',
                'nav.preprocessing': '数据预处理',
                'nav.normalization': '标准化处理',
                'nav.qc': '质量控制',
                'nav.featureSelection': '特征选择',
                'nav.dimReduction': '可视化',
                'nav.dimReductionSub': '降维',
                'nav.linearDR': '线性降维',
                'nav.nonlinearDR': '非线性降维',
                'nav.visualization': '可视化',
                'nav.clustering': '基础分析',
                'nav.clusteringSub': '聚类',
                'nav.communityDetection': '社区检测',
                'nav.cellTypeId': '细胞类型识别',
                'nav.clusterValidation': '聚类验证',
                'nav.omicverse': '进阶分析',
                'nav.cellAnnotation': '细胞注释',
                'nav.trajectory': '轨迹分析',
                'nav.envSection': '环境管理',
                'nav.envInstall': 'Python 包安装',
                'nav.envInfo': 'Python 环境信息',
                'envInfo.title': 'Python 环境信息',
                'envInfo.loading': '正在加载环境信息...',
                'envInfo.system': '系统信息',
                'envInfo.python': 'Python 环境',
                'envInfo.gpu': 'GPU / 加速器',
                'envInfo.noGpu': '未检测到 GPU / 加速器',
                'envInfo.keyPkgs': '核心包版本',
                'envInfo.pkgName': '包名',
                'envInfo.pkgVersion': '版本',
                'envInfo.pkgStatus': '状态',
                'envInfo.notInstalled': '未安装',
                'envInfo.allPkgs': '所有已安装包',
                'envInfo.filter': '过滤包名...',
                'nav.diffAnalysis': '差异分析',
                'env.title': 'Python 环境管理',
                'env.pkgPlaceholder': '例如 sctour, scvi-tools, harmonypy',
                'env.search': '搜索',
                'env.foundOnPypi': 'PyPI 上有此包',
                'env.notFound': 'PyPI 上未找到该包。',
                'env.tabPip': 'uv pip',
                'env.tabConda': 'mamba / conda',
                'env.mirror': '镜像源:',
                'env.testMirrors': '测速',
                'env.testingMirrors': '正在测试镜像延迟...',
                'env.extraArgs': '附加参数:',
                'env.channels': 'Channel:',
                'env.customChannel': '自定义 channel',
                'env.addChannel': '+ 添加',
                'env.install': '安装',
                'env.output': '输出',
                'env.clear': '清空',
                'env.consolePlaceholder': '就绪。',
                'env.installSuccess': '✓ 安装成功。',
                'env.installFailed': '✗ 安装失败',
                'env.enterPkg': '请先输入包名。',
                'nav.enrichment': '功能富集',
                'search.placeholder': '搜索基因、细胞类型...',
                'search.hint': '搜索内容...',
                'search.genes': '基因',
                'search.cellTypes': '细胞类型',
                'search.pathways': '通路',
                'search.analysis': '分析',
                'view.visualization': '可视化',
                'view.codeEditor': '代码编辑器',
                'view.skills': '技能库',
                'view.terminal': '终端',
                'view.skillsTitle': '技能商店',
                'terminal.label': '终端',
                'terminal.panelTitle': '终端列表',
                'terminal.newSession': '新建终端',
                'breadcrumb.home': '主页',
                'breadcrumb.title': '单细胞分析',
                'upload.title': '上传H5AD文件',
                'upload.subtitle': '拖拽文件到此处或点击选择文件',
                'upload.button': '选择文件',
                'upload.analysisMode': '分析读取模式',
                'upload.analysisModeDesc': '完整加载数据到内存，支持所有分析功能',
                'upload.previewMode': '仅预览模式',
                'upload.previewModeDesc': "低内存读取（backed='r'），分析功能禁用",
                'upload.previewButton': '预览文件',
                'upload.previewUploading': '正在以预览模式读取…',
                'upload.previewStart': '预览读取',
                'upload.previewSuccess': '预览模式已加载',
                'upload.invalidFormat': '请上传.h5ad格式的文件',
                'upload.uploading': '正在上传文件...',
                'upload.start': '开始上传文件',
                'upload.htmlError': '服务器返回HTML错误页面',
                'upload.invalidResponse': '服务器返回非JSON',
                'upload.failed': '上传失败',
                'upload.success': '文件上传成功',
                'upload.successDetail': '文件上传成功',
                'preview.bannerTitle': '预览模式',
                'preview.bannerDesc': ' — 分析功能已禁用，数据以低内存方式读取',
                'preview.switchBtn': '切换分析读取',
                'preview.switchConfirm': '切换分析模式将完整加载数据到内存，可能需要一些时间。确定继续？',
                'preview.switchHint': '请在左侧"分析读取模式"区域重新上传文件以完整加载数据。',
                'preview.modeBadge': '预览模式',
                'preview.toolDisabledTip': '预览模式下无法进行分析，如需分析请切换成"分析读取"模式',
                'preview.toolDisabledAlert': '⚠️ 预览模式下无法进行分析操作。\n\n如需分析，请点击数据状态栏中的「切换分析读取」按钮，\n以完整加载模式重新打开文件。',
                'preview.saveDisabled': '预览模式下无法保存',
                'memory.title': '内存占用',
                'memory.process': '本程序',
                'memory.other': '本机已用',
                'status.filename': '文件名',
                'status.cells': '细胞',
                'status.genes': '基因',
                'status.save': '保存',
                'status.reset': '重置',
                'status.dsMax': '最大值',
                'status.dsInt': '整数',
                'status.dsFloat': '浮点数',
                'status.dsNorm': '已标准化',
                'status.dsNormTip': '各细胞行和均匀，数据已完成标准化',
                'status.dsRaw': '原始计数',
                'status.dsRawTip': '各细胞行和差异显著，数据为原始计数',
                'status.dsLog1pTip': '已执行 log1p 变换',
                'status.dsScaled': '已缩放',
                'status.dsScaledTip': '数据存在负值，可能已执行 sc.pp.scale',
                'status.dsTargetTip': '推测的标准化 target_sum',
                'controls.embedding': '坐标轴预设',
                'controls.embeddingPlaceholder': '选择降维方法',
                'controls.customBadge': '自定义',
                'controls.customAxes': '自定义 X/Y',
                'controls.xAxis': 'X 轴',
                'controls.yAxis': 'Y 轴',
                'controls.randomEmbedding': '🎲 随机布局',
                'controls.colorBy': '着色方式',
                'controls.colorNone': '无着色',
                'controls.gene': '基因表达',
                'controls.genePlaceholder': '输入基因名',
                'controls.paletteContinuous': '调色板（连续）',
                'controls.paletteDefault': '默认',
                'controls.paletteDiverging': 'RdBu（发散）',
                'controls.paletteSpectral': 'Spectral（发散）',
                'controls.categoryPalette': '分类调色板',
                'controls.ovDefault': 'OmicVerse（28色）',
                'controls.ov56': 'OmicVerse 56',
                'controls.ov112': 'OmicVerse 112',
                'controls.vibrant': 'Vibrant（24色）',
                'controls.paletteDefaultScanpy': '默认（Scanpy）',
                'controls.tab10': 'Tab10（10色）',
                'controls.tab20': 'Tab20（20色）',
                'controls.set1': 'Set1（9色）',
                'controls.set2': 'Set2（8色）',
                'controls.set3': 'Set3（12色）',
                'controls.paired': 'Paired（12色）',
                'controls.vmin': '最小值 (vmin)',
                'controls.vmax': '最大值 (vmax)',
                'controls.auto': '自动',
                'controls.apply': '应用',
                'controls.pointSize': '点大小',
                'controls.opacity': '透明度',
                'controls.densityEnable': '启用密度调节',
                'controls.densityAdjust': '密度调节',
                'controls.densityDisabledByToggle': '密度调节已关闭',
                'controls.densityDisabledNoFeature': '请选择特征以启用密度调节',
                'controls.densityPendingType': '正在检查特征类型...',
                'controls.resetStyle': '重置样式',
                'controls.renderer': '渲染器',
                'controls.rendererAuto': '自动',
                'controls.rendererHint': 'WebGL：GPU加速，任意规模均支持悬停与动画',
                'loading.processing': '正在处理数据...',
                'panel.parameters': '参数设置',
                'panel.selectAnalysis': '点击左侧菜单栏选取功能',
                'panel.adataStatus': 'adata 状态',
                'panel.analysisStatus': '分析状态',
                'panel.waitingUpload': '等待上传数据...',
                'toolbar.kernel': '内核',
                'toolbar.runAll': '运行全部',
                'toolbar.addCell': '新增单元',
                'toolbar.save': '保存',
                'toolbar.insertTemplate': '插入模板',
                'toolbar.clearAll': '清空所有',
                'toolbar.fontDown': '减小字号',
                'toolbar.fontUp': '增大字号',
                'toolbar.interrupt': '中断',
                'file.browser': '文件浏览器',
                'file.root': '运行目录',
                'file.empty': '空目录',
                'skills.title': '技能商店',
                'skills.subtitle': '浏览本地 skills，导入新 skill，并直接打开任意 skill 进行编辑。',
                'skills.searchPlaceholder': '按名称或描述搜索 skill',
                'skills.import': '导入 Skill',
                'skills.create': '新建 Skill',
                'skills.emptyTitle': '还没有 skill',
                'skills.emptyBody': '新建或导入一个 skill 后，这里会显示卡片。',
                'skills.openEditor': '编辑',
                'skills.openViewer': '打开',
                'skills.modifySkill': '修改 Skill',
                'skills.modifyReference': '修改 Reference',
                'skills.viewSkill': '查看 Skill',
                'skills.viewReference': '查看 Reference',
                'skills.viewDetail': '查看详情',
                'skills.openHomepage': '主页',
                'skills.location': '位置',
                'skills.local': '本地',
                'skills.online': '在线',
                'skills.version': '版本',
                'skills.path': '路径',
                'skills.author': '作者',
                'skills.tags': '标签',
                'skills.package': '包名',
                'skills.sources': '来源',
                'skills.all': '全部',
                'skills.reference': '参考',
                'skills.referenceEmpty': '还没有 reference.md。你可以先留空，之后再补充说明。',
                'skills.readOnly': '只读',
                'skills.cardView': '卡片',
                'skills.listView': '列表',
                'skills.createPrompt': '输入 skill 名称',
                'skills.descriptionPrompt': '输入简短描述',
                'skills.importExists': '同名 slug 的 skill 已存在。',
                'skills.createFailed': '创建 skill 失败',
                'skills.loadFailed': '加载 skills 失败',
                'skills.onlineLoadFailed': '在线技能暂时不可用',
                'kernel.monitor': 'Kernel 监控',
                'kernel.memory': '内存占用',
                'kernel.topVars': '变量占用 Top 10',
                'kernel.vars': '变量查看器',
                'kernel.varName': '变量',
                'kernel.varType': '类型',
                'kernel.varPreview': '预览',
                'kernel.switching': '正在切换内核到',
                'kernel.switched': '内核已切换:',
                'kernel.switchFailed': '切换内核失败:',
                'common.refresh': '刷新',
                'common.loading': '加载中...',
                'common.noData': '暂无数据',
                'common.collapse': '收起',
                'common.expand': '展开',
                'common.close': '关闭',
                'common.cancel': '取消',
                'common.run': '运行',
                'common.failed': '失败',
                'common.error': '错误',
                'common.unknownError': '未知错误',
                'agent.configTitle': 'Agent 配置',
                'agent.apiBase': 'API Base URL',
                'agent.apiKey': 'API Key',
                'agent.model': '模型',
                'agent.modelPlaceholder': '模型名称',
                'agent.temperature': 'Temperature',
                'agent.topP': 'Top P',
                'agent.maxTokens': 'Max Tokens',
                'agent.timeout': 'Timeout (s)',
                'agent.systemPrompt': 'System Prompt',
                'agent.systemPromptPlaceholder': '你是一个单细胞分析助手...',
                'agent.save': '保存',
                'agent.reset': '重置',
                'agent.localNotice': '配置保存在浏览器本地',
                'agent.title': '单细胞分析 Agent',
                'agent.configButton': '配置模型',
                'agent.greeting': '你好，我可以帮你分析单细胞数据。告诉我你想做什么分析。',
                'agent.inputPlaceholder': '输入你的问题，比如：帮我做细胞类型注释建议',
                'agent.send': '发送',
                'agent.stop': '停止',
                'agent.newChat': '新对话',
                'agent.analyzing': '正在分析...',
                'agent.done': '已完成分析。',
                'agent.stopped': '用户已停止生成。',
                'agent.dataUpdated': '数据已更新',
                'agent.reconnectFailed': '重连失败',
                'agent.sessionId': '会话',
                'agent.cancelled': '已被服务端取消',
                'agent.rememberKey': '跨会话记住密钥',
                'agent.keyNotice': 'API 密钥仅存储在当前会话内存中。勾选"记住"可跨浏览器会话保留。',
                'status.ready': '就绪',
                'status.agentSaved': 'Agent 配置已保存',
                'status.agentReset': 'Agent 配置已重置',
                'status.downloadingData': '正在下载处理后的数据...',
                'status.downloadStart': '开始下载处理后的数据...',
                'status.saveFailed': '保存失败',
                'status.dataSaved': '数据保存成功',
                'status.resetConfirm': '确定要重置所有数据吗？所有未保存的分析结果将丢失。',
                'status.importFailed': '导入失败',
                'status.openFailed': '打开失败',
                'status.saveSuccess': '保存成功',
                'status.uploadFirst': '请先上传数据',
                'status.executing': '执行中...',
                'status.noOutput': '(执行成功，无输出)',
                'status.beforeLeave': '您有未保存的数据，刷新页面将丢失所有分析结果。确定要离开吗？',
                'status.backendUnavailable': '无法连接后端，请确认服务已启动并允许访问。',
                'status.createFailed': '创建失败',
                'status.deleteFailed': '删除失败',
                'status.renameFailed': '重命名失败',
                'status.pasteFailed': '粘贴失败',
                'status.moveFailed': '移动失败',
                'prompt.newFile': '新建文件名',
                'prompt.newFolder': '新建文件夹名',
                'prompt.deleteConfirm': '确认删除',
                'prompt.renameTo': '重命名为',
                'prompt.moveTo': '移动到 (相对路径)',
                'common.comingSoon': '该功能正在开发中，敬请期待！',
                'tools.codeRef': '代码参考',
                'tools.backToList': '返回',
                'tools.codeRefSubtitle': 'adata 为当前加载的 AnnData 对象',
                'tools.codeTabLabel': '代码',
                'tools.paramsTabLabel': '参数说明',
                'tools.copyCode': '复制代码',
                'tools.codeCopied': '已复制',
                'tools.paramName': '参数名',
                'tools.paramType': '类型',
                'tools.paramDefault': '默认值',
                'tools.paramDesc': '说明',
                'panel.categorySelected': '选择分析类别:',
                'tools.normalize': '归一化',
                'tools.normalizeDesc': 'Total-count 归一化',
                'tools.log1p': '对数转换',
                'tools.log1pDesc': '自然对数 log1p',
                'tools.scale': '数据缩放',
                'tools.scaleDesc': 'Z-score 标准化',
                'tools.filterCells': '过滤细胞',
                'tools.filterCellsDesc': '按UMI/基因数上下限过滤',
                'tools.filterGenes': '过滤基因',
                'tools.filterGenesDesc': '按表达细胞数/UMI上下限过滤',
                'tools.filterOutliers': '过滤异常细胞',
                'tools.filterOutliersDesc': '先计算QC，再按线粒体比例等过滤',
                'tools.doublets': '去除双细胞',
                'tools.doubletsDesc': '识别并去除潜在双细胞（Scrublet）',
                'tools.hvg': '高变基因',
                'tools.hvgDesc': '选择高变基因',
                'tools.pca': 'PCA分析',
                'tools.pcaDesc': '主成分分析',
                'tools.umap': 'UMAP降维',
                'tools.umapDesc': '统一流形近似投影',
                'tools.tsne': 't-SNE降维',
                'tools.tsneDesc': 't-分布随机邻域嵌入',
                'tools.neighbors': '邻域计算',
                'tools.neighborsDesc': 'K近邻图构建',
                'tools.leiden': 'Leiden聚类',
                'tools.leidenDesc': '高质量社区检测',
                'tools.louvain': 'Louvain聚类',
                'tools.louvainDesc': '经典社区检测',
                'tools.cellAnnotation': '细胞注释',
                'tools.cellAnnotationDesc': '自动细胞类型注释',
                'tools.trajectory': '轨迹分析',
                'tools.trajectoryDesc': '细胞发育轨迹',
                'tools.diff': '差异分析',
                'tools.diffDesc': '差异表达基因',
                'tools.enrichment': '功能富集',
                'tools.enrichmentDesc': 'GO/KEGG富集分析',
                'tools.celltypist': 'CellTypist',
                'tools.celltypistDesc': '基于逻辑回归的细胞类型自动注释',
                'tools.gpt4celltype': 'GPT4Celltype',
                'tools.gpt4celltypeDesc': '基于大语言模型的细胞类型注释',
                'tools.scsa': 'SCSA',
                'tools.scsaDesc': '基于打分系统的细胞类型注释',
                'tools.diffusionmap': 'Diffusion Map',
                'tools.diffusionmapDesc': '扩散映射轨迹分析，计算 DPT 拟时序',
                'tools.slingshot': 'Slingshot',
                'tools.slingshotDesc': '细胞谱系与拟时序推断',
                'tools.palantir': 'Palantir',
                'tools.palantirDesc': '基于扩散过程的细胞命运预测',
                'tools.paga': 'PAGA',
                'tools.pagaDesc': '基于分区的图抽象可视化',
                'tools.sctour': 'scTour',
                'tools.sctourDesc': '基于深度学习的轨迹分析',
                'gene.error': '基因表达错误',
                'gene.notFound': '基因未找到',
                'gene.showing': '显示基因表达',
                'gene.loaded': '基因表达加载完成',
                'gene.loadFailed': '基因表达加载失败',
                'gene.updating': '正在更新基因表达...',
                'gene.loading': '正在加载基因表达...',
                'plot.generating': '正在生成图表...',
                'plot.errorPrefix': '绘图错误',
                'plot.failedPrefix': '绘图失败',
                'plot.done': '图表生成完成',
                'plot.switchEmbedding': '正在切换降维方法...',
                'plot.updateColor': '正在更新着色...',
                'plot.colorUpdated': '着色更新完成',
                'plot.embeddingSwitched': '降维方法切换完成',
                'tool.running': '正在执行',
                'tool.start': '开始执行',
                'tool.failed': '失败',
                'tool.completed': '完成',
                'tool.execFailed': '执行失败',
                'traj.title': '轨迹分析可视化',
                'traj.embTitle': '拟时序嵌入图',
                'traj.hmTitle': '基因拟时序热图',
                'traj.generate': '生成',
                'traj.clickGenerate': '点击「生成」显示嵌入图',
                'traj.hmHint': '选择基因后点击「生成」',
                'traj.pseudotimeCol': '拟时序列',
                'traj.autoDetect': '自动检测',
                'traj.basis': '嵌入方法',
                'traj.colormap': '配色',
                'traj.pointSize': '点大小',
                'traj.pagaOverlay': '叠加 PAGA 图',
                'traj.pagaGroups': '聚类列 (groups)',
                'traj.selectCol': '选择',
                'traj.minEdge': '最小边宽',
                'traj.nodeScale': '节点大小缩放',
                'traj.genes': '基因列表（逗号或换行分隔）',
                'traj.layer': '数据层',
                'traj.bins': '拟时序分箱数',
                'traj.hmColormap': '热图配色',
                'nav.deg': '差异表达分析',
                'nav.dct': '差异细胞类型',
                // DCT analysis strings
                'dct.title': '差异细胞类型组成分析',
                'dct.method': '分析方法',
                'dct.conditionCol': '条件列',
                'dct.ctrlGroup': '对照组',
                'dct.testGroup': '实验组',
                'dct.celltypeKey': '细胞类型列',
                'dct.sampleKey': '样本列',
                'dct.sampleKeyOptional': '-- scCODA 可选 --',
                'dct.sampleKeySelect':   '-- milopy 必填 --',
                'dct.sampleKeyHintSccoda': 'obs 中包含样本信息的列（scCODA 建议提供）',
                'dct.sampleKeyHintMilopy': 'obs 中包含样本信息的列（milopy 必须提供）',
                'dct.useRep': '嵌入空间（milopy 使用）',
                'dct.estFdr': '估计 FDR 阈值',
                'dct.run': '运行 DCT 分析',
                'dct.methodScocodaDesc': '贝叶斯层次模型，统计严谨，适合样本数较少的情况',
                'dct.methodMilopyDesc': 'Milo 邻域分析，可捕捉连续细胞组成变化',
                'dct.running': '正在运行 DCT 分析...',
                'dct.done': 'DCT 分析完成',
                'dct.selectCol': '-- 请选择 --',
                'dct.totalCellTypes': '细胞类型数:',
                'dct.credible': '显著变化:',
                'dct.totalNhoods': '邻域数:',
                'dct.sigUp': '显著升高:',
                'dct.sigDown': '显著降低:',
                'dct.compositionTitle': '细胞类型组成',
                'dct.effectsTitle': 'scCODA 效应图',
                'dct.beeswarmTitle': 'DA 蜂群图 (milopy)',
                'dct.generate': '生成',
                'dct.compositionHint': '请先运行 DCT 分析，然后点击生成',
                'dct.effectsHint': '请先运行 DCT 分析，然后点击生成',
                'dct.resultsTitle': 'DCT 结果',
                'dct.tableEmpty': '请运行 DCT 分析以查看结果',
                'dct.col.celltype': '细胞类型',
                'dct.col.effect': '效应量',
                'dct.col.log2fc': 'log₂FC',
                'dct.col.logfc': 'logFC',
                'dct.col.inclProb': '纳入概率',
                'dct.col.sig': '显著性',
                'dct.col.nhoodSize': '邻域大小',
                'deg.title': '差异表达分析',
                'deg.setupTitle': '运行 DEG 分析',
                'deg.volcanoTitle': '火山图',
                'deg.run': '运行',
                'deg.methodWilcoxonDesc': '非参数秩和检验，推荐用于大多数场景',
                'deg.methodTtestDesc': 'Student\'s t 检验，计算速度更快',
                'deg.generate': '生成',
                'deg.conditionCol': '条件列',
                'deg.ctrlGroup': '对照组',
                'deg.testGroup': '实验组',
                'deg.celltypeKey': '细胞类型列',
                'deg.celltypeGroup': '分析的细胞类型',
                'deg.allTypes': '所有细胞类型',
                'deg.celltypeHint': '按住 Ctrl/Cmd 多选，全不选则分析所有类型',
                'deg.method': '统计方法',
                'deg.maxCells': '最大细胞数',
                'deg.selectCol': '-- 选择 --',
                'deg.volcanoHint': '请先运行 DEG 分析，再点击「生成」',
                'deg.fcThresh': 'FC 阈值',
                'deg.padjThresh': 'padj 阈值',
                'deg.pointSize': '点大小',
                'deg.labelTop': '标注前 N 个基因',
                'deg.total': '总基因数:',
                'deg.sigUp': '上调:',
                'deg.sigDown': '下调:',
                'deg.topGenes': '显著差异基因',
                'deg.geneCol': '基因',
                'deg.log2fcCol': 'log2FC',
                'deg.padjCol': 'padj',
                'deg.running': '正在运行 DEG 分析...',
                'deg.done': 'DEG 分析完成',
                'deg.violinTitle': '小提琴图',
                'deg.violinHint': '点击下方结果表中的基因，或在此手动输入',
                'deg.violinGenes': '基因（逗号分隔）',
                'deg.violinGroupby': '分组依据',
                'deg.violinLayer': '数据层',
                'deg.resultsTitle': 'DEG 结果列表',
                'deg.filterFC': '|log₂FC| ≥',
                'deg.filterPadj': 'padj ≤',
                'deg.filterPct': '表达比例 ≥',
                'deg.filterDir': '方向',
                'deg.dirAll': '全部',
                'deg.dirUp': '上调',
                'deg.dirDown': '下调',
                'deg.filterSearch': '搜索基因',
                'deg.pctCtrlCol': '表达比%(对照)',
                'deg.pctTestCol': '表达比%(实验)',
                'deg.tableEmpty': '请先运行 DEG 分析',
                'deg.tableClickHint': '点击基因行可加入小提琴图',
                'deg.noResults': '当前筛选条件下无匹配基因',
                'notebook.selectIpynb': '请选择 .ipynb 文件',
                'notebook.importConfirm': '导入笔记本会替换当前代码单元，是否继续？',
                'notebook.openConfirm': '打开笔记本会替换当前代码单元，是否继续？',
                'file.noSaveContent': '没有可保存的内容',
                'cell.placeholderMarkdown': '输入 Markdown...',
                'cell.placeholderRaw': '输入原始文本...',
                'cell.deleteConfirm': '确定要删除这个代码单元吗？',
                'cell.clearConfirm': '确定要清空所有代码单元吗？',
                'template.chooseTitle': '选择模板:',
                'template.chooseIndex': '输入编号',
                'var.preview50': '预览 50x50',
                'var.dataframeLabel': 'DataFrame',
                'var.anndataLabel': 'AnnData',
                'var.loadToViz': '加载到可视化',
                'parameter.none': '该工具无需参数设置',
                'view.agentTitle': 'Agent 对话',
                'view.accountTitle': '个人中心',
                'view.gatewayTitle': '网关',
                'view.codeTitle': 'Python 代码编辑器',
                'breadcrumb.agent': 'Agent 对话',
                'breadcrumb.account': '个人中心',
                'breadcrumb.gateway': '网关',
                'breadcrumb.code': 'Python 代码编辑器',
                'code.placeholder': '# 输入Python代码 (可用变量: adata, sc, pd, np)\n# Shift+Enter 运行代码',
                'cell.typeCode': 'Code',
                'cell.typeMarkdown': 'Markdown',
                'cell.typeRaw': 'Raw',
                'cell.moveUp': '上移单元',
                'cell.moveDown': '下移单元',
                'cell.run': '运行 (Shift+Enter)',
                'cell.toggleOutput': '折叠输出',
                'cell.hideOutput': '隐藏输出',
                'cell.clearOutput': '清空输出',
                'cell.delete': '删除'
                , 'cell.outputHidden': '输出已隐藏'
                , 'context.newFile': '新建文件'
                , 'context.newFolder': '新建文件夹'
                , 'context.rename': '重命名'
                , 'context.copy': '复制'
                , 'context.paste': '粘贴'
                , 'context.move': '移动'
                , 'context.delete': '删除'
                , 'notify.title': '分析通知'
                , 'notify.markRead': '标记为已读'
                , 'notify.preprocessing': '数据预处理'
                , 'notify.preprocessingDone': '已完成标准化处理'
                , 'notify.timeAgo': '2分钟前'
                , 'notify.empty': '暂无通知'
                , 'notify.all': '所有通知'
                , 'user.menu': '用户菜单'
                , 'user.profile': '个人资料'
                , 'user.settings': '设置'
                , 'user.help': '帮助'
                , 'user.logout': '退出'
                , 'account.center': '个人中心'
                , 'account.guest': '访客'
                , 'account.guestHint': '登录后可管理右上角个人资料。'
                , 'account.serverOffline': '账号服务暂不可用'
                , 'account.signIn': '登录'
                , 'account.register': '注册'
                , 'account.profile': '个人资料'
                , 'account.settings': '设置'
                , 'account.help': '帮助'
                , 'account.logout': '退出登录'
                , 'account.email': '邮箱'
                , 'account.emailPlaceholder': 'name@lab.org'
                , 'account.password': '密码'
                , 'account.passwordPlaceholder': '至少 8 位字符'
                , 'account.displayName': '显示名称'
                , 'account.displayNamePlaceholder': '界面中显示的名称'
                , 'account.fullName': '真实姓名'
                , 'account.fullNamePlaceholder': '请输入真实姓名'
                , 'account.fullNameHint': '用于账号身份识别和协作记录。'
                , 'account.institution': '机构'
                , 'account.institutionPlaceholder': '学校、实验室、公司或医院'
                , 'account.researchArea': '研究方向'
                , 'account.researchAreaPlaceholder': '例如：单细胞图谱、空间转录组'
                , 'account.usagePurpose': '使用目的'
                , 'account.usagePurposePlaceholder': '简单描述你计划如何使用 OmicVerse 或 OmicClaw'
                , 'account.brandWorkspaceTitle': '科研账号工作台'
                , 'account.brandWorkspaceSubtitle': '登录后可同步你的个人资料、在线技能访问权限和长期身份信息。'
                , 'account.brandWorkspaceTitleClaw': 'Agent 登录工作台'
                , 'account.brandWorkspaceSubtitleClaw': '登录后可同步你的 Agent 身份、在线技能权限与长期使用资料。'
                , 'account.signInTitle': '欢迎回来'
                , 'account.signInBody': '登录后可恢复你的个人资料，并继续使用已保存的账号身份。'
                , 'account.registerTitle': '创建科研账号'
                , 'account.registerBody': '注册时补全基本信息，后续个人中心和在线功能会直接复用这些资料。'
                , 'account.authPanelSubtitle': '同一个账号可用于个人资料管理和在线功能访问。'
                , 'account.registerNote': '这些信息会随账号一起保存，之后可在个人中心继续修改。'
                , 'account.resendActivation': '重新发送激活邮件'
                , 'account.resendActivationCountdown': '重新发送激活邮件（{seconds}秒）'
                , 'account.activationEmailPrompt': '请先输入邮箱地址。'
                , 'account.activationEmailSent': '激活邮件已发送，请检查收件箱。'
                , 'account.activationResendFailed': '重新发送失败，请稍后再试。'
                , 'account.activationCheckEmail': '请检查邮箱并完成账号激活。'
                , 'account.featureProfileTitle': '持久化资料'
                , 'account.featureProfileBody': '将你的身份、机构和研究背景集中保存在一个账号中。'
                , 'account.featureStoreTitle': '在线技能访问'
                , 'account.featureStoreBody': '登录后可访问需要账号的在线技能商店功能。'
                , 'account.featurePrivacyTitle': '最小化采集'
                , 'account.featurePrivacyBody': '我们只收集管理账号和服务访问所需的基础资料。'
                , 'account.saveProfile': '保存资料'
                , 'account.loginSuccess': '登录成功'
                , 'account.registerSuccess': '注册成功'
                , 'account.logoutSuccess': '已退出登录'
                , 'account.loginFailed': '登录失败'
                , 'account.registerFailed': '注册失败'
                , 'account.profileSaved': '个人资料已更新'
                , 'account.profileSaveFailed': '个人资料更新失败'
                , 'account.profileHintReadonly': '当前视图仅展示个人资料。'
                , 'account.profileHintEditable': '当前可修改完整的账号资料信息。'
                , 'account.profileIntro': '管理个人中心中展示的资料信息。'
                , 'account.loggedInAs': '当前登录邮箱'
                , 'account.memberSince': '注册时间'
                , 'account.status': '状态'
                , 'account.active': '已登录'
                , 'account.editing': '正在编辑资料'
                , 'account.editProfile': '编辑资料'
                , 'account.helpText': '这个账号只影响右上角个人中心，不会影响分析、上传、Notebook 或其他工具使用。'
                , 'gateway.navTitle': '网关'
                , 'gateway.overview': '总览'
                , 'gateway.channels': '通道'
                , 'gateway.sessions': '会话'
                , 'gateway.memory': '记忆库'
                , 'gateway.actions': '操作'
                , 'gateway.activeChannels': '活动通道'
                , 'gateway.messages24h': '消息数（24 小时）'
                , 'gateway.memoryDocs': '记忆文档'
                , 'gateway.statusTitle': '网关状态'
                , 'gateway.webOnlyMode': 'Gateway API 不可用，当前处于仅 Web 模式。'
                , 'gateway.noChannelsConnected': '当前没有已连接的通道。'
                , 'gateway.noChannelsHint': '网关启动后，已配置的通道会显示在这里。'
                , 'gateway.sessionsShort': '会话'
                , 'gateway.sessionsLower': '会话'
                , 'gateway.noActiveSessions': '当前没有活跃会话。'
                , 'gateway.sessionId': '会话 ID'
                , 'gateway.channel': '通道'
                , 'gateway.messages': '消息数'
                , 'gateway.lastActive': '最后活跃时间'
                , 'gateway.allDocuments': '全部文档'
                , 'gateway.noDocumentsYet': '还没有文档。'
                , 'gateway.editDocument': '编辑文档'
                , 'gateway.newMemoryDocument': '新建记忆文档'
                , 'gateway.titleRequired': '标题不能为空'
                , 'gateway.deleteDocumentConfirm': '确定删除这份文档吗？'
                , 'gateway.nameRequired': '名称不能为空'
                , 'gateway.loadingConfigError': '无法加载配置，请确认 gateway 后端正在运行。'
                , 'gateway.llmModel': 'LLM 模型'
                , 'gateway.model': '模型'
                , 'gateway.apiKey': 'API 密钥'
                , 'gateway.apiEndpoint': 'API 地址'
                , 'gateway.optional': '可选'
                , 'gateway.activeSessionsTitle': '活跃会话'
                , 'gateway.memoryStore': '记忆库'
                , 'gateway.searchMemory': '搜索记忆...'
                , 'gateway.newDoc': '新建文档'
                , 'gateway.folders': '文件夹'
                , 'gateway.newFolder': '新建文件夹'
                , 'gateway.folderName': '文件夹名称'
                , 'gateway.folderNamePlaceholder': '我的文件夹'
                , 'gateway.documentTitle': '标题'
                , 'gateway.documentTitlePlaceholder': '文档标题'
                , 'gateway.folder': '文件夹'
                , 'gateway.root': '- 根目录 -'
                , 'gateway.tags': '标签（逗号分隔）'
                , 'gateway.tagsPlaceholder': '标签1, 标签2'
                , 'gateway.content': '内容'
                , 'gateway.contentPlaceholder': 'Markdown 内容...'
                , 'gateway.status.running': '运行中'
                , 'gateway.status.failed': '失败'
                , 'gateway.status.starting': '启动中'
                , 'gateway.status.stopped': '已停止'
                , 'gateway.status.notConfigured': '未配置'
                , 'gateway.button.running': '运行中'
                , 'gateway.button.stopped': '已停止'
                , 'gateway.button.notConfigured': '未配置'
                , 'gateway.button.stop': '停止'
                , 'gateway.button.logs': '日志'
                , 'gateway.button.test': '测试'
                , 'gateway.button.testConnection': '测试连接'
                , 'gateway.button.save': '保存'
                , 'gateway.noOutputYet': '暂无输出。'
                , 'gateway.secret.set': '已设置'
                , 'gateway.secret.notSet': '未设置'
                , 'gateway.secret.keepExisting': '（已设置，留空则保持不变）'
                , 'gateway.secret.enterValue': '输入值'
                , 'gateway.telegram.token': 'Bot Token'
                , 'gateway.telegram.tokenHint': '从 @BotFather 获取，也可通过 TELEGRAM_BOT_TOKEN 环境变量设置'
                , 'gateway.telegram.allowedUsers': '允许的用户 <span class="text-muted fw-normal">（逗号分隔用户名/ID）</span>'
                , 'gateway.telegram.allowedUsersPlaceholder': '@username 或 123456789'
                , 'gateway.telegram.allowedUsersHint': '留空表示允许所有用户'
                , 'gateway.feishu.appId': 'App ID'
                , 'gateway.feishu.appSecret': 'App Secret'
                , 'gateway.feishu.connectionMode': '连接模式'
                , 'gateway.feishu.verificationToken': 'Verification Token'
                , 'gateway.feishu.verificationTokenHint': '仅 webhook 模式需要，可选'
                , 'gateway.feishu.encryptKey': 'Encrypt Key'
                , 'gateway.feishu.encryptKeyHint': '可选'
                , 'gateway.feishu.webhookHost': 'Webhook Host'
                , 'gateway.feishu.webhookHostHint': '仅 webhook 模式使用'
                , 'gateway.feishu.webhookPort': 'Webhook Port'
                , 'gateway.feishu.webhookPath': 'Webhook Path'
                , 'gateway.qq.appId': 'App ID'
                , 'gateway.qq.appIdHint': '来自 QQ 开放平台 → 我的应用'
                , 'gateway.qq.clientSecret': 'Client Secret'
                , 'gateway.qq.clientSecretHint': '也可通过 QQ_CLIENT_SECRET 环境变量设置'
                , 'gateway.qq.imageHost': '图片 Host <span class="text-muted fw-normal">（用于图表）</span>'
                , 'gateway.qq.imageHostHint': '发送图表时必填；留空则跳过'
                , 'gateway.qq.imageServerPort': '图片服务端口'
                , 'gateway.qq.markdown': '启用 Markdown 回复（msg_type=2，需要 QQ 开放平台权限）'
                , 'gateway.imessage.cliPath': 'imsg CLI 路径'
                , 'gateway.imessage.cliPathHint': 'imsg 可执行文件路径'
                , 'gateway.imessage.dbPath': 'chat.db 路径'
                , 'gateway.imessage.includeAttachments': '包含附件元数据'
                , 'gateway.savedTo': '已保存到'
                , 'gateway.saveFailed': '保存失败'
                , 'gateway.deleteFailed': '删除失败'
                , 'gateway.testing': '测试中...'
                , 'gateway.startingEllipsis': '启动中...'
                , 'gateway.started': '已启动'
                , 'gateway.startFailed': '启动失败'
                , 'gateway.logUnavailable': '日志接口不可用。'
                , 'gateway.processNotRunning': '进程未运行。请先点击启动，再打开日志。'
                , 'gateway.waitingOutput': '（暂无输出，正在等待...）'
                , 'gateway.deleteSessionConfirm': '确定删除会话'
                , 'gateway.loadingSessions': '正在加载会话...'
                , 'gateway.loadingDocuments': '正在加载文档...'
                , 'gateway.apiKeyKeepPlaceholder': 'sk-...（留空则保留现有值）'
                , 'gateway.apiKeySetPrefix': '已设置：'
                , 'gateway.channel.telegram': 'Telegram'
                , 'gateway.channel.feishu': '飞书 / Lark'
                , 'gateway.channel.qq': 'QQ 机器人'
                , 'gateway.channel.imessage': 'iMessage'
            }
        };
        if (!key) return '';
        return (dict[this.currentLang] && dict[this.currentLang][key]) || key;
    },

    applyLanguage(lang) {
        this.currentLang = lang;
        const elements = document.querySelectorAll('[data-i18n]');
        elements.forEach(el => {
            const key = el.getAttribute('data-i18n');
            if (key) {
                el.textContent = this.t(key);
            }
        });
        const placeholders = document.querySelectorAll('[data-i18n-placeholder]');
        placeholders.forEach(el => {
            const key = el.getAttribute('data-i18n-placeholder');
            if (key) {
                el.setAttribute('placeholder', this.t(key));
            }
        });
        const titles = document.querySelectorAll('[data-i18n-title]');
        titles.forEach(el => {
            const key = el.getAttribute('data-i18n-title');
            if (key) {
                el.setAttribute('title', this.t(key));
            }
        });
        const langToggle = document.getElementById('lang-toggle');
        if (langToggle) {
            langToggle.textContent = this.t('lang.toggle');
        }
        // Re-render dynamic content in the parameter panel
        if (this.currentTool) {
            // A tool form is open — re-render it (saves & restores values)
            this.refreshParameterFormLanguage();
        } else if (this.currentCategory) {
            // The tool list is showing — re-render so category labels update
            this.selectAnalysisCategory(this.currentCategory, { silent: true });
        }
        if (this.skillsLoaded && this.currentView === 'skills' && this.renderSkills) {
            this.renderSkills();
        }
        if (this.applyRuntimeBranding) {
            this.applyRuntimeBranding();
        }
        this.updateCodeCellPlaceholders();
    },

    translateFormHtml(html) {
        if (this.currentLang !== 'en') {
            return html;
        }
        const replacements = [
            ['最小UMI数', 'Min UMI'],
            ['最小基因数', 'Min genes'],
            ['最大UMI数', 'Max UMI'],
            ['最大基因数', 'Max genes'],
            ['最少表达细胞数', 'Min expressed cells'],
            ['最多表达细胞数', 'Max expressed cells'],
            ['可留空', 'Optional'],
            ['留空表示不限制。仅填写需要的阈值即可。', 'Leave blank for no limit.'],
            ['可选：设置上下限阈值，未填表示不限制。', 'Optional: set min/max thresholds; blank means no limit.'],
            ['将先计算线粒体/核糖体/血红蛋白等QC指标，再按阈值过滤。', 'QC metrics will be computed first, then filtered by thresholds.'],
            ['最大线粒体比例', 'Max mitochondrial'],
            ['线粒体基因前缀 (自动检测)', 'Mito gene prefixes (auto-detect)'],
            ['例如', 'e.g.'],
            ['最大核糖体基因比例', 'Max ribosomal'],
            ['最大血红蛋白基因比例', 'Max hemoglobin'],
            ['(百分比)', '(%)'],
            ['未填写的阈值不生效；线粒体前缀自动检测可编辑。', 'Unfilled thresholds are ignored; prefix can be edited.'],
            ['无', 'None'],
            ['批次列', 'Batch column'],
            ['模拟双细胞比', 'Simulated doublet ratio'],
            ['期望双细胞率', 'Expected doublet rate'],
            ['双细胞率标准差', 'Doublet rate stdev'],
            ['UMI子采样', 'UMI subsampling'],
            ['KNN距离度量', 'KNN distance metric'],
            ['PCA主成分数', 'PCA components'],
            ['目标总数', 'Target sum'],
            ['最大值', 'Max value'],
            ['基因数量', 'Gene count'],
            ['方法', 'Method'],
            ['主成分数量', 'Number of PCs'],
            ['邻居数量', 'Number of neighbors'],
            ['距离度量', 'Distance metric'],
            ['最小距离', 'Min distance'],
            ['困惑度', 'Perplexity'],
            ['分辨率', 'Resolution'],
            ['该工具无需参数设置', 'No parameters required.'],
            ['返回工具列表', 'Back to tools'],
            ['运行', 'Run'],
            // Annotation forms
            ['模型文件路径', 'Model file path'],
            ['本地 .pkl', 'local .pkl'],
            ['下载后自动填入，或手动输入路径', 'Auto-filled after download, or enter path manually'],
            ['从 CellTypist 获取模型列表', 'Fetch CellTypist model list'],
            ['获取中...', 'Fetching...'],
            ['模型列表已加载', 'Model list loaded'],
            ['-- 选择模型 --', '-- Select model --'],
            ['下载选中模型', 'Download selected model'],
            ['下载中...', 'Downloading...'],
            ['✓ 已下载', '✓ Downloaded'],
            ['✓ 已找到本地模型: ', '✓ Found local model: '],
            ['运行注释', 'Run annotation'],
            ['聚类键', 'Cluster key'],
            ['组织类型', 'Tissue type'],
            ['物种', 'Species'],
            ['LLM 提供商', 'LLM Provider'],
            ['通义千问', 'Tongyi Qianwen'],
            ['模型名称', 'Model name'],
            ['留空则读取 AGI_API_KEY 环境变量', 'Leave blank to use AGI_API_KEY env var'],
            ['(可选)', '(optional)'],
            ['自定义 OpenAI 兼容端点', 'Custom OpenAI-compatible endpoint'],
            ['Top marker 基因数', 'Top marker genes'],
            ['SCSA 数据库路径', 'SCSA database path'],
            ['留空将自动下载', 'Leave blank to auto-download'],
            ['下载', 'Download'],
            ['倍数变化阈值', 'Fold change threshold'],
            ['P 值阈值', 'P-value threshold'],
            ['细胞类型', 'Cell type'],
            ['参考数据库', 'Reference database'],
            ['组织', 'Tissue'],
            ['留空=All', 'blank=All'],
            // Trajectory forms
            ['聚类列', 'Cluster column'],
            ['低维表示', 'Low-dim representation'],
            ['主成分数', 'Number of PCs'],
            ['起始细胞类型', 'Origin cell type'],
            ['终止细胞类型', 'Terminal cell types'],
            ['逗号分隔', 'comma-separated'],
            ['训练轮数', 'Training epochs'],
            ['路标点数', 'Waypoints'],
            ['拟时序列', 'Pseudotime column'],
            ['低维表示 (use_rep，用于重算邻居)', 'Low-dim rep (use_rep, for neighbor recompute)'],
            ['可视化嵌入', 'Visualization embedding'],
            ['lec 重建权重', 'lec reconstruction weight'],
            ['lode 重建权重', 'lode reconstruction weight'],
            ['将在 obs 中添加 dpt_pseudotime 列，可用于可视化着色。', 'Adds dpt_pseudotime column to obs for coloring.'],
            ['将在 obs 中添加 slingshot_pseudotime 列。', 'Adds slingshot_pseudotime column to obs.'],
            ['将在 obs 中添加 palantir_pseudotime 和 palantir_entropy 列。', 'Adds palantir_pseudotime and palantir_entropy columns to obs.'],
            ['将生成 PAGA 图并在结果区域显示。', 'Generates a PAGA graph shown in the result overlay.'],
            ['需要原始 count 数据在 adata.X。将在 obs 中添加 sctour_pseudotime 列。', 'Requires raw counts in adata.X. Adds sctour_pseudotime column to obs.'],
            ['-- 选择列 --', '-- Select column --'],
            ['-- 自动检测 --', '-- Auto-detect --'],
            ['-- 不设置 --', '-- Not set --'],
            ['例如: Ductal', 'e.g. Ductal'],
            ['例如: Alpha,Beta', 'e.g. Alpha,Beta'],
            ['例如: Alpha,Beta,Delta', 'e.g. Alpha,Beta,Delta'],
            // Save modal
            ['文件名', 'Filename'],
            ['文件将下载到浏览器默认下载目录', 'File will be saved to the browser default download folder'],
            ['确认下载', 'Confirm download']
        ];
        let output = html;
        replacements.forEach(([from, to]) => {
            output = output.split(from).join(to);
        });
        return output;
    },

    refreshParameterFormLanguage() {
        const parameterContent = document.getElementById('parameter-content');
        if (!parameterContent || !this.currentTool) return;
        const inputs = parameterContent.querySelectorAll('input, select');
        const values = {};
        inputs.forEach(input => {
            if (input.type === 'checkbox') {
                values[input.id] = input.checked;
            } else {
                values[input.id] = input.value;
            }
        });
        const nameKeyOrLabel = this.currentToolLabelKey || this.currentToolLabel;
        const descKeyOrLabel = this.currentToolDescKey || this.currentToolDesc;
        this.renderParameterForm(this.currentTool, nameKeyOrLabel, descKeyOrLabel);
        Object.entries(values).forEach(([id, value]) => {
            const target = parameterContent.querySelector(`#${CSS.escape(id)}`);
            if (!target) return;
            if (target.type === 'checkbox') {
                target.checked = !!value;
            } else {
                target.value = value;
            }
        });
    },

    updateCodeCellPlaceholders() {
        const cells = document.querySelectorAll('.code-cell');
        cells.forEach(cell => {
            const type = cell.dataset.cellType || 'code';
            const textarea = cell.querySelector('.code-input');
            if (!textarea) return;
            if (type === 'markdown') {
                textarea.placeholder = this.t('cell.placeholderMarkdown');
            } else if (type === 'raw') {
                textarea.placeholder = this.t('cell.placeholderRaw');
            } else {
                textarea.placeholder = this.t('code.placeholder');
            }
        });
    }

});
