Skip to content

feat: allow configuring locale base URL #6731

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/docusaurus-module-type-aliases/src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ declare module '@generated/i18n' {
label: string;
direction: string;
htmlLang: string;
baseUrl: string;
}
>;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,33 @@ export function useAlternatePageUtils(): {
} {
const {
siteConfig: {baseUrl, url},
i18n: {defaultLocale, currentLocale},
i18n: {defaultLocale, currentLocale, localeConfigs},
} = useDocusaurusContext();
const {pathname} = useLocation();

const baseUrlUnlocalized =
currentLocale === defaultLocale
? baseUrl
: baseUrl.replace(`/${currentLocale}/`, '/');
function getLocalizedPath(locale: string) {
if (localeConfigs[locale].baseUrl) {
return localeConfigs[locale].baseUrl
.replace(/^\//, '')
.replace(/\/$/, '');
}
return locale === defaultLocale ? '' : locale;
}

const pathnameSuffix = pathname.replace(baseUrl, '');
const currentLocalizedPath = getLocalizedPath(currentLocale);
const baseUrlUnlocalized = baseUrl.replace(
new RegExp(`/${currentLocalizedPath}/$`),
'/',
);

const pathnameSuffix = pathname.match(new RegExp(`^${baseUrl}`))
? pathname.replace(new RegExp(`^${baseUrl}`), '')
: pathname.replace(new RegExp(`^${baseUrlUnlocalized}`), '');
function getLocalizedBaseUrl(locale: string) {
return locale === defaultLocale
? `${baseUrlUnlocalized}`
: `${baseUrlUnlocalized}${locale}/`;
const localizedPath = getLocalizedPath(locale);
return localizedPath
? `${baseUrlUnlocalized}${localizedPath}/`
: baseUrlUnlocalized;
}

// TODO support correct alternate url when localized site is deployed on another domain
Expand Down
1 change: 1 addition & 0 deletions packages/docusaurus-types/src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ export type I18nLocaleConfig = {
label: string;
htmlLang: string;
direction: string;
baseUrl: string;
};

export type I18nConfig = {
Expand Down
1 change: 1 addition & 0 deletions packages/docusaurus/src/server/configValidation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ const LocaleConfigSchema = Joi.object({
label: Joi.string(),
htmlLang: Joi.string(),
direction: Joi.string().equal('ltr', 'rtl').default('ltr'),
baseUrl: Joi.string(),
});

const I18N_CONFIG_SCHEMA = Joi.object<I18nConfig>({
Expand Down
34 changes: 25 additions & 9 deletions packages/docusaurus/src/server/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export function getDefaultLocaleConfig(locale: string): I18nLocaleConfig {
label: getDefaultLocaleLabel(locale),
direction: getLangDir(locale),
htmlLang: locale,
baseUrl: '',
};
}

Expand Down Expand Up @@ -74,6 +75,25 @@ Note: Docusaurus only support running one locale at a time.`;
};
}

function shouldLocalizePath({
i18n,
options = {},
}: {
i18n: I18n;
options?: {localizePath?: boolean};
}): boolean {
if (options.localizePath === true || options.localizePath === false) {
return options.localizePath;
}
if (
i18n.currentLocale !== i18n.defaultLocale ||
i18n.localeConfigs[i18n.currentLocale].baseUrl
) {
return true;
}
return false;
}

export function localizePath({
pathType,
path: originalPath,
Expand All @@ -85,20 +105,16 @@ export function localizePath({
i18n: I18n;
options?: {localizePath?: boolean};
}): string {
const shouldLocalizePath: boolean =
typeof options.localizePath === 'undefined'
? // By default, we don't localize the path of defaultLocale
i18n.currentLocale !== i18n.defaultLocale
: options.localizePath;

if (shouldLocalizePath) {
if (shouldLocalizePath({i18n, options})) {
// FS paths need special care, for Windows support
const localePath =
i18n.localeConfigs[i18n.currentLocale].baseUrl || i18n.currentLocale;
if (pathType === 'fs') {
return path.join(originalPath, path.sep, i18n.currentLocale, path.sep);
return path.join(originalPath, path.sep, localePath, path.sep);
}
// Url paths
else if (pathType === 'url') {
return normalizeUrl([originalPath, '/', i18n.currentLocale, '/']);
return normalizeUrl([originalPath, '/', localePath, '/']);
}
// should never happen
else {
Expand Down
3 changes: 3 additions & 0 deletions website/docs/api/docusaurus.config.js.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,11 +134,13 @@ module.exports = {
label: 'English',
direction: 'ltr',
htmlLang: 'en-US',
baseUrl: 'en-US',
},
fr: {
label: 'Français',
direction: 'ltr',
htmlLang: 'fr-FR',
baseUrl: '/fr-FR/',
},
},
},
Expand All @@ -148,6 +150,7 @@ module.exports = {
- `label`: the label to use for this locale
- `direction`: `ltr` (default) or `rtl` (for [right-to-left languages](https://developer.mozilla.org/en-US/docs/Glossary/rtl) like Arabic, Hebrew, etc.)
- `htmlLang`: BCP 47 language tag to use in `<html lang="...">` and in `<link ... hreflang="...">`
- `baseUrl`: the baseUrl to use for this locale

### `noIndex` {#noindex}

Expand Down