resolveLocale
Pick which of your supported locales to use, from the usual chain: query → cookie → localStorage → navigator → fallback.
The catalogue of messages is yours; this only picks the key.
API
resolveLocale(options)
| Option | Description | Type | Default |
|---|---|---|---|
supported |
Locales you actually ship, most specific first | readonly string[] |
Required |
fallback |
Returned when nothing matches | string |
supported[0] |
query |
Query parameter carrying an explicit choice, e.g. lang |
string |
— |
cookie |
Cookie name carrying the choice | string |
— |
storageKey |
localStorage key carrying the choice the user last picked | string |
— |
useNavigator |
Consult navigator.languages / navigator.language before falling back |
boolean |
true |
url |
URL to read the query from | string |
Current location |
Return
The matching entry of supported: always one of them, never an arbitrary string.
Example
Full chain
import { resolveLocale } from 'ranuts';
const locale = resolveLocale({
supported: ['en', 'zh-CN'],
query: 'lang',
cookie: 'lang',
storageKey: 'app-lang',
});
document.documentElement.lang = locale;
render(messages[locale]);Region variants fall back to the base language
import { resolveLocale } from 'ranuts';
const supported = ['en', 'zh-CN'];
resolveLocale({ supported, query: 'lang', url: '?lang=en-GB' }); // 'en'
resolveLocale({ supported, query: 'lang', url: '?lang=zh' }); // 'zh-CN'
resolveLocale({ supported, query: 'lang', url: '?lang=de' }); // 'en' (unsupported → fallback)Pair with locale URLs
import { resolveLocale, createLocalePath } from 'ranuts';
const paths = createLocalePath({
locales: [{ code: 'en' }, { code: 'zh-CN', prefix: 'zh' }],
});
// Prefer what the URL already says; fall back to the user's own preference.
const locale = paths.localeFromPath(location.pathname) ?? resolveLocale({ supported: ['en', 'zh-CN'] });Notes
The order is the point. A
?lang=in the URL is explicit, shareable and one-off, so it beats everything. A cookie is a server-visible decision, so it beats client-only state. localStorage is what the user last chose in-app.navigator.languageis only a guess about a first-time visitor. Getting this backwards produces the classic bug where a shared?lang=enlink keeps rendering in the recipient's stored language.The result is always one of
supported. A value outside the list is ignored rather than returned, so the result is safe to index a message catalogue with.Matching is case-insensitive and falls back by base language. With
supported: ['en', 'zh-CN'],en-GBmatchesenandzhmatcheszh-CN.navigator.languagesis consulted in order, not justnavigator.language: the list is the user's actual ranked preference, and its head is often not the best available match.Every source degrades quietly. No
window, nodocument.cookie, no localStorage: each simply contributes nothing, so the chain works under SSR and in build-time scripts.