Skip to content

Restore i18n on remaining UI components (rebase onto upstream/main) - #740

Open
leapar wants to merge 33 commits into
pascalorg:mainfrom
leapar:i18n/restore-lost-translations
Open

Restore i18n on remaining UI components (rebase onto upstream/main)#740
leapar wants to merge 33 commits into
pascalorg:mainfrom
leapar:i18n/restore-lost-translations

Conversation

@leapar

@leapar leapar commented Aug 31, 2026

Copy link
Copy Markdown

Summary

Restores useTranslations() across UI components whose hardcoded strings were lost when upstream was merged into this fork's main. The work re-applies commits b8c87042, af9d9eab, d7a991b4 and merges the latest upstream main (commit 51f2a53d).

Components localized

  • command-palette (index + editor-commands): group headings, command labels, wall/level mode cycle, camera switch, rename footer hint
  • settings-panel: project id, export, floorplan, thumbnail, save/load, audio, keyboard, scene graph, danger zone
  • keyboard-shortcuts-dialog: 7 categories, all action/note keys
  • load-build-dialog: stat rows, errors, warnings, schema details
  • floating-level-selector: drag handle, height label, add/insert/delete menus, delete dialog
  • view-toggles: guide/scan/reference controls, opacity, file errors
  • viewer-toolbar (apps/editor): view modes, sidebar toggle, level/wall toggles, display menu, walkthrough/preview buttons
  • panel-manager + node-display + selection-breakdown: node-type labels (panel.nodeType.<x>) flow through the mobile panel sheet title, multi-selection breakdown, and selection bar
  • Plus earlier restorations (in b8c87042/af9d9eab): home page, scene-loader, camera actions, action menus, helpers, icon-rail

Pre-existing regressions fixed

The fork had drifted from upstream in three places that were unrelated to the i18n restore but blocked Bugbot from confirming the branch as clean. They are fixed here:

  • 4100aa30 — restored headers()-based host detection in apps/editor/app/scenes/page.tsx::resolveBaseUrl (was hardcoded to localhost:3000, which broke /scenes fetches in deployed envs). Verified identical to upstream main.
  • 4100aa30 — restored the flex layout in packages/editor/src/components/editor/editor-layout-v2.tsx::RightColumn (a broken 3-column grid was placing toolbarRight in the center column). Verified identical to upstream main.
  • 3e4b0213node-display.ts and selection-breakdown.ts were returning hardcoded English (Item, Wall, Slab, and "1 slab · 1 stair · 2 fences"). Refactored both to accept a translator, added panel.nodeType.<x> keys.

Approach

  • Module-level configs converted from inline labels to labelKey / nameKey / detailKey strings, resolved inside components via t().
  • Pattern: const t = useTranslations() per component (each component needs its own hook call).
  • useEffect dependency arrays updated to include t where applicable.
  • Plural-aware keys follow the <key> / <key>_plural convention (e.g. loadBuild.errors, panel.nodeType.fence / _plural).
  • Helper functions that previously baked in English (getNodeDisplay, getTypeDisplay, formatSelectionBreakdown) now take a Translator argument so the catalog stays the single source of truth.

Translation keys

1217 keys in en.json and zh.json (full parity). The 30-key addition for the panel-manager fix (panel.nodeType.<x> × 14 kinds + singular + _plural) brings this from the previously advertised 1155.

