const translations = new Map([ ['3D View', '3D 视图'], ['Add file to project', '添加项目文件'], ['All', '全部'], ['Angularity', '倾斜度'], ['Ask me anything', '有问题尽管问我'], ['Attach files', '添加附件'], ['Auto', '自动'], ['Back to HiCAD', '返回 HiCAD'], [ 'Beyond geometry creation, I can help you with model-derived properties like center of mass, volume, and surface area.', '除了创建几何体,我还可以分析质心、体积和表面积等模型属性。', ], ['Bodies', '实体'], ['Chamfer', '倒角'], ['Choose your adventure', '选择一种建模方式'], ['Circular Pattern', '圆周阵列'], ['Capture viewport screenshot', '截取视口'], ['Code', '代码'], ['Code Editor', '代码编辑器'], ['Code-driven CAD', '代码驱动 CAD'], ['Commands', '命令'], ['Connected', '已连接'], ['Connecting and setting up scene...', '正在连接并初始化场景...'], ['Create a new project', '新建项目'], ['Create project', '新建项目'], ['Create from a sample', '从示例开始'], [ 'Create a gear with 10 teeth and use sensible defaults for everything else...', '创建一个 10 齿齿轮,其余参数使用合理默认值...', ], ['Design Studio', 'HiCAD'], ['Design using traditional point-and-click', '使用传统点击方式设计'], ['Dismiss', '关闭'], ['Edit', '编辑'], ['Edited', '最近编辑'], ['Export', '导出'], ['Export part', '导出零件'], ['Extrude', '拉伸'], ['Feature Tree', '特征树'], ['Fit all model', '显示全部模型'], ['Fillet', '圆角'], ['Flip Surface', '翻转曲面'], ['Front plane', '前视基准面'], [ 'From designing parts to giving manufacturing feedback, I’m ready to help.', '从零件设计到制造反馈,我都可以协助你。', ], ['Helix', '螺旋线'], ['Hello there, I\u2019m Zookeeper.', '你好,我是 HiCAD AI 助手。'], ["Hello there, I'm Zookeeper.", '你好,我是 HiCAD AI 助手。'], ['Help', '帮助'], ['Help and resources', '帮助与资源'], ['Hole', '孔'], ['How would you like to start?', '想从哪里开始?'], [ "I'm here to help you create and edit real, parametric CAD geometry through incremental, structured commands.", '我可以通过渐进、结构化的命令,帮助你创建和编辑参数化 CAD 几何体。', ], [ 'I create structured, fully editable geometry by building features step by step.', '我会逐步构建特征,生成结构化且可完整编辑的几何模型。', ], ['Insert', '插入'], ['Logs', '日志'], ['Loft', '放样'], ['Measure', '测量'], ['Mirror', '镜像'], ['No selection', '未选择'], ['No selection.', '未选择。'], ['Offset Plane', '偏移基准面'], ['Open project', '打开项目'], ['Project Libraries', '我的 CAD 项目'], ['Projects', '项目'], ['Project Files', '项目文件'], ['Publish', '发布'], ['Redo', '重做'], ['Rename project', '重命名'], ['Delete project', '删除项目'], ['Search projects', '搜索项目'], ['Sort by', '排序'], ['Name', '名称'], ['Last Modified', '最近编辑'], ['No projects found', '还没有项目'], [', ready to make your first one?', ',点击“新建项目”开始设计。'], [ 'No projects found, ready to make your first one?', '还没有项目,点击“新建项目”开始设计。', ], ['Submit', '确认'], ['Continue', '下一步'], ['Step back', '返回上一步'], ['Enter a value', '请输入'], ['Cancel', '取消'], ['Revolve', '旋转'], ['Settings', '设置'], ['Shell', '抽壳'], ['Side plane', '侧视基准面'], ['Sketch', '草图'], ['Split', '分割'], ['Start Sketch', '新建草图'], ['Supplemental analysis', '补充分析'], ['Sweep', '扫掠'], ['Top plane', '顶视基准面'], ['Translate', '平移'], ['Turn thoughts into geometry', '将想法变成几何模型'], ['Undo', '撤销'], ['Union', '并集'], [ 'Upload an image and I’ll interpret it, extract intent, and help you design or improve the geometry step by step.', '上传图片后,我会理解参考内容、提取设计意图,并逐步帮助你设计或改进几何模型。', ], [ 'Upload your image to work from real references', '上传图片并基于真实参考进行设计', ], ['Updating feature tree...', '正在更新特征树...'], ['Variables', '变量'], ['View', '视图'], ['View settings', '视图设置'], ['Zookeeper', 'HiCAD AI 助手'], ['Zookeeper, our conversational CAD agent', 'HiCAD 对话式 CAD 助手'], [ 'Zookeeper can make mistakes. We send selection context to help. Always verify information.', 'HiCAD AI 可能出错。我们会发送选区上下文以提高准确性,请始终核对结果。', ], ['Zoom to fit', '缩放至合适大小'], ['Reset view', '重置视图'], ['loading', '加载中'], ['zoo.dev', 'HiCAD 云端'], ]) const translatableAttributes = ['aria-label', 'placeholder', 'title'] as const const excludedSelector = [ 'code', 'pre', 'script', 'style', 'textarea', 'input', '[contenteditable="true"]', '.cm-editor', '.cm-content', '.cm-line', ].join(',') const excludedAttributeSelector = [ 'script', 'style', '.cm-editor', '.cm-content', '.cm-line', ].join(',') function normalizeLookupKey(value: string) { return value.replace(/\s+/g, ' ').trim() } function isExcluded(node: Node) { const element = node.nodeType === Node.ELEMENT_NODE ? (node as Element) : node.parentElement return Boolean(element?.closest(excludedSelector)) } function translateTextNode(node: Text) { if (isExcluded(node)) return const value = node.nodeValue || '' const normalized = normalizeLookupKey(value) const translated = translations.get(normalized) if (!translated || translated === normalized) return const leadingWhitespace = value.match(/^\s*/)?.[0] || '' const trailingWhitespace = value.match(/\s*$/)?.[0] || '' node.nodeValue = `${leadingWhitespace}${translated}${trailingWhitespace}` } function translateElement(element: Element) { if (element.closest(excludedAttributeSelector)) return for (const attribute of translatableAttributes) { const value = normalizeLookupKey(element.getAttribute(attribute) || '') if (!value) continue const translated = translations.get(value) || (attribute === 'placeholder' && value.startsWith('Search projects') ? '搜索项目' : undefined) if (translated) element.setAttribute(attribute, translated) } } function translateTree(root: Node) { if (root.nodeType === Node.TEXT_NODE) { translateTextNode(root as Text) return } if (root.nodeType === Node.ELEMENT_NODE) { translateElement(root as Element) } const walker = document.createTreeWalker( root, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT ) let current = walker.nextNode() while (current) { if (current.nodeType === Node.TEXT_NODE) { translateTextNode(current as Text) } else { translateElement(current as Element) } current = walker.nextNode() } } export function installHiCadLocalization() { document.documentElement.lang = 'zh-CN' document.documentElement.dataset.hicadEmbedded = 'true' const style = document.createElement('style') style.textContent = ` html[data-hicad-embedded='true'] body { font-family: Inter, "PingFang SC", "Microsoft YaHei", sans-serif; } html[data-hicad-embedded='true'] [data-testid='download-desktop-app'], html[data-hicad-embedded='true'] [data-testid='billing-remaining-bar'], html[data-hicad-embedded='true'] [data-testid='onboarding-toast'], html[data-hicad-embedded='true'] [data-hicad-official-account-link] { display: none !important; } html[data-hicad-embedded='true'] [data-testid='home-sidebar'], html[data-hicad-embedded='true'] [data-testid='home-new-file'], html[data-hicad-embedded='true'] [data-testid='environment-subdomain-bar'], html[data-hicad-embedded='true'] [data-testid='cloud-sync-status'] { display: none !important; } html[data-hicad-embedded='true'] .home-layout { display: block !important; column-gap: 2rem; margin-top: clamp(1.5rem, 5vh, 4rem); } html[data-hicad-embedded='true'] [data-testid='home-header'] > p:has( [data-testid='project-directory-settings-link'] ) { display: none !important; } html[data-hicad-embedded='true'] [data-testid='home-section'] { margin-top: 1.5rem; overflow-y: auto; padding-bottom: 3rem; } html[data-hicad-embedded='true'] [data-testid='home-section'] > ul { align-content: start; } html[data-hicad-embedded='true'] [data-testid='project-status-badge'] { box-shadow: 0 1px 2px rgb(0 0 0 / 8%); } html[data-hicad-embedded='true'] [data-testid='project-directory-settings-link'] { overflow-wrap: anywhere; } ` document.head.appendChild(style) translateTree(document.documentElement) let frame = 0 const pendingRoots = new Set() const flush = () => { frame = 0 for (const root of pendingRoots) translateTree(root) pendingRoots.clear() } const schedule = (root: Node) => { pendingRoots.add(root) if (!frame) frame = window.requestAnimationFrame(flush) } const observer = new MutationObserver((mutations) => { for (const mutation of mutations) { if (mutation.type === 'childList') { for (const node of mutation.addedNodes) schedule(node) } else { schedule(mutation.target) } } }) observer.observe(document.documentElement, { attributeFilter: [...translatableAttributes], attributes: true, characterData: true, childList: true, subtree: true, }) }