MAISON CODE .
/ i18n · Remix · Global

Global Architecture: i18n Routing in Remix

Handling `/en-us`, `/fr-ca`, and `/jp`. How to build a locale-aware router and middleware for international Shopify stores.

AB
Alex B.
Global Architecture: i18n Routing in Remix

Building a store for one country is easy. Building a store for 20 countries is an architectural fractal. You need to handle:

  • Translation (Language: French)
  • Localization (Currency: CAD vs EUR)
  • Market Context (Inventory: US Warehouse vs EU Warehouse)

The URL Structure

We advocate for sub-folders, not sub-domains.

  • Bad: fr.maisoncode.paris (Splits Domain Authority).
  • Good: maisoncode.paris/fr (Consolidates SEO).

Remix Middleware Implementation

We use a customized i18n middleware in entry.server.tsx.

// Detect locale from URL -> Cookie -> Header
export function resolveLocale(request: Request): Locale {
  const url = new URL(request.url);
  const pathLocale = url.pathname.split('/')[1];
  
  if (supportedLocales.includes(pathLocale)) {
    return pathLocale;
  }
  
  // Fallback to GeoIP (Cloudflare Header)
  const country = request.headers.get('cf-ipcountry');
  return countryToLocaleMap[country] || 'en';
}

The Dictionary Context

We don’t hardcode strings. We fetch the dictionary (JSON) for the resolved locale and pass it to a React Context.

const t = useTranslation();
<h1>{t('product.addToCart')}</h1> 
// Renders "Ajouter au panier" if locale is fr

Hreflang Tags

This is critical for SEO. You must tell Google that /fr/chemise is the French version of /en/shirt. We auto-generate these headers for every page.

<link rel="alternate" hreflang="en" href="https://site.com/en/shirt" />
<link rel="alternate" hreflang="fr" href="https://site.com/fr/chemise" />

9. Localized Checkout (Markets Pro)

Routing traffic to /fr is useless if the Checkout is in USD. We leverage Shopify Markets Pro. Middleware detects the country fr. We create the Cart with {"country": "FR", "currency": "EUR"}. This locks the checkout to show duties, taxes (VAT), and local payment methods (Cartes Bancaires) automatically. The seamless flow from maisoncode.paris/fr to a Euro-denominated checkout is why we see +40% conversion in Europe.

10. Geo-Redirect Best Practices

Never Auto-Redirect without asking. If I am an American traveling in Paris, and you force me to .fr, I hate you. Pattern:

  1. Detect IP is France.
  2. Is User on .com?
  3. Show a small “Bar” at the top: “We see you are in France. Switch to France store? [Yes/No]”.
  4. Only redirect if they click Yes. Exceptions: If they directly type .fr, respect it.

11. Global Scale

You sell in US (USD) and UK (GBP). The product description is identical (English). Google sees this as “Duplicate Content”. Solution: Self-Referencing Canonicals. The URL site.com/us/shirt points canonical to site.com/us/shirt. The URL site.com/uk/shirt points canonical to site.com/uk/shirt. Combined with hreflang, this tells Google: “These are distinct pages targeting different markets, not copies.” Without this, Google de-indexes one of them.

7. Content Negotiation (Accept-Language)

Middleware shouldn’t just look at the URL. It should look at the Accept-Language header sent by the browser. If a user arrives at / (root), redirect them:

  1. Check URL Locale (None).
  2. Check Cookie NEXT_LOCALE.
  3. Check Header Accept-Language (fr-FR). -> Redirect to /fr.
  4. Check GeoIP (France). -> Redirect to /fr.
  5. Fallback -> /en. This “Smart Routing” increases conversion by landing the user in their mother tongue automatically.

12. Edge Localization (Cloudflare Workers)

Doing i18n on the origin server (Node.js) is too slow (100ms). We move it to the Edge. Cloudflare Workers intercept the request before it hits the server.

  1. Incoming: GET / (from IP: Berlin).
  2. Worker: “User is German. Redirect to /de.”
  3. Latency: 5ms. The user sees the German site instantly. No “Flash of Wrong Content”. This is “Localization at the Speed of Light”.

13. Currency Nuance: The Display Problem

“10.00 USD” != “10,00 USD”. In France, the space is a non-breaking space used as a thousand separator. In Japan, there are no decimals (Yen). Using Intl.NumberFormat is mandatory. But what about “Psychological Pricing”?

  • USD: $19.99
  • EUR: $19,95 (Europeans prefer ,95) We implement Price Rounding Rules per market. If the exchange rate says $19,12, we round it to $19,95 automatically. This makes the brand feel “Native”, not like a foreign dropshipper.

Why Maison Code Discusses This

At Maison Code, we don’t just translate words; we translate Context. We have deployed stores in 50+ countries for redundant Luxury Brands. We know that in Germany, you must show “Impressum”. We know that in Japan, the address form is reversed (Zip Code first). We build architectures that respect these cultural nuances by default. Your brand should feel local, everywhere.

15. Conclusion

Going global is 10% translation and 90% architecture. You can hire the best translators, but if your URL structure confuses Google, you will fail. If your checkout doesn’t show local taxes, you will fail. Build a “Global Core” with proper routing, middleware, and currency handling. Then, the world is your market.


Lost in translation?

We architect Global Rollouts for brands that refuse to compromise on local nuances.

Hire our Architects.