From 312064e6e8122f958d2961e28ea85ad8b9db40c5 Mon Sep 17 00:00:00 2001 From: 134 Date: Sun, 12 Jul 2026 00:23:29 +0800 Subject: [PATCH] feat: localize Pascal editor in Chinese --- apps/editor/app/layout.tsx | 2 +- apps/editor/app/page.tsx | 14 +-- apps/editor/app/scene/[id]/page.tsx | 8 +- apps/editor/app/scenes/page.tsx | 18 ++-- apps/editor/components/build-tab.tsx | 80 +++++++++------- apps/editor/components/save-button.tsx | 32 +++---- apps/editor/components/scene-loader.tsx | 22 ++--- apps/editor/components/viewer-toolbar.tsx | 96 +++++++++++-------- packages/core/src/lib/level-name.ts | 17 +++- .../src/components/editor/floorplan-panel.tsx | 2 +- .../editor/src/components/editor/index.tsx | 29 +++--- .../components/editor/node-action-menu.tsx | 24 ++--- .../ui/action-menu/control-modes.tsx | 6 +- .../ui/action-menu/furnish-tools.tsx | 10 +- .../ui/action-menu/view-toggles.tsx | 55 ++++++----- .../components/ui/floating-level-selector.tsx | 20 ++-- .../ui/panels/parametric-inspector.tsx | 2 +- .../src/components/ui/sidebar/icon-rail.tsx | 4 +- .../items-panel/function-tree-panel.tsx | 10 +- .../ui/sidebar/panels/items-panel/index.tsx | 8 +- .../settings-panel/audio-settings-dialog.tsx | 14 +-- .../sidebar/panels/settings-panel/index.tsx | 58 +++++------ .../keyboard-shortcuts-dialog.tsx | 8 +- .../ui/sidebar/panels/site-panel/index.tsx | 50 +++++----- .../panels/site-panel/tree-node-actions.tsx | 2 +- packages/plugin-trees/src/index.ts | 2 +- packages/plugin-trees/src/presets-panel.tsx | 2 +- 27 files changed, 316 insertions(+), 279 deletions(-) diff --git a/apps/editor/app/layout.tsx b/apps/editor/app/layout.tsx index c0a6df2a0..043773820 100644 --- a/apps/editor/app/layout.tsx +++ b/apps/editor/app/layout.tsx @@ -29,7 +29,7 @@ export default function RootLayout({ return ( {process.env.NODE_ENV === 'development' && ( diff --git a/apps/editor/app/page.tsx b/apps/editor/app/page.tsx index 794b7fde8..725234ace 100644 --- a/apps/editor/app/page.tsx +++ b/apps/editor/app/page.tsx @@ -21,7 +21,7 @@ function EditorItemsPanel() { const SIDEBAR_TABS = [ { id: 'site', - label: 'Scene', + label: '场景', component: () => null, mobileDefaultSnap: 0.5, mobileIcon: , @@ -37,7 +37,7 @@ const SIDEBAR_TABS = [ }, { id: 'build', - label: 'Build', + label: '搭建', component: BuildTab, mobileDefaultSnap: 0.5, mobileIcon: , @@ -53,7 +53,7 @@ const SIDEBAR_TABS = [ }, { id: 'items', - label: 'Items', + label: '物品', component: EditorItemsPanel, mobileDefaultSnap: 0.5, mobileIcon: , @@ -69,7 +69,7 @@ const SIDEBAR_TABS = [ }, { id: 'settings', - label: 'Settings', + label: '设置', component: () => null, mobileDefaultSnap: 0.5, mobileIcon: , @@ -93,15 +93,15 @@ export default function Home() { {PROJECT_ID === 'local-editor' && (
- Local editor — scenes are not saved. + 本地编辑器中的场景不会自动保存。 - Open recent scenes + 打开最近场景 · - Create new + 新建场景
diff --git a/apps/editor/app/scene/[id]/page.tsx b/apps/editor/app/scene/[id]/page.tsx index 1bfb59a84..cdd997c32 100644 --- a/apps/editor/app/scene/[id]/page.tsx +++ b/apps/editor/app/scene/[id]/page.tsx @@ -29,22 +29,22 @@ export default async function ScenePage({ params }: { params: Promise<{ id: stri

404

-

Scene not found

+

未找到场景

- We couldn't find a scene with id {id}. + 无法找到 ID 为 {id} 的场景。

- Browse scenes + 查看场景 - Back to editor + 返回编辑器
diff --git a/apps/editor/app/scenes/page.tsx b/apps/editor/app/scenes/page.tsx index 532e4de3f..84c8ebd1a 100644 --- a/apps/editor/app/scenes/page.tsx +++ b/apps/editor/app/scenes/page.tsx @@ -16,7 +16,7 @@ async function fetchScenes(): Promise { function formatDate(iso: string): string { try { - return new Date(iso).toLocaleString() + return new Date(iso).toLocaleString('zh-CN', { hour12: false }) } catch { return iso } @@ -34,26 +34,24 @@ export default async function ScenesPage() { className="text-muted-foreground transition-colors hover:text-foreground" href="/" > - Home + 首页 / - Scenes + 场景
-

Your scenes

+

我的场景

- {scenes.length === 0 - ? 'No scenes yet. Create one to get started.' - : `${scenes.length} scene${scenes.length === 1 ? '' : 's'}.`} + {scenes.length === 0 ? '还没有场景,创建一个开始设计吧。' : `共 ${scenes.length} 个场景`}

{scenes.length === 0 ? (
-

You haven't saved any scenes yet.

+

尚未保存任何场景。

@@ -75,7 +73,7 @@ export default async function ScenesPage() { src={scene.thumbnailUrl} /> ) : ( - No thumbnail + 暂无缩略图 )}
@@ -83,7 +81,7 @@ export default async function ScenesPage() { {scene.name}
- {scene.nodeCount} nodes + {scene.nodeCount} 个元素
diff --git a/apps/editor/components/build-tab.tsx b/apps/editor/components/build-tab.tsx index 08b7dfefb..f05fbec5a 100644 --- a/apps/editor/components/build-tab.tsx +++ b/apps/editor/components/build-tab.tsx @@ -69,53 +69,53 @@ type MepItem = { // Same icons + ordering as the community Build sidebar, minus presets. const BUILD_TYPES: BuildType[] = [ - { id: 'wall', label: 'Wall', iconSrc: withPascalBasePath('/icons/wall.webp'), kind: 'wall' }, - { id: 'fence', label: 'Fence', iconSrc: withPascalBasePath('/icons/fence.webp'), kind: 'fence' }, - { id: 'slab', label: 'Slab', iconSrc: withPascalBasePath('/icons/floor.webp'), kind: 'slab' }, + { id: 'wall', label: '墙体', iconSrc: withPascalBasePath('/icons/wall.webp'), kind: 'wall' }, + { id: 'fence', label: '围栏', iconSrc: withPascalBasePath('/icons/fence.webp'), kind: 'fence' }, + { id: 'slab', label: '楼板', iconSrc: withPascalBasePath('/icons/floor.webp'), kind: 'slab' }, { id: 'ceiling', - label: 'Ceiling', + label: '天花板', iconSrc: withPascalBasePath('/icons/ceiling.webp'), kind: 'ceiling', }, - { id: 'roof', label: 'Roof', iconSrc: withPascalBasePath('/icons/roof.webp'), kind: 'roof' }, + { id: 'roof', label: '屋顶', iconSrc: withPascalBasePath('/icons/roof.webp'), kind: 'roof' }, { id: 'stair', - label: 'Stairs', + label: '楼梯', iconSrc: withPascalBasePath('/icons/stairs.webp'), kind: 'stair', }, { id: 'elevator', - label: 'Elevator', + label: '电梯', iconSrc: withPascalBasePath('/icons/elevator.webp'), kind: 'elevator', }, - { id: 'door', label: 'Door', iconSrc: withPascalBasePath('/icons/door.webp'), kind: 'door' }, + { id: 'door', label: '门', iconSrc: withPascalBasePath('/icons/door.webp'), kind: 'door' }, { id: 'window', - label: 'Window', + label: '窗户', iconSrc: withPascalBasePath('/icons/window.webp'), kind: 'window', }, { id: 'column', - label: 'Column', + label: '立柱', iconSrc: withPascalBasePath('/icons/column.webp'), kind: 'column', }, - { id: 'shelf', label: 'Shelf', iconSrc: withPascalBasePath('/icons/shelf.webp'), kind: 'shelf' }, + { id: 'shelf', label: '置物架', iconSrc: withPascalBasePath('/icons/shelf.webp'), kind: 'shelf' }, { id: 'spawn', - label: 'Spawn Point', + label: '出生点', iconSrc: withPascalBasePath('/icons/spawn-point.webp'), kind: 'spawn', }, // Group tile — no tool of its own; opens the MEP sub-grid below (like Roof). - { id: 'mep', label: 'MEP', iconSrc: withPascalBasePath('/icons/HVAC.webp') }, + { id: 'mep', label: '机电管线', iconSrc: withPascalBasePath('/icons/HVAC.webp') }, { id: 'painting', - label: 'Painting', + label: '材质涂装', iconSrc: withPascalBasePath('/icons/paint.webp'), mode: 'material-paint', }, @@ -126,37 +126,37 @@ const BUILD_TYPES: BuildType[] = [ const MEP_ITEMS: MepItem[] = [ { id: 'duct-segment', - label: 'Duct', + label: '风管', iconSrc: withPascalBasePath('/icons/duct.webp'), kind: 'duct-segment', }, { id: 'duct-terminal', - label: 'Register', + label: '风口', iconSrc: withPascalBasePath('/icons/registers.webp'), kind: 'duct-terminal', }, { id: 'hvac-equipment', - label: 'HVAC Unit', + label: '暖通设备', iconSrc: withPascalBasePath('/icons/HVAC.webp'), kind: 'hvac-equipment', }, { id: 'lineset', - label: 'Lineset', + label: '冷媒管组', iconSrc: withPascalBasePath('/icons/lineset.webp'), kind: 'lineset', }, { id: 'liquid-line', - label: 'Liquid Line', + label: '液管', iconSrc: withPascalBasePath('/icons/lineset.webp'), kind: 'liquid-line', }, { id: 'pipe-segment', - label: 'DWV Pipe', + label: '排水通气管', iconSrc: withPascalBasePath('/icons/dwv-pipes.webp'), kind: 'pipe-segment', }, @@ -186,6 +186,20 @@ function activatePaintMode(): void { type RoofFeature = { kind: string; label: string; iconSrc: string } +const ROOF_FEATURE_LABELS: Record = { + 'box-vent': '箱式通风口', + chimney: '烟囱', + cupola: '屋顶钟楼', + dormer: '老虎窗', + downspout: '落水管', + 'eyebrow-vent': '眉形通风口', + gutter: '天沟', + 'ridge-vent': '屋脊通风口', + skylight: '天窗', + 'solar-panel': '太阳能板', + 'turbine-vent': '涡轮通风器', +} + const ROOF_FEATURE_FALLBACK_ICON = withPascalBasePath('/icons/roof.webp') /** @@ -259,7 +273,7 @@ export function BuildTab() { const icon = def.presentation?.icon features.push({ kind, - label: def.presentation?.label ?? kind, + label: ROOF_FEATURE_LABELS[kind] ?? def.presentation?.label ?? kind, iconSrc: icon?.kind === 'url' ? icon.src : ROOF_FEATURE_FALLBACK_ICON, }) } @@ -362,7 +376,7 @@ export function BuildTab() { (activeTool === 'roof' || isRoofFeatureActive) && roofFeatures.length > 0 ? (
-
Features
+
屋顶构件
) : isMepActive ? (
-
MEP
+
机电管线
- Duct + 风管
) : null} {pipeContext ? (
- DWV Pipe + 排水通气管
) : null} {liquidLineContext ? (
- Liquid Line + 液管 - {follow - ? 'Click a lineset to lay the line beside it.' - : 'Trace a line alongside an existing lineset (F).'} + {follow ? '单击冷媒管组,在其旁边铺设液管。' : '沿现有冷媒管组绘制液管(F)。'}
) : null} diff --git a/apps/editor/components/save-button.tsx b/apps/editor/components/save-button.tsx index de8825535..8d2764243 100644 --- a/apps/editor/components/save-button.tsx +++ b/apps/editor/components/save-button.tsx @@ -20,7 +20,7 @@ interface SaveButtonProps { /** * Creates a new empty scene and navigates the user to it. */ -export function CreateSceneButton({ label = 'Create new scene' }: { label?: string } = {}) { +export function CreateSceneButton({ label = '新建场景' }: { label?: string } = {}) { const router = useRouter() const [isCreating, setIsCreating] = useState(false) const [error, setError] = useState(null) @@ -32,16 +32,16 @@ export function CreateSceneButton({ label = 'Create new scene' }: { label?: stri const response = await fetch(withPascalBasePath('/api/scenes'), { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ name: 'Untitled scene', graph: EMPTY_GRAPH }), + body: JSON.stringify({ name: '未命名场景', graph: EMPTY_GRAPH }), }) if (!response.ok) { - setError(`Failed to create scene (${response.status})`) + setError(`创建场景失败 (${response.status})`) return } const meta = (await response.json()) as { id: string } router.push(`/scene/${meta.id}`) } catch (err) { - setError(err instanceof Error ? err.message : 'Failed to create scene') + setError(err instanceof Error ? err.message : '创建场景失败') } finally { setIsCreating(false) } @@ -56,7 +56,7 @@ export function CreateSceneButton({ label = 'Create new scene' }: { label?: stri onClick={handleCreate} type="button" > - {isCreating ? 'Creating…' : label} + {isCreating ? '正在创建…' : label}
) @@ -75,7 +75,7 @@ export function SaveButton({ sceneId, name, version, getGraph }: SaveButtonProps const handleSave = useCallback(async () => { const graph = getGraph() if (!graph) { - setStatus('No scene to save') + setStatus('没有可保存的场景') return } setIsSaving(true) @@ -90,16 +90,16 @@ export function SaveButton({ sceneId, name, version, getGraph }: SaveButtonProps body: JSON.stringify({ name, graph }), }) if (response.status === 409) { - setStatus('Conflict — reload to continue') + setStatus('保存冲突,请刷新后继续') return } if (!response.ok) { - setStatus(`Save failed (${response.status})`) + setStatus(`保存失败 (${response.status})`) return } - setStatus('Saved') + setStatus('已保存') } catch (error) { - setStatus(error instanceof Error ? error.message : 'Save failed') + setStatus(error instanceof Error ? error.message : '保存失败') } finally { setIsSaving(false) } @@ -108,10 +108,10 @@ export function SaveButton({ sceneId, name, version, getGraph }: SaveButtonProps const handleSaveAs = useCallback(async () => { const graph = getGraph() if (!graph) { - setStatus('No scene to save') + setStatus('没有可保存的场景') return } - const newName = typeof window !== 'undefined' ? window.prompt('New scene name', name) : null + const newName = typeof window !== 'undefined' ? window.prompt('请输入新场景名称', name) : null if (!newName) return setIsSaving(true) setStatus(null) @@ -122,13 +122,13 @@ export function SaveButton({ sceneId, name, version, getGraph }: SaveButtonProps body: JSON.stringify({ name: newName, graph }), }) if (!response.ok) { - setStatus(`Save-as failed (${response.status})`) + setStatus(`另存为失败 (${response.status})`) return } const meta = (await response.json()) as { id: string } router.push(`/scene/${meta.id}`) } catch (error) { - setStatus(error instanceof Error ? error.message : 'Save-as failed') + setStatus(error instanceof Error ? error.message : '另存为失败') } finally { setIsSaving(false) } @@ -142,7 +142,7 @@ export function SaveButton({ sceneId, name, version, getGraph }: SaveButtonProps onClick={handleSave} type="button" > - {isSaving ? 'Saving…' : 'Save'} + {isSaving ? '正在保存…' : '保存'} {status && {status}}
diff --git a/apps/editor/components/scene-loader.tsx b/apps/editor/components/scene-loader.tsx index d863d1a3e..dbd9edbe1 100644 --- a/apps/editor/components/scene-loader.tsx +++ b/apps/editor/components/scene-loader.tsx @@ -35,7 +35,7 @@ export interface SceneMeta { const BASE_SIDEBAR_TABS: (SidebarTab & { component: React.ComponentType })[] = [ { id: 'site', - label: 'Scene', + label: '场景', component: () => null, // Built-in SitePanel handles this mobileDefaultSnap: 0.5, mobileIcon: , @@ -51,7 +51,7 @@ const BASE_SIDEBAR_TABS: (SidebarTab & { component: React.ComponentType })[] = [ }, { id: 'build', - label: 'Build', + label: '搭建', component: BuildTab, mobileDefaultSnap: 0.5, mobileIcon: , @@ -109,7 +109,7 @@ export function SceneLoader({ initialScene, meta }: SceneLoaderProps) { ...BASE_SIDEBAR_TABS, { id: 'ai', - label: 'AI Designer', + label: 'AI 设计助手', component: SceneAgentPanel, mobileDefaultSnap: 0.72, mobileIcon: , @@ -152,7 +152,7 @@ export function SceneLoader({ initialScene, meta }: SceneLoaderProps) { } if (!response.ok) { - setSaveError(`Save failed (${response.status})`) + setSaveError(`保存失败 (${response.status})`) return } @@ -160,7 +160,7 @@ export function SceneLoader({ initialScene, meta }: SceneLoaderProps) { versionRef.current = next.version setSaveError(null) } catch (error) { - setSaveError(error instanceof Error ? error.message : 'Save failed') + setSaveError(error instanceof Error ? error.message : '保存失败') } }, [meta.id, meta.name], @@ -189,7 +189,7 @@ export function SceneLoader({ initialScene, meta }: SceneLoaderProps) { source.addEventListener('error', () => { if (source.readyState === EventSource.CLOSED) { - setSaveError('Live scene connection closed') + setSaveError('场景实时连接已断开') } }) @@ -214,9 +214,9 @@ export function SceneLoader({ initialScene, meta }: SceneLoaderProps) {
{conflict && (
-

Another session saved first — refresh?

+

其他会话已先保存,是否刷新?

- Your changes haven't been saved. Reload to pick up the latest version. + 当前更改尚未保存,请重新加载最新版本。

@@ -246,7 +246,7 @@ export function SceneLoader({ initialScene, meta }: SceneLoaderProps) { className="pointer-events-auto rounded-md border border-border bg-background/90 px-3 py-1.5 font-medium text-xs shadow-sm backdrop-blur hover:bg-accent/40" href="/scenes" > - All scenes + 全部场景
, }, ] const levelModeOrder = ['stacked', 'exploded', 'solo'] as const const levelModeLabels: Record = { - manual: 'Stack', - stacked: 'Stack', - exploded: 'Exploded', - solo: 'Solo', + manual: '堆叠', + stacked: '堆叠', + exploded: '分层', + solo: '单层', } const wallModeOrder = ['cutaway', 'up', 'down', 'translucent'] as const const wallModeConfig: Record = { - up: { icon: withPascalBasePath('/icons/room.webp'), label: 'Full height' }, - cutaway: { icon: withPascalBasePath('/icons/wallcut.webp'), label: 'Cutaway' }, - down: { icon: withPascalBasePath('/icons/walllow.webp'), label: 'Low' }, - translucent: { icon: withPascalBasePath('/icons/wall.webp'), label: 'Translucent' }, + up: { icon: withPascalBasePath('/icons/room.webp'), label: '全高' }, + cutaway: { icon: withPascalBasePath('/icons/wallcut.webp'), label: '剖切' }, + down: { icon: withPascalBasePath('/icons/walllow.webp'), label: '低墙' }, + translucent: { icon: withPascalBasePath('/icons/wall.webp'), label: '半透明' }, } const SHADING_OPTIONS = [ - { id: 'solid', name: 'Solid', detail: 'Flat and fast — no ambient occlusion', icon: Box }, - { id: 'rendered', name: 'Rendered', detail: 'Full ambient occlusion', icon: Sparkles }, + { id: 'solid', name: '实体', detail: '快速平面显示,不含环境光遮蔽', icon: Box }, + { id: 'rendered', name: '渲染', detail: '完整环境光遮蔽效果', icon: Sparkles }, ] as const function ViewModeControl() { @@ -175,9 +175,9 @@ function CollapseSidebarButton() { return (
- + ) @@ -247,7 +247,7 @@ function WallModeToggle() { } return ( - + @@ -328,7 +344,7 @@ function DisplayMenu() { > keepOpen(e, () => setShowGrid(!showGrid))}> - Grid + 网格 {showGrid ? ( ) : ( @@ -337,15 +353,15 @@ function DisplayMenu() { keepOpen(e, () => setMagneticSnap(!magneticSnap))}> - Magnetic snap + 磁吸捕捉 - {magneticSnap ? 'On' : 'Off'} + {magneticSnap ? '开' : '关'} keepOpen(e, () => setShadows(!shadows))}> - Shadows - {shadows ? 'On' : 'Off'} + 阴影 + {shadows ? '开' : '关'} @@ -359,9 +375,9 @@ function DisplayMenu() { icon={cameraMode === 'perspective' ? 'icon-park-outline:perspective' : 'vaadin:grid'} width={16} /> - Camera + 相机 - {cameraMode === 'perspective' ? 'Perspective' : 'Orthographic'} + {cameraMode === 'perspective' ? '透视' : '正交'} {unit === 'metric' ? 'm' : 'ft'} - Units + 单位 - {unit === 'metric' ? 'Metric' : 'Imperial'} + {unit === 'metric' ? '公制' : '英制'} @@ -381,7 +397,7 @@ function DisplayMenu() { - Render + 渲染 {activeShading.name} @@ -406,7 +422,7 @@ function DisplayMenu() { - Edges + 边线 {activeEdges.name} @@ -425,9 +441,9 @@ function DisplayMenu() { - Theme + 主题 - {activeTheme.name} + {getSceneThemeLabel(activeTheme.id, activeTheme.name)} @@ -445,7 +461,9 @@ function DisplayMenu() { ))} - {theme.name} + + {getSceneThemeLabel(theme.id, theme.name)} + {sceneTheme === theme.id ? ( ) : null} @@ -473,7 +491,7 @@ function WalkthroughButton() { }, [isFirstPersonMode, setFirstPersonMode]) return ( - + ) diff --git a/packages/core/src/lib/level-name.ts b/packages/core/src/lib/level-name.ts index dd4f904f4..17f5e6138 100644 --- a/packages/core/src/lib/level-name.ts +++ b/packages/core/src/lib/level-name.ts @@ -1,11 +1,20 @@ import type { LevelNode } from '../schema' export function getDefaultLevelName(level: number): string { - if (level === 0) return 'Ground Floor' - if (level > 0) return `Floor ${level}` - return `Basement ${-level}` + if (level === 0) return '首层' + if (level > 0) return `${level + 1} 层` + return `地下 ${-level} 层` } export function getLevelDisplayName(level: Pick): string { - return level.name || getDefaultLevelName(level.level) + const name = level.name?.trim() + if (!name || name === 'Ground Floor') return getDefaultLevelName(level.level) + + const floorMatch = name.match(/^Floor\s+(\d+)$/i) + if (floorMatch) return `${Number(floorMatch[1]) + 1} 层` + + const basementMatch = name.match(/^Basement\s+(\d+)$/i) + if (basementMatch) return `地下 ${Number(basementMatch[1])} 层` + + return name } diff --git a/packages/editor/src/components/editor/floorplan-panel.tsx b/packages/editor/src/components/editor/floorplan-panel.tsx index 26532f64f..2b3095301 100644 --- a/packages/editor/src/components/editor/floorplan-panel.tsx +++ b/packages/editor/src/components/editor/floorplan-panel.tsx @@ -473,7 +473,7 @@ function FloorplanCompassButton({
@@ -685,14 +684,14 @@ function ReferencesControl() { ? 'bg-white/15' : 'opacity-60 grayscale hover:bg-white/5 hover:opacity-100 hover:grayscale-0', )} - label={`References: ${anyVisible ? 'Visible' : 'Hidden'}`} + label={`参考资源:${anyVisible ? '可见' : '隐藏'}`} onClick={toggleAll} size="icon" variant="ghost" >
References @@ -705,7 +704,7 @@ function ReferencesControl() { ) })}
) : (
- No lower floor available. + 暂无可用的下方楼层。
)} diff --git a/packages/editor/src/components/ui/floating-level-selector.tsx b/packages/editor/src/components/ui/floating-level-selector.tsx index ef922dfaa..06e6aeab0 100644 --- a/packages/editor/src/components/ui/floating-level-selector.tsx +++ b/packages/editor/src/components/ui/floating-level-selector.tsx @@ -166,7 +166,7 @@ function LevelRow({ > diff --git a/packages/editor/src/components/ui/panels/parametric-inspector.tsx b/packages/editor/src/components/ui/panels/parametric-inspector.tsx index a5076dd78..cbfcd8024 100644 --- a/packages/editor/src/components/ui/panels/parametric-inspector.tsx +++ b/packages/editor/src/components/ui/panels/parametric-inspector.tsx @@ -174,7 +174,7 @@ export function ParametricInspector({ )} {(canMove || canDelete || (parametrics.actions && parametrics.actions.length > 0)) && ( - + {canMove && ( } label="Move" onClick={handleMove} /> diff --git a/packages/editor/src/components/ui/sidebar/icon-rail.tsx b/packages/editor/src/components/ui/sidebar/icon-rail.tsx index 9718fb552..0fc6fe32f 100644 --- a/packages/editor/src/components/ui/sidebar/icon-rail.tsx +++ b/packages/editor/src/components/ui/sidebar/icon-rail.tsx @@ -23,13 +23,13 @@ interface IconRailProps { const sitePanel: { id: PanelId; iconSrc: string; label: string } = { id: 'site', iconSrc: '/icons/level.webp', - label: 'Site', + label: '场景', } const settingsPanel: { id: PanelId; iconSrc: string; label: string } = { id: 'settings', iconSrc: '/icons/settings.webp', - label: 'Settings', + label: '设置', } const panels: { id: PanelId; iconSrc: string; label: string }[] = [sitePanel, settingsPanel] diff --git a/packages/editor/src/components/ui/sidebar/panels/items-panel/function-tree-panel.tsx b/packages/editor/src/components/ui/sidebar/panels/items-panel/function-tree-panel.tsx index 54b0002ea..ce6d49375 100644 --- a/packages/editor/src/components/ui/sidebar/panels/items-panel/function-tree-panel.tsx +++ b/packages/editor/src/components/ui/sidebar/panels/items-panel/function-tree-panel.tsx @@ -22,9 +22,9 @@ export type FunctionTreeNode = { } const SOURCE_CHIPS: Array<{ id: NonNullable; label: string }> = [ - { id: 'library', label: 'Library' }, - { id: 'community', label: 'Community' }, - { id: 'mine', label: 'Mine' }, + { id: 'library', label: '素材库' }, + { id: 'community', label: '社区' }, + { id: 'mine', label: '我的' }, ] /** Every slug at or below `node`, so a non-leaf selection matches descendants. */ @@ -173,7 +173,7 @@ export function FunctionTreePanel({ setSearch(e.target.value) onSearchChange?.(e.target.value) }} - placeholder="Search..." + placeholder="搜索物品…" type="text" value={search} /> @@ -212,7 +212,7 @@ export function FunctionTreePanel({ onClick={() => setActiveChildSlug(null)} type="button" > - All + 全部 {activeRoot.children.map((child) => { const isActive = activeChildSlug === child.slug diff --git a/packages/editor/src/components/ui/sidebar/panels/items-panel/index.tsx b/packages/editor/src/components/ui/sidebar/panels/items-panel/index.tsx index df81988a6..cad62ce2c 100644 --- a/packages/editor/src/components/ui/sidebar/panels/items-panel/index.tsx +++ b/packages/editor/src/components/ui/sidebar/panels/items-panel/index.tsx @@ -167,9 +167,9 @@ function LegacyItemsPanel({ // filter even before they own any items. Selecting "Mine" with no // matching items falls through to the empty/no-results state. const sourceChips: Array<{ id: AssetInput['source']; label: string }> = [ - { id: 'library', label: 'Library' }, - { id: 'community', label: 'Community' }, - { id: 'mine', label: 'Mine' }, + { id: 'library', label: '素材库' }, + { id: 'community', label: '社区' }, + { id: 'mine', label: '我的' }, ] const allTags = Array.from(new Set(categoryItems.flatMap((item) => item.tags ?? []))) const placementTags = allTags.filter((t) => PLACEMENT_TAGS.has(t)) @@ -242,7 +242,7 @@ function LegacyItemsPanel({ setSearch(e.target.value) onSearchChange?.(e.target.value) }} - placeholder="Search..." + placeholder="搜索物品…" type="text" value={search} /> diff --git a/packages/editor/src/components/ui/sidebar/panels/settings-panel/audio-settings-dialog.tsx b/packages/editor/src/components/ui/sidebar/panels/settings-panel/audio-settings-dialog.tsx index dbe1446ce..0eb394a03 100644 --- a/packages/editor/src/components/ui/sidebar/panels/settings-panel/audio-settings-dialog.tsx +++ b/packages/editor/src/components/ui/sidebar/panels/settings-panel/audio-settings-dialog.tsx @@ -28,19 +28,19 @@ export function AudioSettingsDialog() { - Audio Settings - Adjust volume levels and mute settings + 音频设置 + 调整音量和静音设置
{/* Master Volume */}
- + {masterVolume}%
- + {radioVolume}%
- + {sfxVolume}%
{muted ? : } - {muted ? 'Unmute All Sounds' : 'Mute All Sounds'} + {muted ? '恢复全部声音' : '静音全部声音'}
diff --git a/packages/editor/src/components/ui/sidebar/panels/settings-panel/index.tsx b/packages/editor/src/components/ui/sidebar/panels/settings-panel/index.tsx index e559ab394..f519587d4 100644 --- a/packages/editor/src/components/ui/sidebar/panels/settings-panel/index.tsx +++ b/packages/editor/src/components/ui/sidebar/panels/settings-panel/index.tsx @@ -298,12 +298,12 @@ export function SettingsPanel({ {/* Visibility Section (only for cloud projects) */} {projectId && !isLocalProject && (
- +
-
Public
+
公开访问
- {projectVisibility?.isPrivate ? 'Only you' : 'Anyone'} can view + {projectVisibility?.isPrivate ? '仅自己可见' : '所有人可见'}
-
Show 3D Scans
-
Visible to public viewers
+
显示 3D 扫描
+
对公开访问者可见
-
Show Floorplans
-
Visible to public viewers
+
显示平面图
+
对公开访问者可见
-
Shadows
-
Cast shadows from lights
+
阴影
+
显示灯光投射阴影
- +
-
3D model
+
3D 模型
-
Floorplan
+
平面图
@@ -400,7 +400,7 @@ export function SettingsPanel({ {/* Thumbnail Section (only for cloud projects) */} {projectId && !isLocalProject && (
- +
)} {/* Save/Load Section */}
- + - +
{/* Keyboard Section */}
- +
{/* Scene Graph */}
- + - Scene Graph + 场景树
- +
diff --git a/packages/editor/src/components/ui/sidebar/panels/settings-panel/keyboard-shortcuts-dialog.tsx b/packages/editor/src/components/ui/sidebar/panels/settings-panel/keyboard-shortcuts-dialog.tsx index 7e516dcee..57913f747 100644 --- a/packages/editor/src/components/ui/sidebar/panels/settings-panel/keyboard-shortcuts-dialog.tsx +++ b/packages/editor/src/components/ui/sidebar/panels/settings-panel/keyboard-shortcuts-dialog.tsx @@ -195,15 +195,15 @@ export function KeyboardShortcutsDialog() { - Keyboard Shortcuts + 键盘快捷键 - Shortcuts are context-aware. Guided constraints are enabled by default; hold Shift - during an active gesture to build freely. + 快捷键会根据当前操作场景生效。默认启用辅助约束,操作时按住 Shift + 可自由搭建。 diff --git a/packages/editor/src/components/ui/sidebar/panels/site-panel/index.tsx b/packages/editor/src/components/ui/sidebar/panels/site-panel/index.tsx index af7fe82de..b121e0ece 100644 --- a/packages/editor/src/components/ui/sidebar/panels/site-panel/index.tsx +++ b/packages/editor/src/components/ui/sidebar/panels/site-panel/index.tsx @@ -165,7 +165,7 @@ const PropertyLineSection = memo(function PropertyLineSection() {
- Property Line + 地块边界
Level e.stopPropagation()} - title="Camera snapshot" + title="相机快照" > {level.camera && ( @@ -949,7 +949,7 @@ const LevelsSection = memo(function LevelsSection({
- Add level + 添加楼层 {levels.length === 0 && (
@@ -957,7 +957,7 @@ const LevelsSection = memo(function LevelsSection({
{/* Horizontal branch line */}
- No levels yet + 暂无楼层
)} {[...levels].reverse().map((level, index) => ( @@ -1017,14 +1017,14 @@ const LayerToggle = memo(function LayerToggle() { )}
Structure - Structure + 结构
@@ -1053,14 +1053,14 @@ const LayerToggle = memo(function LayerToggle() { )}
Furnish - Furnish + 陈设
@@ -1090,14 +1090,14 @@ const LayerToggle = memo(function LayerToggle() { )}
Zones - Zones + 区域
@@ -1206,7 +1206,7 @@ const ZoneItem = memo(function ZoneItem({ zone, isLast }: { zone: ZoneNode; isLa @@ -1324,7 +1324,7 @@ const ContentSection = memo(function ContentSection() { if (!level) { return ( -
Select a level to view content
+
请选择楼层以查看内容
) } @@ -1338,9 +1338,9 @@ const ContentSection = memo(function ContentSection() { if (levelZones.length === 0) { return (
- No zones on this level.{' '} + 该楼层暂无区域。{' '}
) @@ -1356,7 +1356,7 @@ const ContentSection = memo(function ContentSection() { } if (elementChildren.length === 0) { - return
No elements on this level
+ return
该楼层暂无元素
} return ( @@ -1431,14 +1431,14 @@ const BuildingItem = memo(function BuildingItem({ >
Building - {building.name || 'Building'} + {building.name || '建筑'}
setBuildingCameraOpen(open ? building.id : null)} @@ -1453,7 +1453,7 @@ const BuildingItem = memo(function BuildingItem({ : 'text-muted-foreground hover:bg-accent hover:text-foreground', )} onClick={(e) => e.stopPropagation()} - title="Camera snapshot" + title="相机快照" > {building.camera && ( @@ -1587,14 +1587,14 @@ export function SitePanel({ projectId, onUploadAsset, onDeleteAsset }: SitePanel >
Site - {siteNode.name || 'Site'} + {siteNode.name || '场地'}
e.stopPropagation()} - title="Camera snapshot" + title="相机快照" > {hasCamera && ( diff --git a/packages/plugin-trees/src/index.ts b/packages/plugin-trees/src/index.ts index 07a6f3a98..f75a73490 100644 --- a/packages/plugin-trees/src/index.ts +++ b/packages/plugin-trees/src/index.ts @@ -25,7 +25,7 @@ export const treesPlugin: Plugin = { panels: [ { id: 'trees', - label: 'Nature', + label: '自然资产', icon: { kind: 'url', src: NATURE_ICON }, component: () => import('./presets-panel'), }, diff --git a/packages/plugin-trees/src/presets-panel.tsx b/packages/plugin-trees/src/presets-panel.tsx index b095d43c9..8afbfe229 100644 --- a/packages/plugin-trees/src/presets-panel.tsx +++ b/packages/plugin-trees/src/presets-panel.tsx @@ -41,7 +41,7 @@ export default function TreesPanel() {
-

Nature

+

自然资产

{count} planted