mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-09 08:59:40 +02:00
fix(tiles): a tile's + menu items get their disabled reason in Chinese
Found live in zh-CN: the + menu's session items stayed titled "The grid
holds at most 6 tiles". Each item carried data-i18n-skip on the whole
button to keep the session name as typed, and the translator skips an
element's attributes along with its text. Only the name is skipped now (a
child span, as the picker does), so the title is translated.
The zh-CN coverage test classified a title inside a skipped subtree as
user text, which is how this got past it; such a label is now a failure
of its own ("no UI label sits inside a skipped subtree").
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -529,8 +529,12 @@ Object.assign(CodemanApp.prototype, {
|
|||||||
const item = document.createElement('button');
|
const item = document.createElement('button');
|
||||||
item.type = 'button';
|
item.type = 'button';
|
||||||
item.setAttribute('role', 'menuitem');
|
item.setAttribute('role', 'menuitem');
|
||||||
item.setAttribute('data-i18n-skip', '');
|
// Only the name is user text: skipping the whole button would keep its
|
||||||
item.textContent = c.label;
|
// title (why it is disabled) out of the translator too.
|
||||||
|
const name = document.createElement('span');
|
||||||
|
name.setAttribute('data-i18n-skip', '');
|
||||||
|
name.textContent = c.label;
|
||||||
|
item.appendChild(name);
|
||||||
item.disabled = full;
|
item.disabled = full;
|
||||||
if (full) item.title = limit.full;
|
if (full) item.title = limit.full;
|
||||||
item.addEventListener('click', () => {
|
item.addEventListener('click', () => {
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ describe('every way in stops at the cap', () => {
|
|||||||
const app = fullGrid();
|
const app = fullGrid();
|
||||||
app.openTileAddMenu({ stopPropagation: vi.fn(), preventDefault: vi.fn(), currentTarget: null }, 's-1');
|
app.openTileAddMenu({ stopPropagation: vi.fn(), preventDefault: vi.fn(), currentTarget: null }, 's-1');
|
||||||
const menu = body.children.find((c) => c.className.includes('tile-add-menu'))!;
|
const menu = body.children.find((c) => c.className.includes('tile-add-menu'))!;
|
||||||
const items = menu.children.filter((c) => c.textContent === 's-7' || c.textContent === 's-8');
|
const items = menu.children.filter((c) => ['s-7', 's-8'].includes(c.children[0]?.textContent ?? ''));
|
||||||
expect(items).toHaveLength(2);
|
expect(items).toHaveLength(2);
|
||||||
for (const item of items) {
|
for (const item of items) {
|
||||||
expect(item.disabled).toBe(true);
|
expect(item.disabled).toBe(true);
|
||||||
|
|||||||
@@ -72,13 +72,20 @@ const leftover = (text: string) => text.replace(ALLOWED, '').match(/[A-Za-z]+/g)
|
|||||||
const seen = new Map<string, string>();
|
const seen = new Map<string, string>();
|
||||||
/** Text in a data-i18n-skip subtree (user text): must never be translated. */
|
/** Text in a data-i18n-skip subtree (user text): must never be translated. */
|
||||||
const userText = new Set<string>();
|
const userText = new Set<string>();
|
||||||
|
/**
|
||||||
|
* A title or accessible name inside a skipped subtree: the translator skips
|
||||||
|
* the element's attributes along with its text, so a UI label there stays
|
||||||
|
* English. Only the user text itself may be skipped.
|
||||||
|
*/
|
||||||
|
const labelsInSkip: string[] = [];
|
||||||
|
|
||||||
function harvest(root: FakeEl | null | undefined, where: string) {
|
function harvest(root: FakeEl | null | undefined, where: string) {
|
||||||
const walk = (el: FakeEl, inSkip: boolean) => {
|
const walk = (el: FakeEl, inSkip: boolean) => {
|
||||||
const skip = inSkip || 'data-i18n-skip' in el.attrs;
|
const skip = inSkip || 'data-i18n-skip' in el.attrs;
|
||||||
const add = (value: unknown, kind: string) => {
|
const add = (value: unknown, kind: string) => {
|
||||||
if (typeof value !== 'string' || !/[A-Za-z]/.test(value)) return;
|
if (typeof value !== 'string' || !/[A-Za-z]/.test(value)) return;
|
||||||
if (skip) userText.add(value.trim());
|
if (skip && kind === 'text') userText.add(value.trim());
|
||||||
|
else if (skip) labelsInSkip.push(`${where} (${kind}): ${value}`);
|
||||||
else if (!seen.has(value.trim())) seen.set(value.trim(), `${where} (${kind})`);
|
else if (!seen.has(value.trim())) seen.set(value.trim(), `${where} (${kind})`);
|
||||||
};
|
};
|
||||||
add(el.textContent, 'text');
|
add(el.textContent, 'text');
|
||||||
@@ -298,6 +305,10 @@ describe('every tile grid string the code puts on screen translates to zh-CN', (
|
|||||||
expect(bad).toEqual([]);
|
expect(bad).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('no UI label sits inside a skipped subtree (where the translator cannot reach it)', () => {
|
||||||
|
expect(labelsInSkip).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
it('each one reads exactly as before in English', () => {
|
it('each one reads exactly as before in English', () => {
|
||||||
const changed = [...seen.keys()].filter((s) => en.api.t(s) !== s);
|
const changed = [...seen.keys()].filter((s) => en.api.t(s) !== s);
|
||||||
expect(changed).toEqual([]);
|
expect(changed).toEqual([]);
|
||||||
|
|||||||
@@ -212,7 +212,7 @@ describe("a tile's + menu", () => {
|
|||||||
app.markIdleAlertSeen.mockClear();
|
app.markIdleAlertSeen.mockClear();
|
||||||
addButtonOf('s-a').dispatch('click', { stopPropagation: vi.fn(), currentTarget: null });
|
addButtonOf('s-a').dispatch('click', { stopPropagation: vi.fn(), currentTarget: null });
|
||||||
const items = addMenu()!.children.filter((i) => i.className !== 'tile-add-new');
|
const items = addMenu()!.children.filter((i) => i.className !== 'tile-add-new');
|
||||||
expect(items.map((i) => i.textContent)).toEqual(['s-other', 's-c']);
|
expect(items.map((i) => i.children[0].textContent)).toEqual(['s-other', 's-c']);
|
||||||
items[1].dispatch('click');
|
items[1].dispatch('click');
|
||||||
expect(app._tileGrid.ids).toEqual(['s-a', 's-b', 's-c']);
|
expect(app._tileGrid.ids).toEqual(['s-a', 's-b', 's-c']);
|
||||||
expect(app.activeSessionId).toBe('s-c');
|
expect(app.activeSessionId).toBe('s-c');
|
||||||
|
|||||||
Reference in New Issue
Block a user