• trilingual-publishing
  • structured-data

jewelry.am: a trilingual reference site, live in 8 days

Published on
Discuss a full-time role

Lighthouse, median of 3

213 URLs, Lighthouse 95/100/100/100, CLS 0.000

Role
Designed and built the platform
Duration
First commit 2026-06-11, live 2026-06-19
Scope
213 URLs, three locales, 571 commits
Measurement
Lighthouse 95/100/100/100, CLS 0.000 (2026-09-11)

The English home page. The same page exists in Armenian and Russian at its own URL.

jewelry.am is a reference publication on Armenian jewelry: long-form articles, a register of makers, and the citation apparatus that goes with them, published in English, Armenian and Russian, with every page at its own URL in each language. I designed and built the platform: architecture, front end, the three-language setup, structured data, build and release tooling, and the measurement pipeline. Google had 194 of its 213 production URLs indexed when I checked them one at a time through the Search Console inspection API on 2026-09-12, and the raw pull of that date is committed in the repository.

Live site: jewelry.am.

A reference that holds up when someone checks it

The site had to survive being used as a source. Articles carry their references. The maker register gives each maker a page of their own. The text is published under CC BY 4.0, which lets anyone reuse it with attribution, and the photographs stay under their own licenses, stated per figure in the markup. A dataset drawn from the same corpus is published with a DOI, a permanent identifier that keeps resolving after URLs move: 10.5281/zenodo.22257644, resolving since 2026-09-02, with a reproduction script that runs.

Live in eight days, with three scripts to carry

First commit 2026-06-11. Live and publicly indexable on its own domain 2026-06-19, eight days later, per the repository's own log and the go-live entry in its project log.

Three writing systems share one design: Latin, Armenian and Cyrillic. That decides how the fonts load, and a wrong call is paid for by a locale that never asked for it.

A new article has to enter eight hand-edited registries. Each of them fails open. A missed entry renders nothing, skips enrichment, or omits a URL, and the build still passes, so typecheck, lint and build cannot see the failure that matters.

What I built

Fonts took four instrumented rounds. Preloading the Armenian face across the whole site cost the Russian locale 35 KB on its critical path, two Lighthouse performance points and 145 ms of LCP, the time until the largest thing on screen finishes drawing. Loading that face on demand instead cost the Armenian locale 0.018 CLS, the score for how much the page jumps around while it loads. The shipped form is ten self-hosted faces with a per-locale preload list chosen from a codepoint census of the built corpus. All four rounds are recorded in the repository's framework rules.

A build-tracing defect packed 2,332 files into every server function, 1,044 of them scratch output. After the fix each function carries 208, verified by reading each route's own trace manifest rather than the build warning. Both numbers come from that rules file and the fix commit.

Three custom pre-merge scans cover the eight fail-open registries. Each scan is negative-tested against a deliberately broken input, so it has to catch a real fault before I let it clear a release. The scripts and the checklist they enforce live in the repository.

Structured data is typed. A seven-layer interconnected @graph, the machine-readable description that tells search and answer engines what a page is, is built with schema-dts and no JSON is written by hand. Social cards render per route at request time for 36 routed segments, per the wiring scan's own output.

People and places are pinned to Wikidata, the open database that search engines read as identity: 39 identifiers, 228 occurrences across three locales. A verifier checks each identifier against Wikidata's own record and refuses to write when one that was previously verified stops resolving. Its ledger is committed.

Translation provenance is machine-checkable. Every shipped non-English string traces to a committed model-output artifact, 28 of 28 on the last audited batch, with the provenance verifier re-run on 2026-09-12. Translated bytes are moved by script and none is retyped by hand.

The figures the project quotes come out of a pipeline. It pulls from five external APIs, Google Search Console, Yandex Webmaster, Bing Webmaster, Vercel Analytics and Wikimedia, writes dated raw pulls, and feeds two generators that produce the project's figures of record. Both generators are deterministic: two runs give byte-identical output, and each has a known-positive test it has to reproduce before it is trusted. The pull scripts, the generators and their outputs are committed.

Where it stands, as of 2026-09-12

  • 571 commits between 2026-06-11 and 2026-09-12, by git rev-list --count on main.
  • 213 URLs in production, all prerendered: 26 long-form articles, a 42-entry maker register with 36 profile pages, and five topic indexes, each in three locales, per the site's sitemap and the article and register registries in source.
  • 194 of those 213 indexed by Google, checked URL by URL through the Search Console inspection API on 2026-09-12, and 186 searchable pages in Yandex on 2026-09-11. The raw pulls of both dates are committed.
  • Lighthouse on production /en, mobile, run 2026-09-11: 95 performance, 100 accessibility, 100 best practices, 100 SEO.
  • Cumulative Layout Shift 0.000 on every page type and form factor, the median of three runs across the home page, a topic index, an article and a profile page, on mobile and desktop, from the Lighthouse reports committed 2026-09-11.

Stack

Next.js 16.2.9 (App Router), React 19.2.7, TypeScript in strict mode, next-intl 4.13.0, next-sanity 13.1.0, schema-dts 2.0.0, pnpm and Vercel, per the repository's package.json.