@docusaurus/core
Version:
Easy to Maintain Open Source Documentation Websites
154 lines (153 loc) • 6.54 kB
JavaScript
;
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.getDefaultLocaleConfig = getDefaultLocaleConfig;
exports.getLocaleList = getLocaleList;
exports.loadI18n = loadI18n;
const tslib_1 = require("tslib");
const path_1 = tslib_1.__importDefault(require("path"));
const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
const combine_promises_1 = tslib_1.__importDefault(require("combine-promises"));
const utils_1 = require("@docusaurus/utils");
function inferLanguageDisplayName(locale) {
const tryLocale = (l) => {
try {
return new Intl.DisplayNames(l, {
type: 'language',
fallback: 'code',
}).of(l);
}
catch (e) {
// This is to compensate "of()" that is a bit strict
// Looks like starting Node 22, this locale throws: "en-US-u-ca-buddhist"
// RangeError: invalid_argument
return null;
}
};
const parts = locale.split('-');
// This is a best effort, we try various locale forms that could give a result
return (tryLocale(locale) ??
tryLocale(`${parts[0]}-${parts[1]}`) ??
tryLocale(parts[0]));
}
function getDefaultLocaleLabel(locale) {
const languageName = inferLanguageDisplayName(locale);
if (!languageName) {
return locale;
}
return (languageName.charAt(0).toLocaleUpperCase(locale) + languageName.substring(1));
}
function getDefaultCalendar(localeStr) {
const locale = new Intl.Locale(localeStr);
// If the locale name includes -u-ca-xxx the calendar will be defined
if (locale.calendar) {
return locale.calendar;
}
// Not well-supported but server code can infer a calendar from the locale
// See https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/getCalendars
// See https://caniuse.com/mdn-javascript_builtins_intl_locale_getcalendars
const calendars = locale.getCalendars?.() ??
// @ts-expect-error: non-std attribute (V8/Chromium/Node)
locale.calendars;
if (calendars instanceof Array && calendars[0]) {
return calendars[0];
}
return 'gregory';
}
function getDefaultDirection(localeStr) {
const locale = new Intl.Locale(localeStr);
// see https://github.com/tc39/proposal-intl-locale-info
// see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/getTextInfo
// Node 18.0 implements a former version of the getTextInfo() proposal
// TODO Docusaurus v4: remove the fallback to locale.textInfo
// @ts-expect-error: The TC39 proposal was updated
const textInto = locale.getTextInfo?.() ?? locale.textInfo;
return textInto.direction ?? 'ltr';
}
function getDefaultLocaleConfig(
// Locale "key/identifier"
// Can be anything, but usually a country / BCP47 code
locale,
// optionally provided in i18n.localConfigs, need to respect BCP47
htmlLang) {
try {
return {
label: getDefaultLocaleLabel(htmlLang ?? locale),
direction: getDefaultDirection(htmlLang ?? locale),
htmlLang: htmlLang ?? locale,
calendar: getDefaultCalendar(htmlLang ?? locale),
// Fot the i18n/<path>, we don't use htmlLang on purpose
// see bug https://github.com/facebook/docusaurus/issues/11952
path: locale,
};
}
catch (e) {
throw new Error(`Docusaurus couldn't infer a default locale config for ${logger_1.default.name(locale)}.
Make sure it is a valid BCP 47 locale name (e.g. en, fr, fr-FR, etc.) and/or provide a valid BCP 47 ${logger_1.default.code(`siteConfig.i18n.localeConfig['${locale}'].htmlLang`)} attribute.`, { cause: e });
}
}
function getLocaleList({ i18nConfig, currentLocale, }) {
if (!i18nConfig.locales.includes(currentLocale)) {
logger_1.default.warn `The locale name=${currentLocale} was not found in your Docusaurus site configuration.
We recommend adding the name=${currentLocale} to your site i18n config, but we will still try to run your site.
Declared site config locales are: ${i18nConfig.locales}`;
return i18nConfig.locales.concat(currentLocale);
}
return i18nConfig.locales;
}
async function loadI18n({ siteDir, config, currentLocale, automaticBaseUrlLocalizationDisabled, }) {
const { i18n: i18nConfig } = config;
const locales = getLocaleList({
i18nConfig,
currentLocale,
});
async function getFullLocaleConfig(locale) {
const localeConfigInput = i18nConfig.localeConfigs[locale] ?? {};
const localeConfig = {
...getDefaultLocaleConfig(locale, localeConfigInput.htmlLang),
...localeConfigInput,
};
// By default, translations will be enabled if i18n/<locale> dir exists
async function inferTranslate() {
const localizationDir = path_1.default.resolve(siteDir, i18nConfig.path, localeConfig.path);
return fs_extra_1.default.pathExists(localizationDir);
}
function getInferredBaseUrl() {
const addLocaleSegment = locale !== i18nConfig.defaultLocale &&
!automaticBaseUrlLocalizationDisabled;
return (0, utils_1.normalizeUrl)([
'/',
config.baseUrl,
addLocaleSegment ? locale : '',
'/',
]);
}
const translate = localeConfigInput.translate ?? (await inferTranslate());
const url = typeof localeConfigInput.url !== 'undefined'
? localeConfigInput.url
: config.url;
const baseUrl = typeof localeConfigInput.baseUrl !== 'undefined'
? (0, utils_1.normalizeUrl)(['/', localeConfigInput.baseUrl, '/'])
: getInferredBaseUrl();
return {
...localeConfig,
translate,
url,
baseUrl,
};
}
const localeConfigs = await (0, combine_promises_1.default)(Object.fromEntries(locales.map((locale) => [locale, getFullLocaleConfig(locale)])));
return {
defaultLocale: i18nConfig.defaultLocale,
locales,
path: i18nConfig.path,
currentLocale,
localeConfigs,
};
}