createLocalePath
URL maths for a multilingual site. Pure functions, no global state, no DOM — usable in a
build script (sitemap, hreflang) and in the browser alike.
Uses sub-directories (/zh/book/) rather than sub-domains (zh.example.com/book/):
search engines treat a sub-domain as a separate site whose authority starts from zero, while
a sub-directory inherits the main site's. The default locale lives at the root; every other
locale carries a prefix.
API
createLocalePath(config)
| Parameter | Description | Type | Default |
|---|---|---|---|
locales |
{ code, prefix? }[]; no prefix means "default locale, lives at root" |
LocaleRoute[] |
Required |
defaultLocale |
Default locale code | string |
first prefix-less |
base |
Deployment sub-path, e.g. /weread; trailing slash is ignored |
string |
'' |
Returns:
| Member | Description |
|---|---|
base / defaultLocale |
Normalized config, read-only |
localeFromPath(pathname) |
Detect the locale; unknown paths fall back to the default |
stripLocale(pathname) |
Drop the locale prefix: the language-agnostic path for routing |
href(path, code?) |
Build a link for a locale |
hrefForLocale(pathname, code) |
Re-point the current path at another locale (language switcher) |
alternates(pathname) |
Every locale's URL, for <link rel="alternate" hreflang> |
Example
import { createLocalePath } from 'ranuts';
const paths = createLocalePath({
locales: [{ code: 'en' }, { code: 'zh-CN', prefix: 'zh' }, { code: 'zh-HK', prefix: 'zh-hant' }],
base: '/docs',
});
paths.href('/book/walden/'); // '/docs/book/walden/'
paths.href('/book/walden/', 'zh-CN'); // '/docs/zh/book/walden/'
paths.localeFromPath('/docs/zh/book/'); // 'zh-CN'
paths.stripLocale('/docs/zh/book/'); // '/docs/book/'
paths.hrefForLocale('/docs/zh/book/', 'zh-HK'); // '/docs/zh-hant/book/'
// hreflang tags
paths.alternates(location.pathname).forEach(({ code, href }) => {
head.append(link({ rel: 'alternate', hreflang: code, href }));
});Notes
hrefis idempotent. It strips any existing prefix before adding the new one, so feeding it an already-localized path does not double up — andhrefForLocaleis justhref.- Longest prefix wins, so
zhdoes not swallow/zh-hant/.... baseis only stripped from the start. Usingreplace(base, '')would strip the first occurrence anywhere, which breaks when the path contains the base string mid-way.- Query and hash are preserved: the maths only touches the pathname.
- No global "current locale". Pass the code explicitly, or default it. Which locale is active is the i18n runtime's job, not this module's.