Landing page i18n: Spanish, Portuguese, and Italian translations via an Anthropic-backed CLI

2026-04-06

Miami is a Spanish-and-Portuguese-speaking city and a lot of my target users (property managers, board members, owners) default to one of those languages before English. Italian is mostly opportunistic — a significant Italian-speaking condo population in South Florida and a translation is cheap when you already have the pipeline built.

This PR adds all three, plus a language switcher, hreflang tags, and Vercel locale routing, for 16 total landing pages (4 original English × 4 locales).

The translation CLI

honestcam i18n is a new command family with three subcommands:

honestcam i18n add-language <code>
honestcam i18n translate <path>
honestcam i18n sync
  • add-language scaffolds a new locale: creates the directory structure, copies the English pages as starting points, and queues them for translation.
  • translate runs any given HTML page through the translation pipeline.
  • sync re-translates any page whose English source has been modified since the last translation (tracked via a .i18n-sources.json manifest).

Under the hood each page is walked as an HTML tree, text nodes are extracted and sent to the Anthropic API for translation, and the results are written back into the same tree. HTML structure is preserved — tags, attributes, class names, IDs, script blocks, and JSON-LD blobs are never touched. Only actual user-visible text gets translated. That matters because (a) the English pages use a specific CSS class naming convention that I don't want translated, and (b) the JSON-LD structured data on each page has machine-readable fields (@type, addressCountry) that must stay in English.

Language switcher

A new widget in the nav of every landing page: EN | ES | IT | PT. Clicking a flag swaps the user to the same page in the new locale — managers-ES → managers-IT, not managers-ES → index-IT. That required a small bit of URL awareness: the switcher reads the current filename and rewrites only the locale prefix.

hreflang, for search engines

Every one of the 16 pages now emits <link rel="alternate" hreflang="..."> tags pointing at the three other-locale versions of itself, plus an x-default pointing at the English version. Google uses these hints to return the right locale in search results without duplicating content across languages. Without them, the four locales would compete against each other for ranking, which would be strictly worse than having one.

Vercel locale routing

Added rewrites to vercel.json so that /es, /pt, and /it as root URLs route to the right subdirectory. Someone can land on honest-cam.com/es and see the Spanish homepage directly, without seeing /es/index.html in the URL bar.

CSS

A new .lang-switch class with mobile responsiveness. The switcher collapses into a compact dropdown on narrow viewports so it doesn't fight with the hero headline for space.

Adding a new language later

The whole reason the translation pipeline is a CLI is that adding the next language is one command:

honestcam i18n add-language ht    # Haitian Creole

add-language runs translate against every existing English page in the correct order, registers the new locale in vercel.json and the language switcher widget, and emits the right hreflang tags on every existing page so the new locale is discoverable from them. No manual file shuffling. Haitian Creole is the likely next addition for the Miami market.

A small non-i18n extra

While I was already in the landing pages I added a Miami Beach bulk disposal link to the government portals reference section. Nothing to do with translation, but it was a note-to-self I kept forgetting.

Test plan

  • All 16 pages render correctly on desktop and mobile.
  • Language switcher navigates correctly between locales.
  • Nav links within a single-locale page stay within that locale.
  • Every page has a full set of hreflang tags.
  • <html lang="…"> matches the locale on every page.
  • CSS, JS, and images still load from the shared ../ paths and don't get duplicated per locale.
  • Zero console errors on any page.
  • Verify the Vercel preview deployment's locale routing end to end.

PR: https://github.com/StevieIsmagic/honest-cam/pull/15