Skip to content

Commit da7999a

Browse files
committed
fix: lint-glossary only checked the first path, and the skills taught the wrong anchor rule
Lessons from the first nine translation PRs, where five of them broke the docs build. - lint-glossary took `argv._[0]`, so `lint-glossary -- a.md b.md` silently checked only `a.md` and reported success. It now takes every path, and fails on one that matches no translation instead of passing green on a typo. Covered by tests. - The quality checklist said "internal links point to the translated anchors", which is exactly what broke the build: translating a heading changes its anchor and orphans every link pointing at it, from this page and from others. Headings now keep the English anchor via `{#anchor}`. batch-translate carried the same wrong instruction. - The skills said nothing about delivery. They now state one commit per issue, an English message with `Fixes #<issue>`, `.md` and `.en.md` together, and no tool attribution in the commit or the PR body. CONTRIBUTING carries the same two rules for human contributors.
1 parent a57f3eb commit da7999a

7 files changed

Lines changed: 125 additions & 13 deletions

File tree

.agents/skills/batch-translate/SKILL.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,18 @@ Aplica todas las reglas del skill `/translate-angular-docs`:
5959

6060
Sobreescribe `archivo.md` con la traducción.
6161

62-
### 6. Verificar anchors
62+
### 6. Fijar anchors
6363

64-
Si se tradujeron encabezados con enlaces internos, actualiza los anchors.
64+
Cada encabezado traducido conserva su anchor inglés con `{#anchor}`:
65+
66+
```markdown
67+
### Versiones con soporte activo {#actively-supported-versions}
68+
```
69+
70+
Los enlaces —los internos de la página y los que llegan desde otras— apuntan a
71+
ese anchor inglés. Si dejas que el anchor se derive del español, el build de
72+
Angular falla al validar los enlaces. Ver
73+
[`translate-angular-docs`](../translate-angular-docs/SKILL.md), paso 4.
6574

6675
### 7. Stage en git
6776

.agents/skills/translate-angular-docs/SKILL.md

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,42 @@ Si el archivo afecta la navegación del sitio, revisa:
104104
adev-es/src/app/routing/sub-navigation-data.ts
105105
```
106106

107-
### Paso 5 — Checklist de calidad
107+
### Paso 5 — Entregar: commit y pull request
108+
109+
Un solo commit por issue, con el `.md` y su `.en.md` **juntos**. Separarlos deja
110+
el archivo marcado como desactualizado de forma permanente, porque la detección
111+
busca el commit donde se tocaron ambos.
112+
113+
El mensaje va **en inglés**, aunque el contenido que traduces sea español: lo que
114+
se traduce es la documentación, no el historial.
115+
116+
```
117+
translate: signals debounced and effect guides (Angular 22.1)
118+
119+
Translate guide/signals/debounced.md and guide/signals/effect.md into
120+
Spanish and keep the English originals as .en.md backups.
121+
122+
Fixes #186
123+
```
124+
125+
- Prefijo `translate:`, también cuando el trabajo es actualizar una traducción.
126+
- `Fixes #<issue>` en el cuerpo del commit **y** en la descripción del PR.
127+
- **Nada de atribución a herramientas**: ni `Co-Authored-By: Claude…`, ni
128+
`Claude-Session:`, ni «Generated with Claude Code», ni en el commit ni en el PR.
129+
El historial de este repo se lee como trabajo de la comunidad.
130+
131+
Comprueba lo que vas a entregar antes de commitear:
132+
133+
```shell
134+
npm run lint-glossary -- <ruta1> <ruta2> ... # acepta varias rutas
135+
npm run check-translations # ya no deben aparecer
136+
git status # solo .md y .en.md tuyos
137+
```
138+
139+
`lint-glossary` falla si una de las rutas no casa con ninguna traducción, así que
140+
una ruta mal escrita se nota en vez de pasar en verde.
141+
142+
### Paso 6 — Checklist de calidad
108143

109144
Ejecuta el checklist al final de este documento antes de entregar.
110145

