Lokalen beitragen
@templatical/quality liefert lokal-aware Datensätze, getrennt nach Sprache:
- Barrierefreiheits-Regelnachrichten (
src/accessibility/messages/{locale}.ts) — die Texte, die der Editor für jedesa11y.*-Issue zeigt. - Vague-Text-Dictionaries (
src/accessibility/dictionaries/{locale}.ts) — Phrasenlisten, die vona11y.link-vague-text,a11y.button-vague-labelunda11y.img-linked-no-contextgenutzt werden. - Struktur-Regelnachrichten (
src/structure/messages/{locale}.ts) — Texte für jedesstructure.*-Issue.
Jede Datenmenge spiegelt die Locale-Auswahl des Editors. Der Struktur-Linter hat kein Vague-Text-Dictionary-Pendant — seine Regeln sind deterministisch und sprachunabhängig, nur der Nachrichtentext muss übersetzt werden.
Verzeichnisstruktur
packages/quality/src/accessibility/messages/
en.ts ← Source of Truth (implizit typisiert)
de.ts ← annotiert mit `typeof en`
index.ts ← exportiert formatMessage(), getMessages()
packages/quality/src/accessibility/dictionaries/
en.ts
de.ts
index.ts ← exportiert getDictionary(), normalizeForMatch()
packages/quality/src/structure/messages/
en.ts ← Source of Truth
de.ts ← annotiert mit `typeof en`
index.ts ← exportiert formatStructureMessage(), getStructureMessages()Eine Locale hinzufügen
Du brauchst drei Dateien (oder zwei, falls du das Vague-Text-Dictionary auslässt): eine Message-Map pro Linter und ein Dictionary. Dateien ablegen — sie werden automatisch erkannt. Jede Locale-Registry wird zur Compile-Zeit per import.meta.glob gebaut, es gibt keine Map zu pflegen.
Folge dem typeof en-Muster in jeder Datei. Die Annotation ist der Vertrag: jeder fehlende Key, jeder zusätzliche Key oder jeder falsche Typ lässt pnpm run typecheck scheitern. Laufzeit-Paritätstests prüfen zusätzlich, dass {name}-Platzhalter über Locales hinweg übereinstimmen.
1. Barrierefreiheits-Regelnachrichten
accessibility/messages/<lang>.ts ablegen und jeden Wert übersetzen — {name}-Platzhalter exakt beibehalten:
// accessibility/messages/pt.ts
import type en from "./en";
const pt: typeof en = {
"a11y.img-missing-alt":
"Imagem sem texto alternativo. Adicione uma descrição curta ou marque a imagem como decorativa.",
"a11y.img-alt-too-long":
"Texto alternativo tem {length} caracteres; mantenha abaixo de {max}.",
// …ein Key pro Barrierefreiheits-Regel
};
export default pt;2. Vague-Text-Dictionary
accessibility/dictionaries/<lang>.ts ablegen:
// accessibility/dictionaries/pt.ts
import type en from "./en";
const pt: typeof en = {
vagueLinkText: ["clique aqui", "aqui", "leia mais", "saiba mais"],
vagueButtonLabels: ["clique aqui", "clique", "enviar"],
linkedImageActionHints: ["compre", "leia", "veja", "baixe", "descubra"],
};
export default pt;3. Struktur-Regelnachrichten
structure/messages/<lang>.ts ablegen:
// structure/messages/pt.ts
import type en from "./en";
const pt: typeof en = {
"structure.duplicate-block-id":
"ID de bloco aparece {count} vezes na árvore. Cada bloco precisa ter um ID único.",
"structure.section-column-mismatch":
'Seção usa layout "{layout}" (espera {expected} colunas) mas tem {actual}. Estado corrompido.',
// …ein Key pro Struktur-Regel
};
export default pt;Das war's — SUPPORTED_MESSAGE_LOCALES, SUPPORTED_DICTIONARY_LOCALES und SUPPORTED_STRUCTURE_MESSAGE_LOCALES reflektieren die neue Locale automatisch. Keine Registry zu editieren, kein Test zu aktualisieren.
Phrasen-Richtlinien (Vague-Text-Dictionary)
- Match, nicht Regex. Die Vague-Text-Regeln normalisieren den Anchor- / Button-Text — kleinschreiben, Whitespace zusammenfassen, führende/abschließende nicht-alphanumerische Zeichen (Interpunktion, Pfeile, dekorative Anführungszeichen) entfernen — und testen dann
phrases.includes(text). „Click here!", „→ click here" und „»click here«" kollabieren also alle zuclick hereund matchen denselben Dictionary-Eintrag. Füge keine Interpunktionsvarianten hinzu — sie sind redundant. Jeder Eintrag ist trotzdem ein exakter Phrasen-Match; versuche nicht, Regex-Muster zu codieren. - Nur Kleinbuchstaben. Der Vergleich ist auf der Input-Seite case-insensitive.
- Häufig, nicht erschöpfend. Ziel ist, die häufigsten vagen Phrasen zu erwischen, die Autoren der Sprache schreiben. Eine 50-Einträge-Liste richtet mehr Schaden an als Nutzen (False Positives).
- Englische Phrasen nicht übersetzen. Das Dictionary ist eine sprachübergreifende Vereinigung — die Phrasen jeder registrierten Locale matchen unabhängig von der aktiven
locale-Option. Deinept.tsbraucht also nur portugiesische Phrasen; das englischeclick hereist über die Vereinigung bereits abgedeckt. - Keine Regions-Duplikate.
de-ATlöst sich auf dieselbe Vereinigung auf; ein Eintrag pro Sprache. linkedImageActionHintsist pro Token, nicht pro Phrase.a11y.img-linked-no-contexttokenisiert den Alt-Text an Nicht-Buchstaben/Ziffer-Grenzen und prüft jeden Token gegen die Hint-Liste. Trage einzelne Action-Verben in der Form ein, in der Autoren sie schreiben („buy", „kaufen", „compre") — nicht Mehrwort-Phrasen. „jetzt kaufen" wird nie matchen, weil Tokens einzeln geprüft werden.
Wie das Matching aufgelöst wird
- Vague-Text-Dictionary —
getDictionary(locale)liefert eine Vereinigung der Phrasen (und Action-Hints) aller registrierten Locales. Daslocale-Argument wird der API-Symmetrie wegen akzeptiert, ändert aber aktuell nichts an der zurückgegebenen Menge — eine vage Phrase ist universell vage, und ein Action-Verb in einer beliebigen registrierten Sprache zählt als Link-Ziel-Kontext. Die Erkennung ist by design sprachübergreifend. - Regelnachrichten —
formatMessage(locale, ruleId, params?)(Barrierefreiheit) undformatStructureMessage(locale, ruleId, params?)(Struktur) lösen das lokalisierte Template über die jeweiligemessages/{locale}.ts-Datei auf und interpolieren{name}-Platzhalter. Beide fallen auf Englisch zurück, wenn die Locale nicht gebündelt ist.