From 6cb526b9e333b732a2a894faf08c2ed913d89949 Mon Sep 17 00:00:00 2001 From: George Adams Date: Thu, 9 Feb 2023 12:44:30 +0000 Subject: [PATCH 1/3] website i18n refactor --- .../{about.adoc => about/index.adoc} | 0 .../index.adoc} | 0 content/asciidoc-pages/docs/faq/index.es.adoc | 2 +- .../asciidoc-pages/docs/faq/index.zh-cn.adoc | 2 +- .../index.adoc} | 0 .../docs/qvs-policy/index.de.adoc | 2 +- .../{archives.adoc => archives/index.adoc} | 0 .../{linux.adoc => linux/index.adoc} | 0 .../{macOS.adoc => macOS/index.adoc} | 0 .../{windows.adoc => windows/index.adoc} | 0 .../{jmc.adoc => jmc/index.adoc} | 0 .../{support.adoc => support/index.adoc} | 0 .../index.adoc} | 0 .../{buttons.adoc => buttons/index.adoc} | 0 gatsby-config.js | 13 +- gatsby-node.js | 193 ++++++++++++++---- i18n-redirects.js | 88 -------- locales/i18n.js | 25 +++ src/templates/asciidocTemplate.tsx | 12 +- src/util/gatsby-node-helpers.js | 24 +++ 20 files changed, 215 insertions(+), 146 deletions(-) rename content/asciidoc-pages/{about.adoc => about/index.adoc} (100%) rename content/asciidoc-pages/docs/{aqavit-verification.adoc => aqavit-verification/index.adoc} (100%) rename content/asciidoc-pages/docs/{marketplace-listing.adoc => marketplace-listing/index.adoc} (100%) rename content/asciidoc-pages/installation/{archives.adoc => archives/index.adoc} (100%) rename content/asciidoc-pages/installation/{linux.adoc => linux/index.adoc} (100%) rename content/asciidoc-pages/installation/{macOS.adoc => macOS/index.adoc} (100%) rename content/asciidoc-pages/installation/{windows.adoc => windows/index.adoc} (100%) rename content/asciidoc-pages/{jmc.adoc => jmc/index.adoc} (100%) rename content/asciidoc-pages/{support.adoc => support/index.adoc} (100%) rename content/asciidoc-pages/{supported-platforms.adoc => supported-platforms/index.adoc} (100%) rename content/asciidoc-pages/temurin/{buttons.adoc => buttons/index.adoc} (100%) delete mode 100644 i18n-redirects.js create mode 100644 locales/i18n.js create mode 100644 src/util/gatsby-node-helpers.js diff --git a/content/asciidoc-pages/about.adoc b/content/asciidoc-pages/about/index.adoc similarity index 100% rename from content/asciidoc-pages/about.adoc rename to content/asciidoc-pages/about/index.adoc diff --git a/content/asciidoc-pages/docs/aqavit-verification.adoc b/content/asciidoc-pages/docs/aqavit-verification/index.adoc similarity index 100% rename from content/asciidoc-pages/docs/aqavit-verification.adoc rename to content/asciidoc-pages/docs/aqavit-verification/index.adoc diff --git a/content/asciidoc-pages/docs/faq/index.es.adoc b/content/asciidoc-pages/docs/faq/index.es.adoc index 93cb8fbfd..b8db6f89c 100644 --- a/content/asciidoc-pages/docs/faq/index.es.adoc +++ b/content/asciidoc-pages/docs/faq/index.es.adoc @@ -1,4 +1,4 @@ -= Preguntas Frecuentes += Frequently Asked Questions :page-authors: eddumelendez, czelabueno, jdluna, raulmj, tellison, gdams Hemos reunido unas cuantas preguntas frecuentes (FAQs) en este documento. diff --git a/content/asciidoc-pages/docs/faq/index.zh-cn.adoc b/content/asciidoc-pages/docs/faq/index.zh-cn.adoc index 185f73d17..349416226 100644 --- a/content/asciidoc-pages/docs/faq/index.zh-cn.adoc +++ b/content/asciidoc-pages/docs/faq/index.zh-cn.adoc @@ -1,4 +1,4 @@ -= 常见问题解答 += Frequently Asked Questions :page-authors: zdtsw, gdams, tellison 在该文件中我们收集了一些常见问题。如果您想与我们讨论这些话题或有其他问题,最好的途径是通过以下链接 diff --git a/content/asciidoc-pages/docs/marketplace-listing.adoc b/content/asciidoc-pages/docs/marketplace-listing/index.adoc similarity index 100% rename from content/asciidoc-pages/docs/marketplace-listing.adoc rename to content/asciidoc-pages/docs/marketplace-listing/index.adoc diff --git a/content/asciidoc-pages/docs/qvs-policy/index.de.adoc b/content/asciidoc-pages/docs/qvs-policy/index.de.adoc index 3d49c6234..f090bfd63 100644 --- a/content/asciidoc-pages/docs/qvs-policy/index.de.adoc +++ b/content/asciidoc-pages/docs/qvs-policy/index.de.adoc @@ -1,4 +1,4 @@ -= AQAvit Quality Verification Suite Policy += AQAvit(TM) Quality Verification Suite Policy :description: Adoptium QVS Policy :keywords: adoptium AQAvit quality policy :orgname: Eclipse Adoptium diff --git a/content/asciidoc-pages/installation/archives.adoc b/content/asciidoc-pages/installation/archives/index.adoc similarity index 100% rename from content/asciidoc-pages/installation/archives.adoc rename to content/asciidoc-pages/installation/archives/index.adoc diff --git a/content/asciidoc-pages/installation/linux.adoc b/content/asciidoc-pages/installation/linux/index.adoc similarity index 100% rename from content/asciidoc-pages/installation/linux.adoc rename to content/asciidoc-pages/installation/linux/index.adoc diff --git a/content/asciidoc-pages/installation/macOS.adoc b/content/asciidoc-pages/installation/macOS/index.adoc similarity index 100% rename from content/asciidoc-pages/installation/macOS.adoc rename to content/asciidoc-pages/installation/macOS/index.adoc diff --git a/content/asciidoc-pages/installation/windows.adoc b/content/asciidoc-pages/installation/windows/index.adoc similarity index 100% rename from content/asciidoc-pages/installation/windows.adoc rename to content/asciidoc-pages/installation/windows/index.adoc diff --git a/content/asciidoc-pages/jmc.adoc b/content/asciidoc-pages/jmc/index.adoc similarity index 100% rename from content/asciidoc-pages/jmc.adoc rename to content/asciidoc-pages/jmc/index.adoc diff --git a/content/asciidoc-pages/support.adoc b/content/asciidoc-pages/support/index.adoc similarity index 100% rename from content/asciidoc-pages/support.adoc rename to content/asciidoc-pages/support/index.adoc diff --git a/content/asciidoc-pages/supported-platforms.adoc b/content/asciidoc-pages/supported-platforms/index.adoc similarity index 100% rename from content/asciidoc-pages/supported-platforms.adoc rename to content/asciidoc-pages/supported-platforms/index.adoc diff --git a/content/asciidoc-pages/temurin/buttons.adoc b/content/asciidoc-pages/temurin/buttons/index.adoc similarity index 100% rename from content/asciidoc-pages/temurin/buttons.adoc rename to content/asciidoc-pages/temurin/buttons/index.adoc diff --git a/gatsby-config.js b/gatsby-config.js index 55f0ff082..6f9dbdb17 100644 --- a/gatsby-config.js +++ b/gatsby-config.js @@ -5,6 +5,7 @@ */ const path = require('path') +const locales = require('./locales/i18n') module.exports = { siteMetadata: { @@ -45,19 +46,25 @@ module.exports = { options: { name: 'locale', path: path.join(__dirname, 'locales'), - ignore: ['**/*.md'] + ignore: ['**/*.md', 'i18n.js'] } }, { resolve: 'gatsby-plugin-react-i18next', options: { localeJsonSourceName: 'locale', - languages: ['en', 'en-GB', 'es', 'de', 'zh-CN'], + languages: Object.keys(locales), defaultLanguage: 'en', i18nextOptions: { transSupportBasicHtmlNodes: true, transKeepBasicHtmlNodesFor: ['u', 'a'] - } + }, + pages: [ + { + matchPath: '/:lang?/docs/:uid', + getLanguageFromPath: true + } + ] } }, { diff --git a/gatsby-node.js b/gatsby-node.js index d59a2705b..3ce6b3d9e 100644 --- a/gatsby-node.js +++ b/gatsby-node.js @@ -3,7 +3,125 @@ const fs = require('fs') const { pipeline } = require('stream') const { promisify } = require('util') const { createFilePath } = require('gatsby-source-filesystem') -const createMultilingualRedirects = require('./i18n-redirects') +const locales = require('./locales/i18n') + +const { localizedSlug, findKey, removeTrailingSlash } = require('./src/util/gatsby-node-helpers') + +exports.onCreatePage = ({ page, actions }) => { + const { createPage, deletePage } = actions + + // Delete pages such as /about/index.de + if (page.path.includes('index')) { + return deletePage(page) + } + + // First delete the incoming page that was automatically created by Gatsby + // So everything in src/pages/ + // Don't do anything to the page if context has a language already set + if (page.component.includes('asciidocTemplate') && page.context.locale === 'en') { + // Grab the keys ('en' & 'de') of locales and map over them + // eslint-disable-next-line array-callback-return + Object.keys(locales).map(lang => { + if (lang !== 'en') { + // Use the values defined in "locales" to construct the path + const localizedPath = locales[lang].default + ? page.path + : `${locales[lang].path}${page.path}` + + let locale = 'en' + + if (fs.existsSync(`./content/asciidoc-pages${page.path}index.${lang}.adoc`)) { + locale = lang + } + + return createPage({ + // Pass on everything from the original page + ...page, + // Since page.path returns with a trailing slash (e.g. "/de/") + // We want to remove that + path: removeTrailingSlash(localizedPath), + // Pass in the locale as context to every page + // This context also gets passed to the src/components/layout file + // This should ensure that the locale is available on every page + context: { + ...page.context, + locale, + language: lang, + i18n: { + ...page.context.i18n, + routed: true, + originalPath: page.path, + path: removeTrailingSlash(localizedPath), + language: lang + } + } + }) + } + }) + } else { + deletePage(page) + } + + return createPage({ + // Pass on everything from the original page + ...page, + // Pass in the locale as context to every page + // This context also gets passed to the src/components/layout file + // This should ensure that the locale is available on every page + context: { + ...page.context + } + }) +} + +exports.onCreateNode = async ({ node, actions, getNode, getNodes }) => { + const { createNodeField } = actions + + if (node.internal.type === 'Asciidoc') { + const fetchFilePath = getNodes().find(n => n.id === node.parent) + const name = path.basename(fetchFilePath.relativePath, '.adoc') + + // Check if post.name is "index" -- because that's the file for default language + // (In this case "en") + const isDefault = name === 'index' + + // Find the key that has "default: true" set (in this case it returns "en") + const defaultKey = findKey(locales, o => o.default === true) + + // Files are defined with "name-with-dashes.lang.adoc" + // name returns "name-with-dashes.lang" + // So grab the lang from that string + // If it's the default language, pass the locale for that + const lang = isDefault ? defaultKey : name.split('.')[1] + + createNodeField({ node, name: 'relativePath', value: fetchFilePath.relativePath }) + createNodeField({ node, name: 'locale', value: lang }) + createNodeField({ node, name: 'isDefault', value: isDefault }) + + const value = createFilePath({ node, getNode }) + createNodeField({ + name: 'slug', + node, + value + }) + } else if (node.internal.type === 'Mdx') { + const slug = createFilePath({ node, getNode }) + const date = new Date(node.frontmatter.date) + const year = date.getFullYear() + const zeroPaddedMonth = `${date.getMonth() + 1}`.padStart(2, '0') + + createNodeField({ + name: 'slug', + node, + value: slug + }) + createNodeField({ + name: 'postPath', + node, + value: `/blog/${year}/${zeroPaddedMonth}${slug}` + }) + } +} exports.createPages = async ({ graphql, actions }) => { const { createPage } = actions @@ -13,17 +131,17 @@ exports.createPages = async ({ graphql, actions }) => { const asciidocResults = await graphql(` { - allAsciidoc { + docs: allFile(filter: {sourceInstanceName: {eq: "asciidoc-pages"}}) { edges { node { - id - fields { - slug - } - parent { - ... on File { - relativePath - absolutePath + childAsciidoc { + document { + title + } + fields { + locale + isDefault + slug } } } @@ -32,15 +150,29 @@ exports.createPages = async ({ graphql, actions }) => { } `) - asciidocResults.data.allAsciidoc.edges.forEach(({ node }) => { - const articleNodes = asciidocResults.data.allAsciidoc.edges - createMultilingualRedirects(actions, articleNodes, node) - // Create page for each asciidoc file + if (asciidocResults.errors) { + throw asciidocResults.errors + } + + const docs = asciidocResults.data.docs.edges + + docs.forEach(({ node: doc }) => { + const title = doc.childAsciidoc.document.title + const slug = doc.childAsciidoc.fields.slug + + // Use the fields created in exports.onCreateNode + const locale = doc.childAsciidoc.fields.locale + const isDefault = doc.childAsciidoc.fields.isDefault + createPage({ - path: node.fields.slug, + path: localizedSlug({ isDefault, locale, slug }), component: asciidocTemplate, context: { - id: node.id + // Pass both the "title" and "locale" to find a unique file + // Only the title would not have been sufficient as articles could have the same title + // in different languages, e.g. because an english phrase is also common in german + title, + locale } }) }) @@ -164,32 +296,3 @@ exports.createPages = async ({ graphql, actions }) => { }) }) } - -exports.onCreateNode = async ({ node, actions, getNode, loadNodeContent }) => { - const { createNodeField } = actions - - if (node.internal.type === 'Asciidoc') { - const value = createFilePath({ node, getNode }) - createNodeField({ - name: 'slug', - node, - value - }) - } else if (node.internal.type === 'Mdx') { - const slug = createFilePath({ node, getNode }) - const date = new Date(node.frontmatter.date) - const year = date.getFullYear() - const zeroPaddedMonth = `${date.getMonth() + 1}`.padStart(2, '0') - - createNodeField({ - name: 'slug', - node, - value: slug - }) - createNodeField({ - name: 'postPath', - node, - value: `/blog/${year}/${zeroPaddedMonth}${slug}` - }) - } -} diff --git a/i18n-redirects.js b/i18n-redirects.js deleted file mode 100644 index 841acfe34..000000000 --- a/i18n-redirects.js +++ /dev/null @@ -1,88 +0,0 @@ -/** - * Returns relative file path of given node under given `basePath` (optionally omitting file-extension) - */ -function getNodeRelativePath ( - { parent }, - omitFileExt, - basePath = '/content/asciidoc-pages' -) { - const fileAbsolutePath = parent.absolutePath - const relativePath = (fileAbsolutePath || '').split(basePath)?.[1] - if (!relativePath || !omitFileExt) return relativePath - return relativePath.split('.')?.[0] -} - -/** - * Returns language code from file path of given node. As a fallback `defaultLang` is returned. - */ -function getNodeLangCode ({ parent }, defaultLang = 'en') { - const fileAbsolutePath = parent.absolutePath - const langCodeRegex = /(?:[^/]*\.)(.*)(?:\.adoc)/ - return (fileAbsolutePath || '').match(langCodeRegex)?.[1] || defaultLang -} - -/** - * Creates and returns the theoretically translated url with the original slug. - * If `omitDefaultLang` is true, the given `defaultLang` will not be included in paths for this language. - */ -function getTranslatedUrlPath ( - slug, - sourceLang, - destLang, - omitDefaultLang, - defaultLang = 'en' -) { - const baseUrlPathRegex = new RegExp(`(?:/${sourceLang})?(/.*)`) - let baseUrlPath = slug.match(baseUrlPathRegex)?.[1] || '/' - if (baseUrlPath.includes('index')) { - baseUrlPath = baseUrlPath.split('/index')[0] - } - // Trim trailing slash - baseUrlPath = baseUrlPath.replace(/\/$/, '') - return omitDefaultLang && destLang === defaultLang - ? `${baseUrlPath}` - : `/${destLang}${baseUrlPath}` -} - -/** - * Determines equally named asciidoc-files with a different language, - * and creates redirects from the theoretically translated url to the actual slug. - */ -module.exports = function createMultilingualRedirects ( - { createRedirect }, - allNodes, - node -) { - const { slug } = node.fields - const relativePath = getNodeRelativePath(node, true) - const langCode = getNodeLangCode(node) - allNodes - .map(({ node: translatedNode }) => ({ - translatedNode, - translatedNodeLangCode: getNodeLangCode(translatedNode), - translatedNodeRelativePath: getNodeRelativePath(translatedNode, true) - })) - .filter( - ({ translatedNodeLangCode, translatedNodeRelativePath }) => - langCode !== translatedNodeLangCode && - relativePath === translatedNodeRelativePath - ) - .forEach(({ translatedNode, translatedNodeLangCode }) => { - if (translatedNode.fields.slug.includes('index')) { - const newRedirect = { - fromPath: getTranslatedUrlPath( - slug, - langCode, - translatedNodeLangCode, - true - ), - toPath: translatedNode.fields.slug, - isPermanent: true, - force: true, - redirectInBrowser: true - } - - createRedirect(newRedirect) - } - }) -} diff --git a/locales/i18n.js b/locales/i18n.js new file mode 100644 index 000000000..b53f7b912 --- /dev/null +++ b/locales/i18n.js @@ -0,0 +1,25 @@ +// Only one item MUST have the "default: true" key + +module.exports = { + en: { + default: true, + locale: 'en-US', + path: 'en' + }, + 'en-GB': { + locale: 'en-GB', + path: 'en-GB' + }, + de: { + locale: 'de-DE', + path: 'de' + }, + es: { + locale: 'es-ES', + path: 'es' + }, + 'zh-CN': { + locale: 'zh-CN', + path: 'zh-CN' + } +} diff --git a/src/templates/asciidocTemplate.tsx b/src/templates/asciidocTemplate.tsx index 186f1f779..23cfd8a45 100644 --- a/src/templates/asciidocTemplate.tsx +++ b/src/templates/asciidocTemplate.tsx @@ -18,10 +18,10 @@ const AsciidocTemplate = ({ data }) => { asciidocFormatter() highlightCode() }) - const { asciidoc, file } = data // data.asciidoc holds our data + const { asciidoc } = data // data.asciidoc holds our data const { document, fields, html, pageAttributes } = asciidoc const pageAuthorList = pageAttributes.authors || '' - const { relativePath } = file + const { relativePath } = fields return (
@@ -64,8 +64,8 @@ export const Head = ({ data: { asciidoc: { document } } }) => { }; export const pageQuery = graphql` - query($id: String!, $language: String!) { - asciidoc(id: { eq: $id }) { + query($locale: String!, $title: String!, $language: String!) { + asciidoc(fields: {locale: {eq: $locale}}, document: {title: {eq: $title}}) { html document { title @@ -73,14 +73,12 @@ export const pageQuery = graphql` } fields { slug + relativePath } pageAttributes { authors } } - file(childAsciidoc: {id: {eq: $id }}) { - relativePath - } locales: allLocale(filter: {language: {eq: $language}}) { edges { node { diff --git a/src/util/gatsby-node-helpers.js b/src/util/gatsby-node-helpers.js new file mode 100644 index 000000000..dc68d2005 --- /dev/null +++ b/src/util/gatsby-node-helpers.js @@ -0,0 +1,24 @@ +// Use a little helper function to remove trailing slashes from paths +exports.removeTrailingSlash = path => + path === '/' ? path : path.replace(/\/$/, '') + +exports.localizedSlug = ({ isDefault, locale, slug }) => + isDefault ? `${slug}` : `${locale}/${slug}` + +// From lodash: +// https://github.com/lodash/lodash/blob/750067f42d3aa5f927604ece2c6df0ff2b2e9d72/findKey.js +exports.findKey = (object, predicate) => { + let result + if (object == null) { + return result + } + Object.keys(object).some(key => { + const value = object[key] + if (predicate(value, key, object)) { + result = key + return true + } + return false + }) + return result +} From 7e96fd96eba67222f9df221671369f6a81cd28f9 Mon Sep 17 00:00:00 2001 From: George Adams Date: Thu, 9 Feb 2023 13:30:43 +0000 Subject: [PATCH 2/3] fix tests --- .../asciidoc-pages/docs/first-timer-support/index.de.adoc | 2 +- netlify.toml | 1 + src/__fixtures__/page.tsx | 6 +----- src/types/index.tsx | 5 +---- 4 files changed, 4 insertions(+), 10 deletions(-) diff --git a/content/asciidoc-pages/docs/first-timer-support/index.de.adoc b/content/asciidoc-pages/docs/first-timer-support/index.de.adoc index 4bdbcc8ed..42b6922d4 100644 --- a/content/asciidoc-pages/docs/first-timer-support/index.de.adoc +++ b/content/asciidoc-pages/docs/first-timer-support/index.de.adoc @@ -1,4 +1,4 @@ -= First Timers Support (DRAFT) += Eclipse Adoptium(R) First Timers Support :page-authors: gdams, HanSolo, hendrikebbers, tellison :description: Support with first time contributions :keywords: adoptium documentation contribute first-time diff --git a/netlify.toml b/netlify.toml index ba21a1979..58105f76e 100644 --- a/netlify.toml +++ b/netlify.toml @@ -3,6 +3,7 @@ publish = "public" command = "npm run build" [build.environment] +NODE_OPTIONS = "--max_old_space_size=4096" NODE_VERSION = "18" GATSBY_CONCURRENT_DOWNLOAD = "15" GATSBY_CPU_COUNT = "1" diff --git a/src/__fixtures__/page.tsx b/src/__fixtures__/page.tsx index 83832e97b..cc53f441f 100644 --- a/src/__fixtures__/page.tsx +++ b/src/__fixtures__/page.tsx @@ -1,6 +1,5 @@ import { AsciidocPage, - File, MDXPage, SingleMDXPage, SiteMetaData @@ -8,7 +7,6 @@ import { export const createAsciidocData = (): { asciidoc: AsciidocPage; - file: File; } => ({ asciidoc: { id: 'asciidoc-1', @@ -33,11 +31,9 @@ export const createAsciidocData = (): { }, fields: { slug: '/asciidoc/asciidoc-page-title', + relativePath: 'test.adoc', } }, - file: { - relativePath: 'test.adoc', - } }); export const createMDXData = (): { diff --git a/src/types/index.tsx b/src/types/index.tsx index 8bb79859b..0dad136cb 100644 --- a/src/types/index.tsx +++ b/src/types/index.tsx @@ -7,16 +7,13 @@ export interface AsciidocPage { }, fields: { slug: string; + relativePath: string; }, pageAttributes: { authors: string; } } -export interface File { - relativePath: string; -} - export interface MDXPage { edges: MDXPageItem[]; } From 5f93a4e367805c9d1befbafcc8dfbe96a08ebfa9 Mon Sep 17 00:00:00 2001 From: George Adams Date: Thu, 9 Feb 2023 13:42:56 +0000 Subject: [PATCH 3/3] final move --- .../docs/faq/{index.zh-cn.adoc => index.zh-CN.adoc} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename content/asciidoc-pages/docs/faq/{index.zh-cn.adoc => index.zh-CN.adoc} (100%) diff --git a/content/asciidoc-pages/docs/faq/index.zh-cn.adoc b/content/asciidoc-pages/docs/faq/index.zh-CN.adoc similarity index 100% rename from content/asciidoc-pages/docs/faq/index.zh-cn.adoc rename to content/asciidoc-pages/docs/faq/index.zh-CN.adoc