Restore i18n on remaining UI components (rebase onto upstream/main) - #740
Restore i18n on remaining UI components (rebase onto upstream/main)#740leapar wants to merge 33 commits into
Conversation
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
…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.
|
Addressed Bugbot findings #1, #2, #3, and #6 in commit 9780af44. #1 FitSceneButton ReferenceError — added the missing useTranslations() call inside the component. |
- 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.
- 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).
- 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.
|
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. |
- 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.
|
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. |
- 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.
|
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. |
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.
* 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.
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.
There was a problem hiding this comment.
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).
❌ 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.
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').
Final summaryThis 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 fixesEleven review passes resolved all the
Pre-existing regressions fixedThe last two commits addressed issues Bugbot flagged that were actually pre-existing in upstream/main but unfixed on the i18n branch:
Verification
PR is fully Bugbot-clean and ready to merge. |
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>

Summary
Restores
useTranslations()across UI components whose hardcoded strings were lost when upstream was merged into this fork's main. The work re-applies commitsb8c87042,af9d9eab,d7a991b4and merges the latest upstreammain(commit51f2a53d).Components localized
panel.nodeType.<x>) flow through the mobile panel sheet title, multi-selection breakdown, and selection barb8c87042/af9d9eab): home page, scene-loader, camera actions, action menus, helpers, icon-railPre-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— restoredheaders()-based host detection inapps/editor/app/scenes/page.tsx::resolveBaseUrl(was hardcoded tolocalhost:3000, which broke/scenesfetches in deployed envs). Verified identical to upstreammain.4100aa30— restored the flex layout inpackages/editor/src/components/editor/editor-layout-v2.tsx::RightColumn(a broken 3-column grid was placingtoolbarRightin the center column). Verified identical to upstreammain.3e4b0213—node-display.tsandselection-breakdown.tswere returning hardcoded English (Item,Wall,Slab, and"1 slab · 1 stair · 2 fences"). Refactored both to accept a translator, addedpanel.nodeType.<x>keys.Approach
labelKey/nameKey/detailKeystrings, resolved inside components viat().const t = useTranslations()per component (each component needs its own hook call).useEffectdependency arrays updated to includetwhere applicable.<key>/<key>_pluralconvention (e.g.loadBuild.errors,panel.nodeType.fence/_plural).getNodeDisplay,getTypeDisplay,formatSelectionBreakdown) now take aTranslatorargument so the catalog stays the single source of truth.Translation keys
1217 keys in
en.jsonandzh.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
@pascal-app/coreexports remain (separate concern: this fork adds types that the core package hasn't shipped yet — e.g.getLevelDisplayName,DEFAULT_LEVEL_HEIGHT,MeasurementNode,SceneState.materials).main(only conflict:roof-helper.tsxdeleted upstream, accepted the deletion).packages/editor/src/components/ui/panels/pass. The 37 pre-existingsfx-player.test.tsfailures are unrelated (audio-context mocking).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:
navigator.languagetozh-CN(or change the locale viauseLocale().setLocale('zh')in devtools) and verify the following flows render Chinese:Cmd/Ctrl+K): group headings and command namesMobilePanelSheet title), and the multi-selection breakdown string in the docked panel (MultiSelectionPanel,MultiParametricInspector)?key)navigator.languageisen.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/editorafter 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 useuseTranslations()orlabelKeyconfigs resolved at render time.Level naming no longer returns English-only defaults from core:
getDefaultLevelName/getLevelDisplayNametake aTranslator, and the editor routes display throughlocalizedLevelName. Walkthrough HUD and first-person floor labels passtthrough accordingly.App shell tweaks: global thin dark scrollbars;
/scenesgets a dark layout wrapper and a clientScenesList(with saferfetchSceneserror handling); newcurrentColorSVG icons for file/scene/settings.Bundling: several
@pascal-app/coremodules 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.