fix(i18n): clamp unsupported browser locales to a shipped tag#11712
fix(i18n): clamp unsupported browser locales to a shipped tag#11712dante01yoon wants to merge 5 commits intomainfrom
Conversation
navigator.language base tags outside the 12 shipped locales (de, it, nl, pl, pt, vi, hi, etc.) caused Comfy.Locale's defaultValue and createI18n's initial locale to set i18n.global.locale.value to a tag with no messages. loadLocale silently no-op'd, te() returned false for the missing locale (it does not consult fallbackLocale), and st() rendered the literal i18n key — so on a fresh install the sidebar buttons displayed 'sideToolbar.labels.assets' etc. instead of 'Assets'. Add resolveSupportedLocale() that tries the full BCP-47 tag first (preserving zh-TW/pt-BR), then the base tag, then 'en'. Wire it through both entry points (createI18n initial locale, Comfy.Locale defaultValue) and clamp inside loadLocale + propagate the resolved value to GraphView so a stale stored Comfy.Locale='de' from older builds also recovers. Side benefit: pt-BR users were previously falling through pt → unshipped → broken; the full-tag-first lookup now lands them on the pt-BR bundle. Fixes #10563 FE-480
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughAdds a centralized locale configuration and resolver, refactors locale loading to always resolve requested locales to supported canonical values, updates initialization and settings to use the resolver, and adapts tests and a browser test to assert clamping, canonicalization, and preserved shipped tags. Changes
Sequence Diagram(s)sequenceDiagram
participant Browser as Browser\nNavigator
participant Settings as App\nSettings
participant Resolver as resolveSupportedLocale
participant Loader as loadLocale
participant I18n as Vue I18n
Browser->>Settings: navigator.language (e.g., "de-DE")
Settings->>Resolver: resolveSupportedLocale("de-DE")
activate Resolver
Resolver->>Resolver: case-insensitive exact match?
alt exact shipped
Resolver-->>Settings: return canonical (e.g., "zh-TW")
else not shipped
Resolver->>Resolver: check base subtag (e.g., "de")
alt base shipped
Resolver-->>Settings: return base canonical (e.g., "pt")
else
Resolver-->>Settings: return "en"
end
end
deactivate Resolver
Settings->>Loader: loadLocale(resolved)
activate Loader
Loader->>Loader: dedupe in-flight, dynamic import messages
Loader->>I18n: set/mergeLocaleMessage(resolved, messages)
Loader->>I18n: set i18n.locale = resolved
Loader-->>Settings: return resolved
deactivate Loader
Estimated Code Review Effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 6 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (6 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.Comment |
🎨 Storybook: ✅ Built — View Storybook |
🎭 Playwright: ✅ 1435 passed, 0 failed📊 Browser Reports
|
📦 Bundle: 5.23 MB gzip 🔴 +377 BDetailsSummary
Category Glance App Entry Points — 22.5 kB (baseline 22.5 kB) • ⚪ 0 BMain entry bundles and manifests
Status: 1 added / 1 removed Graph Workspace — 1.24 MB (baseline 1.24 MB) • 🟢 -512 BGraph editor runtime, canvas, workflow orchestration
Status: 1 added / 1 removed Views & Navigation — 77.7 kB (baseline 77.7 kB) • ⚪ 0 BTop-level views, pages, and routed surfaces
Status: 9 added / 9 removed / 2 unchanged Panels & Settings — 488 kB (baseline 488 kB) • ⚪ 0 BConfiguration panels, inspectors, and settings screens
Status: 10 added / 10 removed / 11 unchanged User & Accounts — 17.5 kB (baseline 17.5 kB) • ⚪ 0 BAuthentication, profile, and account management bundles
Status: 5 added / 5 removed / 2 unchanged Editors & Dialogs — 113 kB (baseline 113 kB) • ⚪ 0 BModals, dialogs, drawers, and in-app editors
Status: 4 added / 4 removed UI Components — 61 kB (baseline 61 kB) • ⚪ 0 BReusable component library chunks
Status: 5 added / 5 removed / 8 unchanged Data & Services — 3.04 MB (baseline 3.04 MB) • ⚪ 0 BStores, services, APIs, and repositories
Status: 13 added / 13 removed / 4 unchanged Utilities & Hooks — 364 kB (baseline 364 kB) • ⚪ 0 BHelpers, composables, and utility bundles
Status: 16 added / 16 removed / 15 unchanged Vendor & Third-Party — 9.88 MB (baseline 9.88 MB) • ⚪ 0 BExternal libraries and shared vendor chunks Status: 16 unchanged Other — 8.83 MB (baseline 8.83 MB) • 🔴 +1.45 kBBundles that do not match a named category
Status: 62 added / 62 removed / 73 unchanged ⚡ Performance Report
All metrics
Historical variance (last 15 runs)
Trend (last 15 commits on main)
Raw data{
"timestamp": "2026-04-30T08:55:38.306Z",
"gitSha": "8f66b16065329b1e00e5b92ae3c3c836092fefcc",
"branch": "jaewon/fe-480-fix-unsupported-locale",
"measurements": [
{
"name": "canvas-idle",
"durationMs": 2015.7899999999813,
"styleRecalcs": 8,
"styleRecalcDurationMs": 7.997999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 386.966,
"heapDeltaBytes": 23096228,
"heapUsedBytes": 71338860,
"domNodes": 16,
"jsHeapTotalBytes": 15204352,
"scriptDurationMs": 20.40500000000001,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-idle",
"durationMs": 2015.0820000000067,
"styleRecalcs": 9,
"styleRecalcDurationMs": 9.912999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 451.93799999999993,
"heapDeltaBytes": 22714180,
"heapUsedBytes": 70981144,
"domNodes": 18,
"jsHeapTotalBytes": 14680064,
"scriptDurationMs": 25.536,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-idle",
"durationMs": 2017.9620000000114,
"styleRecalcs": 8,
"styleRecalcDurationMs": 8.864999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 427.07000000000005,
"heapDeltaBytes": 22888548,
"heapUsedBytes": 71521156,
"domNodes": 16,
"jsHeapTotalBytes": 14680064,
"scriptDurationMs": 23.515,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1853.4049999999809,
"styleRecalcs": 71,
"styleRecalcDurationMs": 46.858000000000004,
"layouts": 12,
"layoutDurationMs": 3.194,
"taskDurationMs": 875.8229999999999,
"heapDeltaBytes": 907712,
"heapUsedBytes": 49202408,
"domNodes": -265,
"jsHeapTotalBytes": 15069184,
"scriptDurationMs": 137.757,
"eventListeners": -131,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1848.747000000003,
"styleRecalcs": 71,
"styleRecalcDurationMs": 36.263999999999996,
"layouts": 12,
"layoutDurationMs": 3.3149999999999995,
"taskDurationMs": 856.3599999999999,
"heapDeltaBytes": -1069792,
"heapUsedBytes": 47283188,
"domNodes": -265,
"jsHeapTotalBytes": 15593472,
"scriptDurationMs": 136.34799999999998,
"eventListeners": -131,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1884.2129999999315,
"styleRecalcs": 72,
"styleRecalcDurationMs": 39.099999999999994,
"layouts": 12,
"layoutDurationMs": 3.683,
"taskDurationMs": 869.518,
"heapDeltaBytes": 5022964,
"heapUsedBytes": 53701500,
"domNodes": -265,
"jsHeapTotalBytes": 16379904,
"scriptDurationMs": 136.343,
"eventListeners": -131,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1746.155999999985,
"styleRecalcs": 33,
"styleRecalcDurationMs": 19.617999999999995,
"layouts": 6,
"layoutDurationMs": 0.551,
"taskDurationMs": 371.61299999999994,
"heapDeltaBytes": 298916,
"heapUsedBytes": 49076892,
"domNodes": 79,
"jsHeapTotalBytes": 15204352,
"scriptDurationMs": 33.749,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1754.1629999999486,
"styleRecalcs": 33,
"styleRecalcDurationMs": 20.136999999999997,
"layouts": 6,
"layoutDurationMs": 0.621,
"taskDurationMs": 376.116,
"heapDeltaBytes": 215748,
"heapUsedBytes": 48501128,
"domNodes": 78,
"jsHeapTotalBytes": 14680064,
"scriptDurationMs": 28.072,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1751.7540000000054,
"styleRecalcs": 31,
"styleRecalcDurationMs": 19.883000000000006,
"layouts": 6,
"layoutDurationMs": 0.7800000000000001,
"taskDurationMs": 365.244,
"heapDeltaBytes": 210876,
"heapUsedBytes": 48543048,
"domNodes": 75,
"jsHeapTotalBytes": 14417920,
"scriptDurationMs": 26.341000000000005,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "dom-widget-clipping",
"durationMs": 682.1150000000102,
"styleRecalcs": 8,
"styleRecalcDurationMs": 6.944999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 412.40900000000005,
"heapDeltaBytes": 9110632,
"heapUsedBytes": 57406440,
"domNodes": 12,
"jsHeapTotalBytes": 15204352,
"scriptDurationMs": 75.34700000000001,
"eventListeners": 2,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "dom-widget-clipping",
"durationMs": 575.396000000012,
"styleRecalcs": 9,
"styleRecalcDurationMs": 6.485000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 346.334,
"heapDeltaBytes": 9424152,
"heapUsedBytes": 57340436,
"domNodes": 14,
"jsHeapTotalBytes": 15728640,
"scriptDurationMs": 61.07099999999999,
"eventListeners": 0,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "dom-widget-clipping",
"durationMs": 572.8320000000622,
"styleRecalcs": 10,
"styleRecalcDurationMs": 7.303000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 355.9939999999999,
"heapDeltaBytes": 8827416,
"heapUsedBytes": 56828716,
"domNodes": 16,
"jsHeapTotalBytes": 14417920,
"scriptDurationMs": 63.019999999999996,
"eventListeners": 0,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.669999999999998,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-idle",
"durationMs": 2030.9929999999667,
"styleRecalcs": 8,
"styleRecalcDurationMs": 8.806999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 702.0260000000001,
"heapDeltaBytes": 1849528,
"heapUsedBytes": 60113904,
"domNodes": -265,
"jsHeapTotalBytes": 5013504,
"scriptDurationMs": 114.37000000000002,
"eventListeners": -127,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-idle",
"durationMs": 2033.3210000000008,
"styleRecalcs": 7,
"styleRecalcDurationMs": 8.358999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 682.3000000000001,
"heapDeltaBytes": 1659308,
"heapUsedBytes": 60194904,
"domNodes": -267,
"jsHeapTotalBytes": 5799936,
"scriptDurationMs": 113.34700000000001,
"eventListeners": -125,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "large-graph-idle",
"durationMs": 2040.04000000009,
"styleRecalcs": 9,
"styleRecalcDurationMs": 10.468000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 694.5669999999999,
"heapDeltaBytes": 13460056,
"heapUsedBytes": 70348688,
"domNodes": -260,
"jsHeapTotalBytes": -753664,
"scriptDurationMs": 122.12599999999999,
"eventListeners": -125,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-pan",
"durationMs": 2134.736999999973,
"styleRecalcs": 68,
"styleRecalcDurationMs": 18.772000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1208.4279999999999,
"heapDeltaBytes": -1376684,
"heapUsedBytes": 58096184,
"domNodes": -267,
"jsHeapTotalBytes": 761856,
"scriptDurationMs": 421.918,
"eventListeners": -125,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-pan",
"durationMs": 2165.4189999999858,
"styleRecalcs": 67,
"styleRecalcDurationMs": 18.392000000000003,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1276.0240000000001,
"heapDeltaBytes": 9598168,
"heapUsedBytes": 69139452,
"domNodes": -269,
"jsHeapTotalBytes": -24576,
"scriptDurationMs": 453.22799999999995,
"eventListeners": -127,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-pan",
"durationMs": 2129.7309999999925,
"styleRecalcs": 68,
"styleRecalcDurationMs": 18.583000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1206.055,
"heapDeltaBytes": -864920,
"heapUsedBytes": 58443096,
"domNodes": -265,
"jsHeapTotalBytes": 761856,
"scriptDurationMs": 422.531,
"eventListeners": -127,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-zoom",
"durationMs": 3200.4230000000007,
"styleRecalcs": 65,
"styleRecalcDurationMs": 19.156999999999996,
"layouts": 60,
"layoutDurationMs": 7.7250000000000005,
"taskDurationMs": 1484.969,
"heapDeltaBytes": 15018332,
"heapUsedBytes": 76005724,
"domNodes": -271,
"jsHeapTotalBytes": -491520,
"scriptDurationMs": 540.79,
"eventListeners": -125,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "large-graph-zoom",
"durationMs": 3212.1099999999956,
"styleRecalcs": 65,
"styleRecalcDurationMs": 20.039999999999996,
"layouts": 60,
"layoutDurationMs": 7.808000000000001,
"taskDurationMs": 1480.5070000000003,
"heapDeltaBytes": 9136472,
"heapUsedBytes": 70035260,
"domNodes": -269,
"jsHeapTotalBytes": 4751360,
"scriptDurationMs": 540.8219999999999,
"eventListeners": -123,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-zoom",
"durationMs": 3198.8509999999906,
"styleRecalcs": 64,
"styleRecalcDurationMs": 18.94,
"layouts": 60,
"layoutDurationMs": 7.8919999999999995,
"taskDurationMs": 1466.608,
"heapDeltaBytes": 8080972,
"heapUsedBytes": 69133240,
"domNodes": -271,
"jsHeapTotalBytes": 5799936,
"scriptDurationMs": 535.988,
"eventListeners": -123,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "minimap-idle",
"durationMs": 2016.6430000000446,
"styleRecalcs": 8,
"styleRecalcDurationMs": 10.041000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 673.9999999999999,
"heapDeltaBytes": 6027344,
"heapUsedBytes": 65533876,
"domNodes": -266,
"jsHeapTotalBytes": 557056,
"scriptDurationMs": 111.299,
"eventListeners": -125,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "minimap-idle",
"durationMs": 2008.1390000000283,
"styleRecalcs": 6,
"styleRecalcDurationMs": 6.532,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 677.856,
"heapDeltaBytes": 925196,
"heapUsedBytes": 60661268,
"domNodes": -272,
"jsHeapTotalBytes": 4227072,
"scriptDurationMs": 112.428,
"eventListeners": -157,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "minimap-idle",
"durationMs": 2050.2539999999954,
"styleRecalcs": 8,
"styleRecalcDurationMs": 9.949,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 664.156,
"heapDeltaBytes": 14985772,
"heapUsedBytes": 75403228,
"domNodes": -260,
"jsHeapTotalBytes": -229376,
"scriptDurationMs": 109.597,
"eventListeners": -125,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 583.4169999999972,
"styleRecalcs": 45,
"styleRecalcDurationMs": 11.033000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 378.935,
"heapDeltaBytes": 9399484,
"heapUsedBytes": 58032528,
"domNodes": 16,
"jsHeapTotalBytes": 14942208,
"scriptDurationMs": 126.76300000000002,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666682,
"p95FrameDurationMs": 16.799999999999727
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 587.5929999999698,
"styleRecalcs": 47,
"styleRecalcDurationMs": 12.648,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 391.15,
"heapDeltaBytes": 9098336,
"heapUsedBytes": 57847684,
"domNodes": 19,
"jsHeapTotalBytes": 15204352,
"scriptDurationMs": 128.079,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66666666666665,
"p95FrameDurationMs": 16.700000000000273
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 616.7960000000221,
"styleRecalcs": 45,
"styleRecalcDurationMs": 10.985000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 407.91900000000004,
"heapDeltaBytes": 8821724,
"heapUsedBytes": 57425780,
"domNodes": 16,
"jsHeapTotalBytes": 15990784,
"scriptDurationMs": 135.01399999999998,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998,
"p95FrameDurationMs": 16.700000000000273
},
{
"name": "subgraph-idle",
"durationMs": 2021.2329999999952,
"styleRecalcs": 11,
"styleRecalcDurationMs": 11.665,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 471.473,
"heapDeltaBytes": 23306764,
"heapUsedBytes": 71735332,
"domNodes": 21,
"jsHeapTotalBytes": 14942208,
"scriptDurationMs": 33.522,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-idle",
"durationMs": 2005.4420000000164,
"styleRecalcs": 9,
"styleRecalcDurationMs": 9.414000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 429.61499999999995,
"heapDeltaBytes": 22190196,
"heapUsedBytes": 71820080,
"domNodes": 18,
"jsHeapTotalBytes": 15204352,
"scriptDurationMs": 22.311,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-idle",
"durationMs": 2006.4540000000761,
"styleRecalcs": 9,
"styleRecalcDurationMs": 9.642999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 432.848,
"heapDeltaBytes": 23016828,
"heapUsedBytes": 71454072,
"domNodes": 18,
"jsHeapTotalBytes": 14942208,
"scriptDurationMs": 21.589000000000006,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1733.1949999999665,
"styleRecalcs": 76,
"styleRecalcDurationMs": 40.759,
"layouts": 16,
"layoutDurationMs": 4.800000000000001,
"taskDurationMs": 764.358,
"heapDeltaBytes": 14420252,
"heapUsedBytes": 62310312,
"domNodes": 62,
"jsHeapTotalBytes": 15466496,
"scriptDurationMs": 110.36,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1682.6770000000124,
"styleRecalcs": 75,
"styleRecalcDurationMs": 40.373,
"layouts": 16,
"layoutDurationMs": 4.4590000000000005,
"taskDurationMs": 727.2769999999999,
"heapDeltaBytes": 14402032,
"heapUsedBytes": 62905824,
"domNodes": 60,
"jsHeapTotalBytes": 15204352,
"scriptDurationMs": 104.34299999999999,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1757.2129999999788,
"styleRecalcs": 75,
"styleRecalcDurationMs": 40.024,
"layouts": 16,
"layoutDurationMs": 4.504,
"taskDurationMs": 785.0429999999999,
"heapDeltaBytes": -1365872,
"heapUsedBytes": 46996664,
"domNodes": -262,
"jsHeapTotalBytes": 15069184,
"scriptDurationMs": 107.41300000000001,
"eventListeners": -131,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "viewport-pan-sweep",
"durationMs": 8198.429999999973,
"styleRecalcs": 250,
"styleRecalcDurationMs": 54.294999999999995,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 4159.284,
"heapDeltaBytes": 10774816,
"heapUsedBytes": 69070188,
"domNodes": -265,
"jsHeapTotalBytes": 7577600,
"scriptDurationMs": 1348.4199999999998,
"eventListeners": -111,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "viewport-pan-sweep",
"durationMs": 8271.413999999935,
"styleRecalcs": 250,
"styleRecalcDurationMs": 54.44799999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 4502.509,
"heapDeltaBytes": 21133060,
"heapUsedBytes": 78224840,
"domNodes": -261,
"jsHeapTotalBytes": 8626176,
"scriptDurationMs": 1590.968,
"eventListeners": -111,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "viewport-pan-sweep",
"durationMs": 8177.445000000034,
"styleRecalcs": 249,
"styleRecalcDurationMs": 54.161,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 4191.372,
"heapDeltaBytes": 9305792,
"heapUsedBytes": 67545176,
"domNodes": -263,
"jsHeapTotalBytes": 6266880,
"scriptDurationMs": 1380.912,
"eventListeners": -111,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "vue-large-graph-idle",
"durationMs": 12982.458000000008,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 12967.167,
"heapDeltaBytes": -51044948,
"heapUsedBytes": 169640232,
"domNodes": -9850,
"jsHeapTotalBytes": 21295104,
"scriptDurationMs": 634.1699999999998,
"eventListeners": -23961,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.220000000000073,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-idle",
"durationMs": 12628.350000000068,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 12611.270999999999,
"heapDeltaBytes": -52509492,
"heapUsedBytes": 173032320,
"domNodes": -9850,
"jsHeapTotalBytes": 25489408,
"scriptDurationMs": 640.4069999999999,
"eventListeners": -23954,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.223333333333237,
"p95FrameDurationMs": 16.80000000000291
},
{
"name": "vue-large-graph-idle",
"durationMs": 13095.962999999983,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 13077.715,
"heapDeltaBytes": -45629776,
"heapUsedBytes": 181846288,
"domNodes": -9848,
"jsHeapTotalBytes": -9113600,
"scriptDurationMs": 634.3639999999999,
"eventListeners": -23957,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.219999999999953,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-pan",
"durationMs": 15812.74400000001,
"styleRecalcs": 81,
"styleRecalcDurationMs": 20.272000000000013,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 15790.467,
"heapDeltaBytes": -61336292,
"heapUsedBytes": 165164268,
"domNodes": -9850,
"jsHeapTotalBytes": -8851456,
"scriptDurationMs": 993.4690000000002,
"eventListeners": -23983,
"totalBlockingTimeMs": 7,
"frameDurationMs": 17.223333333333358,
"p95FrameDurationMs": 16.80000000000291
},
{
"name": "vue-large-graph-pan",
"durationMs": 14752.6509999999,
"styleRecalcs": 67,
"styleRecalcDurationMs": 18.666000000000015,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 14730.542,
"heapDeltaBytes": -41828500,
"heapUsedBytes": 176238100,
"domNodes": -9850,
"jsHeapTotalBytes": -12083200,
"scriptDurationMs": 913.0029999999999,
"eventListeners": -23981,
"totalBlockingTimeMs": 83,
"frameDurationMs": 17.223333333333358,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-pan",
"durationMs": 14887.180000000058,
"styleRecalcs": 68,
"styleRecalcDurationMs": 18.432000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 14863.256,
"heapDeltaBytes": -73693224,
"heapUsedBytes": 155131616,
"domNodes": -9850,
"jsHeapTotalBytes": -11472896,
"scriptDurationMs": 968.403,
"eventListeners": -23983,
"totalBlockingTimeMs": 100,
"frameDurationMs": 17.776666666666642,
"p95FrameDurationMs": 16.80000000000291
},
{
"name": "workflow-execution",
"durationMs": 473.6419999999839,
"styleRecalcs": 18,
"styleRecalcDurationMs": 28.951999999999998,
"layouts": 5,
"layoutDurationMs": 1.5770000000000002,
"taskDurationMs": 144.651,
"heapDeltaBytes": 5399592,
"heapUsedBytes": 55130788,
"domNodes": 167,
"jsHeapTotalBytes": 524288,
"scriptDurationMs": 31.758000000000003,
"eventListeners": 69,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.663333333333338,
"p95FrameDurationMs": 16.700000000000273
},
{
"name": "workflow-execution",
"durationMs": 459.1650000000982,
"styleRecalcs": 15,
"styleRecalcDurationMs": 22.945999999999998,
"layouts": 5,
"layoutDurationMs": 1.517,
"taskDurationMs": 125.84700000000001,
"heapDeltaBytes": 5009184,
"heapUsedBytes": 54897504,
"domNodes": 154,
"jsHeapTotalBytes": 262144,
"scriptDurationMs": 26.463,
"eventListeners": 69,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000273
},
{
"name": "workflow-execution",
"durationMs": 466.47700000005443,
"styleRecalcs": 21,
"styleRecalcDurationMs": 25.264000000000003,
"layouts": 4,
"layoutDurationMs": 1.1090000000000002,
"taskDurationMs": 122.446,
"heapDeltaBytes": 5114776,
"heapUsedBytes": 56145788,
"domNodes": 178,
"jsHeapTotalBytes": 524288,
"scriptDurationMs": 20.857999999999993,
"eventListeners": 69,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
}
]
} |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/i18n.test.ts`:
- Around line 2-3: After calling vi.resetModules(), re-import the i18n module to
get a fresh singleton instance (e.g. await import('./i18n')) and re-destructure
the bindings i18n, loadLocale, mergeCustomNodesI18n, and resolveSupportedLocale
so tests don't hold references to the old singleton; update every test block
that calls vi.resetModules() to perform this re-import and replace any top-level
references to the original imported symbols with the newly destructured ones.
In `@src/i18n.ts`:
- Around line 128-135: resolveSupportedLocale currently compares locale strings
with exact casing so inputs like "pt-br" or "zh-tw" miss matches; update
resolveSupportedLocale to perform case-insensitive matching: normalize the
incoming input and its base to lowercase (or otherwise canonicalize per BCP-47
case-insensitivity), then compare against supportedLocaleSet in a
case-insensitive way (either by lowercasing entries or by using a temporary map
from lowercased locale -> canonical SupportedLocale). Ensure the function still
returns the canonical SupportedLocale type (e.g., the original casing from
supportedLocaleSet) when a case-insensitive match on the full tag or the base
language is found, otherwise fall back to 'en'.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: e11de931-eacb-4be9-947a-50f59b33a396
📒 Files selected for processing (4)
src/i18n.test.tssrc/i18n.tssrc/platform/settings/constants/coreSettings.tssrc/views/GraphView.vue
Codecov Report❌ Patch coverage is
@@ Coverage Diff @@
## main #11712 +/- ##
===========================================
- Coverage 69.33% 54.55% -14.79%
===========================================
Files 1483 1374 -109
Lines 83511 70267 -13244
Branches 22069 18677 -3392
===========================================
- Hits 57904 38334 -19570
- Misses 24663 31387 +6724
+ Partials 944 546 -398
Flags with carried forward coverage won't be shown. Click here to find out more.
... and 1015 files with indirect coverage changes 🚀 New features to boost your workflow:
|
Address CodeRabbit feedback on PR #11712. BCP-47 tags are case-insensitive, so older browsers emitting 'pt-br' or 'zh-tw' should resolve to the shipped 'pt-BR'/'zh-TW' bundles instead of falling through to 'en'.
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
src/i18n.test.ts (1)
2-3:⚠️ Potential issue | 🟠 MajorRe-import
./i18naftervi.resetModules().The top-level bindings still point at the pre-reset singleton, so the suite can leak locale state across tests. The one local re-import at lines 149-152 only isolates that single case.
Suggested fix
import { beforeEach, describe, expect, it, vi } from 'vitest' -const { i18n, loadLocale, mergeCustomNodesI18n, resolveSupportedLocale } = - await import('./i18n') + +let i18n: typeof import('./i18n')['i18n'] +let loadLocale: typeof import('./i18n')['loadLocale'] +let mergeCustomNodesI18n: typeof import('./i18n')['mergeCustomNodesI18n'] +let resolveSupportedLocale: typeof import('./i18n')['resolveSupportedLocale'] // Mock the JSON imports before importing i18n module vi.mock('./locales/en/main.json', () => ({ default: { welcome: 'Welcome' } })) @@ describe('i18n', () => { beforeEach(async () => { vi.resetModules() + ;({ + i18n, + loadLocale, + mergeCustomNodesI18n, + resolveSupportedLocale + } = await import('./i18n')) })Also applies to: 26-28
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/i18n.test.ts` around lines 2 - 3, The test file currently imports i18n, loadLocale, mergeCustomNodesI18n, and resolveSupportedLocale at module scope which holds the pre-reset singleton and leaks locale state; after calling vi.resetModules() you must re-import those symbols so each test gets a fresh instance—move or add an import statement (await import('./i18n')) immediately after every vi.resetModules() call (including the setup around the tests that previously re-imported only locally) and replace top-level bindings with re-assigned locals from that post-reset import so i18n, loadLocale, mergeCustomNodesI18n, and resolveSupportedLocale reference the fresh module instance.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/i18n.test.ts`:
- Around line 183-199: The test should call loadLocale rather than only
resolveSupportedLocale to ensure the full loading path updates the active
locale/messages; update the third test to await loadLocale('pt-BR') and assert
it resolves to 'pt-BR' and that the module's active locale/messages state
reflect the shipped variant, then await loadLocale('pt') and assert it resolves
to 'en' (matching resolveSupportedLocale('pt') behavior). Use the existing
symbols loadLocale and resolveSupportedLocale to locate the code under test and
confirm the loaded locale and the module's exported active locale/messages are
updated after each load.
---
Duplicate comments:
In `@src/i18n.test.ts`:
- Around line 2-3: The test file currently imports i18n, loadLocale,
mergeCustomNodesI18n, and resolveSupportedLocale at module scope which holds the
pre-reset singleton and leaks locale state; after calling vi.resetModules() you
must re-import those symbols so each test gets a fresh instance—move or add an
import statement (await import('./i18n')) immediately after every
vi.resetModules() call (including the setup around the tests that previously
re-imported only locally) and replace top-level bindings with re-assigned locals
from that post-reset import so i18n, loadLocale, mergeCustomNodesI18n, and
resolveSupportedLocale reference the fresh module instance.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 7c220ef0-5b3b-47ac-9cce-7505668f50a6
📒 Files selected for processing (2)
src/i18n.test.tssrc/i18n.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- src/i18n.ts
The fallback test set Comfy.Locale='de' and expected an index.de.json request. With the locale-clamping fix on this branch, unshipped tags get resolved to 'en' before any template fetch, so that path no longer fires. Switch to 'fa' (a shipped locale) and mock its index.fa.json to 404 — exercises the same fetcher-fallback code path. Drop the English 'All Templates' header assertion (the dialog is now translated to fa); assert on a language-neutral testid instead.
There was a problem hiding this comment.
🧹 Nitpick comments (2)
browser_tests/tests/templates.spec.ts (2)
134-137: Optional cleanup: encode intent in names instead of new block comments.You can reduce comment drift by replacing these explanations with self-describing identifiers (e.g.,
simulatedMissingShippedLocale,fallbackContentTestId).As per coding guidelines: "Avoid new usage of code comments; do not add or retain redundant comments."
Also applies to: 172-174
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@browser_tests/tests/templates.spec.ts` around lines 134 - 137, Replace the explanatory block comments by introducing self-descriptive identifiers: create a constant like simulatedMissingShippedLocale (set to 'fa') and use that constant instead of the variable locale in the test, and similarly replace the comment near lines 172-174 with a named test id (e.g., fallbackContentTestId) or descriptive helper function; update usages in the test cases (references to locale and any fallback assertions) to use these new identifiers so the intent is encoded in names rather than comments.
141-143: Assert fallback behavior via response statuses (not only request URLs).This currently proves both URLs were requested, but not that locale failed and English fallback succeeded. Switching to response-level checks makes the regression guard tighter.
Proposed tightening
- const localeRequestPromise = comfyPage.page.waitForRequest( - `**/templates/index.${locale}.json` - ) - const englishRequestPromise = comfyPage.page.waitForRequest( - '**/templates/index.json' - ) + const localeResponsePromise = comfyPage.page.waitForResponse( + (response) => + response.url().includes(`templates/index.${locale}.json`) && + response.status() === 404 + ) + const englishResponsePromise = comfyPage.page.waitForResponse( + (response) => + response.url().includes('templates/index.json') && + response.status() === 200 + ) @@ - const localeRequest = await localeRequestPromise - const englishRequest = await englishRequestPromise + const localeResponse = await localeResponsePromise + const englishResponse = await englishResponsePromise - expect(localeRequest.url()).toContain(`templates/index.${locale}.json`) - expect(englishRequest.url()).toContain('templates/index.json') + expect(localeResponse.url()).toContain(`templates/index.${locale}.json`) + expect(englishResponse.url()).toContain('templates/index.json')Also applies to: 166-170
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@browser_tests/tests/templates.spec.ts` around lines 141 - 143, The test currently waits for requests via comfyPage.page.waitForRequest using the localeRequestPromise variables; change these to wait for responses (use comfyPage.page.waitForResponse) so you can assert response.status() as well as the URL — assert that the locale response returns a failure status (e.g., !== 200 or specific 404) and that the English fallback response returns 200 to prove fallback succeeded; apply the same change to the second occurrence around the block currently at lines 166-170 so both locale and fallback checks validate response statuses and URLs.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@browser_tests/tests/templates.spec.ts`:
- Around line 134-137: Replace the explanatory block comments by introducing
self-descriptive identifiers: create a constant like
simulatedMissingShippedLocale (set to 'fa') and use that constant instead of the
variable locale in the test, and similarly replace the comment near lines
172-174 with a named test id (e.g., fallbackContentTestId) or descriptive helper
function; update usages in the test cases (references to locale and any fallback
assertions) to use these new identifiers so the intent is encoded in names
rather than comments.
- Around line 141-143: The test currently waits for requests via
comfyPage.page.waitForRequest using the localeRequestPromise variables; change
these to wait for responses (use comfyPage.page.waitForResponse) so you can
assert response.status() as well as the URL — assert that the locale response
returns a failure status (e.g., !== 200 or specific 404) and that the English
fallback response returns 200 to prove fallback succeeded; apply the same change
to the second occurrence around the block currently at lines 166-170 so both
locale and fallback checks validate response statuses and URLs.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: eb104685-9dcb-41eb-af46-2126cece81a7
📒 Files selected for processing (1)
browser_tests/tests/templates.spec.ts
| 'zh', | ||
| 'zh-TW', | ||
| 'pt-BR' | ||
| ] as const |
There was a problem hiding this comment.
Avoid adding a 5th hand-rolled locale list, consolidate to one source of truth instead.
This file already maintains the same set of locales in four separate places: localeLoaders, nodeDefsLoaders, commandsLoaders, and settingsLoaders. Adding SUPPORTED_LOCALES makes it five (and Comfy.Locale.options in coreSettings.ts is a sixth). Each new language now requires touching six locations, and any one of them being out of sync produces a silent bug — e.g. a locale present in the dropdown but missing a loader, or shipped messages that resolveSupportedLocale refuses to recognize.
We should treat this as a chance to unify rather than to duplicate.
There was a problem hiding this comment.
Thanks, agreed. I consolidated the locale source of truth here.
The main fix landed in d6f1b9c: src/locales/localeConfig.ts now owns the supported locale keys, display labels, and lazy loaders. coreSettings.ts derives the dropdown options from SUPPORTED_LOCALE_OPTIONS, and i18n.ts resolves/loads locales from the same localeDefinitions map, so adding a locale no longer requires keeping the previous loader maps and settings options in sync manually.
I also pushed e70382649 to update the locale contributor troubleshooting note to point at the new shared config file.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/locales/CONTRIBUTING.md (1)
143-146:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winTroubleshooting step still points to outdated config file.
After centralizing locale wiring in
src/locales/localeConfig.ts, the “Language not appearing in dropdown” guidance at Line 145 still tells contributors to checkcoreSettings.ts. Please update this reference tosrc/locales/localeConfig.tsto avoid misconfiguration during new locale additions.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/locales/CONTRIBUTING.md` around lines 143 - 146, Update the troubleshooting text that points contributors to the wrong config file: replace the reference to coreSettings.ts with the centralized locale wiring file src/locales/localeConfig.ts in the CONTRIBUTING.md “Language not appearing in dropdown” section so new locale additions check the correct file (ensure the exact filename and path src/locales/localeConfig.ts are used in that guidance).
🧹 Nitpick comments (1)
src/i18n.test.ts (1)
187-227: ⚡ Quick winGood unit coverage—please add/land the E2E regression in
browser_tests/(or document why not feasible).Given this is a bug-fix PR, please ensure the regression spec is committed under
browser_tests/(not onlytemp/scripts/...) or add a concrete PR note explaining why E2E is not practical here.Based on learnings, bug-fix PRs should include
browser_tests/changes or a concrete non-placeholder explanation of why an end-to-end regression test is not practical.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/i18n.test.ts` around lines 187 - 227, The PR is missing an E2E regression under browser_tests for the i18n loadLocale behaviors exercised in src/i18n.test.ts; add a browser_tests spec that reproduces the key scenarios (reloading same locale, unsupported locale clamped to "en", preservation of shipped BCP-47 tags like "zh-TW" and "pt-BR", and concurrent loadLocale('zh') calls) or, if an E2E test truly cannot be implemented, add a concrete PR note under browser_tests/ explaining why (environmental constraints, flakiness, or test infra gaps) and link to the unit tests (loadLocale) verifying the fix; ensure the test file references loadLocale and asserts resolved locale values and message availability for "pt-BR" as in the unit tests.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@src/locales/CONTRIBUTING.md`:
- Around line 143-146: Update the troubleshooting text that points contributors
to the wrong config file: replace the reference to coreSettings.ts with the
centralized locale wiring file src/locales/localeConfig.ts in the
CONTRIBUTING.md “Language not appearing in dropdown” section so new locale
additions check the correct file (ensure the exact filename and path
src/locales/localeConfig.ts are used in that guidance).
---
Nitpick comments:
In `@src/i18n.test.ts`:
- Around line 187-227: The PR is missing an E2E regression under browser_tests
for the i18n loadLocale behaviors exercised in src/i18n.test.ts; add a
browser_tests spec that reproduces the key scenarios (reloading same locale,
unsupported locale clamped to "en", preservation of shipped BCP-47 tags like
"zh-TW" and "pt-BR", and concurrent loadLocale('zh') calls) or, if an E2E test
truly cannot be implemented, add a concrete PR note under browser_tests/
explaining why (environmental constraints, flakiness, or test infra gaps) and
link to the unit tests (loadLocale) verifying the fix; ensure the test file
references loadLocale and asserts resolved locale values and message
availability for "pt-BR" as in the unit tests.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 4a0822a6-d229-4f32-b881-d5bd7627b043
📒 Files selected for processing (5)
src/i18n.test.tssrc/i18n.tssrc/locales/CONTRIBUTING.mdsrc/locales/localeConfig.tssrc/platform/settings/constants/coreSettings.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- src/platform/settings/constants/coreSettings.ts
|
Follow-up on the latest review notes:
|
jtydhr88
left a comment
There was a problem hiding this comment.
Code looks good to me, also did some local testing, works fine. Since this refactors core i18n behavior, I'd like to bring in a second reviewer.
One thing worth verifying before merge: please confirm this doesn't break the custom node i18n flow. Could you load one or two custom nodes that ship locale bundles and confirm their translated strings still render correctly?
Want to make sure the mergeCustomNodesI18n path still works with the new resolution flow
Summary
Sidebar buttons rendered literal i18n keys (e.g.
sideToolbar.labels.assets) on a fresh install when the user'snavigator.languagebase tag wasn't one of the 12 shipped locales — German/Italian/Polish/Dutch/Brazilian-Portuguese users among others.Changes
resolveSupportedLocale()that tries the full BCP-47 tag first (preserveszh-TW,pt-BR), then the base tag, then'en'. Wire through both entry points (createI18n's initial locale,Comfy.Locale'sdefaultValue) and clamp insideloadLocale, propagating the resolved tag toGraphViewso a stale storedComfy.Locale='de'from older builds also recovers.pt-BR→pt(unshipped) → broken. The full-tag-first lookup now correctly lands them on thept-BRbundle.Root Cause
Three-link chain:
Comfy.Locale's default was() => navigator.language.split('-')[0] || 'en'. German →'de'(unshipped).loadLocale('de')silentlyconsole.warn'd and returned without throwing.GraphViewthen rani18n.global.locale.value = 'de'anyway.st(key, fallback) = te(key) ? t(key) : fallback. vue-i18n'ste()checks only the current locale and ignoresfallbackLocale— every key missed →st()returned the literal key string.Two pathways reached the broken state (defaultValue path, and unset-setting path through
createI18n's ownnavigator.languagesnapshot); the new helper closes both.Review Focus
loadLocalenow returnsSupportedLocale(wasvoid). Oldvoidcallers continue to compile; the only change isGraphViewconsuming the return value.src/i18n.test.ts(addedresolveSupportedLocaleblock + updated theloadLocaleunsupported-locale case from "warn" to "clamp to en").navigator.language='de-DE'+ fresh-install state on bothmain(shows the bug) and this branch (shows the fix). Spec saved attemp/scripts/issue-10563-locale-bug.spec.ts.Fixes #10563
FE-480 — https://linear.app/comfyorg/issue/FE-480
Screenshots
Before (from #10563, on
main):After (this branch, same
navigator.language='de-DE'):┆Issue is synchronized with this Notion page by Unito