diff --git a/docs/src/modules/utils/parseMarkdown.js b/docs/src/modules/utils/parseMarkdown.js index 8e29eabcf2386f..9761008f6c9cae 100644 --- a/docs/src/modules/utils/parseMarkdown.js +++ b/docs/src/modules/utils/parseMarkdown.js @@ -10,6 +10,7 @@ const titleRegExp = /# (.*)[\r\n]/; const descriptionRegExp = /
(.*)<\/p>[\r\n]/; const headerKeyValueRegExp = /(.*): (.*)/g; const emptyRegExp = /^\s*$/; +const notEnglishMarkdownRegExp = /-([a-z]{2})\.md$/; /** * Extract information from the top of the markdown. @@ -130,19 +131,32 @@ export function prepareMarkdown(config) { const demos = {}; const docs = {}; + const headingHashes = {}; + + // Process the English markdown before the other locales. + let filenames = []; requireRaw.keys().forEach((filename) => { + if (filename.match(notEnglishMarkdownRegExp)) { + filenames.push(filename); + } else { + filenames = [filename].concat(filenames); + } + }); + + filenames.forEach((filename) => { if (filename.indexOf('.md') !== -1) { - const match = filename.match(/-([a-z]{2})\.md$/); + const matchNotEnglishMarkdown = filename.match(notEnglishMarkdownRegExp); const userLanguage = - match && LANGUAGES_IN_PROGRESS.indexOf(match[1]) !== -1 ? match[1] : 'en'; + matchNotEnglishMarkdown && LANGUAGES_IN_PROGRESS.indexOf(matchNotEnglishMarkdown[1]) !== -1 + ? matchNotEnglishMarkdown[1] + : 'en'; const markdown = requireRaw(filename); - const contents = getContents(markdown); const headers = getHeaders(markdown); - const title = headers.title || getTitle(markdown); const description = headers.description || getDescription(markdown); + const contents = getContents(markdown); if (headers.components.length > 0) { contents.push(` @@ -157,8 +171,10 @@ ${headers.components `); } - const headingHashes = {}; const toc = []; + const headingHashesFallbackTranslated = {}; + let headingIndex = -1; + const rendered = contents.map((content) => { if (demos && demoRegexp.test(content)) { try { @@ -186,17 +202,28 @@ ${headers.components ) // remove emojis .replace(/<\/?[^>]+(>|$)/g, '') // remove HTML .trim(); - const hash = textToHash(headingText, headingHashes); + + // Standardizes the hash from the default location (en) to different locations + // Need english.md file parsed first + let hash; + if (userLanguage === 'en') { + hash = textToHash(headingText, headingHashes); + } else { + headingIndex += 1; + hash = Object.keys(headingHashes)[headingIndex]; + if (!hash) { + hash = textToHash(headingText, headingHashesFallbackTranslated); + } + } // enable splitting of long words from function name + first arg name // Closing parens are less interesting since this would only allow breaking one character earlier. // Applying the same mechanism would also allow breaking of non-function signatures like "Community help (free)". // To detect that we enabled breaking of open/closing parens we'd need a context-sensitive parser. const displayText = headingText.replace(/([^\s]\()/g, '$1'); - /** - * create a nested structure with 2 levels starting with level 2 e.g. - * [{...level2, children: [level3, level3, level3]}, level2] - */ + + // create a nested structure with 2 levels starting with level 2 e.g. + // [{...level2, children: [level3, level3, level3]}, level2] if (level === 2) { toc.push({ text: displayText, @@ -247,6 +274,7 @@ ${headers.components }, }); }); + // fragment link symbol rendered.unshift(``); - const location = headers.filename || `/docs/src/pages/${pageFilename}/${filename}`; - - const localized = { description, location, rendered, toc, title }; - - docs[userLanguage] = localized; + docs[userLanguage] = { + description, + location: headers.filename || `/docs/src/pages/${pageFilename}/${filename}`, + rendered, + toc, + title, + }; } else if (filename.indexOf('.tsx') !== -1) { const demoName = `pages/${pageFilename}/${filename .replace(/\.\//g, '') diff --git a/docs/src/modules/utils/parseMarkdown.test.js b/docs/src/modules/utils/parseMarkdown.test.js index 670a59c9ba4830..51f41b1bed534d 100644 --- a/docs/src/modules/utils/parseMarkdown.test.js +++ b/docs/src/modules/utils/parseMarkdown.test.js @@ -104,5 +104,189 @@ describe('parseMarkdown', () => { }, ]); }); + + it('use english hash for different locales', () => { + const markdownEn = ` +# Localization +## Locales +### Example +### Use same hash +`; + + const markdownPt = ` +# Localização +## Idiomas +### Exemplo +### Usar o mesmo hash +`; + + const markdownZh = ` +# 所在位置 +## 语言环境 +### 例 +### 使用相同的哈希 +`; + // mock require.context + function requireRaw(filename) { + switch (filename) { + case 'localization-pt.md': + return markdownPt; + case 'localization-zh.md': + return markdownZh; + default: + return markdownEn; + } + } + requireRaw.keys = () => ['localization-pt.md', 'localization.md', 'localization-zh.md']; + + const { + docs: { + en: { toc: tocEn }, + pt: { toc: tocPt }, + zh: { toc: tocZh }, + }, + } = prepareMarkdown({ + pageFilename: 'same-hash-test', + requireRaw, + }); + + expect(tocZh).to.have.deep.ordered.members([ + { + children: [ + { + hash: 'example', + level: 3, + text: '例', + }, + { + hash: 'use-same-hash', + level: 3, + text: '使用相同的哈希', + }, + ], + hash: 'locales', + level: 2, + text: '语言环境', + }, + ]); + + expect(tocPt).to.have.deep.ordered.members([ + { + children: [ + { + hash: 'example', + level: 3, + text: 'Exemplo', + }, + { + hash: 'use-same-hash', + level: 3, + text: 'Usar o mesmo hash', + }, + ], + hash: 'locales', + level: 2, + text: 'Idiomas', + }, + ]); + + expect(tocEn).to.have.deep.ordered.members([ + { + children: [ + { + hash: 'example', + level: 3, + text: 'Example', + }, + { + hash: 'use-same-hash', + level: 3, + text: 'Use same hash', + }, + ], + hash: 'locales', + level: 2, + text: 'Locales', + }, + ]); + }); + + it('use translated hash for translations are not synced', () => { + const markdownEn = ` +# Localization +## Locales +### Example +### Use same hash +`; + + const markdownPt = ` +# Localização +## Idiomas +### Exemplo +### Usar o mesmo hash +### Usar traduzido +`; + + // mock require.context + function requireRaw(filename) { + return filename === 'localization-pt.md' ? markdownPt : markdownEn; + } + requireRaw.keys = () => ['localization-pt.md', 'localization.md']; + + const { + docs: { + en: { toc: tocEn }, + pt: { toc: tocPt }, + }, + } = prepareMarkdown({ + pageFilename: 'same-hash-test', + requireRaw, + }); + + expect(tocPt).to.have.deep.ordered.members([ + { + children: [ + { + hash: 'example', + level: 3, + text: 'Exemplo', + }, + { + hash: 'use-same-hash', + level: 3, + text: 'Usar o mesmo hash', + }, + { + hash: 'usar-traduzido', + level: 3, + text: 'Usar traduzido', + }, + ], + hash: 'locales', + level: 2, + text: 'Idiomas', + }, + ]); + + expect(tocEn).to.have.deep.ordered.members([ + { + children: [ + { + hash: 'example', + level: 3, + text: 'Example', + }, + { + hash: 'use-same-hash', + level: 3, + text: 'Use same hash', + }, + ], + hash: 'locales', + level: 2, + text: 'Locales', + }, + ]); + }); }); });