@@ -459,10 +494,13 @@ Antes de finalizar, verifica:
459494
- [ ] **Etiquetas `<docs-*>`:** contenido interno traducido, estructura preservada
460495
- [ ] **Archivos y rutas:** sin traducir
461496
- [ ] **Versiones:** en formato original ("Angular 17", no "Angular diecisiete")
462-
- [ ] **Anchors actualizados:** enlaces internos apuntan a los anchors traducidos
497+
- [ ] **Anchors fijados:** cada encabezado traducido conserva su anchor inglés con `{#anchor}`
498+
- [ ] **Enlaces internos:** apuntan al anchor inglés, no al que derivaría del español
463499
- [ ] **Comentarios en código:** traducidos
464500
- [ ] **Naturalidad:** el texto español suena natural, no como traducción literal
465501
- [ ] **Consistencia:** mismo término español para mismo concepto en inglés
466502
- [ ] **Preposición:** "en Angular" en lugar de "de Angular"
467503
- [ ] **Navegación:** si aplica, `sub-navigation-data.ts` actualizado
468-
- [ ] **Git:** archivos `.md` y `.en.md` staged para el commit
504+
- [ ] **Git:** `.md` y `.en.md` en el mismo commit, y solo eso
505+
- [ ] **Commit:** uno solo, mensaje en inglés, con `Fixes #<issue>`
506+
- [ ] **Sin atribución:** ni `Co-Authored-By`, ni `Claude-Session`, ni menciones a herramientas

.agents/skills/translate-delta/SKILL.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,13 @@ Comprueba cuatro cosas. Si alguna falla, **no commitees**:
9494
`.md` y `.en.md` **en el mismo commit**. Romper ese invariante deja el archivo marcado como
9595
desactualizado para siempre: es exactamente el origen del falso positivo de `selectors.md`.
9696

97+
Un solo commit por issue, con el mensaje **en inglés** y `Fixes #<issue>` en el cuerpo. Prefijo
98+
`translate:`, también aquí. Sin atribución a herramientas: ni `Co-Authored-By: Claude…`, ni
99+
`Claude-Session:`, ni en el commit ni en la descripción del PR.
100+
101+
Si el delta toca un encabezado, conserva su anchor inglés con `{#anchor}`: cambiarlo rompe los
102+
enlaces de otras páginas y tumba el build.
103+
97104
## Reglas de edición
98105

99106
Aplica el glosario de [`translate-angular-docs`](../translate-angular-docs/SKILL.md), más estas

CONTRIBUTING.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,10 +114,15 @@ Si deseas traducir un documento nuevo:
114114

115115
1. Haz push de los cambios a tu fork:
116116
```bash
117-
git add .
118-
git commit -m "translate: complete translation of components guide"
117+
git add adev-es/src/content/guide/components.md adev-es/src/content/guide/components.en.md
118+
git commit -m "translate: components guide"
119119
git push origin translate-components-guide
120120
```
121+
122+
El mensaje del commit va **en inglés**, con prefijo `translate:` y
123+
`Fixes #<issue>` en el cuerpo. Lo que se traduce es la documentación, no el
124+
historial. Y el `.md` y su `.en.md` **en el mismo commit**: separarlos deja el
125+
archivo marcado como desactualizado de forma permanente.
121126
2. Ve a tu fork en GitHub
122127
3. Haz clic en "Compare & pull request"
123128
4. Completa la descripción del PR con detalles de tu traducción

tools/glossary.test.mjs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import assert from 'node:assert/strict';
33
import { readFileSync } from 'node:fs';
44
import { resolve } from 'node:path';
55
import { YAML } from 'zx';
6-
import { mask, lintText } from './lib/glossary.mjs';
6+
import { mask, lintText, selectFiles } from './lib/glossary.mjs';
77

