The site now speaks German, French, Spanish, Ukrainian, Russian, Chinese,
Korean and Hindi alongside English — 623 strings per language, 5,607
translations, covering the landing page, the key exchange, the chat, group
calls and every error along the way.
Each locale is a real page at a real URL (/de/, /fr/, …), generated at build
time from locales/*.json. That is the whole point: the app is client-rendered,
so a language that only swaps strings at runtime has no address for a crawler
to index and no link anyone can share. Each page carries its own canonical, a
reciprocal hreflang cluster and translated schema.org.
The URL decides the language, always. A stored preference only applies where
the address does not say, and nothing ever redirects on Accept-Language —
that is how sites become invisible to search engines outside one country.
robots.txt and sitemap.xml did not exist before; both are generated now.
Bugs found on the way, each with a test that would have caught it:
- partner logos used page-relative paths and 404'd from any /xx/ page
- post-build stamped ?v= only into the root shell, leaving locales behind
- "Back online" appeared on every page load, not just after being offline
- update timestamps were hard-coded to US format for every reader
- t() threw on a partial window, taking whole components down with it
Ground-up visual redesign across the entire surface (landing, connection
setup, chat header, security verification report, file transfer, PWA
install/update/offline dialogs).
Offline reworked: store-and-forward queue (send while offline → queued,
delivered on reconnect), WhatsApp-style per-message delivery status
(sending/sent/delivered/not-sent) via delivery receipts, offline buffering
for messages to an offline peer, and offline state no longer leaking into
the connection indicator. Resilient chunked file transfer with retransmission
and auto-save. README + screenshots added.