Verification

  • Lint: 0 errors
  • Typecheck: 0 new errors caused by this change. Pre-existing type mismatches between local code and @pascal-app/core exports remain (separate concern: this fork adds types that the core package hasn't shipped yet — e.g. getLevelDisplayName, DEFAULT_LEVEL_HEIGHT, MeasurementNode, SceneState.materials).
  • Both JSON files parse cleanly.
  • Rebased cleanly onto upstream main (only conflict: roof-helper.tsx deleted upstream, accepted the deletion).
  • All 16 tests in packages/editor/src/components/ui/panels/ pass. The 37 pre-existing sfx-player.test.ts failures are unrelated (audio-context mocking).
  • Cursor Bugbot: conclusion: success, "no issues found! ✅", 0 annotations across 12 review passes.

Manual test checklist (suggested for reviewer)

The i18n restoration is mostly auto-tested by catalog parity, but reviewers may want to spot-check rendered copy by toggling the language:

  • In the editor, set navigator.language to zh-CN (or change the locale via useLocale().setLocale('zh') in devtools) and verify the following flows render Chinese:
    • Sidebar tab labels (Build / Settings / Plugins / …)
    • Settings panel: project id, export, floorplan, thumbnail, audio, keyboard, danger zone sections
    • Command palette (Cmd/Ctrl+K): group headings and command names
    • Selection: title bar of the mobile panel sheet (MobilePanelSheet title), and the multi-selection breakdown string in the docked panel (MultiSelectionPanel, MultiParametricInspector)
    • Viewer toolbar: view mode buttons, sidebar toggle, display menu
    • Keyboard shortcuts dialog (? key)
    • Floating level selector (floor names)
  • Verify English rendering is unchanged when navigator.language is en.
  • Run bun test packages/editor/src/components/ui/panels/ to confirm the breakdown still formats as "1 slab · 1 stair · 2 fences" in English.

Note

Medium Risk
Wide UI surface and a breaking change to level-name helpers (call sites must pass t); save/load and scenes fetch behavior touch user data paths but logic is mostly string substitution.

Overview
Restores locale-aware copy across the editor app and @pascal-app/editor after upstream merge dropped hardcoded strings—sidebar tabs, save/create flows, scenes list, viewer toolbar, command palette, action menus, level selector, view toggles, and related helpers now use useTranslations() or labelKey configs resolved at render time.

Level naming no longer returns English-only defaults from core: getDefaultLevelName / getLevelDisplayName take a Translator, and the editor routes display through localizedLevelName. Walkthrough HUD and first-person floor labels pass t through accordingly.

App shell tweaks: global thin dark scrollbars; /scenes gets a dark layout wrapper and a client ScenesList (with safer fetchScenes error handling); new currentColor SVG icons for file/scene/settings.

Bundling: several @pascal-app/core modules gain 'use client' so they can ship in client bundles without breaking Next.js boundaries.

Reviewed by Cursor Bugbot for commit 4204c6f. Bugbot is set up for automated code reviews on this repo. Configure here.

leapar and others added 14 commits May 30, 2026 23:34
Re-applies the i18n integration that was lost when syncing with
upstream's i18n-clean rewrite of these files. Only adds the translation
hooks (useTranslations / useLocale / messages) and replaces the
hardcoded English strings — leaves upstream's structural changes (Build
tab, sidebar tab shape, control-mode set, icon sources) untouched.

- packages/editor/src/index.tsx: re-export I18nProvider, useLocale,
  useTranslations, defaultLocale, Locale, messages from lib/i18n so
  community shells can compose them again.
- apps/editor/app/page.tsx: use useTranslations for sidebar labels
  (scene/build/items/settings) and the local-editor banner; new key
  editor.openSavedScenes.
- apps/editor/components/scene-loader.tsx: same pattern for the
  scene/build/settings tabs; new key sidebar.build.
- packages/editor/src/components/ui/action-menu/camera-actions.tsx:
  viewer.orbitLeft / orbitRight / topView.
- packages/editor/src/components/ui/action-menu/control-modes.tsx:
  controlModes.select / zone + common.delete via labelKey.
- apps/editor/components/save-button.tsx: cast locale to Locale so the
  indexed access typechecks.
- apps/editor/messages/{en,zh}.json: add sidebar.build,
  editor.openSavedScenes, viewer.{orbitLeft,orbitRight,topView}.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Continues the i18n restoration started in the previous commit. Same
pattern: only adds the translation hooks and replaces hardcoded English
strings, leaving upstream's structural changes intact.

- action-menu/structure-tools.tsx, furnish-tools.tsx: switch the
  hardcoded 'label' fields to 'labelKey' (cursor-sphere.tsx already
  expected labelKey, so this resolves the existing inconsistency).
- helpers/building-helper.tsx, item-helper.tsx, roof-helper.tsx:
  useTranslations for the contextual-helper hint labels.
- helpers/registered-tool-helper.tsx: localize the new
  'Guided constraints bypassed' label.
- panels/panel-manager.tsx: localize the 'Selection' fallback label
  for multi-selection mobile panels.
- sidebar/icon-rail.tsx: switch the panels[] entries to labelKey and
  resolve them inside the component.

New translation keys:
- structureTools: duct, ductFitting, register, hvacUnit, dwvPipe,
  trap, pipeFitting, lineset, liquidLine (HVAC/plumbing lines added
  by upstream since the local fork).
- editor: placeBuilding, rotate, chooseRoom, selectCurvedWall,
  setDiameter, setCorner, rotateRoofDirection, place, forcePlace,
  guidedConstraintsBypassed.
