This is an automated email from the ASF dual-hosted git repository.
lizhimins pushed a commit to branch rocketmq-studio
in repository https://gitbox.apache.org/repos/asf/rocketmq-dashboard.git
The following commit(s) were added to refs/heads/rocketmq-studio by this push:
new f3252ab16 fix(web): localize the remaining hardcoded strings in the
main layout (#4806)
f3252ab16 is described below
commit f3252ab16f7dd7bcc342f8c173631af43c2ca093
Author: Wang1rrr <[email protected]>
AuthorDate: Thu Sep 24 18:17:44 2026 +0800
fix(web): localize the remaining hardcoded strings in the main layout
(#4806)
fix(web): localize the remaining hardcoded strings in the main layout
MainLayout pulls every navigation and command label through the language
context but left
seven strings as literals, so an English console mixed languages:
- the warning shown when the server-side logout call fails
- the /studio/users breadcrumb entry and the matching admin user-menu item
- the "no matching pages" description in the navigation search palette
- the three keyboard hints under that palette: navigate, open and close
Two of the seven already had dictionary entries carrying the identical
Chinese value, so
they are pointed at the existing userMgmt.title and common.close rather
than given
duplicate keys. Of the rest, "open" joins the existing common.close as
common.open, and
the two palette strings become layout.noMatchingPage and
layout.shortcutNavigate beside
the other layout.* entries. Only user.logoutFailed is genuinely new copy.
The language toggle that renders "En" / the Chinese glyph is deliberately
untouched:
showing the name of the language being switched to is the point of that
control.
Adds a case that puts the console in English, fails the logout call and
asserts the toast
arrives as the English dictionary value instead of a literal.
---
web/src/i18n/translations.ts | 7 +++++++
web/src/layouts/MainLayout.test.tsx | 29 +++++++++++++++++++++++++++++
web/src/layouts/MainLayout.tsx | 16 +++++++++-------
3 files changed, 45 insertions(+), 7 deletions(-)
diff --git a/web/src/i18n/translations.ts b/web/src/i18n/translations.ts
index bb0261ad1..057924b79 100644
--- a/web/src/i18n/translations.ts
+++ b/web/src/i18n/translations.ts
@@ -68,6 +68,7 @@ const translations: Record<string, Record<Lang, string>> = {
'common.export': { zh: '导出', en: 'Export' },
'common.detail': { zh: '详情', en: 'Detail' },
'common.close': { zh: '关闭', en: 'Close' },
+ 'common.open': { zh: '打开', en: 'Open' },
'common.noData': { zh: '暂无数据', en: 'No Data' },
'common.autoRefresh': { zh: '自动刷新', en: 'Auto Refresh' },
'common.liveRefresh': { zh: '实时刷新', en: 'Live Refresh' },
@@ -79,6 +80,8 @@ const translations: Record<string, Record<Lang, string>> = {
// ─── Global layout controls ───
'layout.skipToMain': { zh: '跳到主要内容', en: 'Skip to main content' },
+ 'layout.noMatchingPage': { zh: '未找到匹配页面', en: 'No matching pages' },
+ 'layout.shortcutNavigate': { zh: '切换', en: 'Navigate' },
'layout.goHome': { zh: '返回首页', en: 'Go to home' },
'layout.openSearch': { zh: '打开导航搜索', en: 'Open navigation search' },
'layout.switchToRealData': { zh: '切换到真实数据', en: 'Switch to real data' },
@@ -2745,6 +2748,10 @@ const translations: Record<string, Record<Lang, string>>
= {
// ─── User Menu ───
'user.profile': { zh: '个人中心', en: 'Profile' },
'user.logout': { zh: '退出登录', en: 'Logout' },
+ 'user.logoutFailed': {
+ zh: '服务端退出失败,已清除本地登录状态',
+ en: 'Signing out on the server failed. The local session has been
cleared.',
+ },
// ─── User Management ───
'userMgmt.title': { zh: '用户管理', en: 'User Management' },
diff --git a/web/src/layouts/MainLayout.test.tsx
b/web/src/layouts/MainLayout.test.tsx
index 71f42ca3b..8cfb846d9 100644
--- a/web/src/layouts/MainLayout.test.tsx
+++ b/web/src/layouts/MainLayout.test.tsx
@@ -16,10 +16,12 @@
*/
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
+import { message } from 'antd';
import { MemoryRouter, Route, Routes } from 'react-router-dom';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { logout } from '../api/auth';
import { LangProvider } from '../i18n/LangContext';
+import translations from '../i18n/translations';
import useAuthStore from '../stores/authStore';
import { ThemeProvider } from '../theme/ThemeProvider';
import MainLayout from './MainLayout';
@@ -135,6 +137,33 @@ describe('MainLayout authentication navigation', () => {
expect(localStorage.getItem('token')).toBeNull();
});
+ it('reports a failed server logout in the language the console is in', async
() => {
+ localStorage.setItem('rocketmq-studio-language', 'en');
+ vi.mocked(message.warning).mockClear();
+ vi.mocked(logout).mockRejectedValueOnce(new Error('server gone'));
+ render(
+ <LangProvider>
+ <ThemeProvider>
+ <MemoryRouter initialEntries={['/instance/topic']}>
+ <Routes>
+ <Route path="/login" element={<div>login page</div>} />
+ <Route path="/" element={<MainLayout />}>
+ <Route path="instance/topic" element={<div>protected
topic</div>} />
+ </Route>
+ </Routes>
+ </MemoryRouter>
+ </ThemeProvider>
+ </LangProvider>,
+ );
+
+ fireEvent.click(screen.getByRole('button', { name:
translations['user.logout'].en }));
+ await waitFor(() => expect(screen.getByText('login
page')).toBeInTheDocument());
+
+ // The toast has to come out of the dictionary in the active language: it
used to be a
+ // literal, so an English console reported the failed server logout in
Chinese.
+
expect(vi.mocked(message.warning)).toHaveBeenCalledWith(translations['user.logoutFailed'].en);
+ });
+
it('exposes global layout commands as localized semantic buttons', () => {
render(
<LangProvider>
diff --git a/web/src/layouts/MainLayout.tsx b/web/src/layouts/MainLayout.tsx
index 0a183d25e..1e6bbd75a 100644
--- a/web/src/layouts/MainLayout.tsx
+++ b/web/src/layouts/MainLayout.tsx
@@ -109,7 +109,7 @@ const MainLayout = () => {
try {
await requestLogout();
} catch {
- message.warning('服务端退出失败,已清除本地登录状态');
+ message.warning(t('user.logoutFailed'));
} finally {
clearAuth();
navigate('/login', { replace: true });
@@ -271,7 +271,7 @@ const MainLayout = () => {
'/ops/audit': t('nav.audit'),
'/ai': t('nav.ai'),
'/settings': t('nav.settings'),
- '/studio/users': '用户管理',
+ '/studio/users': t('userMgmt.title'),
}),
[t],
);
@@ -331,7 +331,9 @@ const MainLayout = () => {
onClick: handleUserMenuClick,
items: [
{ key: 'profile', icon: <UserGear size={14} />, label: t('user.profile')
},
- ...(admin ? [{ key: 'users', icon: <UserGear size={14} />, label: '用户管理'
}] : []),
+ ...(admin
+ ? [{ key: 'users', icon: <UserGear size={14} />, label:
t('userMgmt.title') }]
+ : []),
{
key: 'dataMode',
icon: (
@@ -796,7 +798,7 @@ const MainLayout = () => {
) : (
<Empty
image={Empty.PRESENTED_IMAGE_SIMPLE}
- description="未找到匹配页面"
+ description={t('layout.noMatchingPage')}
style={{ padding: '24px 0' }}
/>
)}
@@ -816,15 +818,15 @@ const MainLayout = () => {
<span style={{ display: 'inline-flex', alignItems: 'center', gap: 6
}}>
<kbd style={kbdStyle}>↑</kbd>
<kbd style={kbdStyle}>↓</kbd>
- 切换
+ {t('layout.shortcutNavigate')}
</span>
<span style={{ display: 'inline-flex', alignItems: 'center', gap: 6
}}>
<kbd style={kbdStyle}>↵</kbd>
- 打开
+ {t('common.open')}
</span>
<span style={{ display: 'inline-flex', alignItems: 'center', gap: 6
}}>
<kbd style={kbdStyle}>ESC</kbd>
- 关闭
+ {t('common.close')}
</span>
</div>
</Modal>