diff --git a/frontend/src/i18n/__tests__/i18n.test.ts b/frontend/src/i18n/__tests__/i18n.test.ts index b1ae453107..f4a9654dde 100644 --- a/frontend/src/i18n/__tests__/i18n.test.ts +++ b/frontend/src/i18n/__tests__/i18n.test.ts @@ -38,6 +38,24 @@ describe('i18n configuration', () => { expect(i18n.t(key, { lng: 'en' })).toBe(expected); }); + it.each([ + [0, '1 · 0 agents', '1 member, 0 agents'], + [1, '1 · 1 agent', '1 member, 1 agent'], + [2, '1 · 2 agents', '1 member, 2 agents'], + ])('pluralizes the compact pod header at %i agents', (count, visible, accessible) => { + const members = i18n.t('podChat.header.members', { lng: 'en', count: 1 }); + const agents = i18n.t('podChat.header.agents', { lng: 'en', count }); + expect(i18n.t('podChat.header.compactMeta', { lng: 'en', members: 1, agents })).toBe(visible); + expect(i18n.t('podChat.header.compactMetaAccessible', { lng: 'en', members, agents })).toBe(accessible); + }); + + it('keeps the Chinese compact count while naming it for assistive tech', () => { + const members = i18n.t('podChat.header.members', { lng: 'zh-CN', count: 1 }); + const agents = i18n.t('podChat.header.agents', { lng: 'zh-CN', count: 1 }); + expect(i18n.t('podChat.header.compactMeta', { lng: 'zh-CN', members: 1, agents })).toBe('1 · 1个智能体'); + expect(i18n.t('podChat.header.compactMetaAccessible', { lng: 'zh-CN', members, agents })).toBe('1名成员,1个智能体'); + }); + it('defaults to English when no stored supported language exists', async () => { const languages = jest.spyOn(window.navigator, 'languages', 'get').mockReturnValue(['en-US']); const language = jest.spyOn(window.navigator, 'language', 'get').mockReturnValue('en-US'); diff --git a/frontend/src/i18n/locales/en.json b/frontend/src/i18n/locales/en.json index 0ed63fecb4..db4438f0db 100644 --- a/frontend/src/i18n/locales/en.json +++ b/frontend/src/i18n/locales/en.json @@ -518,7 +518,8 @@ "agents_other": "{{count}} agents", "boardOpen": "board {{count}} open", "backToPods": "Back to pods", - "compactMeta": "{{members}} · {{count}} agents" + "compactMeta": "{{members}} · {{agents}}", + "compactMetaAccessible": "{{members}}, {{agents}}" }, "team": { "view": "View pod team", diff --git a/frontend/src/i18n/locales/zh-CN.json b/frontend/src/i18n/locales/zh-CN.json index 623e7e9b7e..6bcc60ec01 100644 --- a/frontend/src/i18n/locales/zh-CN.json +++ b/frontend/src/i18n/locales/zh-CN.json @@ -517,7 +517,8 @@ "agents_other": "{{count}}个智能体", "boardOpen": "看板{{count}}项待办", "backToPods": "返回 Pod 列表", - "compactMeta": "{{members}} · {{count}}个智能体" + "compactMeta": "{{members}} · {{agents}}", + "compactMetaAccessible": "{{members}},{{agents}}" }, "team": { "view": "查看 Pod 团队", diff --git a/frontend/src/v2/__tests__/V2Composer.test.tsx b/frontend/src/v2/__tests__/V2Composer.test.tsx index 02da3266c1..fbd52e7558 100644 --- a/frontend/src/v2/__tests__/V2Composer.test.tsx +++ b/frontend/src/v2/__tests__/V2Composer.test.tsx @@ -97,7 +97,8 @@ describe('V2Composer send button', () => { const meta = header.querySelector('.v2-pod-header__meta:not(.v2-pod-header__meta--compact)'); expect(meta).toHaveTextContent('2 members'); expect(meta).toHaveTextContent('1 agent'); - expect(header.querySelector('.v2-pod-header__meta--compact')).toHaveTextContent('2 · 1 agents'); + expect(header.querySelector('.v2-pod-header__meta--compact')).toHaveTextContent('2 · 1 agent'); + expect(view.getByRole('group', { name: '2 members, 1 agent' })).toBeInTheDocument(); expect(view.container.querySelector('.v2-thread__working')).toBeNull(); }); diff --git a/frontend/src/v2/__tests__/V2ThreadStarter.test.tsx b/frontend/src/v2/__tests__/V2ThreadStarter.test.tsx index 099ddcdbfb..e30c7050c5 100644 --- a/frontend/src/v2/__tests__/V2ThreadStarter.test.tsx +++ b/frontend/src/v2/__tests__/V2ThreadStarter.test.tsx @@ -106,6 +106,17 @@ describe('V2Thread teaching empty states', () => { expect(document.querySelector('.v2-thread__empty-line')).toHaveTextContent('no messages yet'); }); + test('compact header shows singular agents and names both counts for assistive tech', () => { + const detail = makeDetail({ + agents: [{ agentName: 'openclaw', instanceId: 'aria', displayName: 'Aria' }], + }); + renderChat(detail); + + const compact = screen.getByRole('group', { name: '1 member, 1 agent' }); + expect(compact).toHaveTextContent('1 · 1 agent'); + expect(compact.querySelector('[aria-hidden="true"]')).toHaveTextContent('1 · 1 agent'); + }); + test('multi-member regular pods use the same teaching state', () => { renderChat(makeDetail({ members: [ diff --git a/frontend/src/v2/components/V2Thread.tsx b/frontend/src/v2/components/V2Thread.tsx index e6141d4621..f92f31c877 100644 --- a/frontend/src/v2/components/V2Thread.tsx +++ b/frontend/src/v2/components/V2Thread.tsx @@ -1564,9 +1564,11 @@ const V2Thread: React.FC = ({ detail, firstRunVisible = false, in // Header meta (direction C): members · agents · board N open · bound // channels. The agents-working count left the header for the inspector. const humanMemberCount = (members || []).filter((member) => !member?.isBot).length; + const memberCountText = t('podChat.header.members', { count: humanMemberCount }); + const agentCountText = t('podChat.header.agents', { count: agents.length }); const metaParts: React.ReactNode[] = [ - {t('podChat.header.members', { count: humanMemberCount })}, - {t('podChat.header.agents', { count: agents.length })}, + {memberCountText}, + {agentCountText}, ]; if (headerMeta.boardOpen !== null) { metaParts.push( @@ -1591,8 +1593,12 @@ const V2Thread: React.FC = ({ detail, firstRunVisible = false, in {pod.description &&

{pod.description}

} -