fix(webui): enforce localized group copy

This commit is contained in:
Xubin Ren
2026-08-14 19:55:25 +09:00
parent 4266ef2099
commit b378319d4a
15 changed files with 64 additions and 51 deletions
-2
View File
@@ -2718,7 +2718,6 @@ function Shell({
addPaneDisabled={creatingPane || activePaneLimitReached} addPaneDisabled={creatingPane || activePaneLimitReached}
addPaneDisabledLabel={activePaneLimitReached addPaneDisabledLabel={activePaneLimitReached
? t("workbench.paneLimit", { ? t("workbench.paneLimit", {
defaultValue: "Maximum {{count}} panes",
count: MAX_WORKBENCH_PANES, count: MAX_WORKBENCH_PANES,
}) })
: undefined} : undefined}
@@ -2812,7 +2811,6 @@ function Shell({
composerPortalTarget={context.composerPortalTarget} composerPortalTarget={context.composerPortalTarget}
composerActive={context.active} composerActive={context.active}
composerInputAriaLabel={t("workbench.composerAria", { composerInputAriaLabel={t("workbench.composerAria", {
defaultValue: "Message {{title}}",
title: pane.title, title: pane.title,
})} })}
emptyComposerVariant="thread" emptyComposerVariant="thread"
+7 -20
View File
@@ -1013,9 +1013,7 @@ export const ChatList = memo(function ChatList({
{paneGroup && onCreateTab ? ( {paneGroup && onCreateTab ? (
<DropdownMenuItem onSelect={() => onCreateTab(s.key)}> <DropdownMenuItem onSelect={() => onCreateTab(s.key)}>
<PanelsTopLeft className="h-4 w-4 shrink-0" aria-hidden /> <PanelsTopLeft className="h-4 w-4 shrink-0" aria-hidden />
{t("workbench.createGroup", { {t("workbench.createGroup")}
defaultValue: "Create group",
})}
</DropdownMenuItem> </DropdownMenuItem>
) : null} ) : null}
{paneGroup && onAttachPane ? ( {paneGroup && onAttachPane ? (
@@ -1220,7 +1218,7 @@ function WorkbenchTabHeader({
{onDissolve ? ( {onDissolve ? (
<DropdownMenuItem onSelect={onDissolve}> <DropdownMenuItem onSelect={onDissolve}>
<Ungroup className="h-4 w-4 shrink-0" /> <Ungroup className="h-4 w-4 shrink-0" />
{t("workbench.dissolveTab", { defaultValue: "Dissolve group" })} {t("workbench.dissolveTab")}
</DropdownMenuItem> </DropdownMenuItem>
) : null} ) : null}
<DropdownMenuItem <DropdownMenuItem
@@ -1229,9 +1227,7 @@ function WorkbenchTabHeader({
className="whitespace-nowrap" className="whitespace-nowrap"
> >
<Trash2 className="h-4 w-4 shrink-0" /> <Trash2 className="h-4 w-4 shrink-0" />
{t("workbench.deleteConversations", { {t("workbench.deleteConversations")}
defaultValue: "Delete all chats",
})}
</DropdownMenuItem> </DropdownMenuItem>
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>
@@ -1322,10 +1318,7 @@ function ActivePaneRows({
return ( return (
<ul <ul
id={id} id={id}
aria-label={t("workbench.panesInTab", { aria-label={t("workbench.panesInTab", { title: tabTitle })}
defaultValue: "Panes in {{title}}",
title: tabTitle,
})}
className="mt-0.5 space-y-0.5 rounded-es-[14px] border-s-2 border-sidebar-foreground/25 pb-1" className="mt-0.5 space-y-0.5 rounded-es-[14px] border-s-2 border-sidebar-foreground/25 pb-1"
> >
{panes.map((pane) => { {panes.map((pane) => {
@@ -1335,10 +1328,7 @@ function ActivePaneRows({
: updated.has(pane.chatId) && !active : updated.has(pane.chatId) && !active
? "updated" ? "updated"
: null; : null;
const paneActionsLabel = t("workbench.paneActions", { const paneActionsLabel = t("workbench.paneActions", { title: pane.title });
defaultValue: "{{title}} pane actions",
title: pane.title,
});
const selected = selectedDeleteKeys.has(pane.key); const selected = selectedDeleteKeys.has(pane.key);
const isPinned = pinned.has(pane.key); const isPinned = pinned.has(pane.key);
const isArchived = archived.has(pane.key); const isArchived = archived.has(pane.key);
@@ -1452,10 +1442,7 @@ function ActivePaneRows({
{onDetachPane ? ( {onDetachPane ? (
<DropdownMenuItem onSelect={() => onDetachPane(group.tabKey, pane.key)}> <DropdownMenuItem onSelect={() => onDetachPane(group.tabKey, pane.key)}>
<Unplug className="h-4 w-4 shrink-0" /> <Unplug className="h-4 w-4 shrink-0" />
{t("workbench.detachPane", { {t("workbench.detachPane", { title: pane.title })}
defaultValue: "Remove",
title: pane.title,
})}
</DropdownMenuItem> </DropdownMenuItem>
) : null} ) : null}
{onAttachPane ? ( {onAttachPane ? (
@@ -1521,7 +1508,7 @@ function MoveToGroupSubmenu({
<DropdownMenuSub> <DropdownMenuSub>
<DropdownMenuSubTrigger> <DropdownMenuSubTrigger>
<MoveRight className="h-4 w-4 shrink-0" aria-hidden /> <MoveRight className="h-4 w-4 shrink-0" aria-hidden />
{t("workbench.moveTo", { defaultValue: "Move to" })} {t("workbench.moveTo")}
</DropdownMenuSubTrigger> </DropdownMenuSubTrigger>
<DropdownMenuSubContent> <DropdownMenuSubContent>
{targets.map((target) => ( {targets.map((target) => (
+1 -1
View File
@@ -1451,7 +1451,7 @@
"workbench": { "workbench": {
"aria": "Conversation workbench", "aria": "Conversation workbench",
"panes": "Panes", "panes": "Panes",
"tabAria": "Tab: {{title}}", "tabAria": "Group: {{title}}",
"panesInTab": "Panes in {{title}}", "panesInTab": "Panes in {{title}}",
"collapseTabGroup": "Collapse panes in {{title}}", "collapseTabGroup": "Collapse panes in {{title}}",
"expandTabGroup": "Expand panes in {{title}}", "expandTabGroup": "Expand panes in {{title}}",
+1 -1
View File
@@ -1438,7 +1438,7 @@
"workbench": { "workbench": {
"aria": "Área de conversaciones", "aria": "Área de conversaciones",
"panes": "Paneles", "panes": "Paneles",
"tabAria": "Pestaña: {{title}}", "tabAria": "Grupo: {{title}}",
"panesInTab": "Paneles de {{title}}", "panesInTab": "Paneles de {{title}}",
"collapseTabGroup": "Contraer los paneles de {{title}}", "collapseTabGroup": "Contraer los paneles de {{title}}",
"expandTabGroup": "Expandir los paneles de {{title}}", "expandTabGroup": "Expandir los paneles de {{title}}",
+1 -1
View File
@@ -1437,7 +1437,7 @@
"workbench": { "workbench": {
"aria": "Espace de conversations", "aria": "Espace de conversations",
"panes": "Volets", "panes": "Volets",
"tabAria": "Onglet : {{title}}", "tabAria": "Groupe : {{title}}",
"panesInTab": "Volets dans {{title}}", "panesInTab": "Volets dans {{title}}",
"collapseTabGroup": "Réduire les volets de {{title}}", "collapseTabGroup": "Réduire les volets de {{title}}",
"expandTabGroup": "Développer les volets de {{title}}", "expandTabGroup": "Développer les volets de {{title}}",
+1 -1
View File
@@ -1437,7 +1437,7 @@
"workbench": { "workbench": {
"aria": "Ruang kerja percakapan", "aria": "Ruang kerja percakapan",
"panes": "Panel", "panes": "Panel",
"tabAria": "Tab: {{title}}", "tabAria": "Grup: {{title}}",
"panesInTab": "Panel di {{title}}", "panesInTab": "Panel di {{title}}",
"collapseTabGroup": "Ciutkan panel di {{title}}", "collapseTabGroup": "Ciutkan panel di {{title}}",
"expandTabGroup": "Luaskan panel di {{title}}", "expandTabGroup": "Luaskan panel di {{title}}",
+1 -1
View File
@@ -1437,7 +1437,7 @@
"workbench": { "workbench": {
"aria": "会話ワークベンチ", "aria": "会話ワークベンチ",
"panes": "ペイン", "panes": "ペイン",
"tabAria": "タブ{{title}}", "tabAria": "グループ{{title}}",
"panesInTab": "{{title}} のペイン", "panesInTab": "{{title}} のペイン",
"collapseTabGroup": "{{title}} のペインを折りたたむ", "collapseTabGroup": "{{title}} のペインを折りたたむ",
"expandTabGroup": "{{title}} のペインを展開する", "expandTabGroup": "{{title}} のペインを展開する",
+1 -1
View File
@@ -1437,7 +1437,7 @@
"workbench": { "workbench": {
"aria": "대화 워크벤치", "aria": "대화 워크벤치",
"panes": "창", "panes": "창",
"tabAria": ": {{title}}", "tabAria": "그룹: {{title}}",
"panesInTab": "{{title}}의 창", "panesInTab": "{{title}}의 창",
"collapseTabGroup": "{{title}}의 창 접기", "collapseTabGroup": "{{title}}의 창 접기",
"expandTabGroup": "{{title}}의 창 펼치기", "expandTabGroup": "{{title}}의 창 펼치기",
+1 -1
View File
@@ -1451,7 +1451,7 @@
"workbench": { "workbench": {
"aria": "Área de conversas", "aria": "Área de conversas",
"panes": "Painéis", "panes": "Painéis",
"tabAria": "Aba: {{title}}", "tabAria": "Grupo: {{title}}",
"panesInTab": "Painéis em {{title}}", "panesInTab": "Painéis em {{title}}",
"collapseTabGroup": "Recolher os painéis em {{title}}", "collapseTabGroup": "Recolher os painéis em {{title}}",
"expandTabGroup": "Expandir os painéis em {{title}}", "expandTabGroup": "Expandir os painéis em {{title}}",
+1 -1
View File
@@ -1437,7 +1437,7 @@
"workbench": { "workbench": {
"aria": "Không gian hội thoại", "aria": "Không gian hội thoại",
"panes": "Khung", "panes": "Khung",
"tabAria": "Thẻ: {{title}}", "tabAria": "Nhóm: {{title}}",
"panesInTab": "Các khung trong {{title}}", "panesInTab": "Các khung trong {{title}}",
"collapseTabGroup": "Thu gọn các khung trong {{title}}", "collapseTabGroup": "Thu gọn các khung trong {{title}}",
"expandTabGroup": "Mở rộng các khung trong {{title}}", "expandTabGroup": "Mở rộng các khung trong {{title}}",
+1 -1
View File
@@ -1451,7 +1451,7 @@
"workbench": { "workbench": {
"aria": "会话工作台", "aria": "会话工作台",
"panes": "窗格", "panes": "窗格",
"tabAria": "标签页{{title}}", "tabAria": "分组{{title}}",
"panesInTab": "{{title}} 中的窗格", "panesInTab": "{{title}} 中的窗格",
"collapseTabGroup": "折叠 {{title}} 中的窗格", "collapseTabGroup": "折叠 {{title}} 中的窗格",
"expandTabGroup": "展开 {{title}} 中的窗格", "expandTabGroup": "展开 {{title}} 中的窗格",
+1 -1
View File
@@ -1437,7 +1437,7 @@
"workbench": { "workbench": {
"aria": "對話工作台", "aria": "對話工作台",
"panes": "窗格", "panes": "窗格",
"tabAria": "標籤頁{{title}}", "tabAria": "群組{{title}}",
"panesInTab": "{{title}} 中的窗格", "panesInTab": "{{title}} 中的窗格",
"collapseTabGroup": "收合 {{title}} 中的窗格", "collapseTabGroup": "收合 {{title}} 中的窗格",
"expandTabGroup": "展開 {{title}} 中的窗格", "expandTabGroup": "展開 {{title}} 中的窗格",
+5 -5
View File
@@ -3099,7 +3099,7 @@ describe("App layout", () => {
await waitFor(() => expect(connectSpy).toHaveBeenCalled()); await waitFor(() => expect(connectSpy).toHaveBeenCalled());
const sidebar = screen.getByRole("navigation", { name: "Sidebar navigation" }); const sidebar = screen.getByRole("navigation", { name: "Sidebar navigation" });
const alphaTab = await within(sidebar).findByRole("button", { name: "Tab: Alpha tab" }); const alphaTab = await within(sidebar).findByRole("button", { name: "Group: Alpha tab" });
const betaTab = within(sidebar).getByRole("button", { name: "Beta tab" }); const betaTab = within(sidebar).getByRole("button", { name: "Beta tab" });
expect(alphaTab.compareDocumentPosition(betaTab) & Node.DOCUMENT_POSITION_FOLLOWING) expect(alphaTab.compareDocumentPosition(betaTab) & Node.DOCUMENT_POSITION_FOLLOWING)
.toBeTruthy(); .toBeTruthy();
@@ -3218,7 +3218,7 @@ describe("App layout", () => {
statusHandlers.forEach((handler) => handler("open")); statusHandlers.forEach((handler) => handler("open"));
}); });
const sidebar = screen.getByRole("navigation", { name: "Sidebar navigation" }); const sidebar = screen.getByRole("navigation", { name: "Sidebar navigation" });
expect(within(sidebar).queryByRole("button", { name: "Tab: Solo pane" })) expect(within(sidebar).queryByRole("button", { name: "Group: Solo pane" }))
.not.toBeInTheDocument(); .not.toBeInTheDocument();
setSidebarStateSpy.mockClear(); setSidebarStateSpy.mockClear();
@@ -3228,14 +3228,14 @@ describe("App layout", () => {
fireEvent.click(await screen.findByRole("menuitem", { name: "Create group" })); fireEvent.click(await screen.findByRole("menuitem", { name: "Create group" }));
const tabButton = await within(sidebar).findByRole("button", { const tabButton = await within(sidebar).findByRole("button", {
name: "Tab: Solo pane", name: "Group: Solo pane",
}); });
const tabGroup = tabButton.closest("[data-sidebar-tab-group]") as HTMLElement; const tabGroup = tabButton.closest("[data-sidebar-tab-group]") as HTMLElement;
expect(within(tabGroup).getByRole("list", { name: "Panes in Solo pane" })) expect(within(tabGroup).getByRole("list", { name: "Panes in Solo pane" }))
.toBeInTheDocument(); .toBeInTheDocument();
expect(within(tabGroup).getAllByRole("button", { name: "Solo pane" })) expect(within(tabGroup).getAllByRole("button", { name: "Solo pane" }))
.toHaveLength(1); .toHaveLength(1);
expect(within(sidebar).queryByRole("button", { name: "Tab: Other pane" })) expect(within(sidebar).queryByRole("button", { name: "Group: Other pane" }))
.not.toBeInTheDocument(); .not.toBeInTheDocument();
await waitFor(() => expect(setSidebarStateSpy).toHaveBeenCalledWith( await waitFor(() => expect(setSidebarStateSpy).toHaveBeenCalledWith(
expect.objectContaining({ expect.objectContaining({
@@ -3312,7 +3312,7 @@ describe("App layout", () => {
render(<App />); render(<App />);
await waitFor(() => expect(connectSpy).toHaveBeenCalled()); await waitFor(() => expect(connectSpy).toHaveBeenCalled());
const secondSidebar = screen.getByRole("navigation", { name: "Sidebar navigation" }); const secondSidebar = screen.getByRole("navigation", { name: "Sidebar navigation" });
expect(await within(secondSidebar).findByRole("button", { name: "Tab: Solo pane" })) expect(await within(secondSidebar).findByRole("button", { name: "Group: Solo pane" }))
.toBeInTheDocument(); .toBeInTheDocument();
}); });
+14 -14
View File
@@ -90,7 +90,7 @@ describe("ChatList", () => {
/>, />,
); );
expect(screen.getByRole("button", { name: "Tab: Root topic" })) expect(screen.getByRole("button", { name: "Group: Root topic" }))
.toHaveAttribute("draggable", "false"); .toHaveAttribute("draggable", "false");
const pane = screen.getByRole("button", { name: "Research pane" }); const pane = screen.getByRole("button", { name: "Research pane" });
expect(pane).toHaveAttribute("draggable", "true"); expect(pane).toHaveAttribute("draggable", "true");
@@ -209,7 +209,7 @@ describe("ChatList", () => {
/>, />,
); );
expect(screen.queryByRole("button", { name: "Tab: Solo pane" })) expect(screen.queryByRole("button", { name: "Group: Solo pane" }))
.not.toBeInTheDocument(); .not.toBeInTheDocument();
expect(screen.getAllByText("Solo pane")).toHaveLength(1); expect(screen.getAllByText("Solo pane")).toHaveLength(1);
expect(screen.queryByRole("list", { name: "Panes in Solo pane" })) expect(screen.queryByRole("list", { name: "Panes in Solo pane" }))
@@ -293,10 +293,10 @@ describe("ChatList", () => {
types: [SESSION_DRAG_TYPE], types: [SESSION_DRAG_TYPE],
} as unknown as DataTransfer; } as unknown as DataTransfer;
const source = screen.getByRole("button", { name: "Solo topic" }); const source = screen.getByRole("button", { name: "Solo topic" });
const targetGroup = screen.getByRole("button", { name: "Tab: Target group" }) const targetGroup = screen.getByRole("button", { name: "Group: Target group" })
.closest("[data-sidebar-tab-group]")!; .closest("[data-sidebar-tab-group]")!;
const targetSurface = targetGroup.querySelector("[data-workbench-tab-surface]")!; const targetSurface = targetGroup.querySelector("[data-workbench-tab-surface]")!;
const fullGroup = screen.getByRole("button", { name: "Tab: Full group" }) const fullGroup = screen.getByRole("button", { name: "Group: Full group" })
.closest("[data-sidebar-tab-group]")!; .closest("[data-sidebar-tab-group]")!;
const fullSurface = fullGroup.querySelector("[data-workbench-tab-surface]")!; const fullSurface = fullGroup.querySelector("[data-workbench-tab-surface]")!;
@@ -367,7 +367,7 @@ describe("ChatList", () => {
types: [SESSION_DRAG_TYPE], types: [SESSION_DRAG_TYPE],
} as unknown as DataTransfer; } as unknown as DataTransfer;
const source = screen.getByRole("button", { name: "Research pane" }); const source = screen.getByRole("button", { name: "Research pane" });
const sourceSurface = screen.getByRole("button", { name: "Tab: Root topic" }) const sourceSurface = screen.getByRole("button", { name: "Group: Root topic" })
.closest("[data-workbench-tab-surface]")!; .closest("[data-workbench-tab-surface]")!;
const standaloneList = document.querySelector("[data-chat-list-content]")!; const standaloneList = document.querySelector("[data-chat-list-content]")!;
@@ -443,7 +443,7 @@ describe("ChatList", () => {
expect(child.closest("[data-sidebar-pane]")) expect(child.closest("[data-sidebar-pane]"))
.toHaveAttribute("data-sidebar-pane", "websocket:child"); .toHaveAttribute("data-sidebar-pane", "websocket:child");
expect(child).toHaveAttribute("aria-current", "true"); expect(child).toHaveAttribute("aria-current", "true");
const targetTabRow = screen.getByRole("button", { name: "Tab: Target tab" }) const targetTabRow = screen.getByRole("button", { name: "Group: Target tab" })
.closest("li")!; .closest("li")!;
const targetChild = within(targetTabRow).getByRole("button", { const targetChild = within(targetTabRow).getByRole("button", {
name: "Target research", name: "Target research",
@@ -464,7 +464,7 @@ describe("ChatList", () => {
expect(onSelect).not.toHaveBeenCalled(); expect(onSelect).not.toHaveBeenCalled();
onSelectPane.mockClear(); onSelectPane.mockClear();
const rootTab = screen.getByRole("button", { name: "Tab: Root topic" }); const rootTab = screen.getByRole("button", { name: "Group: Root topic" });
fireEvent.click(rootTab); fireEvent.click(rootTab);
expect(onSelectPane).not.toHaveBeenCalled(); expect(onSelectPane).not.toHaveBeenCalled();
expect(onSelect).not.toHaveBeenCalled(); expect(onSelect).not.toHaveBeenCalled();
@@ -510,7 +510,7 @@ describe("ChatList", () => {
fireEvent.keyDown(document, { key: "Escape" }); fireEvent.keyDown(document, { key: "Escape" });
expect(child).toHaveAttribute("draggable", "false"); expect(child).toHaveAttribute("draggable", "false");
expect(screen.getByRole("button", { name: "Tab: Target tab" })) expect(screen.getByRole("button", { name: "Group: Target tab" }))
.toHaveAttribute("draggable", "false"); .toHaveAttribute("draggable", "false");
}); });
@@ -547,7 +547,7 @@ describe("ChatList", () => {
/>, />,
); );
const tabButton = screen.getByRole("button", { name: "Tab: Root topic" }); const tabButton = screen.getByRole("button", { name: "Group: Root topic" });
const tabGroup = tabButton.closest("[data-sidebar-tab-group]")!; const tabGroup = tabButton.closest("[data-sidebar-tab-group]")!;
const tabHeader = tabButton.closest("[data-workbench-tab]")!; const tabHeader = tabButton.closest("[data-workbench-tab]")!;
const tabSurface = tabButton.closest("[data-workbench-tab-surface]")!; const tabSurface = tabButton.closest("[data-workbench-tab-surface]")!;
@@ -587,7 +587,7 @@ describe("ChatList", () => {
expect(within(tabGroup).getByRole("button", { expect(within(tabGroup).getByRole("button", {
name: "Expand panes in Root topic", name: "Expand panes in Root topic",
})).toHaveAttribute("aria-expanded", "false"); })).toHaveAttribute("aria-expanded", "false");
expect(within(tabGroup).getByRole("button", { name: "Tab: Root topic" })) expect(within(tabGroup).getByRole("button", { name: "Group: Root topic" }))
.not.toHaveAttribute("aria-current"); .not.toHaveAttribute("aria-current");
fireEvent.click(within(tabGroup).getByRole("button", { fireEvent.click(within(tabGroup).getByRole("button", {
@@ -634,8 +634,8 @@ describe("ChatList", () => {
fireEvent.click(await screen.findByRole("menuitem", { name: "Select" })); fireEvent.click(await screen.findByRole("menuitem", { name: "Select" }));
expect(screen.getByText("1 selected")).toBeInTheDocument(); expect(screen.getByText("1 selected")).toBeInTheDocument();
fireEvent.click(screen.getByRole("button", { name: "Tab: Root topic" })); fireEvent.click(screen.getByRole("button", { name: "Group: Root topic" }));
expect(screen.getByRole("button", { name: "Tab: Root topic" })) expect(screen.getByRole("button", { name: "Group: Root topic" }))
.toHaveAttribute("aria-pressed", "true"); .toHaveAttribute("aria-pressed", "true");
expect(screen.getByRole("button", { name: "Root topic" })) expect(screen.getByRole("button", { name: "Root topic" }))
.toHaveAttribute("aria-pressed", "true"); .toHaveAttribute("aria-pressed", "true");
@@ -942,12 +942,12 @@ describe("ChatList", () => {
}; };
const firstRender = render(<ChatList {...props} />); const firstRender = render(<ChatList {...props} />);
fireEvent.click(screen.getByRole("button", { name: "Tab: Root topic" })); fireEvent.click(screen.getByRole("button", { name: "Group: Root topic" }));
expect(screen.queryByRole("button", { name: "Research pane" })).not.toBeInTheDocument(); expect(screen.queryByRole("button", { name: "Research pane" })).not.toBeInTheDocument();
firstRender.unmount(); firstRender.unmount();
render(<ChatList {...props} />); render(<ChatList {...props} />);
expect(screen.getByRole("button", { name: "Tab: Root topic" })) expect(screen.getByRole("button", { name: "Group: Root topic" }))
.toHaveAttribute("aria-expanded", "false"); .toHaveAttribute("aria-expanded", "false");
expect(screen.queryByRole("button", { name: "Research pane" })).not.toBeInTheDocument(); expect(screen.queryByRole("button", { name: "Research pane" })).not.toBeInTheDocument();
}); });
+28
View File
@@ -269,6 +269,22 @@ const LOCALIZED_NEW_SURFACE_KEYS = [
"chat.groups.yesterday", "chat.groups.yesterday",
"chat.groups.earlier", "chat.groups.earlier",
"chat.groups.archived", "chat.groups.archived",
"workbench.tabAria",
"workbench.panesInTab",
"workbench.collapseTabGroup",
"workbench.expandTabGroup",
"workbench.dropPane",
"workbench.createGroup",
"workbench.moveTo",
"workbench.renameGroupTitle",
"workbench.renameGroupDescription",
"workbench.renameGroupPlaceholder",
"workbench.dissolveTab",
"workbench.deleteConversations",
"workbench.paneLimit",
"workbench.paneActions",
"workbench.detachPane",
"workbench.composerAria",
"thread.promptNavigator.railAria", "thread.promptNavigator.railAria",
"thread.composer.mentions.cliTitle", "thread.composer.mentions.cliTitle",
"thread.composer.mentions.mcpTitle", "thread.composer.mentions.mcpTitle",
@@ -582,6 +598,18 @@ describe("webui i18n", () => {
expect(settings.skills.marketplaceTrendingTitle).toBe("各市场热门技能"); expect(settings.skills.marketplaceTrendingTitle).toBe("各市场热门技能");
}); });
it("keeps the Simplified Chinese group workflow localized", () => {
const workbench = resources["zh-CN"].common.workbench;
expect(workbench.tabAria).toBe("分组:{{title}}");
expect(workbench.createGroup).toBe("创建分组");
expect(workbench.renameGroupTitle).toBe("重命名分组");
expect(workbench.renameGroupDescription).toBe("为这个分组命名。");
expect(workbench.renameGroupPlaceholder).toBe("分组名称");
expect(workbench.moveTo).toBe("移动到");
expect(workbench.detachPane).toBe("移出");
});
it("keeps Indonesian and Vietnamese settings free of copied Spanish help text", () => { it("keeps Indonesian and Vietnamese settings free of copied Spanish help text", () => {
const spanish = flattenResource(resources.es.common); const spanish = flattenResource(resources.es.common);