Contribute
Add your language, or fix a rule.
polytypo is open source and the spec is the product: a locale is a data file, fixtures and a citation, and a rule is prose that every runtime implements. Pull requests are welcome on GitHub. Locales that exist today are listed on Locales.
Adding a locale
This is the sequence every locale is added through. xx stands for the new locale's
tag.
-
Read
spec/schema/locale.schema.jsonIt defines what a locale file may say: quote glyphs and inner-space rule, the parenthetical and range dash convention, whether ellipsis abbreviates after terminal punctuation, hyphen-binding word lists, and the no-break-space triggers. Literal strings, literal code points, string lists and enum values only — no regex, no priority numbers, no conditionals. If the locale needs a distinction the schema can't express, that is a spec discussion, not a workaround in the locale file.
-
Write
spec/locales/xx.jsonOne field at a time, each backed by a citation in the mandatory
sourcesarray — a national style guide, a standards body (Accademia della Crusca, a major dictionary or publisher's house style), never a blog post or a contributor's own habit. A locale is accepted only as the triple this project requires: data, fixtures, citation. Where the source exists but is paywalled, the entry names the prevailing form it standardises on and is marked as a decision rather than a citation — uniform everywhere beats correct in a book nobody can open. -
Add cases to
spec/fixtures/xx.jsonOne
{ "in", "out", "rule" }pair per behaviour the citation asserts, plus at least one edge case per rule — the ordinary sentence is not the interesting test, the nested quote or nasty numeric range is. Every fixture is automatically also an idempotency case:transform(out) === outis checked for free. -
Register it in
spec/locales/registry.jsonAdd
"xx"to thelocalesarray. Only add an alias if the fallback is itself a cited decision, not a guess of convenience. -
Validate and test
npm run validate:specchecks the file against the schema and NFC-normalizes it;npm testchecks the structure and citations of the new fixtures. Each runtime's conformance suite then runs every fixture, and every fixture doubles as an idempotency case. All of it must be green — a red run blocks the locale, not just a warning. -
Open a pull request
Disagreements about what a language actually does are settled by citation, not preference — if a review turns up a more normative source, that source wins, not whoever argued longer.
Changing or adding a rule
Rules are cross-locale algorithms, not per-language code. spec/rules/order.json is
the single source of truth for a rule's id, pipeline order, default and which locale fields it
reads; spec/rules/<id>.md is normative prose written before the
implementation — extracting a spec from a finished implementation is exactly the rework this
project's spec-first discipline exists to avoid. A behaviour change starts with a fixture in
spec/fixtures/, not a change in a runtime. Implementation constraints that are
already binding, across all five runtimes: no regex in a rule (Go's RE2 has no lookbehind — a
rule is a single left-to-right scan over code points with explicit lookaround), index by Unicode
code point rather than a native string, and rules emit edits for the pipeline to apply
rather than mutating text directly, so applyEdits can enforce the contract instead
of trusting it.
Already using polytypo? Add our badge
Two lines: a script tag and a span. No per-embed network call. Pick a language and a theme, then copy the code into your footer or credits. Embedding notes cover Content Security Policy. Sites with the badge are listed on Sites using polytypo.
<script async src="https://polytypo.dev/badge.js"></script>
<span data-polytypo-lang="en" data-polytypo-theme="light"></span>