Skip to content

Commit c379043

Browse files
committed
fix(i18n): relocalize opened tab titles
1 parent 6367e70 commit c379043

6 files changed

Lines changed: 106 additions & 7 deletions

File tree

apps/lina-vben/packages/@core/ui-kit/tabs-ui/src/components/tabs-chrome/tabs.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type { TabDefinition } from '@vben-core/typings';
33
44
import type { TabConfig, TabsProps } from '../../types';
55
6-
import { computed, ref } from 'vue';
6+
import { computed, ref, unref } from 'vue';
77
88
import { Pin, X } from '@vben-core/icons';
99
import { VbenContextMenu, VbenIcon } from '@vben-core/shadcn-ui';
@@ -53,7 +53,7 @@ const tabsView = computed(() => {
5353
meta,
5454
name,
5555
path,
56-
title: (newTabTitle || title || name) as string,
56+
title: String(unref(newTabTitle) || title || name || ''),
5757
} as TabConfig;
5858
});
5959
});

apps/lina-vben/packages/@core/ui-kit/tabs-ui/src/components/tabs/tabs.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type { TabDefinition } from '@vben-core/typings';
33
44
import type { TabConfig, TabsProps } from '../../types';
55
6-
import { computed } from 'vue';
6+
import { computed, unref } from 'vue';
77
88
import { Pin, X } from '@vben-core/icons';
99
import { VbenContextMenu, VbenIcon } from '@vben-core/shadcn-ui';
@@ -58,7 +58,7 @@ const tabsView = computed(() => {
5858
meta,
5959
name,
6060
path,
61-
title: (newTabTitle || title || name) as string,
61+
title: String(unref(newTabTitle) || title || name || ''),
6262
} as TabConfig;
6363
});
6464
});

apps/lina-vben/packages/effects/layouts/src/basic/tabbar/use-tabbar.ts

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import type { TabDefinition } from '@vben/types';
44

55
import type { IContextMenuItem } from '@vben-core/tabs-ui';
66

7-
import { computed, ref, watch } from 'vue';
7+
import { computed, ref, unref, watch } from 'vue';
88
import { useRoute, useRouter } from 'vue-router';
99

1010
import { useContentMaximize, useTabs } from '@vben/hooks';
@@ -21,7 +21,7 @@ import {
2121
RotateCw,
2222
X,
2323
} from '@vben/icons';
24-
import { $t, useI18n } from '@vben/locales';
24+
import { $t, $te, useI18n } from '@vben/locales';
2525
import { getTabKey, useAccessStore, useTabbarStore } from '@vben/stores';
2626
import { filterTree } from '@vben/utils';
2727

@@ -86,15 +86,45 @@ export function useTabbar() {
8686
};
8787

8888
function wrapperTabLocale(tab: RouteLocationNormalizedGeneric) {
89+
const title = resolveTabTitle(tab);
90+
const newTabTitle = tab?.meta?.newTabTitle
91+
? computed(() => translateIfExists(tab?.meta?.newTabTitle))
92+
: undefined;
8993
return {
9094
...tab,
9195
meta: {
9296
...tab?.meta,
93-
title: $t(tab?.meta?.title as string),
97+
...(newTabTitle ? { newTabTitle } : {}),
98+
title,
9499
},
95100
};
96101
}
97102

