fix(docs): add support for missing `sidebar_key` front matter attribute (#11490)
Some checks are pending
Argos CI / take-screenshots (push) Waiting to run
Build Hash Router / Build Hash Router (push) Waiting to run
Canary Release / Publish Canary (push) Waiting to run
CodeQL / Analyze (javascript) (push) Waiting to run
Continuous Releases / Continuous Releases (push) Waiting to run
E2E Tests / E2E — Yarn v1 (20) (push) Waiting to run
E2E Tests / E2E — Yarn v1 (20.0) (push) Waiting to run
E2E Tests / E2E — Yarn v1 (22) (push) Waiting to run
E2E Tests / E2E — Yarn v1 (24) (push) Waiting to run
E2E Tests / E2E — Yarn v1 Windows (push) Waiting to run
E2E Tests / E2E — Yarn Berry (node-modules, -s) (push) Waiting to run
E2E Tests / E2E — Yarn Berry (node-modules, -st) (push) Waiting to run
E2E Tests / E2E — Yarn Berry (pnp, -s) (push) Waiting to run
E2E Tests / E2E — Yarn Berry (pnp, -st) (push) Waiting to run
E2E Tests / E2E — npm (push) Waiting to run
E2E Tests / E2E — pnpm (push) Waiting to run

This commit is contained in:
Sébastien Lorber 2025-10-17 12:34:08 +02:00 committed by GitHub
parent d9d29046ec
commit c8fc3311f1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 131 additions and 47 deletions

View File

@ -5,27 +5,27 @@ exports[`getLoadedContentTranslationFiles returns translation files 1`] = `
{
"content": {
"sidebar.docs.category.Getting started": {
"description": "The label for category Getting started in sidebar docs",
"description": "The label for category 'Getting started' in sidebar 'docs'",
"message": "Getting started",
},
"sidebar.docs.category.Getting started.link.generated-index.description": {
"description": "The generated-index page description for category Getting started in sidebar docs",
"description": "The generated-index page description for category 'Getting started' in sidebar 'docs'",
"message": "Getting started index description",
},
"sidebar.docs.category.Getting started.link.generated-index.title": {
"description": "The generated-index page title for category Getting started in sidebar docs",
"description": "The generated-index page title for category 'Getting started' in sidebar 'docs'",
"message": "Getting started index title",
},
"sidebar.docs.doc.Second doc translatable": {
"description": "The label for the doc item Second doc translatable in sidebar docs, linking to the doc doc2",
"description": "The label for the doc item 'Second doc translatable' in sidebar 'docs', linking to the doc doc2",
"message": "Second doc translatable",
},
"sidebar.docs.link.Link label": {
"description": "The label for link Link label in sidebar docs, linking to https://facebook.com",
"description": "The label for link 'Link label' in sidebar 'docs', linking to 'https://facebook.com'",
"message": "Link label",
},
"sidebar.otherSidebar.doc.Fifth doc translatable": {
"description": "The label for the doc item Fifth doc translatable in sidebar otherSidebar, linking to the doc doc5",
"description": "The label for the doc item 'Fifth doc translatable' in sidebar 'otherSidebar', linking to the doc doc5",
"message": "Fifth doc translatable",
},
"version.label": {
@ -38,27 +38,27 @@ exports[`getLoadedContentTranslationFiles returns translation files 1`] = `
{
"content": {
"sidebar.docs.category.Getting started": {
"description": "The label for category Getting started in sidebar docs",
"description": "The label for category 'Getting started' in sidebar 'docs'",
"message": "Getting started",
},
"sidebar.docs.category.Getting started.link.generated-index.description": {
"description": "The generated-index page description for category Getting started in sidebar docs",
"description": "The generated-index page description for category 'Getting started' in sidebar 'docs'",
"message": "Getting started index description",
},
"sidebar.docs.category.Getting started.link.generated-index.title": {
"description": "The generated-index page title for category Getting started in sidebar docs",
"description": "The generated-index page title for category 'Getting started' in sidebar 'docs'",
"message": "Getting started index title",
},
"sidebar.docs.doc.Second doc translatable": {
"description": "The label for the doc item Second doc translatable in sidebar docs, linking to the doc doc2",
"description": "The label for the doc item 'Second doc translatable' in sidebar 'docs', linking to the doc doc2",
"message": "Second doc translatable",
},
"sidebar.docs.link.Link label": {
"description": "The label for link Link label in sidebar docs, linking to https://facebook.com",
"description": "The label for link 'Link label' in sidebar 'docs', linking to 'https://facebook.com'",
"message": "Link label",
},
"sidebar.otherSidebar.doc.Fifth doc translatable": {
"description": "The label for the doc item Fifth doc translatable in sidebar otherSidebar, linking to the doc doc5",
"description": "The label for the doc item 'Fifth doc translatable' in sidebar 'otherSidebar', linking to the doc doc5",
"message": "Fifth doc translatable",
},
"version.label": {
@ -71,27 +71,27 @@ exports[`getLoadedContentTranslationFiles returns translation files 1`] = `
{
"content": {
"sidebar.docs.category.Getting started": {
"description": "The label for category Getting started in sidebar docs",
"description": "The label for category 'Getting started' in sidebar 'docs'",
"message": "Getting started",
},
"sidebar.docs.category.Getting started.link.generated-index.description": {
"description": "The generated-index page description for category Getting started in sidebar docs",
"description": "The generated-index page description for category 'Getting started' in sidebar 'docs'",
"message": "Getting started index description",
},
"sidebar.docs.category.Getting started.link.generated-index.title": {
"description": "The generated-index page title for category Getting started in sidebar docs",
"description": "The generated-index page title for category 'Getting started' in sidebar 'docs'",
"message": "Getting started index title",
},
"sidebar.docs.doc.Second doc translatable": {
"description": "The label for the doc item Second doc translatable in sidebar docs, linking to the doc doc2",
"description": "The label for the doc item 'Second doc translatable' in sidebar 'docs', linking to the doc doc2",
"message": "Second doc translatable",
},
"sidebar.docs.link.Link label": {
"description": "The label for link Link label in sidebar docs, linking to https://facebook.com",
"description": "The label for link 'Link label' in sidebar 'docs', linking to 'https://facebook.com'",
"message": "Link label",
},
"sidebar.otherSidebar.doc.Fifth doc translatable": {
"description": "The label for the doc item Fifth doc translatable in sidebar otherSidebar, linking to the doc doc5",
"description": "The label for the doc item 'Fifth doc translatable' in sidebar 'otherSidebar', linking to the doc doc5",
"message": "Fifth doc translatable",
},
"version.label": {

View File

@ -186,10 +186,24 @@ describe('validateDocFrontMatter slug', () => {
});
});
describe('validateDocFrontMatter sidebar_key', () => {
testField({
prefix: 'sidebar_key',
validFrontMatters: [
{sidebar_key: undefined},
{sidebar_key: 'Awesome docs'},
],
invalidFrontMatters: [[{sidebar_key: ''}, 'is not allowed to be empty']],
});
});
describe('validateDocFrontMatter sidebar_label', () => {
testField({
prefix: 'sidebar_label',
validFrontMatters: [{sidebar_label: 'Awesome docs'}],
validFrontMatters: [
{sidebar_label: undefined},
{sidebar_label: 'Awesome docs'},
],
invalidFrontMatters: [[{sidebar_label: ''}, 'is not allowed to be empty']],
});
});

View File

@ -232,35 +232,35 @@ describe('getLoadedContentTranslationFiles', () => {
{
"content": {
"sidebar.sidebarWithConflicts.category.key-cat1": {
"description": "The label for category COMMON LABEL in sidebar sidebarWithConflicts",
"description": "The label for category 'COMMON LABEL' in sidebar 'sidebarWithConflicts'",
"message": "COMMON LABEL",
},
"sidebar.sidebarWithConflicts.category.key-cat2": {
"description": "The label for category COMMON LABEL in sidebar sidebarWithConflicts",
"description": "The label for category 'COMMON LABEL' in sidebar 'sidebarWithConflicts'",
"message": "COMMON LABEL",
},
"sidebar.sidebarWithConflicts.doc.key-doc4": {
"description": "The label for the doc item COMMON LABEL in sidebar sidebarWithConflicts, linking to the doc doc4",
"description": "The label for the doc item 'COMMON LABEL' in sidebar 'sidebarWithConflicts', linking to the doc doc4",
"message": "COMMON LABEL",
},
"sidebar.sidebarWithConflicts.doc.key-doc5": {
"description": "The label for the doc item COMMON LABEL in sidebar sidebarWithConflicts, linking to the doc doc5",
"description": "The label for the doc item 'COMMON LABEL' in sidebar 'sidebarWithConflicts', linking to the doc doc5",
"message": "COMMON LABEL",
},
"sidebar.sidebarWithConflicts.doc.key-ref4": {
"description": "The label for the doc item COMMON LABEL in sidebar sidebarWithConflicts, linking to the doc doc4",
"description": "The label for the doc item 'COMMON LABEL' in sidebar 'sidebarWithConflicts', linking to the doc doc4",
"message": "COMMON LABEL",
},
"sidebar.sidebarWithConflicts.doc.key-ref5": {
"description": "The label for the doc item COMMON LABEL in sidebar sidebarWithConflicts, linking to the doc doc5",
"description": "The label for the doc item 'COMMON LABEL' in sidebar 'sidebarWithConflicts', linking to the doc doc5",
"message": "COMMON LABEL",
},
"sidebar.sidebarWithConflicts.link.key-link1": {
"description": "The label for link COMMON LABEL in sidebar sidebarWithConflicts, linking to https://example.com",
"description": "The label for link 'COMMON LABEL' in sidebar 'sidebarWithConflicts', linking to 'https://example.com'",
"message": "COMMON LABEL",
},
"sidebar.sidebarWithConflicts.link.key-link2": {
"description": "The label for link COMMON LABEL in sidebar sidebarWithConflicts, linking to https://example.com",
"description": "The label for link 'COMMON LABEL' in sidebar 'sidebarWithConflicts', linking to 'https://example.com'",
"message": "COMMON LABEL",
},
"version.label": {
@ -279,21 +279,24 @@ describe('getLoadedContentTranslationFiles', () => {
.toThrowErrorMatchingInlineSnapshot(`
"Multiple docs sidebar items produce the same translation key.
- \`sidebar.sidebarWithConflicts.category.COMMON LABEL\`: 2 duplicates found:
- COMMON LABEL (The label for category COMMON LABEL in sidebar sidebarWithConflicts)
- COMMON LABEL (The label for category COMMON LABEL in sidebar sidebarWithConflicts)
- COMMON LABEL (The label for category 'COMMON LABEL' in sidebar 'sidebarWithConflicts')
- COMMON LABEL (The label for category 'COMMON LABEL' in sidebar 'sidebarWithConflicts')
- \`sidebar.sidebarWithConflicts.link.COMMON LABEL\`: 2 duplicates found:
- COMMON LABEL (The label for link COMMON LABEL in sidebar sidebarWithConflicts, linking to https://example.com)
- COMMON LABEL (The label for link COMMON LABEL in sidebar sidebarWithConflicts, linking to https://example.com)
- COMMON LABEL (The label for link 'COMMON LABEL' in sidebar 'sidebarWithConflicts', linking to 'https://example.com')
- COMMON LABEL (The label for link 'COMMON LABEL' in sidebar 'sidebarWithConflicts', linking to 'https://example.com')
- \`sidebar.sidebarWithConflicts.doc.COMMON LABEL\`: 4 duplicates found:
- COMMON LABEL (The label for the doc item COMMON LABEL in sidebar sidebarWithConflicts, linking to the doc doc4)
- COMMON LABEL (The label for the doc item COMMON LABEL in sidebar sidebarWithConflicts, linking to the doc doc5)
- COMMON LABEL (The label for the doc item COMMON LABEL in sidebar sidebarWithConflicts, linking to the doc doc4)
- COMMON LABEL (The label for the doc item COMMON LABEL in sidebar sidebarWithConflicts, linking to the doc doc5)
- COMMON LABEL (The label for the doc item 'COMMON LABEL' in sidebar 'sidebarWithConflicts', linking to the doc doc4)
- COMMON LABEL (The label for the doc item 'COMMON LABEL' in sidebar 'sidebarWithConflicts', linking to the doc doc5)
- COMMON LABEL (The label for the doc item 'COMMON LABEL' in sidebar 'sidebarWithConflicts', linking to the doc doc4)
- COMMON LABEL (The label for the doc item 'COMMON LABEL' in sidebar 'sidebarWithConflicts', linking to the doc doc5)
To avoid translation key conflicts, use the \`key\` attribute on the sidebar items above to uniquely identify them.
"
When using autogenerated sidebars, you can provide a unique translation key by adding:
- the \`key\` attribute to category item metadata (\`_category_.json\` / \`_category_.yml\`)
- the \`sidebar_key\` attribute to doc item metadata (front matter in \`Category/index.mdx\`)"
`);
});
});

