Skip to content

Commit aaa2eb0

Browse files
fix(dashboard): Activate fallback locale before extensions (#5061)
Co-authored-by: vendure-ci-automation-bot[bot] <221402278+vendure-ci-automation-bot[bot]@users.noreply.github.com>
1 parent db8482a commit aaa2eb0

4 files changed

Lines changed: 85 additions & 3 deletions

File tree

docs/docs/reference/dashboard/vite-plugin/vendure-dashboard-plugin.mdx

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ generated: true
44
---
55
## vendureDashboardPlugin
66

7-
<GenerationInfo sourceFile="packages/dashboard/vite/vite-plugin-vendure-dashboard.ts" sourceLine="242" packageName="@vendure/dashboard" since="3.4.0" />
7+
<GenerationInfo sourceFile="packages/dashboard/vite/vite-plugin-vendure-dashboard.ts" sourceLine="254" packageName="@vendure/dashboard" since="3.4.0" />
88

99
This is the Vite plugin which powers the Vendure Dashboard, including:
1010

@@ -23,7 +23,7 @@ Parameters
2323

2424
## VitePluginVendureDashboardOptions
2525

26-
<GenerationInfo sourceFile="packages/dashboard/vite/vite-plugin-vendure-dashboard.ts" sourceLine="39" packageName="@vendure/dashboard" since="3.4.0" />
26+
<GenerationInfo sourceFile="packages/dashboard/vite/vite-plugin-vendure-dashboard.ts" sourceLine="40" packageName="@vendure/dashboard" since="3.4.0" />
2727

2828
Options for the [vendureDashboardPlugin](/reference/dashboard/vite-plugin/vendure-dashboard-plugin#venduredashboardplugin) Vite plugin.
2929

@@ -85,6 +85,17 @@ type VitePluginVendureDashboardOptions = {
8585
* The path to the directory where the generated GraphQL Tada files will be output.
8686
*/
8787
gqlOutputPath?: string;
88+
/**
89+
* @description
90+
* The directory into which the VendureConfig is transpiled and loaded in order
91+
* to introspect the configuration during the dashboard build.
92+
*
93+
* Defaults to `<project>/node_modules/.cache/vendure-dashboard-temp`. It must
94+
* not be located inside a `"type": "module"` package (such as
95+
* `@vendure/dashboard` itself), because the config is compiled to CommonJS and
96+
* Node would then load it as ESM, failing with
97+
* `exports is not defined in ES module scope`.
98+
*/
8899
tempCompilationDir?: string;
89100
/**
90101
* @description

docs/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4271,7 +4271,7 @@
42714271
"title": "VendureDashboardPlugin",
42724272
"slug": "vendure-dashboard-plugin",
42734273
"file": "docs/reference/dashboard/vite-plugin/vendure-dashboard-plugin.mdx",
4274-
"lastModified": "2026-07-10T14:29:40+02:00"
4274+
"lastModified": "2026-07-30T07:46:11Z"
42754275
}
42764276
],
42774277
"lastModified": "2026-01-28T14:49:21+01:00"
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { i18n } from '@lingui/core';
2+
import { act } from 'react';
3+
import { createRoot } from 'react-dom/client';
4+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
5+
6+
import { defaultLocale } from '../../providers/i18n-provider.js';
7+
import { useDashboardExtensions } from './use-dashboard-extensions.js';
8+
9+
const runDashboardExtensions = vi.hoisted(() => vi.fn());
10+
11+
(globalThis as typeof globalThis & { IS_REACT_ACT_ENVIRONMENT: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
12+
13+
vi.mock('virtual:dashboard-extensions', () => ({
14+
runDashboardExtensions,
15+
}));
16+
17+
vi.mock('./define-dashboard-extension.js', () => ({
18+
onExtensionSourceChange: vi.fn(),
19+
}));
20+
21+
describe('useDashboardExtensions', () => {
22+
let container: HTMLDivElement;
23+
let root: ReturnType<typeof createRoot>;
24+
25+
beforeEach(() => {
26+
container = document.createElement('div');
27+
document.body.appendChild(container);
28+
root = createRoot(container);
29+
});
30+
31+
afterEach(async () => {
32+
await act(async () => {
33+
root.unmount();
34+
});
35+
container.remove();
36+
vi.clearAllMocks();
37+
});
38+
39+
it('can evaluate translations before the full locale catalog has loaded', async () => {
40+
let extensionError: unknown;
41+
42+
runDashboardExtensions.mockImplementation(async () => {
43+
try {
44+
const extensionMessageId = ['Extension', 'label'].join(' ');
45+
i18n._(extensionMessageId);
46+
} catch (error) {
47+
extensionError = error;
48+
}
49+
});
50+
51+
function DashboardBootstrap() {
52+
useDashboardExtensions();
53+
return null;
54+
}
55+
56+
await act(async () => {
57+
root.render(<DashboardBootstrap />);
58+
});
59+
60+
expect(runDashboardExtensions).toHaveBeenCalledOnce();
61+
expect(i18n.locale).toBe(defaultLocale);
62+
expect(extensionError).toBeUndefined();
63+
});
64+
});

packages/dashboard/src/lib/providers/i18n-provider.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@ import React from 'react';
55

66
export const defaultLocale = 'en';
77

8+
// Dashboard extensions are evaluated asynchronously during app bootstrap and
9+
// may translate source-locale strings at module scope. Activate an empty source
10+
// catalog synchronously so those translations are safe while the compiled
11+
// dashboard and plugin catalogs load in parallel.
12+
i18n.load(defaultLocale, {});
13+
i18n.activate(defaultLocale);
14+
815
/**
916
* We do a dynamic import of just the catalog that we need
1017
* @param locale any locale string

0 commit comments

Comments
 (0)