- panel: selection.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… toggles, viewer toolbar

Continues the i18n restore from b8c8704 and af9d9ea. Replaces hardcoded
strings across 8 components with useTranslations() calls.

- command-palette (index + editor-commands): group headings, command
  labels, wall/level mode cycle, camera switch, rename footer hint
- settings-panel: project id, visibility, export, floorplan, thumbnail,
  save/load, audio, keyboard, scene graph, danger zone
- keyboard-shortcuts-dialog: 7 categories, all action/note keys
- load-build-dialog: stat rows, errors, warnings, schema details
- floating-level-selector: drag handle, height label, add/insert/delete
  menus, delete dialog
- view-toggles: guide/scan/reference controls, opacity, file errors
- viewer-toolbar (apps/editor): view modes, sidebar toggle, level/wall
  toggles, display menu (grid, measurements, floorplan, units, render,
  edges, theme), walkthrough/preview buttons

Module-level configs converted from inline labels to labelKey/nameKey
strings, resolved inside components via t(). Added 100+ translation
keys to en.json and zh.json (parity verified).
…ranslations

# Conflicts:
#	packages/editor/src/components/ui/helpers/roof-helper.tsx
Comment thread apps/ifc-converter/components/PreviewToolbar.tsx
Comment thread packages/editor/src/components/ui/command-palette/editor-commands.tsx Outdated
Comment thread apps/editor/components/save-button.tsx Outdated
Comment thread apps/editor/app/scenes/page.tsx
Comment thread packages/editor/src/components/editor/editor-layout-v2.tsx Outdated
Comment thread apps/editor/app/page.tsx
…ascalorg#3, pascalorg#6

- pascalorg#1: FitSceneButton now calls useTranslations() (was throwing
  ReferenceError on render).
- pascalorg#2: Command palette group keys use dotted form (commands.group.scene)
  to match the i18n catalog; previously used the flat form
  (commands.groupScene) which produced undefined labels.
- pascalorg#3: save-button.tsx migrated from the bespoke messages[] lookup to
  useTranslations() with save.* keys, removing the unprefixed key leak
  into the global namespace and matching the rest of the codebase.
- pascalorg#6: Added missing catalog keys with full en/zh parity:
    sidebar.build, sidebar.settings
    editor.openSavedScenes, editor.rotate, editor.place,
    editor.forcePlace, editor.guidedConstraintsBypassed

Skipped pascalorg#4 (resolveBaseUrl) and pascalorg#5 (editor-layout-v2 toolbar) — those
predate this branch and are out of scope for the i18n restore PR.
@leapar

leapar commented Aug 31, 2026

Copy link
Copy Markdown
Author

Addressed Bugbot findings #1, #2, #3, and #6 in commit 9780af44.

#1 FitSceneButton ReferenceError — added the missing useTranslations() call inside the component.
#2 Command group keys — fixed all 6 group keys to use the dotted form (commands.group.scene, commands.group.levels, etc.) matching the catalog.
#3 Unprefixed keys in save-button.tsx — migrated the component off the bespoke messages[] lookup to the standard useTranslations() hook with a save.* namespace, removing the global-namespace leak.
#6 Missing catalog keys — added 7 keys (sidebar.build, sidebar.settings, editor.openSavedScenes, editor.rotate, editor.place, editor.forcePlace, editor.guidedConstraintsBypassed) to both en.json and zh.json with full parity (1175 keys each).
Skipped #4 (resolveBaseUrl) and #5 (toolbar layout) — both predate this branch and are out of scope for the i18n restore. Happy to address them in a follow-up if you'd like.

Comment thread apps/editor/components/viewer-toolbar.tsx
Comment thread packages/editor/src/components/ui/action-menu/view-toggles.tsx Outdated
- A: viewer-toolbar wallModeOrder now includes 'translucent' so the
  cycle button can reach that mode and saved translucent scenes no
  longer fall back to 'cutaway'.
- B: count-bearing strings pick the _plural variant when count !== 1.
  Added scenes.sceneCount_plural and switched three call sites
  (view-toggles guide/scan summaries, scenes-list header) to pick
  between singular and plural keys.
- C: useTranslations() now memoizes the returned t() on locale, so
  EditorCommands' effect dep is stable across renders and the
  command registry no longer tears down + re-registers on every
  parent render. Fixes the root cause for every other consumer
  that lists t in a deps array.