View File

@ -30,6 +30,7 @@ export const DocFrontMatterSchema = Joi.object<DocFrontMatter>({
// See https://github.com/facebook/docusaurus/issues/4591#issuecomment-822372398
description: Joi.string().allow(''),
slug: Joi.string(),
sidebar_key: Joi.string(),
sidebar_label: Joi.string(),
sidebar_position: Joi.number(),
sidebar_class_name: Joi.string(),

View File

@ -339,7 +339,15 @@ declare module '@docusaurus/plugin-content-docs' {
* @see {@link DocMetadata.slug}
*/
slug?: string;
/** Customizes the sidebar label for this doc. Will default to its title. */
/**
* Customizes the sidebar key for this doc,
* to uniquely identify it in translations.
*/
sidebar_key?: string;
/**
* Customizes the sidebar label for this doc.
* Will default to its title.
*/
sidebar_label?: string;
/**
* Controls the position of a doc inside the generated sidebar slice when

View File

@ -91,6 +91,7 @@ exports[`DefaultSidebarItemsGenerator generates simple flat sidebar 1`] = `
"custom": "prop",
},
"id": "doc1",
"key": "doc1-sidebar-key",
"label": "doc1 sidebar label",
"type": "doc",
},
@ -120,6 +121,7 @@ exports[`DefaultSidebarItemsGenerator generates subfolder sidebar 1`] = `
"type": "doc",
},
],
"key": "doc1-sidebar-key",
"label": "Subsubsubfolder category label",
"link": {
"id": "doc1",
@ -142,6 +144,7 @@ exports[`DefaultSidebarItemsGenerator generates subfolder sidebar 1`] = `
},
{
"id": "doc1",
"key": "doc1-sidebar-key",
"type": "doc",
},
{

View File

@ -63,6 +63,7 @@ describe('DefaultSidebarItemsGenerator', () => {
sourceDirName: '.',
sidebarPosition: 2,
frontMatter: {
sidebar_key: 'doc1-sidebar-key',
sidebar_label: 'doc1 sidebar label',
sidebar_custom_props: {custom: 'prop'},
},
@ -254,7 +255,9 @@ describe('DefaultSidebarItemsGenerator', () => {
sourceDirName: 'subfolder/subsubfolder',
title: 'Subsubsubfolder category label',
sidebarPosition: undefined,
frontMatter: {},
frontMatter: {
sidebar_key: 'doc1-sidebar-key',
},
},
{
id: 'doc2',

View File

@ -139,6 +139,7 @@ Available doc IDs:
const {
sidebarPosition: position,
frontMatter: {
sidebar_key: key,
sidebar_label: label,
sidebar_class_name: className,
sidebar_custom_props: customProps,
@ -151,6 +152,7 @@ Available doc IDs:
source: fileName,
// We don't want these fields to magically appear in the generated
// sidebar
...(key !== undefined && {key}),
...(label !== undefined && {label}),
...(className !== undefined && {className}),
...(customProps !== undefined && {customProps}),
@ -191,6 +193,7 @@ Available doc IDs:
function getCategoryLinkedDocMetadata():
| {
id: string;
key?: string;
position?: number;
label?: string;
customProps?: {[key: string]: unknown};
@ -212,6 +215,7 @@ Available doc IDs:
return {
id,
position: doc.sidebarPosition,
key: doc.frontMatter.sidebar_key,
label: doc.frontMatter.sidebar_label ?? doc.title,
customProps: doc.frontMatter.sidebar_custom_props,
className: doc.frontMatter.sidebar_class_name,
@ -236,6 +240,8 @@ Available doc IDs:
categoryMetadata?.customProps ?? categoryLinkedDoc?.customProps;
const {filename, numberPrefix} = numberPrefixParser(folderName);
const key = categoryMetadata?.key ?? categoryLinkedDoc?.key;
return {
type: 'category',
label: categoryMetadata?.label ?? categoryLinkedDoc?.label ?? filename,
@ -252,7 +258,7 @@ Available doc IDs:
...(categoryMetadata?.description && {
description: categoryMetadata?.description,
}),
...(categoryMetadata?.key && {key: categoryMetadata?.key}),
...(key && {key}),
...(link && {link}),
};
}

View File

@ -71,7 +71,16 @@ function ensureNoSidebarDuplicateEntries(
To avoid translation key conflicts, use the ${logger.code(
'key',
)} attribute on the sidebar items above to uniquely identify them.
`);
When using autogenerated sidebars, you can provide a unique translation key by adding:
- the ${logger.code('key')} attribute to category item metadata (${logger.code(
'_category_.json',
)} / ${logger.code('_category_.yml')})
- the ${logger.code(
'sidebar_key',
)} attribute to doc item metadata (front matter in ${logger.code(
'Category/index.mdx',
)})`);
}
}
@ -90,7 +99,7 @@ function getSidebarTranslationFileContent(
`sidebar.${sidebarName}.category.${categoryKey}`,
{
message: category.label,
description: `The label for category ${category.label} in sidebar ${sidebarName}`,
description: `The label for category '${category.label}' in sidebar '${sidebarName}'`,
},
]);
@ -100,7 +109,7 @@ function getSidebarTranslationFileContent(
`sidebar.${sidebarName}.category.${categoryKey}.link.generated-index.title`,
{
message: category.link.title,
description: `The generated-index page title for category ${category.label} in sidebar ${sidebarName}`,
description: `The generated-index page title for category '${category.label}' in sidebar '${sidebarName}'`,
},
]);
}
@ -109,7 +118,7 @@ function getSidebarTranslationFileContent(
`sidebar.${sidebarName}.category.${categoryKey}.link.generated-index.description`,
{
message: category.link.description,
description: `The generated-index page description for category ${category.label} in sidebar ${sidebarName}`,
description: `The generated-index page description for category '${category.label}' in sidebar '${sidebarName}'`,
},
]);
}
@ -126,7 +135,7 @@ function getSidebarTranslationFileContent(
`sidebar.${sidebarName}.link.${linkKey}`,
{
message: link.label,
description: `The label for link ${link.label} in sidebar ${sidebarName}, linking to ${link.href}`,
description: `The label for link '${link.label}' in sidebar '${sidebarName}', linking to '${link.href}'`,
},
];
});
@ -140,7 +149,7 @@ function getSidebarTranslationFileContent(
`sidebar.${sidebarName}.doc.${docKey}`,
{
message: doc.label!,
description: `The label for the doc item ${doc.label!} in sidebar ${sidebarName}, linking to the doc ${
description: `The label for the doc item '${doc.label!}' in sidebar '${sidebarName}', linking to the doc ${
doc.id
}`,
},

View File

@ -0,0 +1,8 @@
{
"label": "Category Index Name conflict 2",
"link": {
"type": "generated-index",
"title": "Category Index Name conflict 2",
"description": "Testing what happens when 2 categories with index docs have the same label"
}
}

View File

@ -0,0 +1,3 @@
# Doc
This extra category doc is important, otherwise the category would be empty and the index would be converted to a regular doc.

View File

@ -0,0 +1,6 @@
---
sidebar_label: 'Test'
sidebar_key: 'category-index-name-2.alpha.test'
---
# Test

View File

@ -0,0 +1,3 @@
# Doc
This extra category doc is important, otherwise the category would be empty and the index would be converted to a regular doc.

View File

@ -0,0 +1,6 @@
---
sidebar_label: 'Test'
sidebar_key: 'category-index-name-2.beta.test'
---
# Test

View File

@ -282,6 +282,7 @@ Accepted fields:
| `sidebar_label` | `string` | `title` | The text shown in the document sidebar for this document. |
| `sidebar_position` | `number` | Default ordering | Controls the position of a doc inside the generated sidebar slice when using `autogenerated` sidebar items. See also [Autogenerated sidebar metadata](/docs/sidebar/autogenerated#autogenerated-sidebar-metadata). |
| `sidebar_class_name` | `string` | `undefined` | Gives the corresponding sidebar label a special class name when using autogenerated sidebars. |
| `sidebar_key` | `string` | `undefined` | Gives the corresponding sidebar item a key to uniquely identify the sidebar item. This is mostly useful for i18n sites to generate unique translation keys for categories sharing the same labels. An error will tell you when this extra metadata is needed. |
| `sidebar_custom_props` | `object` | `undefined` | Assign [custom props](../../guides/docs/sidebar/index.mdx#passing-custom-props) to the sidebar item referencing this doc |
| `displayed_sidebar` | `string` | `undefined` | Force the display of a given sidebar when browsing the current document. Read the [multiple sidebars guide](../../guides/docs/sidebar/multiple-sidebars.mdx) for details. |
| `hide_title` | `boolean` | `false` | Whether to hide the title at the top of the doc. It only hides a title declared through the front matter, and have no effect on a Markdown title at the top of your document. |

View File

@ -310,7 +310,7 @@ For handwritten sidebar definitions, you would provide metadata to sidebar items
### Doc item metadata {#doc-item-metadata}
The `label`, `className`, and `customProps` attributes are declared in front matter as `sidebar_label`, `sidebar_class_name`, and `sidebar_custom_props`, respectively. Position can be specified in the same way, via `sidebar_position` front matter.
The `label`, `className`, `key`, and `customProps` attributes are declared in front matter as `sidebar_label`, `sidebar_class_name`, `sidebar_key` and `sidebar_custom_props`, respectively. Position can be specified in the same way, via `sidebar_position` front matter.
```md title="docs/tutorials/tutorial-easy.md"
---
@ -318,6 +318,7 @@ The `label`, `className`, and `customProps` attributes are declared in front mat
sidebar_position: 2
sidebar_label: Easy
sidebar_class_name: green
sidebar_key: unique-sidebar-item-key
# highlight-end
---
@ -328,7 +329,7 @@ This is the easy tutorial!
### Category item metadata {#category-item-metadata}
Add a `_category_.json` or `_category_.yml` file in the respective folder. You can specify any category metadata and also the `position` metadata. `label`, `className`, `position`, and `customProps` will default to the respective values of the category's linked doc, if there is one.
Add a `_category_.json` or `_category_.yml` file in the respective folder. You can specify any category metadata and also the `position` metadata. `label`, `className`, `key`, `position`, and `customProps` will default to the respective values of the category's linked doc, if there is one.
<Tabs>
<TabItem value="JSON">
@ -337,6 +338,7 @@ Add a `_category_.json` or `_category_.yml` file in the respective folder. You c
{
"position": 2.5,
"label": "Tutorial",
"key": "unique-sidebar-item-key",
"collapsible": true,
"collapsed": false,
"className": "red",

View File

@ -204,6 +204,14 @@ export default async function createConfigAsync() {
i18n: {
defaultLocale,
localeConfigs: {
[defaultLocale]: {
// Forces the translation process to run for default locale
// Permits to dogfood translation key conflicts detection
translate: true,
},
},
locales:
isDeployPreview || isBranchDeploy
? // Deploy preview and branch deploys: keep them fast!