@docusaurus/plugin-content-docs 3.9.1 → 3.9.2-alpha.0-canary-6548

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -148,11 +148,18 @@ function getSidebarBreadcrumbs({ sidebarItems, pathname, onlyCategories = false,
148
148
  const breadcrumbs = [];
149
149
  function extract(items) {
150
150
  for (const item of items) {
151
- if ((item.type === 'category' &&
152
- (isSamePath(item.href, pathname) || extract(item.items))) ||
153
- (item.type === 'link' && isSamePath(item.href, pathname))) {
154
- const filtered = onlyCategories && item.type !== 'category';
155
- if (!filtered) {
151
+ // Extract category item
152
+ if (item.type === 'category') {
153
+ if (isSamePath(item.href, pathname) || extract(item.items)) {
154
+ breadcrumbs.unshift(item);
155
+ return true;
156
+ }
157
+ }
158
+ // Extract doc item
159
+ else if (item.type === 'link' &&
160
+ item.docId &&
161
+ isSamePath(item.href, pathname)) {
162
+ if (!onlyCategories) {
156
163
  breadcrumbs.unshift(item);
157
164
  }
158
165
  return true;
package/lib/docs.js CHANGED
@@ -38,7 +38,7 @@ async function readVersionDocs(versionMetadata, options) {
38
38
  }
39
39
  async function doProcessDocMetadata({ docFile, versionMetadata, context, options, env, tagsFile, }) {
40
40
  const { source, content, contentPath, filePath } = docFile;
41
- const { siteDir, siteConfig: { markdown: { parseFrontMatter }, }, } = context;
41
+ const { siteDir, siteConfig: { markdown: { parseFrontMatter }, future: { experimental_vcs: vcs }, }, } = context;
42
42
  const { frontMatter: unsafeFrontMatter, contentTitle, excerpt, } = await (0, utils_1.parseMarkdownFile)({
43
43
  filePath,
44
44
  fileContent: content,
@@ -50,7 +50,7 @@ async function doProcessDocMetadata({ docFile, versionMetadata, context, options
50
50
  // (01-MyFolder/01-MyDoc.md => MyFolder/MyDoc)
51
51
  // but allow to disable this behavior with front matter
52
52
  parse_number_prefixes: parseNumberPrefixes = true, last_update: lastUpdateFrontMatter, } = frontMatter;
53
- const lastUpdate = await (0, utils_1.readLastUpdateData)(filePath, options, lastUpdateFrontMatter);
53
+ const lastUpdate = await (0, utils_1.readLastUpdateData)(filePath, options, lastUpdateFrontMatter, vcs);
54
54
  // E.g. api/plugins/myDoc -> myDoc; myDoc -> myDoc
55
55
  const sourceFileNameWithoutExtension = path_1.default.basename(source, path_1.default.extname(source));
56
56
  // E.g. api/plugins/myDoc -> api/plugins; myDoc -> .
@@ -24,6 +24,7 @@ exports.DocFrontMatterSchema = utils_validation_1.JoiFrontMatter.object({
24
24
  // See https://github.com/facebook/docusaurus/issues/4591#issuecomment-822372398
25
25
  description: utils_validation_1.JoiFrontMatter.string().allow(''),
26
26
  slug: utils_validation_1.JoiFrontMatter.string(),
27
+ sidebar_key: utils_validation_1.JoiFrontMatter.string(),
27
28
  sidebar_label: utils_validation_1.JoiFrontMatter.string(),
28
29
  sidebar_position: utils_validation_1.JoiFrontMatter.number(),
29
30
  sidebar_class_name: utils_validation_1.JoiFrontMatter.string(),
package/lib/index.js CHANGED
@@ -75,7 +75,7 @@ async function pluginContentDocs(context, options) {
75
75
  versionsMetadata,
76
76
  }),
77
77
  ].filter((d) => typeof d === 'string'),
78
- useCrossCompilerCache: siteConfig.future.experimental_faster.mdxCrossCompilerCache,
78
+ useCrossCompilerCache: siteConfig.future.faster.mdxCrossCompilerCache,
79
79
  admonitions: options.admonitions,
80
80
  remarkPlugins,
81
81
  rehypePlugins,
@@ -91,7 +91,7 @@ Available doc IDs:
91
91
  */
92
92
  function generateSidebar(fsModel) {
93
93
  function createDocItem(id, fullPath, fileName) {
94
- const { sidebarPosition: position, frontMatter: { sidebar_label: label, sidebar_class_name: className, sidebar_custom_props: customProps, }, } = getDoc(id);
94
+ const { sidebarPosition: position, frontMatter: { sidebar_key: key, sidebar_label: label, sidebar_class_name: className, sidebar_custom_props: customProps, }, } = getDoc(id);
95
95
  return {
96
96
  type: 'doc',
97
97
  id,
@@ -99,6 +99,7 @@ Available doc IDs:
99
99
  source: fileName,
100
100
  // We don't want these fields to magically appear in the generated
101
101
  // sidebar
102
+ ...(key !== undefined && { key }),
102
103
  ...(label !== undefined && { label }),
103
104
  ...(className !== undefined && { className }),
104
105
  ...(customProps !== undefined && { customProps }),
@@ -139,6 +140,7 @@ Available doc IDs:
139
140
  return {
140
141
  id,
141
142
  position: doc.sidebarPosition,
143
+ key: doc.frontMatter.sidebar_key,
142
144
  label: doc.frontMatter.sidebar_label ?? doc.title,
143
145
  customProps: doc.frontMatter.sidebar_custom_props,
144
146
  className: doc.frontMatter.sidebar_class_name,
@@ -156,6 +158,7 @@ Available doc IDs:
156
158
  const className = categoryMetadata?.className ?? categoryLinkedDoc?.className;
157
159
  const customProps = categoryMetadata?.customProps ?? categoryLinkedDoc?.customProps;
158
160
  const { filename, numberPrefix } = numberPrefixParser(folderName);
161
+ const key = categoryMetadata?.key ?? categoryLinkedDoc?.key;
159
162
  return {
160
163
  type: 'category',
161
164
  label: categoryMetadata?.label ?? categoryLinkedDoc?.label ?? filename,
@@ -171,7 +174,7 @@ Available doc IDs:
171
174
  ...(categoryMetadata?.description && {
172
175
  description: categoryMetadata?.description,
173
176
  }),
174
- ...(categoryMetadata?.key && { key: categoryMetadata?.key }),
177
+ ...(key && { key }),
175
178
  ...(link && { link }),
176
179
  };
177
180
  }
@@ -39,7 +39,10 @@ function ensureNoSidebarDuplicateEntries(translationEntries) {
39
39
  .join('\n\n- ')}
40
40
 
41
41
  To avoid translation key conflicts, use the ${logger_1.default.code('key')} attribute on the sidebar items above to uniquely identify them.
42
- `);
42
+
43
+ When using autogenerated sidebars, you can provide a unique translation key by adding:
44
+ - the ${logger_1.default.code('key')} attribute to category item metadata (${logger_1.default.code('_category_.json')} / ${logger_1.default.code('_category_.yml')})
45
+ - the ${logger_1.default.code('sidebar_key')} attribute to doc item metadata (front matter in ${logger_1.default.code('Category/index.mdx')})`);
43
46
  }
44
47
  }
45
48
  function getSidebarTranslationFileContent(sidebar, sidebarName) {
@@ -51,7 +54,7 @@ function getSidebarTranslationFileContent(sidebar, sidebarName) {
51
54
  `sidebar.${sidebarName}.category.${categoryKey}`,
52
55
  {
53
56
  message: category.label,
54
- description: `The label for category ${category.label} in sidebar ${sidebarName}`,
57
+ description: `The label for category '${category.label}' in sidebar '${sidebarName}'`,
55
58
  },
56
59
  ]);
57
60
  if (category.link?.type === 'generated-index') {
@@ -60,7 +63,7 @@ function getSidebarTranslationFileContent(sidebar, sidebarName) {
60
63
  `sidebar.${sidebarName}.category.${categoryKey}.link.generated-index.title`,
61
64
  {
62
65
  message: category.link.title,
63
- description: `The generated-index page title for category ${category.label} in sidebar ${sidebarName}`,
66
+ description: `The generated-index page title for category '${category.label}' in sidebar '${sidebarName}'`,
64
67
  },
65
68
  ]);
66
69
  }
@@ -69,7 +72,7 @@ function getSidebarTranslationFileContent(sidebar, sidebarName) {
69
72
  `sidebar.${sidebarName}.category.${categoryKey}.link.generated-index.description`,
70
73
  {
71
74
  message: category.link.description,
72
- description: `The generated-index page description for category ${category.label} in sidebar ${sidebarName}`,
75
+ description: `The generated-index page description for category '${category.label}' in sidebar '${sidebarName}'`,
73
76
  },
74
77
  ]);
75
78
  }
@@ -83,7 +86,7 @@ function getSidebarTranslationFileContent(sidebar, sidebarName) {
83
86
  `sidebar.${sidebarName}.link.${linkKey}`,
84
87
  {
85
88
  message: link.label,
86
- description: `The label for link ${link.label} in sidebar ${sidebarName}, linking to ${link.href}`,
89
+ description: `The label for link '${link.label}' in sidebar '${sidebarName}', linking to '${link.href}'`,
87
90
  },
88
91
  ];
89
92
  });
@@ -96,7 +99,7 @@ function getSidebarTranslationFileContent(sidebar, sidebarName) {
96
99
  `sidebar.${sidebarName}.doc.${docKey}`,
97
100
  {
98
101
  message: doc.label,
99
- description: `The label for the doc item ${doc.label} in sidebar ${sidebarName}, linking to the doc ${doc.id}`,
102
+ description: `The label for the doc item '${doc.label}' in sidebar '${sidebarName}', linking to the doc ${doc.id}`,
100
103
  },
101
104
  ];
102
105
  });
@@ -110,8 +113,9 @@ function translateSidebar({ sidebar, sidebarName, sidebarsTranslations, }) {
110
113
  return undefined;
111
114
  }
112
115
  if (category.link.type === 'generated-index') {
113
- const title = sidebarsTranslations[`sidebar.${sidebarName}.category.${category.label}.link.generated-index.title`]?.message ?? category.link.title;
114
- const description = sidebarsTranslations[`sidebar.${sidebarName}.category.${category.label}.link.generated-index.description`]?.message ?? category.link.description;
116
+ const categoryKey = category.key ?? category.label;
117
+ const title = sidebarsTranslations[`sidebar.${sidebarName}.category.${categoryKey}.link.generated-index.title`]?.message ?? category.link.title;
118
+ const description = sidebarsTranslations[`sidebar.${sidebarName}.category.${categoryKey}.link.generated-index.description`]?.message ?? category.link.description;
115
119
  return {
116
120
  ...category.link,
117
121
  title,
@@ -175,12 +179,51 @@ function getVersionTranslationFiles(version) {
175
179
  },
176
180
  ];
177
181
  }
182
+ // TODO Docusaurus v4 or later
183
+ // this temporarily works, but it is not an ideal solution
184
+ // The docs navigation can be computed and shouldn't be part of LoadedVersion
185
+ // We need to derive the navigation from already translated content
186
+ // See https://github.com/facebook/docusaurus/pull/11794
187
+ function translateDocNavigation(docs, translatedSidebars) {
188
+ // Build a map of permalink -> translated label for generated-index categories
189
+ const translatedLabelByPermalink = new Map();
190
+ for (const sidebar of Object.values(translatedSidebars)) {
191
+ for (const category of (0, utils_2.collectSidebarCategories)(sidebar)) {
192
+ if (category.link?.type === 'generated-index') {
193
+ translatedLabelByPermalink.set(category.link.permalink, category.label);
194
+ }
195
+ }
196
+ }
197
+ if (translatedLabelByPermalink.size === 0) {
198
+ return docs;
199
+ }
200
+ return docs.map((doc) => {
201
+ const previous = doc.previous && translatedLabelByPermalink.has(doc.previous.permalink)
202
+ ? {
203
+ ...doc.previous,
204
+ title: translatedLabelByPermalink.get(doc.previous.permalink),
205
+ }
206
+ : doc.previous;
207
+ const next = doc.next && translatedLabelByPermalink.has(doc.next.permalink)
208
+ ? {
209
+ ...doc.next,
210
+ title: translatedLabelByPermalink.get(doc.next.permalink),
211
+ }
212
+ : doc.next;
213
+ if (previous === doc.previous && next === doc.next) {
214
+ return doc;
215
+ }
216
+ return { ...doc, previous, next };
217
+ });
218
+ }
178
219
  function translateVersion(version, translationFiles) {
179
220
  const versionTranslations = translationFiles[getVersionFileName(version.versionName)].content;
221
+ const translatedSidebars = translateSidebars(version, versionTranslations);
180
222
  return {
181
223
  ...version,
182
224
  label: versionTranslations['version.label']?.message ?? version.label,
183
- sidebars: translateSidebars(version, versionTranslations),
225
+ sidebars: translatedSidebars,
226
+ docs: translateDocNavigation(version.docs, translatedSidebars),
184
227
  };
185
228
  }
186
229
  function getVersionsTranslationFiles(versions) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@docusaurus/plugin-content-docs",
3
- "version": "3.9.1",
3
+ "version": "3.9.2-alpha.0-canary-6548",
4
4
  "description": "Docs plugin for Docusaurus.",
5
5
  "main": "lib/index.js",
6
6
  "sideEffects": false,
@@ -35,15 +35,15 @@
35
35
  },
36
36
  "license": "MIT",
37
37
  "dependencies": {
38
- "@docusaurus/core": "3.9.1",
39
- "@docusaurus/logger": "3.9.1",
40
- "@docusaurus/mdx-loader": "3.9.1",
41
- "@docusaurus/module-type-aliases": "3.9.1",
42
- "@docusaurus/theme-common": "3.9.1",
43
- "@docusaurus/types": "3.9.1",
44
- "@docusaurus/utils": "3.9.1",
45
- "@docusaurus/utils-common": "3.9.1",
46
- "@docusaurus/utils-validation": "3.9.1",
38
+ "@docusaurus/core": "3.9.2-alpha.0-canary-6548",
39
+ "@docusaurus/logger": "3.9.2-alpha.0-canary-6548",
40
+ "@docusaurus/mdx-loader": "3.9.2-alpha.0-canary-6548",
41
+ "@docusaurus/module-type-aliases": "3.9.2-alpha.0-canary-6548",
42
+ "@docusaurus/theme-common": "3.9.2-alpha.0-canary-6548",
43
+ "@docusaurus/types": "3.9.2-alpha.0-canary-6548",
44
+ "@docusaurus/utils": "3.9.2-alpha.0-canary-6548",
45
+ "@docusaurus/utils-common": "3.9.2-alpha.0-canary-6548",
46
+ "@docusaurus/utils-validation": "3.9.2-alpha.0-canary-6548",
47
47
  "@types/react-router-config": "^5.0.7",
48
48
  "combine-promises": "^1.1.0",
49
49
  "fs-extra": "^11.1.1",
@@ -67,5 +67,5 @@
67
67
  "engines": {
68
68
  "node": ">=20.0"
69
69
  },
70
- "gitHead": "c0dd59f0e712f85b6053c59e46b0514b5d2d1414"
70
+ "gitHead": "7694572787296be09b95c6ff0a541ae19185a9f5"
71
71
  }
@@ -234,15 +234,22 @@ function getSidebarBreadcrumbs({
234
234
  }): PropSidebarBreadcrumbsItem[] {
235
235
  const breadcrumbs: PropSidebarBreadcrumbsItem[] = [];
236
236
 
237
- function extract(items: PropSidebarItem[]) {
237
+ function extract(items: PropSidebarItem[]): boolean {
238
238
  for (const item of items) {
239
- if (
240
- (item.type === 'category' &&
241
- (isSamePath(item.href, pathname) || extract(item.items))) ||
242
- (item.type === 'link' && isSamePath(item.href, pathname))
239
+ // Extract category item
240
+ if (item.type === 'category') {
241
+ if (isSamePath(item.href, pathname) || extract(item.items)) {
242
+ breadcrumbs.unshift(item);
243
+ return true;
244
+ }
245
+ }
246
+ // Extract doc item
247
+ else if (
248
+ item.type === 'link' &&
249
+ item.docId &&
250
+ isSamePath(item.href, pathname)
243
251
  ) {
244
- const filtered = onlyCategories && item.type !== 'category';
245
- if (!filtered) {
252
+ if (!onlyCategories) {
246
253
  breadcrumbs.unshift(item);
247
254
  }
248
255
  return true;
package/src/docs.ts CHANGED
@@ -97,6 +97,7 @@ async function doProcessDocMetadata({
97
97
  siteDir,
98
98
  siteConfig: {
99
99
  markdown: {parseFrontMatter},
100
+ future: {experimental_vcs: vcs},
100
101
  },
101
102
  } = context;
102
103
 
@@ -125,6 +126,7 @@ async function doProcessDocMetadata({
125
126
  filePath,
126
127
  options,
127
128
  lastUpdateFrontMatter,
129
+ vcs,
128
130
  );
129
131
 
130
132
  // E.g. api/plugins/myDoc -> myDoc; myDoc -> myDoc
@@ -30,6 +30,7 @@ export const DocFrontMatterSchema = Joi.object<DocFrontMatter>({
30
30
  // See https://github.com/facebook/docusaurus/issues/4591#issuecomment-822372398
31
31
  description: Joi.string().allow(''),
32
32
  slug: Joi.string(),
33
+ sidebar_key: Joi.string(),
33
34
  sidebar_label: Joi.string(),
34
35
  sidebar_position: Joi.number(),
35
36
  sidebar_class_name: Joi.string(),
package/src/index.ts CHANGED
@@ -131,8 +131,7 @@ export default async function pluginContentDocs(
131
131
  }),
132
132
  ].filter((d): d is string => typeof d === 'string'),
133
133
 
134
- useCrossCompilerCache:
135
- siteConfig.future.experimental_faster.mdxCrossCompilerCache,
134
+ useCrossCompilerCache: siteConfig.future.faster.mdxCrossCompilerCache,
136
135
  admonitions: options.admonitions,
137
136
  remarkPlugins,
138
137
  rehypePlugins,
@@ -339,7 +339,15 @@ declare module '@docusaurus/plugin-content-docs' {
339
339
  * @see {@link DocMetadata.slug}
340
340
  */
341
341
  slug?: string;
342
- /** Customizes the sidebar label for this doc. Will default to its title. */
342
+ /**
343
+ * Customizes the sidebar key for this doc,
344
+ * to uniquely identify it in translations.
345
+ */
346
+ sidebar_key?: string;
347
+ /**
348
+ * Customizes the sidebar label for this doc.
349
+ * Will default to its title.
350
+ */
343
351
  sidebar_label?: string;
344
352
  /**
345
353
  * Controls the position of a doc inside the generated sidebar slice when
@@ -139,6 +139,7 @@ Available doc IDs:
139
139
  const {
140
140
  sidebarPosition: position,
141
141
  frontMatter: {
142
+ sidebar_key: key,
142
143
  sidebar_label: label,
143
144
  sidebar_class_name: className,
144
145
  sidebar_custom_props: customProps,
@@ -151,6 +152,7 @@ Available doc IDs:
151
152
  source: fileName,
152
153
  // We don't want these fields to magically appear in the generated
153
154
  // sidebar
155
+ ...(key !== undefined && {key}),
154
156
  ...(label !== undefined && {label}),
155
157
  ...(className !== undefined && {className}),
156
158
  ...(customProps !== undefined && {customProps}),
@@ -191,6 +193,7 @@ Available doc IDs:
191
193
  function getCategoryLinkedDocMetadata():
192
194
  | {
193
195
  id: string;
196
+ key?: string;
194
197
  position?: number;
195
198
  label?: string;
196
199
  customProps?: {[key: string]: unknown};
@@ -212,6 +215,7 @@ Available doc IDs:
212
215
  return {
213
216
  id,
214
217
  position: doc.sidebarPosition,
218
+ key: doc.frontMatter.sidebar_key,
215
219
  label: doc.frontMatter.sidebar_label ?? doc.title,
216
220
  customProps: doc.frontMatter.sidebar_custom_props,
217
221
  className: doc.frontMatter.sidebar_class_name,
@@ -236,6 +240,8 @@ Available doc IDs:
236
240
  categoryMetadata?.customProps ?? categoryLinkedDoc?.customProps;
237
241
  const {filename, numberPrefix} = numberPrefixParser(folderName);
238
242
 
243
+ const key = categoryMetadata?.key ?? categoryLinkedDoc?.key;
244
+
239
245
  return {
240
246
  type: 'category',
241
247
  label: categoryMetadata?.label ?? categoryLinkedDoc?.label ?? filename,
@@ -252,7 +258,7 @@ Available doc IDs:
252
258
  ...(categoryMetadata?.description && {
253
259
  description: categoryMetadata?.description,
254
260
  }),
255
- ...(categoryMetadata?.key && {key: categoryMetadata?.key}),
261
+ ...(key && {key}),
256
262
  ...(link && {link}),
257
263
  };
258
264
  }
@@ -71,7 +71,16 @@ function ensureNoSidebarDuplicateEntries(
71
71
  To avoid translation key conflicts, use the ${logger.code(
72
72
  'key',
73
73
  )} attribute on the sidebar items above to uniquely identify them.
74
- `);
74
+
75
+ When using autogenerated sidebars, you can provide a unique translation key by adding:
76
+ - the ${logger.code('key')} attribute to category item metadata (${logger.code(
77
+ '_category_.json',
78
+ )} / ${logger.code('_category_.yml')})
79
+ - the ${logger.code(
80
+ 'sidebar_key',
81
+ )} attribute to doc item metadata (front matter in ${logger.code(
82
+ 'Category/index.mdx',
83
+ )})`);
75
84
  }
76
85
  }
77
86
 
@@ -90,7 +99,7 @@ function getSidebarTranslationFileContent(
90
99
  `sidebar.${sidebarName}.category.${categoryKey}`,
91
100
  {
92
101
  message: category.label,
93
- description: `The label for category ${category.label} in sidebar ${sidebarName}`,
102
+ description: `The label for category '${category.label}' in sidebar '${sidebarName}'`,
94
103
  },
95
104
  ]);
96
105
 
@@ -100,7 +109,7 @@ function getSidebarTranslationFileContent(
100
109
  `sidebar.${sidebarName}.category.${categoryKey}.link.generated-index.title`,
101
110
  {
102
111
  message: category.link.title,
103
- description: `The generated-index page title for category ${category.label} in sidebar ${sidebarName}`,
112
+ description: `The generated-index page title for category '${category.label}' in sidebar '${sidebarName}'`,
104
113
  },
105
114
  ]);
106
115
  }
@@ -109,7 +118,7 @@ function getSidebarTranslationFileContent(
109
118
  `sidebar.${sidebarName}.category.${categoryKey}.link.generated-index.description`,
110
119
  {
111
120
  message: category.link.description,
112
- description: `The generated-index page description for category ${category.label} in sidebar ${sidebarName}`,
121
+ description: `The generated-index page description for category '${category.label}' in sidebar '${sidebarName}'`,
113
122
  },
114
123
  ]);
115
124
  }
@@ -126,7 +135,7 @@ function getSidebarTranslationFileContent(
126
135
  `sidebar.${sidebarName}.link.${linkKey}`,
127
136
  {
128
137
  message: link.label,
129
- description: `The label for link ${link.label} in sidebar ${sidebarName}, linking to ${link.href}`,
138
+ description: `The label for link '${link.label}' in sidebar '${sidebarName}', linking to '${link.href}'`,
130
139
  },
131
140
  ];
132
141
  });
@@ -140,7 +149,7 @@ function getSidebarTranslationFileContent(
140
149
  `sidebar.${sidebarName}.doc.${docKey}`,
141
150
  {
142
151
  message: doc.label!,
143
- description: `The label for the doc item ${doc.label!} in sidebar ${sidebarName}, linking to the doc ${
152
+ description: `The label for the doc item '${doc.label!}' in sidebar '${sidebarName}', linking to the doc ${
144
153
  doc.id
145
154
  }`,
146
155
  },
@@ -168,13 +177,14 @@ function translateSidebar({
168
177
  return undefined;
169
178
  }
170
179
  if (category.link.type === 'generated-index') {
180
+ const categoryKey = category.key ?? category.label;
171
181
  const title =
172
182
  sidebarsTranslations[
173
- `sidebar.${sidebarName}.category.${category.label}.link.generated-index.title`
183
+ `sidebar.${sidebarName}.category.${categoryKey}.link.generated-index.title`
174
184
  ]?.message ?? category.link.title;
175
185
  const description =
176
186
  sidebarsTranslations[
177
- `sidebar.${sidebarName}.category.${category.label}.link.generated-index.description`
187
+ `sidebar.${sidebarName}.category.${categoryKey}.link.generated-index.description`
178
188
  ]?.message ?? category.link.description;
179
189
  return {
180
190
  ...category.link,
@@ -259,16 +269,64 @@ function getVersionTranslationFiles(version: LoadedVersion): TranslationFile[] {
259
269
  },
260
270
  ];
261
271
  }
272
+
273
+ // TODO Docusaurus v4 or later
274
+ // this temporarily works, but it is not an ideal solution
275
+ // The docs navigation can be computed and shouldn't be part of LoadedVersion
276
+ // We need to derive the navigation from already translated content
277
+ // See https://github.com/facebook/docusaurus/pull/11794
278
+ function translateDocNavigation(
279
+ docs: LoadedVersion['docs'],
280
+ translatedSidebars: Sidebars,
281
+ ): LoadedVersion['docs'] {
282
+ // Build a map of permalink -> translated label for generated-index categories
283
+ const translatedLabelByPermalink = new Map<string, string>();
284
+ for (const sidebar of Object.values(translatedSidebars)) {
285
+ for (const category of collectSidebarCategories(sidebar)) {
286
+ if (category.link?.type === 'generated-index') {
287
+ translatedLabelByPermalink.set(category.link.permalink, category.label);
288
+ }
289
+ }
290
+ }
291
+
292
+ if (translatedLabelByPermalink.size === 0) {
293
+ return docs;
294
+ }
295
+
296
+ return docs.map((doc) => {
297
+ const previous =
298
+ doc.previous && translatedLabelByPermalink.has(doc.previous.permalink)
299
+ ? {
300
+ ...doc.previous,
301
+ title: translatedLabelByPermalink.get(doc.previous.permalink)!,
302
+ }
303
+ : doc.previous;
304
+ const next =
305
+ doc.next && translatedLabelByPermalink.has(doc.next.permalink)
306
+ ? {
307
+ ...doc.next,
308
+ title: translatedLabelByPermalink.get(doc.next.permalink)!,
309
+ }
310
+ : doc.next;
311
+ if (previous === doc.previous && next === doc.next) {
312
+ return doc;
313
+ }
314
+ return {...doc, previous, next};
315
+ });
316
+ }
317
+
262
318
  function translateVersion(
263
319
  version: LoadedVersion,
264
320
  translationFiles: {[fileName: string]: TranslationFile},
265
321
  ): LoadedVersion {
266
322
  const versionTranslations =
267
323
  translationFiles[getVersionFileName(version.versionName)]!.content;
324
+ const translatedSidebars = translateSidebars(version, versionTranslations);
268
325
  return {
269
326
  ...version,
270
327
  label: versionTranslations['version.label']?.message ?? version.label,
271
- sidebars: translateSidebars(version, versionTranslations),
328
+ sidebars: translatedSidebars,
329
+ docs: translateDocNavigation(version.docs, translatedSidebars),
272
330
  };
273
331
  }
274
332