103+
function translateIfExists(title: unknown) {
104+
const rawTitle = String(unref(title) ?? '');
105+
const titleKey = rawTitle.trim();
106+
if (!titleKey) {
107+
return '';
108+
}
109+
return $te(titleKey) ? $t(titleKey) : rawTitle;
110+
}
111+
112+
function resolveTabTitle(tab: RouteLocationNormalizedGeneric) {
113+
const meta = tab?.meta;
114+
115+
const i18nKey = String(meta?.i18nKey || '').trim();
116+
if (i18nKey && $te(i18nKey)) {
117+
return $t(i18nKey);
118+
}
119+
120+
const title = translateIfExists(meta?.title);
121+
if (title) {
122+
return title;
123+
}
124+
125+
return String(tab?.name || '');
126+
}
127+
98128
watch(
99129
() => accessStore.accessMenus,
100130
() => {

hack/tests/e2e/dialect/TC0165-sqlite-mode-business-zero-impact.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,17 @@ type ServerMonitorResult = {
3030
};
3131
};
3232

33+
const sqliteMonitorPluginId = "monitor-server";
34+
3335
test.describe("TC-165 SQLite mode business zero impact", () => {
3436
requireSQLiteE2E();
3537

3638
test("TC-165a~d: user CRUD, execution log list, source plugin lifecycle, and monitor database version work on SQLite", async () => {
3739
const api = await createAdminApiContext();
3840
const username = `sqlite_e2e_${Date.now()}`;
3941
let createdUserId = 0;
42+
let originalMonitorInstalled = 0;
43+
let originalMonitorEnabled = 0;
4044

4145
try {
4246
const created = await expectApiSuccess<UserCreateResult>(
@@ -105,6 +109,21 @@ test.describe("TC-165 SQLite mode business zero impact", () => {
105109
expect(plugin?.installed).toBe(1);
106110
expect(plugin?.enabled).toBe(1);
107111

112+
let monitorPlugin = await findPlugin(api, sqliteMonitorPluginId);
113+
expect(
114+
monitorPlugin,
115+
`expected ${sqliteMonitorPluginId} to be discoverable`,
116+
).toBeTruthy();
117+
originalMonitorInstalled = monitorPlugin?.installed ?? 0;
118+
originalMonitorEnabled = monitorPlugin?.enabled ?? 0;
119+
if (monitorPlugin?.installed !== 1) {
120+
await installPlugin(api, sqliteMonitorPluginId);
121+
monitorPlugin = await findPlugin(api, sqliteMonitorPluginId);
122+
}
123+
if (monitorPlugin?.enabled !== 1) {
124+
await updatePluginStatus(api, sqliteMonitorPluginId, true);
125+
}
126+
108127
const monitor = await expectApiSuccess<ServerMonitorResult>(
109128
await api.get("monitor/server"),
110129
"query server monitor in SQLite mode",
@@ -118,6 +137,16 @@ test.describe("TC-165 SQLite mode business zero impact", () => {
118137
if (createdUserId > 0) {
119138
await api.delete(`user/${createdUserId}`).catch(() => undefined);
120139
}
140+
if (originalMonitorEnabled !== 1) {
141+
await updatePluginStatus(api, sqliteMonitorPluginId, false).catch(
142+
() => undefined,
143+
);
144+
}
145+
if (originalMonitorInstalled !== 1) {
146+
await uninstallPlugin(api, sqliteMonitorPluginId, true).catch(
147+
() => undefined,
148+
);
149+
}
121150
await uninstallPlugin(api, sqliteSourcePluginId, true).catch(
122151
() => undefined,
123152
);
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { test, expect } from "../../fixtures/auth";
2+
import { waitForRouteReady } from "../../support/ui";
3+
4+
test.describe("TC-177 tab title language switch", () => {
5+
test("TC-177a: opened tab titles relocalize immediately after language switch", async ({
6+
adminPage,
7+
mainLayout,
8+
}) => {
9+
await mainLayout.switchLanguage("简体中文");
10+
11+
await adminPage.goto("/dashboard/workspace", { waitUntil: "domcontentloaded" });
12+
await waitForRouteReady(adminPage);
13+
await adminPage.goto("/system/user", { waitUntil: "domcontentloaded" });
14+
await waitForRouteReady(adminPage);
15+
16+
await expect(mainLayout.tabTitle("分析页")).toBeVisible();
17+
await expect(mainLayout.tabTitle("工作台")).toBeVisible();
18+
await expect(mainLayout.tabTitle("用户管理")).toBeVisible();
19+
20+
await mainLayout.switchLanguage("English");
21+
22+
await expect(mainLayout.tabTitle("Analytics")).toBeVisible();
23+
await expect(mainLayout.tabTitle("Workspace")).toBeVisible();
24+
await expect(mainLayout.tabTitle("Users")).toBeVisible();
25+
await expect(mainLayout.tabTitle("分析页")).toHaveCount(0);
26+
await expect(mainLayout.tabTitle("工作台")).toHaveCount(0);
27+
await expect(mainLayout.tabTitle("用户管理")).toHaveCount(0);
28+
29+
await mainLayout.switchLanguage("简体中文");
30+
31+
await expect(mainLayout.tabTitle("分析页")).toBeVisible();
32+
await expect(mainLayout.tabTitle("工作台")).toBeVisible();
33+
await expect(mainLayout.tabTitle("用户管理")).toBeVisible();
34+
await expect(mainLayout.tabTitle("Analytics")).toHaveCount(0);
35+
await expect(mainLayout.tabTitle("Workspace")).toHaveCount(0);
36+
await expect(mainLayout.tabTitle("Users")).toHaveCount(0);
37+
});
38+
});

openspec/changes/switch-default-database-to-postgres/tasks.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@
229229
- FB-19 verification passed after adding SQLite translation debug diagnostics for skipped `COMMENT ON TABLE/COLUMN` statements and explicit index-preservation coverage: `cd apps/lina-core && go test -count=1 ./pkg/dialect/internal/sqlite`; `cd apps/lina-core && go test -count=1 ./pkg/dialect`; `cd apps/lina-core && go test -run 'TestTranslateDDLDropsCommentsWithDebugLogPreservingIndexes|TestSQLiteTranslateDDLExecutesPostgreSQLFixture' -count=3 ./pkg/dialect/internal/sqlite ./pkg/dialect`; and `openspec validate switch-default-database-to-postgres`. Review found no issues: the logger uses the propagated translation context, comment filtering remains scoped to PostgreSQL comment metadata, and `CREATE INDEX` / `CREATE UNIQUE INDEX` / `DROP INDEX` / `REINDEX` statements stay on the compatible SQL path. i18n resources, runtime cache behavior, API behavior, and role data-permission behavior are unchanged.
230230
- FB-20 verification passed after converting the `apps/lina-core/Makefile` terminal/help-facing Chinese text to English: `rg -n "[\\p{Han}]" apps/lina-core/Makefile` returned no matches; `make -C apps/lina-core init` printed the English confirmation guidance; and `openspec validate switch-default-database-to-postgres` passed. This is a project governance feedback item, so no unit test or E2E test is required; runtime behavior, API behavior, cache behavior, data-permission behavior, and i18n resources are unchanged.
231231
- FB-21 verification passed after moving the remaining root `Makefile` command implementations into `hack/makefiles`: `make help`; `make -n init confirm=init`; and `make -n mock confirm=mock` all passed. A root target scan confirmed the root `Makefile` now only keeps shared variables and split makefile includes. This is a project governance feedback item, so no unit test or E2E test is required; runtime behavior, API behavior, cache behavior, data-permission behavior, and i18n resources are unchanged.
232+
- FB-22 verification passed after resolving opened tab titles from stable runtime i18n keys at render time: `cd apps/lina-vben && pnpm -F @lina/web-antd run typecheck`; `cd hack/tests && pnpm run test:validate`; `cd hack/tests && E2E_BROWSER_CHANNEL=chrome pnpm exec playwright test e2e/i18n/TC0177-tab-title-language-switch.ts`; and `openspec validate switch-default-database-to-postgres`. i18n impact is limited to frontend tab title relocalization behavior; no runtime language resource, backend API, cache behavior, or role data-permission behavior changed.
232233

233234
## Feedback
234235

@@ -253,3 +254,4 @@
253254
- [x] **FB-19**: SQLite SQL 翻译器丢弃 `COMMENT ON TABLE/COLUMN` 注释语句时输出 Debug 日志,并确保创建和更新索引语句不会被误丢弃
254255
- [x] **FB-20**: 将 `apps/lina-core/Makefile` 中会输出到终端或帮助文本的中文提示统一改为英文
255256
- [x] **FB-21**: 将根目录 `Makefile` 中剩余命令迁移到 `hack/makefiles` 对应命令文件中
257+
- [x] **FB-22**: 切换 i18n 语言时已打开 tab 页标题应联动切换到当前语言

0 commit comments

Comments
 (0)