EURO-JAPAN COMPASS
Two Language Environments, Not One Bilingual Page
一つのバイリンガルページではなく、二つの言語環境




Overview
概要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.
Euro-Japan Compassのウェブサイトです。同社は、ドイツのChiaki Hollowayと英国のI Furushoが共同で設立した、日本企業の欧州市場進出を実務面から支援する会社です。対象は日本の中小企業で、それが技術的な判断の大半を決めました。日本語版は、英語版の後から届く翻訳の層であってはならないということです。
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.
よくある作り方は、ブラウザ上で文字を切り替える一つのページです。このサイトでは代わりにURLを分けています。/ は英語、/ja は日本語で、どちらも事前に生成された本物のHTMLです。これにより、日本語の訪問者には最初の表示から日本語が表示され、ちらつきもJavaScriptもありません。両方の言語が検索エンジンに登録され、それぞれの文書が正しい<html lang>を宣言できます。この属性は、スクリーンリーダーの読み上げ音声や、指定のフォントが読み込まれる前にブラウザが使うフォントを決めるものです。
How it was 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.
文章は型のついた一か所で管理しています。すべての文字列を { en, ja } の組で保存するため、日本語なしで英語だけを追加することは型の仕組み上できません。これが、二つの言語環境が気づかないうちにずれていくのを防ぎます。言語の切り替えはJavaScriptを無効にしても動きます。どちらの言語も常に本物のURLへの普通のリンクだからです。スクリプトは選択を記憶し、現在のセクションを引き継ぐだけです。そのため「プロセス」を読みながら言語を切り替えると、切り替え先でも「プロセス」が表示されます。/ への初回訪問時は表示前に一度だけ言語を推測しますが、保存された選択が常に優先され、/ja がリダイレクトされることはありません。
What it does
機能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.
切り替えボタンのついた一つのページではなく、二つの本物の静的文書です。サイトマップには両言語のURLと代替言語の情報をすべて記載しており、それによって互いの重複コンテンツとして扱われずに済みます。
Only the header, contact form, language switcher and the reveal observer are client components. The page is fully readable with scripting off.
クライアントコンポーネントは、ヘッダー、問い合わせフォーム、言語切り替え、表示アニメーションの監視だけです。スクリプトを無効にしても、ページはすべて読めます。
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.
英語はInter、日本語はNoto Sans JPで、CDNから取得せず自サイトで配信しています。実行時に第三者への通信がなく、レイアウトのずれもなく、ドイツを拠点とする会社にとってGDPR上の懸念もありません。日本語の文書は全体をNotoで組んでいるため、日本語の中の英字も同じベースラインと色味になります。
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.
問い合わせフォームはmailto:リンクに頼らず、JSONをPOSTします。mailto:は、存在しないかもしれないソフトウェアへの依頼です。メールアプリが登録されていないブラウザはURLを受け取って黙って捨て、訪問者には送信成功と表示され、問い合わせは届かず、どちらもそれに気づきません。キーが未設定の場合はmailto:にフォールバックしますが、確認メッセージでメールアプリが開いたとは表示しません。それは知りようがないからです。
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.
ロゴの元データとして手に入ったのは、LinkedInのページにあった200×200のJPEGだけでした。橋と羅針盤のマークをSVGで描き直してインラインで埋め込み、白地でも紺地でもcurrentColorで色を変えられるようにしました。忠実にトレースしたもので、その旨も記録しています。
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.
LinkedInへのリンクはありません。対象は日本の中小企業で、欧州ほどLinkedInがビジネスの標準になっていないからです。トップの写真も、創業者に見せかけた素材写真ではなく、まだ「仮」と明記したプレースホルダーです。
Under the hood
技術の詳細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.
ルートグループで言語ごとに独自のルートレイアウトを持たせ、一つのSiteコンポーネントがページを一つの言語で描画します。こうして、二つのコードベースがずれていくのではなく、事前生成した二つの環境で一つのデザインを保っています。すべての文字列は { en, ja } の組で管理しているため、日本語のない英語は型の仕組み上コミットできません。
The switcher works with JS offJavaScriptなしでも動く言語切り替え
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.
どちらの言語も常に本物のURLへの普通のリンクです。スクリプトは選択を記憶し、現在のセクションを引き継ぐだけなので、「プロセス」を読みながら切り替えれば切り替え先でも「プロセス」が表示されます。/ への初回訪問時は表示前に一度だけ推測しますが、保存された選択が常に優先され、/ja がリダイレクトされることはありません。
Why the form POSTsフォームがPOSTする理由
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.
mailto:は、存在しないかもしれないソフトウェアへの依頼です。メールアプリが登録されていないブラウザは珍しくなく、そうしたブラウザはURLを受け取って黙って捨てます。訪問者には成功と表示され、問い合わせは届かず、どちらも気づきません。今では、ユーザーに見える機能でmailto:だけに頼っているものはありません。
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.
WhatsApp、Facebook、LinkedInはいずれもog:imageをURL単位でキャッシュするため、同じファイル名を使い続けると古いプレビューが表示され続けます。画像は独立したHTMLファイルのスクリーンショットで、PNGではなくJPEGで配信し(約950KBが約150KBに)、変更のたびにファイル名を変えています。
Stack
技術構成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.
Next.js 16(App Router)、TypeScript、Tailwind CSS v4で作り、完全な静的サイトとして書き出し、サーバーの実行環境なしでFirebase Hostingから配信しています。ルートグループで言語ごとに独自のルートレイアウトを持たせ、一つのSiteコンポーネントがページ全体を一つの言語で描画します。デザインは一つ、環境は二つで、同期させるべきコードベースが二つあるわけではありません。robots.txt、sitemap.xml、Webマニフェストはビルド時に生成します。配色は無地の白、会社のロゴから取った紺色 #1E2D4A、唯一のアクセントとなる金色 #B99A5B で、質感は加えていません。デザイン作品ではなく、今の企業サイトとして見えるべきだからです。SNS用の画像は独立したHTMLファイルのスクリーンショットで、デザインが変わるたびに作り直せます。PNGではなくJPEGで配信し(WhatsAppは大きな画像の扱いが不安定なため、約950KBを約150KBにしました)、変更のたびにファイル名を変えています。WhatsApp、Facebook、LinkedInはいずれもog:imageをURL単位でキャッシュし、古いプレビューを表示し続けるからです。TypeScriptはバージョン6に固定しています。Next.js 16はまだTypeScript 7のコンパイラAPIに対応しておらず、明確なエラーでビルドが失敗するためです。
Where it is now
現状と今後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.
サイトはeuro-japan-compass.web.appで両言語とも公開中で、静的に書き出し、サイトマップとhreflangも設定済みです。残っているのはコードではなくコンテンツです。「仮」のプレースホルダーに代わる創業者本人の写真と二人の写真、問い合わせフォームがmailto:に頼らずに済むためのWeb3Formsのキー、独自ドメイン、そしてSearch Consoleへの登録です。
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.
「バイリンガル」は、切り替えボタンの意味で使われがちな言葉です。きちんと実現するには、二つの文書、二つの組版の体系、二つの検索インデックスがあることを受け入れ、その組が離れられないように型の仕組みを作る必要があります。