Skip to content

Commit b0ebd3d

Browse files
committed
fix(dashboard): activate fallback locale before extensions
Dashboard extensions can evaluate translation calls before the asynchronous default catalog finishes loading. Activate an empty source-locale catalog synchronously so extension imports and compiled catalog loading remain parallel without triggering Lingui's missing-locale error.
1 parent db8482a commit b0ebd3d

2 files changed

Lines changed: 71 additions & 0 deletions

File tree

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)