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 a gear with 10 teeth and use sensible defaults for everything else...', '创建一个 10 齿齿轮,其余参数使用合理默认值...', ], ['Design Studio', 'HiCAD'], ['Design using traditional point-and-click', '使用传统点击方式设计'], ['Dismiss', '关闭'], ['Edit', '编辑'], ['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 Files', '项目文件'], ['Publish', '发布'], ['Redo', '重做'], ['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) 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-hicad-official-account-link] { display: none !important; } ` 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, }) }