88
const ROOT = resolve(import.meta.dirname, '..');
99
const { rules } = YAML.parse(readFileSync(resolve(ROOT, 'glosario.yml'), 'utf8'));
@@ -117,3 +117,34 @@ test('sí aplica al texto visible junto a un atributo', () => {
117117
test('no aplica en definiciones de enlace de referencia', () => {
118118
assert.deepEqual(hits('[GuiaX]: tools/cli/librería-y "Título"'), []);
119119
});
120+
121+
// --- selección de archivos ---
122+
123+
const ALL = [
124+
'adev-es/src/content/ai/webmcp.md',
125+
'adev-es/src/content/guide/di/lazy-loading-services.md',
126+
'adev-es/src/content/reference/releases.md',
127+
];
128+
129+
test('sin filtros revisa todas las traducciones', () => {
130+
assert.deepEqual(selectFiles(ALL, []), { files: ALL, unmatched: [] });
131+
});
132+
133+
test('acepta varias rutas a la vez, no solo la primera', () => {
134+
const { files, unmatched } = selectFiles(ALL, [
135+
'adev-es/src/content/ai/webmcp.md',
136+
'adev-es/src/content/reference/releases.md',
137+
]);
138+
assert.deepEqual(files, [ALL[0], ALL[2]]);
139+
assert.deepEqual(unmatched, []);
140+
});
141+
142+
test('delata la ruta que no casa con ninguna traducción', () => {
143+
const { unmatched } = selectFiles(ALL, ['guide/di', 'reference/no-existe.md']);
144+
assert.deepEqual(unmatched, ['reference/no-existe.md']);
145+
});
146+
147+
test('no cuenta un archivo dos veces aunque casen dos filtros', () => {
148+
const { files } = selectFiles(ALL, ['ai/', 'webmcp']);
149+
assert.deepEqual(files, [ALL[0]]);
150+
});

tools/lib/glossary.mjs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,20 @@ export function lintText(file, text, rules) {
5959

6060
return found.sort((a, b) => a.line - b.line);
6161
}
62+
63+
/**
64+
* Selecciona qué traducciones revisar a partir de los filtros de la línea de
65+
* comandos. Sin filtros, se revisan todas.
66+
*
67+
* Devuelve también los filtros que no casaron con nada: un filtro que no
68+
* encuentra archivos casi siempre es una ruta mal escrita, y darlo por bueno
69+
* haría pasar la revisión sin haber mirado nada.
70+
*/
71+
export function selectFiles(all, filters) {
72+
if (filters.length === 0) return { files: all, unmatched: [] };
73+
74+
const unmatched = filters.filter((p) => !all.some((f) => f.includes(p)));
75+
const files = all.filter((f) => filters.some((p) => f.includes(p)));
76+
77+
return { files, unmatched };
78+
}

tools/lint-glossary.mjs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { readFile } from 'node:fs/promises';
22
import { resolve } from 'node:path';
33
import { $, argv, chalk, glob, YAML } from 'zx';
4-
import { lintText } from './lib/glossary.mjs';
4+
import { lintText, selectFiles } from './lib/glossary.mjs';
55

66
/**
77
* Verifica la consistencia terminológica de las traducciones al español.
@@ -14,8 +14,9 @@ import { lintText } from './lib/glossary.mjs';
1414
* rutas de archivo y anchors explícitos `{#id}`.
1515
*
1616
* Uso:
17-
* npm run lint-glossary (todas las traducciones)
18-
* npm run lint-glossary -- guide/forms (solo una ruta)
17+
* npm run lint-glossary (todas las traducciones)
18+
* npm run lint-glossary -- guide/forms (una ruta)
19+
* npm run lint-glossary -- guide/forms ai/ (varias)
1920
*/
2021

2122
$.verbose = false;
@@ -27,9 +28,13 @@ try {
2728
const raw = await readFile(resolve(ROOT, 'glosario.yml'), 'utf8');
2829
const { rules } = YAML.parse(raw);
2930

30-
const filter = argv._[0];
3131
const all = await glob([`${CONTENT_DIR}/**/*.md`, `!${CONTENT_DIR}/**/*.en.md`], { cwd: ROOT });
32-
const files = filter ? all.filter((f) => f.includes(filter)) : all;
32+
const { files, unmatched } = selectFiles(all, argv._.map(String));
33+
34+
if (unmatched.length > 0) {
35+
console.error(chalk.red(`\nNo hay ninguna traducción que case con: ${unmatched.join(', ')}\n`));
36+
process.exit(1);
37+
}
3338

3439
const findings = [];
3540

0 commit comments

Comments
 (0)