Iurii RoguliaIurii Rogulia
AboutServicesPricingProjectsStackReviewsPhrasesBlog
Contact
Iurii ships.

Iurii Rogulia, senior full-stack software engineer. Professionally building software since 2001.

Think of a number
PricingQuality checklistPrivacy PolicyCookie Policy

Business

TMI Iurii Rogulia
VAT ID: FI29845875
DUNS: 368664211
Lappeenranta, Finland 🇫🇮

[email protected]
Back to projects

Iurii Specifies: polytypo.dev — Locale-Correct Typography Engine and Spec

September 8, 2026

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.

Live Demo Source code

Stack

TypeScriptJavaScriptPython

Libraries

VitestAJV

Services

npmPyPIGitHub ActionsGitHub Pages

Topics

Open SourceDeveloper Toolsi18nTypographyConformance Testing

Key Results

  • Spec 1.0.0, frozen: 9 ordered rules across 10 locales, each locale claim backed by a cited normative source (Duden, Imprimerie nationale, Kotus, Chicago, Мильчин and others)
  • Two conformance-tested implementations shipped — JavaScript/TypeScript (npm) and Python (PyPI) — both passing the same fixture suite
  • Every fixture doubles as an idempotency case: transform(transform(x)) == transform(x), asserted in CI, not just claimed
  • Live playground at polytypo.dev runs the published npm package directly in the browser — no server round-trip, no mocked output
  • Deploy to GitHub Pages requires an exact, already-reviewed commit SHA — a workflow_dispatch input checked against main or a signed release tag before a single byte builds
polytypo.dev — Locale-Correct Typography Engine and Spec

CI

npm

PyPI

License

The Problem

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.

The Solution

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.

Under the Hood

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.

Results

MetricValue
Spec version1.0.0
Locales10 — en-US, en-GB, de-DE, de-CH, fr, fr-CA, ru, fi, sv, el
Rules9, fixed order, each independently toggleable
Implementations shipped2 — JavaScript/TypeScript (npm), Python (PyPI)
Modestext, HTML (attribute- and code-tag-safe), Markdown (CommonMark-safe)
ConformanceFixture suite per locale, every case doubling as an idempotency check
LicenseMIT 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.

Iurii RoguliaAvailable

Need something similar?

I build custom solutions — from APIs to full products. Let's talk about your project.

View all projects

Related projects

eu-vat-rates-data — Free & Open-Source EU VAT Rates Dataset
eu-vat-rates-data — Free & Open-Source EU VAT Rates Dataset
February 25, 2026
eu-vat-rates-data — Free & Open-Source EU VAT Rates Dataset

Free, open-source EU VAT rates for all 27 member states + UK. Published as native packages for npm, PyPI, Packagist, Go, and RubyGems.

Stack

TypeScriptPythonPHPGoRuby

Services

GitHub ActionsnpmPyPIPackagistRubyGems

Topics

Open SourceTax/VATAutomation
halloween.js — Zero-Dependency Halloween Effects for Any Website
halloween.js — Zero-Dependency Halloween Effects for Any Website
August 26, 2026
halloween.js — Zero-Dependency Halloween Effects for Any Website

Zero-dependency Halloween screen-corner decorations and ambient page effects — one script tag or npm install, class-driven, seasonal by default, under 10 KB

Stack

TypeScript

Services

npmGitHub ActionsGitHub Pages

Topics

Open SourceVanilla JSAnimationAccessibility

Related posts

Publishing One Package to Five Registries with GitHub Actions
March 27, 2026· 10 min
Publishing One Package to Five Registries with GitHub Actions

How to publish one dataset to npm, PyPI, Go Module, RubyGems, and Packagist automatically with GitHub Actions — architecture, versioning, and per-ecosystem

Stack

TypeScriptPythonPHPGoRuby

Services

GitHub ActionsnpmPyPIPackagistRubyGems

Topics

Open SourceTax/VATAutomation
Halloween Effects for Any Website, Turned On by One CSS Class
August 28, 2026· 10 min
Halloween Effects for Any Website, Turned On by One CSS Class

halloween.js adds Halloween decorations to any website — one class flips them on, a real-calendar season window turns them off automatically, and a

Stack

TypeScript

Services

npmGitHub Actions

Topics

Open SourceVanilla JSAnimationAccessibility