Overview
This integration supports two i18n routing strategies:
- Domain-based routing - Different domains serve different locales (e.g.,
www.example.com,www.example.de) - Path-based routing - Locale prefix in the URL path (e.g.,
www.example.com/en-gb/p/123,www.example.com/de-de/p/123)
Path-based routing is ideal when you want to:
- Serve multiple languages from a single domain
- Simplify domain management and SSL certificates
- Allow users to easily switch between languages
- Maintain SEO benefits with clear locale-specific URLs
How It Works
Locale Detection
The system can extract locales from URL paths using either standard locale codes or custom prefixes:
- Standard locale pattern:
[a-z]{2}-[a-z]{2}(e.g.,en-gb,de-de,fr-fr) - Custom prefix pattern: User-defined URL-safe strings (e.g.,
deutsch,row,en-mt)
When a user visits a URL, the system follows this detection order:
- Direct locale match: If the path starts with a valid locale code or custom prefix, the system uses that locale
- Cookie fallback: If no locale in path, checks for a locale preference cookie
- Default fallback: Uses the configured
fallbackLocalefor the domain
Configuration Options
For full config explanations visit the config page. The key properties needed for path based routing are:
| Property | Type | Description |
|---|---|---|
pathBasedRouting | boolean | Enables path-based routing for the domain |
fallbackLocale | string | Default locale to redirect to (format: xx-xx) |
locales | object | Map of locale codes to locale configurations |
localeCookie | string | Name of cookie used to store user’s locale preference |
customPrefix | string | Optional custom URL prefix for each locale (within locale config) |
Custom Prefix Configuration
i18n: {
domains: {
'www.example.com': {
pathBasedRouting: true,
fallbackLocale: 'en-us',
locales: {
'en-us': {
commerce: { endpoint: 'https://api.example.com/en/graphql' }
},
'fr-fr': {
customPrefix: 'francais',
commerce: { endpoint: 'https://api.example.com/fr/graphql' }
},
'de-de': {
customPrefix: 'german',
commerce: { endpoint: 'https://api.example.com/de/graphql' }
}
}
}
},
localeCookie: 'locale_V6'
}
Features
Automatic Redirects
The system automatically redirects users to appropriate localized URLs:
With standard locale codes:
- No locale in path + valid cookie:
/page→/de-de/page(if cookie hasde-de) - No locale in path + no cookie:
/page→/en-gb/page(using fallbackLocale) - Invalid locale in path:
/invalid/page→/en-gb/page(using fallbackLocale)
With custom prefixes:
-
No locale in path + valid cookie:
/page→/deutsch/page(if cookie hasde-dewithcustomPrefix: "deutsch") -
No locale in path + no cookie:
/page→/english/page(using fallbackLocale with custom prefix) -
Invalid prefix in path:
/invalid/page→/english/page(using fallbackLocale) -
Consistency: The same locale preference works across different configurations
-
Flexibility: You can change custom prefixes without invalidating user cookies
-
Compatibility: The system can switch between standard and custom prefix URLs seamlessly
Multi-tenant Support
Path-based routing works with both single and multi-tenant configurations:
- Single tenant: One configuration serves all domains
- Multi-tenant: Different domains can have different locale sets and routing strategies
Mixed tenant configs supported
One tenant can utilise both path-based and domain based routing. The config setup for this would look like:
// config/site.js
import { fetchIcon } from "@thg-altitude/utils";
import siteConfigFile from "../local/tenants/siteone.json";
import siteLangFile from "../local/lang/siteone.en_gb.properties.json";
import siteConfigDeFile from "../local/tenants/siteoneDe.json";
import siteLangDeFile from "../local/lang/siteone.de_de.properties.json";
import siteConfigAtFile from "../local/tenants/siteoneAt.json";
import siteLangAtFile from "../local/lang/siteone.de_at.properties.json";
export default {
domains: ["www.siteone.com", "www.siteone.de"],
tenantInstance: "siteone",
commerce: {
endpoint: "https://horizon-api.www.siteone.com/en-gb/graphql",
},
kv: [
{
key: "siteone",
namespace: "config",
local: siteConfigFile,
},
{
key: "siteone.en_gb.properties",
namespace: "lang",
local: siteLangFile,
},
],
icons: {
search: await fetchIcon("lucide", "search"),
left: await fetchIcon("lucide", "chevron-left"),
right: await fetchIcon("lucide", "chevron-right"),
},
i18n: {
domains: {
"www.siteone.com": {
locales: {
"en-gb": {
icons: {
flag: await fetchIcon("circle-flags", "gb"),
},
},
},
pathBasedRouting: false,
fallbackLocale: "de-de",
},
"www.siteone.de": {
locales: {
"de-de": {
icons: {
flag: await fetchIcon("circle-flags", "de"),
},
commerce: {
endpoint: "https://horizon-api.www.siteone.com/de-de/graphql",
},
kv: [
{
key: "siteone",
namespace: "config",
local: siteConfigDeFile,
},
{
key: "siteone.de_de.properties",
namespace: "lang",
local: siteLangDeFile,
},
],
},
"de-at": {
icons: {
flag: await fetchIcon("circle-flags", "at"),
},
commerce: {
endpoint: "https://horizon-api.www.siteone.com/de-at/graphql",
},
kv: [
{
key: "siteone",
namespace: "config",
local: siteConfigAtFile,
},
{
key: "siteone.de_at.properties",
namespace: "lang",
local: siteLangAtFile,
},
],
},
},
pathBasedRouting: true,
fallbackLocale: "de-de",
},
},
localeCookie: "locale_V6",
},
};
Advanced Configuration Examples
Combining Domain and Path-Based Routing
You can use both routing strategies within the same configuration:
export default {
domains: ["www.example.com", "www.example.de"],
commerce: {
endpoint: "https://api.global.com/graphql"
},
i18n: {
domains: {
// Global domain with path-based routing
"www.example.com": {
pathBasedRouting: true,
fallbackLocale: "en-us",
locales: {
"en-us": {
commerce: { endpoint: ... }
},
"es-es": {
commerce: { endpoint: ... }
},
"fr-fr": {
commerce: { endpoint: ... }
}
}
},
// German domain with domain-based routing
"www.example.de": {
pathBasedRouting: false,
fallbackLocale: "de-de",
locales: {
"de-de": {
commerce: { endpoint: ... }
}
}
},
},
localeCookie: "user_locale_v3"
}
}
The result of this config would be two different domains supporting 4 locales:
www.example.com/en-us/www.example.com/es-es/www.example.com/fr-fr/www.example.de
Schema Validation
The configuration is validated against JSON Schema v3 with these constraints:
- Domain-based routing: Limited to 1 locale per domain when
pathBasedRouting: false - Path-based routing: Unlimited locales allowed when
pathBasedRouting: true - Locale format: Must match
^[a-z]{2}-[a-z]{2}$pattern - Custom prefix format: Must be URL-safe strings when provided
- Required fields: domains, commerce.endpoint, i18n.localeCookie, pathBasedRouting, fallbackLocale
- KV namespaces: Only “config”, “lang”, or “session” allowed