EURO-JAPAN COMPASS
Two Language Environments, Not One Bilingual Page

A site for Euro-Japan Compass — hands-on European market support for Japanese companies, co-founded by Chiaki Holloway in Germany and I Furusho in the United Kingdom. The audience is Japanese SMEs, which decided most of the engineering: the Japanese half cannot be a translation layer that arrives after the English does.
Origin Story開発の背景
The obvious build is one page that swaps its text in the browser. This one splits the routes instead — / is English, /ja is Japanese, and both are real prerendered HTML. That is what gives a Japanese visitor Japanese in the first paint with no flash and no JavaScript, lets both halves be indexed rather than one, and lets each document set an honest <html lang>, which is what decides screen-reader voice and the font a browser falls back to before yours has loaded.
How It's Builtつくり方
Copy lives in one typed place: every string is stored as a { en, ja } pair, so the type system makes it impossible to add English without the Japanese — which is what stops the two environments quietly drifting apart. The language switcher works with JavaScript disabled, because both languages are always plain links to real URLs; script only adds memory of the choice and carries the current section across, so switching language while reading Process lands on Process. A first visit to / guesses once, inline and before paint, but a stored choice always wins and /ja never redirects.
Engineering Notesエンジニアリングノート
Two documents, one design
Route groups give each language its own root layout while a single Site component renders the page in one language. That is what keeps one design across two prerendered environments instead of two codebases drifting apart — and every string lives as an { en, ja } pair, so the type system makes English-without-Japanese impossible to commit.
The switcher works with JS off
Both languages are always plain links to real URLs. Script only adds memory of the choice and carries the current section across, so switching language while reading Process lands on Process. The first visit to / guesses once, inline and before paint, but a stored choice always wins and /ja never redirects.
Why the form POSTs
mailto: is a request to software that may not exist. A browser with no registered mail handler — a very common state — accepts the URL and discards it in silence: the visitor sees success, the enquiry is never sent, and neither side finds out. Nothing user-facing depends on mailto: alone any more.
Cache-busting the share card
WhatsApp, Facebook and LinkedIn all cache og:image by URL, so reusing a filename keeps serving the stale preview. The card is a screenshot of a standalone HTML file, shipped as JPEG rather than PNG — ~950KB became ~150KB — and renamed whenever it changes.
Galleryギャラリー



Bilingual is a word people use for a toggle. Doing it properly means accepting that you have two documents with two typographic systems and two indexes, and building the type system so the pair cannot come apart.
What It Does主な機能
Prerendered in Both Languages
Two real static documents rather than one page with a toggle. The sitemap lists both language URLs with the full alternates set, which is what stops them being read as duplicates of each other.
Readable Without JavaScript
Only the header, contact form, language switcher and the reveal observer are client components. The page is fully readable with scripting off.
Self-Hosted Type
Inter for English and Noto Sans JP for Japanese, self-hosted rather than fetched from a CDN — no third-party request at runtime, no layout shift, and no GDPR question mark for a company operating out of Germany. The Japanese document is set in Noto throughout so Latin runs inside it keep one baseline and colour.
A Form That Cannot Fail Silently
The contact form POSTs JSON rather than relying on a mailto: link. A mailto: is a request to software that may not exist — a browser with no registered mail handler accepts the URL and discards it in silence, the visitor sees a success message, the enquiry is never sent, and neither side ever finds out. With no key configured it still degrades to mailto:, but its confirmation no longer claims the mail app opened, because that cannot be known.
The Mark, Redrawn
The only available source for the logo was a 200×200 JPEG from a LinkedIn page. The bridge-and-compass mark was redrawn as SVG and inlined so it can take currentColor on both the white and the navy ground. It is a close trace, and it is documented as one.
Deliberate Omissions
No LinkedIn links, because the audience is Japanese SMEs where LinkedIn is not the professional norm it is in Europe. The hero photograph is still a labelled placeholder rather than a stock image pretending to be the founders.
Where It Stands現在地
The site is live at euro-japan-compass.web.app in both languages, statically exported with sitemap and hreflang in place. What is outstanding is content rather than code: real founder portraits and a photograph of the two of them to replace the labelled placeholder, a Web3Forms key so the contact form stops falling back to mailto:, a custom domain, and a Search Console submission.
Next.js 16 with the App Router, TypeScript and Tailwind CSS v4, exported as a fully static site and served from Firebase Hosting with no server runtime. Route groups give each language its own root layout while a single Site component renders the whole page in one language, so there is one design and two environments rather than two codebases to keep in sync. robots.txt, sitemap.xml and the web manifest are emitted at build time. The palette is flat white, navy #1E2D4A taken from the company mark, and gold #B99A5B as the only accent, with no texture — it should read as a current corporate site rather than a designed artefact. The social card is a screenshot of a standalone HTML file so it can be rebuilt whenever the design moves, shipped as JPEG rather than PNG (WhatsApp is unreliable with large images; ~950KB became ~150KB) and renamed on every change, because WhatsApp, Facebook and LinkedIn all cache og:image by URL and will keep serving the stale preview. TypeScript is pinned to 6 — Next.js 16 does not yet accept the TypeScript 7 compiler API and fails the build with a clear message.