Free, open-source EU VAT rates for all 27 member states + UK. Published as native packages for npm, PyPI, Packagist, Go, and RubyGems.
Open-source spec and engine that turns straight quotes, hyphens and three dots into the correct curly quotes, dashes, ellipses and no-break spaces for 10 locales — with a live playground at polytypo.dev running the real engine in the browser.
Key Results

Every language has its own rules for quotes, dashes and spacing, and almost no software respects them. English text gets French guillemets from a careless find-and-replace; German gets English's curly quotes the wrong way round; every locale gets a straight - where a language-specific dash belongs. The usual fix is a pile of regexes that half-work for one language and quietly corrupt the next — because "smart quotes" was never actually a locale-aware problem, it was a find-and-replace problem wearing a locale's clothes.
The deeper issue is that nobody had written down what "correct" means, locale by locale, in a form software could check itself against. Style guides exist — Duden for German, the Imprimerie nationale for French, Kotus for Finnish, Chicago for US English, Мильчин for Russian — but each lives in its own document, in its own language, with no shared vocabulary and no test suite. A team shipping to ten markets either hand-rolls ten sets of rules or ships the same English-shaped punctuation everywhere.
I wrote polytypo spec-first: the rules exist as versioned prose and JSON Schema-validated locale data in spec/ before any code touches them, and the contribution order is enforced — rule semantics, then locale data with a mandatory citation, then fixtures, then the implementation. Nine rules run in a fixed order (spacing, ellipsis, ranges, dashes, hyphen-binding, quotes, apostrophe, symbols, no-break spaces) across 10 locales, each one resolved from an alias table rather than a platform locale library, so en always means en-US and an unrecognized locale throws instead of silently falling back to English.
Every fixture in spec/fixtures/ is a literal in/out pair, mirrored by a CI-generated escaped-\uXXXX copy — because a diff full of invisible U+00A0 no-break spaces is unreviewable by a human otherwise, and the no-break-space rule's entire job is to be invisible. The same fixture set doubles as an idempotency check: the test runner asserts that running output back through the engine returns it unchanged, so "already correct" text is provably a fixed point, not just assumed to be one.
The spec ships with two conformance-tested implementations — JavaScript/TypeScript on npm, Python on PyPI — both validated against the identical fixture suite, so a string transformed in a Node service and the same string transformed in a Python pipeline produce byte-identical output. Go, PHP and Ruby follow the same spec when they land, each as its own repository rather than a monorepo package, so a consumer only ever installs the language they use.
The promo site at polytypo.dev is not a static demo of hand-picked examples — the playground loads the actual published npm bundle and runs the real transform() call on whatever text a visitor pastes in, with the exact API call shown alongside the output. The "six correct answers" comparison on the homepage runs the same input sentence through all ten locales live, so the claim that every one of them is correct is checked by the reader, not taken on faith.
Deploy is gated on provenance, not just on tests passing. pages-deploy.yml takes a workflow_dispatch input naming the exact commit SHA to deploy, then verifies that SHA is either an ancestor of main or tagged site-vX.Y.Z before the build job runs at all — a manually triggered deploy of an unreviewed or off-branch commit is refused outright, on top of GitHub's own protected-environment approval for github-pages. Nothing merges to production by accident, and nothing deploys without first proving where it came from.
The engine is a set of disjoint substring replacements, nothing else. The parser only locates text — in html mode it walks the DOM and skips code, pre, kbd, samp, var, script, style, textarea and all attributes; in markdown mode it skips code spans, fenced blocks and link destinations. It never rewrites structure, so running polytypo over a document can't turn a code sample's straight quotes into curly ones by mistake.
The promo site tests its own restraint. Its test suite (tests/promo/) includes a no-external-requests check and a hostile-output check — assertions that the playground never phones home and that it can't be coaxed into rendering unsanitized output — alongside a link-depth test and a generated-pages check that catches a stale build before it ships.
Locale claims cite a source, or they don't exist. Every spec/locales/<code>.json file carries a mandatory sources array pointing at the normative reference for that locale, and a dedicated fixture-citation-guard test fails the build if a locale file is missing one. A style choice in polytypo is traceable to a style guide, not to a contributor's preference.
| Metric | Value |
|---|---|
| Spec version | 1.0.0 |
| Locales | 10 — en-US, en-GB, de-DE, de-CH, fr, fr-CA, ru, fi, sv, el |
| Rules | 9, fixed order, each independently toggleable |
| Implementations shipped | 2 — JavaScript/TypeScript (npm), Python (PyPI) |
| Modes | text, HTML (attribute- and code-tag-safe), Markdown (CommonMark-safe) |
| Conformance | Fixture suite per locale, every case doubling as an idempotency check |
| License | MIT for the code; brand assets under separate terms |
Building polytypo end to end — spec, two runtimes, a public conformance suite and a live promo site with a SHA-gated deploy pipeline — is the same discipline I bring to client work: write down what "correct" means before writing code, make the test suite prove it rather than assert it, and treat the deploy path itself as something that needs a security review, not just a green checkmark.
AvailableNeed something similar?
I build custom solutions — from APIs to full products. Let's talk about your project.
Free, open-source EU VAT rates for all 27 member states + UK. Published as native packages for npm, PyPI, Packagist, Go, and RubyGems.
Zero-dependency Halloween screen-corner decorations and ambient page effects — one script tag or npm install, class-driven, seasonal by default, under 10 KB
How to publish one dataset to npm, PyPI, Go Module, RubyGems, and Packagist automatically with GitHub Actions — architecture, versioning, and per-ecosystem
halloween.js adds Halloween decorations to any website — one class flips them on, a real-calendar season window turns them off automatically, and a