mirror of
https://github.com/facebook/docusaurus.git
synced 2025-08-06 10:20:09 +02:00
feat(v2): Add localeDropdown navbar item type + i18n localeConfigs field (#3916)
* Add localeDropdown navbar item type * fix type + add localeConfigs test
This commit is contained in:
parent
3570aa06c8
commit
aff656182c
12 changed files with 189 additions and 4 deletions
|
@ -52,6 +52,7 @@ declare module '@generated/i18n' {
|
||||||
defaultLocale: string;
|
defaultLocale: string;
|
||||||
locales: [string, ...string[]];
|
locales: [string, ...string[]];
|
||||||
currentLocale: string;
|
currentLocale: string;
|
||||||
|
localeConfigs: Record<string, {label: string}>;
|
||||||
};
|
};
|
||||||
export default i18n;
|
export default i18n;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import DefaultNavbarItem from './DefaultNavbarItem';
|
||||||
|
import type {Props} from '@theme/NavbarItem/LocaleDropdownNavbarItem';
|
||||||
|
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
||||||
|
import {useLocation} from '@docusaurus/router';
|
||||||
|
|
||||||
|
export default function LocaleDropdownNavbarItem({
|
||||||
|
mobile,
|
||||||
|
...props
|
||||||
|
}: Props): JSX.Element {
|
||||||
|
const {
|
||||||
|
siteConfig: {baseUrl},
|
||||||
|
i18n: {defaultLocale, currentLocale, locales, localeConfigs},
|
||||||
|
} = useDocusaurusContext();
|
||||||
|
const {pathname} = useLocation();
|
||||||
|
|
||||||
|
function getLocaleLabel(locale) {
|
||||||
|
return localeConfigs[locale].label;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Docusaurus could offer some APIs to we should probably
|
||||||
|
const baseUrlUnlocalized =
|
||||||
|
currentLocale === defaultLocale
|
||||||
|
? baseUrl
|
||||||
|
: baseUrl.replace(`/${currentLocale}/`, '/');
|
||||||
|
|
||||||
|
const pathnameSuffix = pathname.replace(baseUrl, '');
|
||||||
|
|
||||||
|
function getLocalizedBaseUrl(locale) {
|
||||||
|
return locale === defaultLocale
|
||||||
|
? `${baseUrlUnlocalized}`
|
||||||
|
: `${baseUrlUnlocalized}${locale}/`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const items = locales.map((locale) => {
|
||||||
|
const to = `${getLocalizedBaseUrl(locale)}${pathnameSuffix}`;
|
||||||
|
console.log({
|
||||||
|
locale,
|
||||||
|
to,
|
||||||
|
pathname,
|
||||||
|
baseUrl,
|
||||||
|
baseUrlUnlocalized,
|
||||||
|
pathnameSuffix,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
isNavLink: true,
|
||||||
|
label: getLocaleLabel(locale),
|
||||||
|
to: `pathname://${to}`,
|
||||||
|
target: '_self',
|
||||||
|
autoAddBaseUrl: false,
|
||||||
|
className: locale === currentLocale ? 'dropdown__link--active' : '',
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// Mobile is handled a bit differently
|
||||||
|
const dropdownLabel = mobile ? 'Languages' : getLocaleLabel(currentLocale);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DefaultNavbarItem
|
||||||
|
{...props}
|
||||||
|
mobile={mobile}
|
||||||
|
label={dropdownLabel}
|
||||||
|
items={items}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
|
@ -7,10 +7,12 @@
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import DefaultNavbarItem from '@theme/NavbarItem/DefaultNavbarItem';
|
import DefaultNavbarItem from '@theme/NavbarItem/DefaultNavbarItem';
|
||||||
|
import LocaleDropdownNavbarItem from '@theme/NavbarItem/LocaleDropdownNavbarItem';
|
||||||
import type {Props} from '@theme/NavbarItem';
|
import type {Props} from '@theme/NavbarItem';
|
||||||
|
|
||||||
const NavbarItemComponents = {
|
const NavbarItemComponents = {
|
||||||
default: () => DefaultNavbarItem,
|
default: () => DefaultNavbarItem,
|
||||||
|
localeDropdown: () => LocaleDropdownNavbarItem,
|
||||||
|
|
||||||
// Need to lazy load these items as we don't know for sure the docs plugin is loaded
|
// Need to lazy load these items as we don't know for sure the docs plugin is loaded
|
||||||
// See https://github.com/facebook/docusaurus/issues/3360
|
// See https://github.com/facebook/docusaurus/issues/3360
|
||||||
|
|
|
@ -304,6 +304,15 @@ declare module '@theme/NavbarItem/DefaultNavbarItem' {
|
||||||
export default DefaultNavbarItem;
|
export default DefaultNavbarItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare module '@theme/NavbarItem/LocaleDropdownNavbarItem' {
|
||||||
|
import type {Props as DefaultNavbarItemProps} from '@theme/NavbarItem/DefaultNavbarItem';
|
||||||
|
|
||||||
|
export type Props = DefaultNavbarItemProps;
|
||||||
|
|
||||||
|
const LocaleDropdownNavbarItem: (props: Props) => JSX.Element;
|
||||||
|
export default LocaleDropdownNavbarItem;
|
||||||
|
}
|
||||||
|
|
||||||
declare module '@theme/NavbarItem/DocsVersionDropdownNavbarItem' {
|
declare module '@theme/NavbarItem/DocsVersionDropdownNavbarItem' {
|
||||||
import type {Props as DefaultNavbarItemProps} from '@theme/NavbarItem/DefaultNavbarItem';
|
import type {Props as DefaultNavbarItemProps} from '@theme/NavbarItem/DefaultNavbarItem';
|
||||||
import type {NavLinkProps} from '@theme/NavbarItem/DefaultNavbarItem';
|
import type {NavLinkProps} from '@theme/NavbarItem/DefaultNavbarItem';
|
||||||
|
|
|
@ -96,6 +96,11 @@ const DocItemSchema = Joi.object({
|
||||||
activeSidebarClassName: Joi.string().default('navbar__link--active'),
|
activeSidebarClassName: Joi.string().default('navbar__link--active'),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const LocaleDropdownNavbarItemSchema = Joi.object({
|
||||||
|
type: Joi.string().equal('localeDropdown').required(),
|
||||||
|
position: NavbarItemPosition,
|
||||||
|
});
|
||||||
|
|
||||||
// Can this be made easier? :/
|
// Can this be made easier? :/
|
||||||
const isOfType = (type) => {
|
const isOfType = (type) => {
|
||||||
let typeSchema = Joi.string().required();
|
let typeSchema = Joi.string().required();
|
||||||
|
@ -124,10 +129,14 @@ const NavbarItemSchema = Joi.object().when({
|
||||||
is: isOfType('doc'),
|
is: isOfType('doc'),
|
||||||
then: DocItemSchema,
|
then: DocItemSchema,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
is: isOfType('localeDropdown'),
|
||||||
|
then: LocaleDropdownNavbarItemSchema,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
is: isOfType(undefined),
|
is: isOfType(undefined),
|
||||||
then: Joi.forbidden().messages({
|
then: Joi.forbidden().messages({
|
||||||
'any.unknown': 'Bad nav item type {.type}',
|
'any.unknown': 'Bad navbar item type {.type}',
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
5
packages/docusaurus-types/src/index.d.ts
vendored
5
packages/docusaurus-types/src/index.d.ts
vendored
|
@ -92,9 +92,14 @@ export type TranslationFileContent = Record<string, TranslationMessage>;
|
||||||
export type TranslationFile = {path: string; content: TranslationFileContent};
|
export type TranslationFile = {path: string; content: TranslationFileContent};
|
||||||
export type TranslationFiles = TranslationFile[];
|
export type TranslationFiles = TranslationFile[];
|
||||||
|
|
||||||
|
export type I18nLocaleConfig = {
|
||||||
|
label: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type I18nConfig = {
|
export type I18nConfig = {
|
||||||
defaultLocale: string;
|
defaultLocale: string;
|
||||||
locales: [string, ...string[]];
|
locales: [string, ...string[]];
|
||||||
|
localeConfigs: Record<string, I18nLocaleConfig>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type I18n = I18nConfig & {
|
export type I18n = I18nConfig & {
|
||||||
|
|
|
@ -26,6 +26,7 @@ interface Props {
|
||||||
readonly activeClassName?: string;
|
readonly activeClassName?: string;
|
||||||
readonly children?: ReactNode;
|
readonly children?: ReactNode;
|
||||||
readonly isActive?: () => boolean;
|
readonly isActive?: () => boolean;
|
||||||
|
readonly autoAddBaseUrl?: boolean;
|
||||||
|
|
||||||
// escape hatch in case broken links check is annoying for a specific link
|
// escape hatch in case broken links check is annoying for a specific link
|
||||||
readonly 'data-noBrokenLinkCheck'?: boolean;
|
readonly 'data-noBrokenLinkCheck'?: boolean;
|
||||||
|
@ -45,6 +46,7 @@ function Link({
|
||||||
activeClassName,
|
activeClassName,
|
||||||
isActive,
|
isActive,
|
||||||
'data-noBrokenLinkCheck': noBrokenLinkCheck,
|
'data-noBrokenLinkCheck': noBrokenLinkCheck,
|
||||||
|
autoAddBaseUrl = true,
|
||||||
...props
|
...props
|
||||||
}: Props): JSX.Element {
|
}: Props): JSX.Element {
|
||||||
const {withBaseUrl} = useBaseUrlUtils();
|
const {withBaseUrl} = useBaseUrlUtils();
|
||||||
|
@ -57,7 +59,9 @@ function Link({
|
||||||
const targetLinkUnprefixed = to || href;
|
const targetLinkUnprefixed = to || href;
|
||||||
|
|
||||||
function maybeAddBaseUrl(str: string) {
|
function maybeAddBaseUrl(str: string) {
|
||||||
return shouldAddBaseUrlAutomatically(str) ? withBaseUrl(str) : str;
|
return autoAddBaseUrl && shouldAddBaseUrlAutomatically(str)
|
||||||
|
? withBaseUrl(str)
|
||||||
|
: str;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isInternal = isInternalUrl(targetLinkUnprefixed);
|
const isInternal = isInternalUrl(targetLinkUnprefixed);
|
||||||
|
|
|
@ -23,6 +23,7 @@ Object {
|
||||||
"favicon": "img/docusaurus.ico",
|
"favicon": "img/docusaurus.ico",
|
||||||
"i18n": Object {
|
"i18n": Object {
|
||||||
"defaultLocale": "en",
|
"defaultLocale": "en",
|
||||||
|
"localeConfigs": Object {},
|
||||||
"locales": Array [
|
"locales": Array [
|
||||||
"en",
|
"en",
|
||||||
],
|
],
|
||||||
|
|
|
@ -5,9 +5,14 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {loadI18n, localizePath} from '../i18n';
|
import {loadI18n, localizePath, defaultLocaleConfig} from '../i18n';
|
||||||
import {DEFAULT_I18N_CONFIG} from '../configValidation';
|
import {DEFAULT_I18N_CONFIG} from '../configValidation';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
import {chain, identity} from 'lodash';
|
||||||
|
|
||||||
|
function testLocaleConfigsFor(locales: string[]) {
|
||||||
|
return chain(locales).keyBy(identity).mapValues(defaultLocaleConfig).value();
|
||||||
|
}
|
||||||
|
|
||||||
describe('loadI18n', () => {
|
describe('loadI18n', () => {
|
||||||
test('should load I18n for default config', async () => {
|
test('should load I18n for default config', async () => {
|
||||||
|
@ -22,6 +27,7 @@ describe('loadI18n', () => {
|
||||||
defaultLocale: 'en',
|
defaultLocale: 'en',
|
||||||
locales: ['en'],
|
locales: ['en'],
|
||||||
currentLocale: 'en',
|
currentLocale: 'en',
|
||||||
|
localeConfigs: testLocaleConfigsFor(['en']),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -33,6 +39,7 @@ describe('loadI18n', () => {
|
||||||
i18n: {
|
i18n: {
|
||||||
defaultLocale: 'fr',
|
defaultLocale: 'fr',
|
||||||
locales: ['en', 'fr', 'de'],
|
locales: ['en', 'fr', 'de'],
|
||||||
|
localeConfigs: {},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -40,6 +47,7 @@ describe('loadI18n', () => {
|
||||||
defaultLocale: 'fr',
|
defaultLocale: 'fr',
|
||||||
locales: ['en', 'fr', 'de'],
|
locales: ['en', 'fr', 'de'],
|
||||||
currentLocale: 'fr',
|
currentLocale: 'fr',
|
||||||
|
localeConfigs: testLocaleConfigsFor(['en', 'fr', 'de']),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -51,6 +59,7 @@ describe('loadI18n', () => {
|
||||||
i18n: {
|
i18n: {
|
||||||
defaultLocale: 'fr',
|
defaultLocale: 'fr',
|
||||||
locales: ['en', 'fr', 'de'],
|
locales: ['en', 'fr', 'de'],
|
||||||
|
localeConfigs: {},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{locale: 'de'},
|
{locale: 'de'},
|
||||||
|
@ -59,6 +68,35 @@ describe('loadI18n', () => {
|
||||||
defaultLocale: 'fr',
|
defaultLocale: 'fr',
|
||||||
locales: ['en', 'fr', 'de'],
|
locales: ['en', 'fr', 'de'],
|
||||||
currentLocale: 'de',
|
currentLocale: 'de',
|
||||||
|
localeConfigs: testLocaleConfigsFor(['en', 'fr', 'de']),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should load I18n for multi-locale config with some xcustom locale configs', async () => {
|
||||||
|
await expect(
|
||||||
|
loadI18n(
|
||||||
|
{
|
||||||
|
i18n: {
|
||||||
|
defaultLocale: 'fr',
|
||||||
|
locales: ['en', 'fr', 'de'],
|
||||||
|
localeConfigs: {
|
||||||
|
fr: {label: 'Français'},
|
||||||
|
// @ts-expect-error: empty on purpose
|
||||||
|
en: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{locale: 'de'},
|
||||||
|
),
|
||||||
|
).resolves.toEqual({
|
||||||
|
defaultLocale: 'fr',
|
||||||
|
locales: ['en', 'fr', 'de'],
|
||||||
|
currentLocale: 'de',
|
||||||
|
localeConfigs: {
|
||||||
|
fr: {label: 'Français'},
|
||||||
|
en: defaultLocaleConfig('en'),
|
||||||
|
de: defaultLocaleConfig('de'),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -70,6 +108,7 @@ describe('loadI18n', () => {
|
||||||
i18n: {
|
i18n: {
|
||||||
defaultLocale: 'fr',
|
defaultLocale: 'fr',
|
||||||
locales: ['en', 'fr', 'de'],
|
locales: ['en', 'fr', 'de'],
|
||||||
|
localeConfigs: {},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{locale: 'it'},
|
{locale: 'it'},
|
||||||
|
@ -88,6 +127,7 @@ describe('localizePath', () => {
|
||||||
defaultLocale: 'en',
|
defaultLocale: 'en',
|
||||||
locales: ['en', 'fr'],
|
locales: ['en', 'fr'],
|
||||||
currentLocale: 'fr',
|
currentLocale: 'fr',
|
||||||
|
localeConfigs: {},
|
||||||
},
|
},
|
||||||
options: {localizePath: true},
|
options: {localizePath: true},
|
||||||
}),
|
}),
|
||||||
|
@ -103,6 +143,7 @@ describe('localizePath', () => {
|
||||||
defaultLocale: 'en',
|
defaultLocale: 'en',
|
||||||
locales: ['en', 'fr'],
|
locales: ['en', 'fr'],
|
||||||
currentLocale: 'fr',
|
currentLocale: 'fr',
|
||||||
|
localeConfigs: {},
|
||||||
},
|
},
|
||||||
options: {localizePath: true},
|
options: {localizePath: true},
|
||||||
}),
|
}),
|
||||||
|
@ -118,6 +159,7 @@ describe('localizePath', () => {
|
||||||
defaultLocale: 'en',
|
defaultLocale: 'en',
|
||||||
locales: ['en', 'fr'],
|
locales: ['en', 'fr'],
|
||||||
currentLocale: 'en',
|
currentLocale: 'en',
|
||||||
|
localeConfigs: {},
|
||||||
},
|
},
|
||||||
options: {localizePath: true},
|
options: {localizePath: true},
|
||||||
}),
|
}),
|
||||||
|
@ -133,6 +175,7 @@ describe('localizePath', () => {
|
||||||
defaultLocale: 'en',
|
defaultLocale: 'en',
|
||||||
locales: ['en', 'fr'],
|
locales: ['en', 'fr'],
|
||||||
currentLocale: 'en',
|
currentLocale: 'en',
|
||||||
|
localeConfigs: {},
|
||||||
},
|
},
|
||||||
// options: {localizePath: true},
|
// options: {localizePath: true},
|
||||||
}),
|
}),
|
||||||
|
@ -148,6 +191,7 @@ describe('localizePath', () => {
|
||||||
defaultLocale: 'en',
|
defaultLocale: 'en',
|
||||||
locales: ['en', 'fr'],
|
locales: ['en', 'fr'],
|
||||||
currentLocale: 'en',
|
currentLocale: 'en',
|
||||||
|
localeConfigs: {},
|
||||||
},
|
},
|
||||||
// options: {localizePath: true},
|
// options: {localizePath: true},
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -19,6 +19,7 @@ const DEFAULT_I18N_LOCALE = 'en';
|
||||||
export const DEFAULT_I18N_CONFIG: I18nConfig = {
|
export const DEFAULT_I18N_CONFIG: I18nConfig = {
|
||||||
defaultLocale: DEFAULT_I18N_LOCALE,
|
defaultLocale: DEFAULT_I18N_LOCALE,
|
||||||
locales: [DEFAULT_I18N_LOCALE],
|
locales: [DEFAULT_I18N_LOCALE],
|
||||||
|
localeConfigs: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DEFAULT_CONFIG: Pick<
|
export const DEFAULT_CONFIG: Pick<
|
||||||
|
@ -66,9 +67,16 @@ const PresetSchema = Joi.alternatives().try(
|
||||||
Joi.array().items(Joi.string().required(), Joi.object().required()).length(2),
|
Joi.array().items(Joi.string().required(), Joi.object().required()).length(2),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const LocaleConfigSchema = Joi.object({
|
||||||
|
label: Joi.string(),
|
||||||
|
});
|
||||||
|
|
||||||
const I18N_CONFIG_SCHEMA = Joi.object<I18nConfig>({
|
const I18N_CONFIG_SCHEMA = Joi.object<I18nConfig>({
|
||||||
defaultLocale: Joi.string().required(),
|
defaultLocale: Joi.string().required(),
|
||||||
locales: Joi.array().items().min(1).items(Joi.string().required()).required(),
|
locales: Joi.array().items().min(1).items(Joi.string().required()).required(),
|
||||||
|
localeConfigs: Joi.object()
|
||||||
|
.pattern(/.*/, LocaleConfigSchema)
|
||||||
|
.default(DEFAULT_I18N_CONFIG.localeConfigs),
|
||||||
})
|
})
|
||||||
.optional()
|
.optional()
|
||||||
.default(DEFAULT_I18N_CONFIG);
|
.default(DEFAULT_I18N_CONFIG);
|
||||||
|
|
|
@ -4,10 +4,16 @@
|
||||||
* This source code is licensed under the MIT license found in the
|
* This source code is licensed under the MIT license found in the
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
import {I18n, DocusaurusConfig} from '@docusaurus/types';
|
import {I18n, DocusaurusConfig, I18nLocaleConfig} from '@docusaurus/types';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import {normalizeUrl} from '@docusaurus/utils';
|
import {normalizeUrl} from '@docusaurus/utils';
|
||||||
|
|
||||||
|
export function defaultLocaleConfig(locale: string): I18nLocaleConfig {
|
||||||
|
return {
|
||||||
|
label: locale,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export async function loadI18n(
|
export async function loadI18n(
|
||||||
config: DocusaurusConfig,
|
config: DocusaurusConfig,
|
||||||
options: {locale?: string} = {},
|
options: {locale?: string} = {},
|
||||||
|
@ -25,9 +31,22 @@ Note: Docusaurus only support running one local at a time.`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getLocaleConfig(locale: string): I18nLocaleConfig {
|
||||||
|
// User provided values
|
||||||
|
const localeConfigOptions: Partial<I18nLocaleConfig> =
|
||||||
|
i18nConfig.localeConfigs[locale];
|
||||||
|
|
||||||
|
return {...defaultLocaleConfig(locale), ...localeConfigOptions};
|
||||||
|
}
|
||||||
|
|
||||||
|
const localeConfigs = i18nConfig.locales.reduce((acc, locale) => {
|
||||||
|
return {...acc, [locale]: getLocaleConfig(locale)};
|
||||||
|
}, {});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...i18nConfig,
|
...i18nConfig,
|
||||||
currentLocale,
|
currentLocale,
|
||||||
|
localeConfigs,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,14 @@ module.exports = {
|
||||||
i18n: {
|
i18n: {
|
||||||
defaultLocale: 'en',
|
defaultLocale: 'en',
|
||||||
locales: ['en', 'fr'],
|
locales: ['en', 'fr'],
|
||||||
|
localeConfigs: {
|
||||||
|
en: {
|
||||||
|
label: 'English',
|
||||||
|
},
|
||||||
|
fr: {
|
||||||
|
label: 'Français',
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
onBrokenLinks: 'throw',
|
onBrokenLinks: 'throw',
|
||||||
onBrokenMarkdownLinks: 'warn',
|
onBrokenMarkdownLinks: 'warn',
|
||||||
|
@ -303,6 +311,7 @@ module.exports = {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
// {type: 'localeDropdown', position: 'right'},
|
||||||
{
|
{
|
||||||
href: 'https://github.com/facebook/docusaurus',
|
href: 'https://github.com/facebook/docusaurus',
|
||||||
position: 'right',
|
position: 'right',
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue