Compare commits

...

4 Commits

Author SHA1 Message Date
Elizabeth Thompson
42379a8253 Delete .github/pr-screenshots/menu-translated.png 2026-06-12 09:51:51 -07:00
Elizabeth Thompson
d62a1771e9 Delete .github/pr-screenshots/embedded-translated.png 2026-06-12 09:51:26 -07:00
Elizabeth Thompson
ad1c976305 chore: add E2E verification screenshots for i18n race condition fix
Screenshots from Playwright tests verifying the fix in menu.tsx and
embedded/index.tsx. Both captured with a 2-second artificial delay on
the language pack endpoint; all UI strings appear in German.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 00:24:09 +00:00
Elizabeth Thompson
efeb981626 fix(i18n): defer plugin init and menu render until language pack is ready
In embedded/index.tsx, setupPlugins() was called at module top level,
causing it to run synchronously before initPreamble()'s async language
pack fetch could resolve. All chart plugin control panel t() calls were
therefore cached in English. This fix chains plugin setup off the
initPreamble() promise so the language pack is always configured first.

In menu.tsx (used for backend-rendered Flask views), createRoot().render()
was called synchronously, allowing React to render Menu components before
the language pack resolved. This fix defers render until initPreamble()
settles.

The spa entry point (views/index.tsx) already handled this correctly via
a dynamic import after awaiting initPreamble().

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 00:24:09 +00:00
2 changed files with 48 additions and 29 deletions

View File

@@ -32,6 +32,7 @@ import {
} from '@apache-superset/core/theme';
import Switchboard from '@superset-ui/switchboard';
import getBootstrapData, { applicationRoot } from 'src/utils/getBootstrapData';
import initPreamble from 'src/preamble';
import setupClient from 'src/setup/setupClient';
import setupPlugins from 'src/setup/setupPlugins';
import { useUiConfig } from 'src/components/UiConfigContext';
@@ -50,8 +51,19 @@ import { embeddedApi } from './api';
import { getDataMaskChangeTrigger } from './utils';
import { validateMessageEvent } from './originValidation';
setupPlugins();
setupCodeOverrides({ embedded: true });
// Defer plugin setup until after the language pack loads to prevent t() calls in
// plugin control panel configs from being cached in English before translations are ready.
const pluginsReady = initPreamble()
.catch(err => {
logging.warn(
'Preamble initialization failed, loading plugins without translations.',
err,
);
})
.then(() => {
setupPlugins();
setupCodeOverrides({ embedded: true });
});
const debugMode = process.env.WEBPACK_MODE === 'development';
const bootstrapData = getBootstrapData();
@@ -172,32 +184,34 @@ function start() {
method: 'GET',
endpoint: '/api/v1/me/roles/',
});
return getMeWithRole().then(
({ result }) => {
// fill in some missing bootstrap data
// (because at pageload, we don't have any auth yet)
// this allows the frontend's permissions checks to work.
bootstrapData.user = result;
store.dispatch({
type: USER_LOADED,
user: result,
});
if (!root) {
root = createRoot(appMountPoint);
}
root.render(<EmbeddedApp />);
},
err => {
// something is most likely wrong with the guest token; reset the guard
// so a rehandshake with a valid token can retry.
logging.error(err);
showFailureMessage(
t(
'Something went wrong with embedded authentication. Check the dev console for details.',
),
);
started = false;
},
return pluginsReady.then(() =>
getMeWithRole().then(
({ result }) => {
// fill in some missing bootstrap data
// (because at pageload, we don't have any auth yet)
// this allows the frontend's permissions checks to work.
bootstrapData.user = result;
store.dispatch({
type: USER_LOADED,
user: result,
});
if (!root) {
root = createRoot(appMountPoint);
}
root.render(<EmbeddedApp />);
},
err => {
// something is most likely wrong with the guest token; reset the guard
// so a rehandshake with a valid token can retry.
logging.error(err);
showFailureMessage(
t(
'Something went wrong with embedded authentication. Check the dev console for details.',
),
);
started = false;
},
),
);
}

View File

@@ -31,6 +31,7 @@ import { ThemeProvider } from '@apache-superset/core/theme';
import { theme } from '@apache-superset/core/theme';
import Menu from 'src/features/home/Menu';
import getBootstrapData from 'src/utils/getBootstrapData';
import initPreamble from 'src/preamble';
import { setupStore } from './store';
import querystring from 'query-string';
@@ -67,5 +68,9 @@ const app = (
const menuMountPoint = document.getElementById('app-menu');
if (menuMountPoint) {
createRoot(menuMountPoint).render(app);
initPreamble()
.catch(() => {}) // preamble logs failures internally; always render the menu
.then(() => {
createRoot(menuMountPoint).render(app);
});
}