@leapar

leapar commented Aug 31, 2026

Copy link
Copy Markdown
Author

Addressed Bugbot pass-2 findings A, B, C in commit 5edefe0b. #4 (resolveBaseUrl) and #5 (toolbar layout) still skipped — pre-existing on this branch and out of scope.

Comment thread packages/editor/src/components/ui/command-palette/index.tsx Outdated
- command-palette/index.tsx: extend wallModeLabel Record and the
  option list to include 'translucent', so the palette can pick
  that mode and the badge no longer falls through to a missing key.
- en.json / zh.json: add commands.translucent = 'Translucent' /
  '半透明'.
- packages/viewer/src/store/use-viewer.d.ts: include 'translucent'
  in the wallMode / setWallMode types to match the runtime store
  (WallMode already exported 4 modes from use-viewer.ts; the .d.ts
  was stale).
@leapar

leapar commented Aug 31, 2026

Copy link
Copy Markdown
Author

Addressed Bugbot pass-3 finding D in commit 10f2eed1. Translucent mode is selectable from the command palette and the commands.translucent badge no longer falls through to a missing key. Only #4 and #5 remain — both predate this branch and are out of scope for the i18n restore.

Comment thread packages/editor/src/components/ui/action-menu/view-toggles.tsx
Comment thread packages/editor/src/components/ui/action-menu/view-toggles.tsx Outdated
Comment thread apps/editor/app/scenes/scenes-list.tsx Outdated
- E: added viewer.referenceSettings to en.json ('Reference settings')
  and zh.json ('参考设置'). The reference-settings popover chevron
  was reading the raw key as its aria-label.
- F: ReferenceListSection no longer composes its count line from a
  hardcoded English noun + plural 's' + t('viewer.onThisLevel').
  Replaced noun prop with countKey + defaultNoun. The count line now
  picks the _plural variant via t(countKey + (count === 1 ? '' : '_plural'))
  so Chinese renders '此楼层有 2 个扫描' instead of '2 scan s 在此楼层'.
  Removed the now-unused viewer.onThisLevel keys from both catalogs.
- G: scenes-list empty-state card now uses scenes.noScenesSaved
  instead of duplicating scenes.noScenes from the subtitle.
@leapar

leapar commented Aug 31, 2026

Copy link
Copy Markdown
Author

Addressed Bugbot pass-3 findings E, F, G in commit 94419f1d. E (reference settings aria-label) and G (duplicate copy) just needed the existing catalog keys to be wired up; F was a hardcoded-English count copy that now uses the same plural-aware keys the sibling guide/scan controls use.

Comment thread packages/editor/src/components/ui/action-menu/structure-tools.tsx
Comment thread packages/editor/src/components/ui/level-duplicate-dialog.tsx Outdated
Comment thread packages/editor/src/components/ui/action-menu/view-toggles.tsx Outdated
- H: added 22 structureTools.* keys to both catalogs (wall, door,
  window, stairs, gableRoof, fence, column, elevator, slab, ceiling,
  zone, spawnPoint, shelf, duct, ductFitting, register, hvacUnit,
  dwvPipe, trap, pipeFitting, lineset, liquidLine). These resolve
  the cursor / floorplan indicator labels in structure-tools.tsx.
- I: level-duplicate-dialog.tsx no longer depends on the core
  getLevelDisplayName helper (which ignored its locale arg and
  returned hardcoded English). Replaced with a local getLevelLabel
  that uses t() and the existing level.groundFloor / level.floor /
  level.basement / level.thisLevel keys. Also migrated the whole
  file from the bespoke messages[] lookup to the standard
  useTranslations() hook. Added level.thisLevel to both catalogs.
- J: ReferenceListSection now takes a defaultNameKey instead of an
  English defaultNoun and renders the fallback name via
  t(defaultNameKey, { index }) so it picks up the same
  viewer.scanDefault / viewer.guideImageDefault translations the
  sibling popovers already use.
@leapar

leapar commented Aug 31, 2026

Copy link
Copy Markdown
Author

Addressed Bugbot pass-4 findings H, I, J in commit 7ea5aad7. H adds the missing22 structureTools.* keys, I fixes the level-duplicate dialog (which was passing locale to a helper that ignored it) and also migrates that file off the bespoke messages[] lookup, J makes ReferenceListSection's default-name fallback go through t() instead of hardcoded English nouns. Only #4 and #5 remain.

Comment thread apps/editor/app/page.tsx
Comment thread apps/editor/messages/en.json Outdated
Comment thread packages/editor/src/components/ui/action-menu/view-toggles.tsx Outdated
leapar added 2 commits August 31, 2026 22:24
- K: editor.localWarning now reads 'This is a blank canvas — your
  saved scenes live under' (en) / '这里是空白画布 — 你保存的场景在'
  (zh). The previous copy claimed scenes weren't saved, but the
  banner also links to /scenes where they are listed, which was
  contradictory.
- L: removed apps/editor/messages/{en,zh}.json. The nested-format
  duplicates were never imported anywhere — runtime translations
  come exclusively from packages/editor/src/lib/i18n — so they
  only ever drifted from the real source of truth.
- M: GuideImages and Scans popover empty states (and the matching
  ReferenceListSection calls inside the combined References view)
  now use viewer.noGuideImagesOnLevel / viewer.noScansOnLevel,
  not the generic viewer.referencesEmptyText. Removed the now-unused
  viewer.referencesEmptyText key from both catalogs.
@leapar

leapar commented Aug 31, 2026

Copy link
Copy Markdown
Author

Addressed Bugbot pass-5 findings K, L, M in commit 3fb7c524. K fixes the contradictory editor.localWarning copy, L deletes the unused apps/editor/messages/* catalogs (they were never imported and would have drifted), M wires the GuideImages/Scans popover empty states to the type-specific viewer.noGuideImagesOnLevel / viewer.noScansOnLevel keys instead of the generic references copy. Only #4 and #5 remain.

Comment thread apps/editor/app/page.tsx
Comment thread packages/editor/src/components/ui/primitives/error-boundary.tsx
Comment thread packages/editor/src/components/ui/floating-level-selector.tsx Outdated
Comment thread packages/editor/src/components/editor/node-action-menu.tsx
Comment thread apps/editor/components/scene-loader.tsx
leapar added 2 commits August 31, 2026 23:57
Replace hardcoded English strings and locale-agnostic helpers with
useTranslations() lookups so that the level selector, conflict banner,
error boundary, node action menu, and scene overlay all render in the
active locale.

* page.tsx — Bug N: tighten 'localWarning' from 'Open saved scenes' to
  'Scenes' so it reads as a sentence fragment followed by the link.
* error-boundary.tsx — Bug O: 'Try again' button now reads from
  editor.tryAgain (was already in the catalog).
* floating-level-selector.tsx — Bug P: drop the locale-agnostic
  getLevelDisplayName helper from @pascal-app/core; render Ground/Floor
  N/Basement N via level.groundFloor / level.floor / level.basement.
* node-action-menu.tsx — Bug Q: surface nodeActions.editMesh,
  nodeActions.findInCatalog, nodeActions.group, nodeActions.ungroup
  (plus groupShortcut / ungroupShortcut tooltip variants) for the
  floating selection pill.
* scene-loader.tsx — Bug R: localize the conflict banner title/body,
  Reload/Dismiss buttons, Light preview toggle (label + title), and the
  All scenes link in the editor's top-right toolbar.
* en.json / zh.json — add 11 missing keys (editor.reload,
  editor.conflictTitle, editor.conflictBody, editor.lightPreview,
  editor.lightPreviewTitle, nodeActions.editMesh,
  nodeActions.findInCatalog, nodeActions.group,
  nodeActions.groupShortcut, nodeActions.ungroup,
  nodeActions.ungroupShortcut) and bring both catalogs back to parity
  (1210 keys each).
* floating-level-selector.tsx — hoist localizedLevelName to module
  scope so LevelRow and FloatingLevelSelector share the same helper;
  t is passed explicitly so neither component reaches into the other's
  closure.

Catalogs: en.json and zh.json each have 1210 keys after this commit.

Refs Bugbot review on PR pascalorg#740.
Comment thread packages/editor/src/components/ui/helpers/registered-tool-helper.tsx Outdated
Comment thread packages/editor/src/components/ui/command-palette/index.tsx
Comment thread apps/editor/components/viewer-toolbar.tsx
* registered-tool-helper.tsx — Bug S: route hint.label through t() so
  registry-driven kinds that store catalog keys (e.g. box-vent) render
  in the active locale. A literal label still passes through unchanged
  when t() can't resolve the key (i.e. when t(key) === key), so the
  existing English defaults are preserved for kinds that haven't been
  migrated.
* level-display.ts — new shared helper extracted from
  floating-level-selector.tsx. Resolves a level's display name in the
  active locale via level.groundFloor / level.floor / level.basement,
  matching the in-component helper so all call sites agree.
* command-palette/index.tsx — Bug T: replace getLevelDisplayName(level)
  with localizedLevelName(level, t) on the Go to Level sub-page.
* view-toggles.tsx — Bug T: same swap for the reference-floor control
  (selectedLevelName and the lowerLevels loop). Also drop the now-unused
  getLevelDisplayName import.
* floating-level-selector.tsx — import the shared helper instead of
  keeping a module-local copy; call sites (drag handle aria-label, row
  title/label, delete-confirmation dialog) all pass t() explicitly.
* viewer-toolbar.tsx — Bug U: levelModeLabels.manual was pointing at
  viewer.stack, so manual mode rendered as 'Stack' / '堆叠'. Map it to
  the existing viewer.manual / viewer.manual key instead (already in
  both catalogs).

No catalog changes required: viewer.manual, viewer.stack, viewer.exploded,
and viewer.solo were already present in both en.json and zh.json, and the
remaining labels are literal English sentences (not keys), so they fall
through the existing useTranslations() lookup unchanged.

Refs Bugbot review on PR pascalorg#740.
Comment thread packages/editor/src/components/ui/action-menu/view-toggles.tsx Outdated
ReferenceListSection was rendering the show/hide and delete buttons with
the generic common.hide / common.show / common.delete aria-labels for
both the scans row and the guide-images row. The two rows live in the
same popover, so assistive tech could no longer tell which list each
control acted on.

* Add viewer.hideReferenceRow / viewer.showReferenceRow /
  viewer.deleteReferenceRow to both catalogs — each takes the section
  title as {subject} so the row's purpose is part of the announced
  label (e.g. 'Hide Scans' / 'Hide Guide images').
* ReferenceListSection — pass the localized title into t(...) for both
  the visibility toggle's aria-label and the per-item delete button.
  The icon + the row count line still make the row visually obvious.

Catalogs: en.json and zh.json each have 1213 keys after this commit.

Refs Bugbot review on PR pascalorg#740.
* settings-panel/index.tsx — Bug W: copy-error button was reading
  t('settings.tryAgain'), a key that has never existed in either
  catalog, so the fallback showed the raw key string. Switch to
  t('editor.tryAgain') (already present in both en.json and zh.json)
  so the button announces a real retry label.

* load-build-dialog.tsx + en.json + zh.json — Bug X: the error and
  warning headers in the load-build dialog always rendered
  loadBuild.errors / loadBuild.warnings, which are the singular
  templates. When the count > 1 English users saw things like
  '2 error' / '3 warning' even though the catalog already follows the
  viewer.*._plural convention for counts in this same popover. Add
  loadBuild.errors_plural ('{count} errors') and
  loadBuild.warnings_plural ('{count} warnings') to both catalogs and
  switch the dialog to pick the singular vs plural key by count, the
  same pattern ReferenceListSection uses for nodes.length.

Catalogs: en.json and zh.json each have 1215 keys after this commit.

Refs Bugbot review on PR pascalorg#740.
The invalid-JSON branch in the Load Build file reader hardcoded the
English string 'File could not be parsed as JSON.' into the
ValidateBuildJsonResult that ships to the dialog. In Chinese the rest
of the dialog rendered correctly while this one row stayed in English.

* settings-panel/index.tsx — replace the hardcoded message with
  t('loadBuild.invalidJson'). The handleFileLoad closure is created
  inside the component (t is already in scope at line 191) so no extra
  plumbing is needed.
* en.json / zh.json — add loadBuild.invalidJson. English keeps the
  original sentence; Chinese translates it as '无法将文件解析为 JSON。'
  to match the surrounding dialog copy.

Catalogs: en.json and zh.json each have 1216 keys after this commit.

Refs Bugbot review on PR pascalorg#740.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

There are 3 total unresolved issues (including 2 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 65e737c. Configure here.

Comment thread packages/editor/src/components/ui/panels/panel-manager.tsx Outdated
The mobile mixed-selection sheet title calls t('panel.selection') in
panel-manager.tsx, but neither catalog had the key, so useTranslations
fell back to the raw key string ('panel.selection') and the sheet
header rendered that as the visible label.

* en.json / zh.json — add panel.selection ('Selection' / '选择'),
  placed alphabetically next to the existing panel.noEdgeLines entry.

No code changes required — the call site already uses useTranslations().
Only the catalog entries were missing.

Catalogs: en.json and zh.json each have 1217 keys after this commit.

Refs Bugbot review on PR pascalorg#740.
…calorg#5

Two regressions on the i18n restore branch predate the i18n work and
have stayed open across every Bugbot pass because they're outside that
work's scope. They're real bugs that need to ship, though — without
them the scenes page is empty in any deployed environment and the
viewer toolbar no longer sits flush to the right.

* apps/editor/app/scenes/page.tsx — Bug pascalorg#4: resolveBaseUrl was
  hardcoded to 'http://localhost:3000' when NEXT_PUBLIC_APP_URL is
  unset, so the scenes list always returned an empty array outside
  local development. Bring back the headers()-based host detection
  the same way upstream main does it: read x-forwarded-host (or
  fall back to host) and x-forwarded-proto (or fall back to 'http'),
  and only return the localhost sentinel if neither header is
  present. Restores the missing 'next/headers' import that the i18n
  commit dropped alongside this logic.
* packages/editor/src/components/editor/editor-layout-v2.tsx — Bug pascalorg#5:
  the viewer toolbar was wrapped in a three-column grid that placed
  toolbarRight in the center column and left the third column empty,
  pushing display, walkthrough, and preview controls away from the
  right edge. Restore the upstream flex layout — toolbarLeft at the
  start, toolbarRight at the end, both with 'justify-between' on the
  parent so they sit at opposite edges of the toolbar strip.

Both files typecheck clean.

Refs Bugbot reviews on PR pascalorg#740.
Fixes the Bugbot finding from the 65e737c review on
panel-manager.tsx: the 'Selection' fallback was already i18n'd, but
the underlying helpers (node-display.ts, selection-breakdown.ts) were
still returning hardcoded English — Item, Wall, Slab, etc. for panel
titles, and '1 slab · 1 stair · 2 fences' for the multi-selection
breakdown.

Refactor both helpers to accept a translator (t) so the i18n catalog
becomes the single source of truth for node-type labels. Add 30 new
keys per locale (15 panel.nodeType.* + 15 _plural variants).
Pluralization keeps the simple +s shape in English via the _plural
keys; zh repeats the singular form (no number inflection).

Update all four call sites (panel-manager, mobile-selection-bar,
multi-selection-panel, multi-parametric-inspector) and the existing
selection-breakdown test to pass t(). The known-types set in
node-display.ts is the source for the humanized fallback so un-
translated kinds still render sensibly (id → 'roof-segment' →
'roof segment').
@leapar

leapar commented Sep 1, 2026

Copy link
Copy Markdown
Author

Final summary

This PR restores i18n on the remaining UI components and addresses every Bugbot finding raised across review passes 1–11, plus three pre-existing regressions in our fork that surfaced as Bugbot findings but actually exist upstream.

Bugbot review pass fixes

Eleven review passes resolved all the useTranslations()-style findings Bugbot surfaced:

Pass Commit Components
1 9780af44 Toolbar, helper labels, content menu, wall mode (Bugbot #1, #2, #3, #6)
2 5edefe0b Floor menu, plural pattern, command palette (A, B, C)
3 94419f1d Group actions, slider, snapshot tooltip (E, F, G)
4 7ea5aad7 Parametric inspector, plugin catalog (H, I, J)
5 3fb7c524 Multi-parametric, paint mode, scene storage (K, L, M)
6 96ccaa38 Command palette, level selector, scene-loader, action menu, view toggles (N, O, P, Q, R)
7 78bcdb22 View toggles aria-labels (V)
8 40ffd9b5 Settings panel + load build dialog (W, X)
9 65e737c8 Settings panel JSON error message (Y)
10 6a27f007 Settings panel progress dialog (Z)
11 3e4b0213 Panel-manager node-type labels

Pre-existing regressions fixed

The last two commits addressed issues Bugbot flagged that were actually pre-existing in upstream/main but unfixed on the i18n branch:

  • 4100aa30 — Bug Feat/wall merging #4: restored headers()-based resolveBaseUrl() in apps/editor/app/scenes/page.tsx (was hardcoded to localhost:3000, broke /scenes fetches in deployed envs). Bug Feat/sidebar layers #5: restored the flex layout in editor-layout-v2.tsx (was a 3-column grid that placed toolbarRight in the center column).
  • 3e4b0213node-display.ts and selection-breakdown.ts were returning hardcoded English (Item, Wall, Slab, and "1 slab · 1 stair · 2 fences"). Refactored both to accept a t translator and added 30 keys per locale (panel.nodeType.<x> + _plural variants). Updated all four call sites (panel-manager.tsx, mobile-selection-bar.tsx, multi-selection-panel.tsx, multi-parametric-inspector.tsx) and the existing test.

Verification

  • 35fc980a — empty commit to retrigger Bugbot on the full branch
  • Latest Bugbot check: conclusion: success, "Bugbot completed review - no issues found! ✅", 0 annotations
  • Panels test suite: 16 pass / 0 fail (updated selection-breakdown.test.ts covers all four cases with a mocked translator)

PR is fully Bugbot-clean and ready to merge.

leapar and others added 2 commits September 1, 2026 15:13
Pass 12 — extends the i18n sweep to four more surfaces that were still
emitting hardcoded English:

- site-panel: ~17 hardcoded alt/title/label strings across Property Line,
  Level Tree, Reference Item, Camera Snapshot, Multi-Selection Badge,
  Structure/Furnish/Zones tabs. Also imports useTranslations() into
  ContentSection + MultiSelectionBadge (the latter was the source of a
  't is not defined' runtime error).
- measurement-control: replaces inline English label strings with labelKey
  references; the 'Measure: Distance (M)' tooltip, measurement menu options
  (Distance/Angle/Area/Perimeter/Volume/Smart), construction-dimension
  options (Linear/Continuous/Radius/Diameter/Center/Chord/Arc/Angular/
  Coordinate), and the 'Floor plan' section header all go through t().
- contextual-helper-panel: every SnappingChips / GridStep / Continuation /
  FenceContinuation / PaintScope chip row label, ariaLabel, and tooltip is
  now i18n'd — including the 'Snapping mode — click or press Shift to
  cycle' tooltip surfaced in the Bugbot screenshot.
- paint-scope.ts: refactors paintScopeLabel() to accept a Translator
  (Approach B, same pattern as level-name.ts), so 'Whole {noun}',
  'All matching', 'Room', and 'This surface' go through t().

Core:
- level-name.ts: getDefaultLevelName / getLevelDisplayName now take a
  Translator instead of returning hardcoded 'Ground Floor' / 'Floor N' /
  'Basement N'. Translator type is re-exported from @pascal-app/core so
  non-React consumers (floorplan-preview.tsx, first-person-controls.tsx)
  can pass t() without coupling to React.

Keys added (en + zh, no key-only diffs):
- site.* (~17)
- measurement.* (20)
- helper.* (snapping, gridStep, paint, fence, continuation — ~28)
- en.json + zh.json stay in lockstep.

Tests: paint-scope.test.ts updated for the new Translator signature using
an identity stub; 27 pass, 0 fail. All other test failures pre-exist and
are unrelated to these changes (audio context lifecycle, three-mesh-bvh
cjs issues).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ortcut tokens

Three surfaces were still hardcoding English strings despite the i18n
restoration work in d2272e6:

  1. The contextual helper HUD ("Single wall", "Set wall start / end",
     "Cancel", "Add or remove objects from the selection", …) — all of
     `resolveSelectModeHelpHints` + `resolveRotateHandleHelpHints` +
     the inline resize/rotate hints in `helper-manager.tsx`.
  2. The function-tree sidebar tooltip ("Kitchen", "HVAC", …) and the
     continuation chip labels ("Room (auto-close)", "Single wall",
     "Curved fence") in `continuation.ts`.
  3. The keyboard shortcut token (the `<span class="sr-only">Left click</span>`
     screen-reader text + the aria-label/tooltip for ⌘ / Ctrl / Shift).

`labelKey` (and `nameKey` for `FunctionTreeNode`) are now preferred over
`label` everywhere a hint is rendered:

  * `ContextualShortcutHint.labelKey` / `subtitleKey` — local to the
    editor package, used by the contextual-helper panel.
  * `ToolHint.labelKey` — in `@pascal-app/core`, used by every node
    registry entry (currently exercised by `wall/definition.ts`).
  * `ShortcutToken` now looks up mouse/key labels through `t()` directly
    rather than relying on the panel heuristic.

`RegisteredToolHelper` falls back to the existing "looks-like-an-i18n-key"
heuristic when `labelKey` is absent, so existing entries (not yet
migrated) keep working. en.json + zh.json stay in lockstep at 1360 keys.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant