This is an automated email from the ASF dual-hosted git repository.
morningman pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris-website.git
The following commit(s) were added to refs/heads/master by this push:
new 072ace699d2 [feat](i18n) Publish Korean docs with correct fallback
page SEO (#4361)
072ace699d2 is described below
commit 072ace699d23a2c10050650326dc7b867e1aacbf
Author: Mingyu Chen (Rayner) <[email protected]>
AuthorDate: Sun Oct 18 21:29:37 2026 +0100
[feat](i18n) Publish Korean docs with correct fallback page SEO (#4361)
Korean documentation is ready behind an opt-in flag, but production
deployment does not enable it. Untranslated ordinary pages also
currently declare Korean or Japanese with their own canonical URLs and
remain indexable, even when their content is English.
This PR enables Korean in both deployment workflows and fixes
ordinary-page fallback metadata before publication. Native Docusaurus
page routes identify ordinary pages without capturing documentation,
search, or other plugin-owned UI. English fallback pages declare
English, use the English canonical/Open Graph URL, stay out of the
sitemap with noindex, and are excluded from translated hreflang groups
on both localized and source pages. Existing Japanese product-page
translations retain their language and alternates. Metadata resets
correctly during SPA navigation.
The static verifier now checks ordinary-page metadata and source-page
alternates in addition to document state. Build Check also runs the
existing marketing-page test suite with the new regressions.
Validation completed:
- Node 20: 198 tests passed, zero failures, one existing Apache test
skipped; scoped TypeScript check and git diff checks passed.
- A native four-language fixture built in separate sequential processes.
Strict static checks passed for Japanese and Korean, including ordinary
fallbacks, translated Japanese pages, and full cross-language
counterparts.
- Chromium verified SSR/hydrated metadata, Korean fallback-to-document
navigation, Japanese fallback-to-translated-page navigation, and back
navigation, with no runtime errors.
- The branch includes merged PR #4360, preserving its English benchmark
entry, page-language controls, and full English footer.
The complete four-language Dev optimized build passed in 410 seconds.
Each managed locale passed checks for 1,810 document pages, 22 ordinary
pages, and 1,721 native navigation routes with zero skipped
counterparts. Korean and Japanese browser searches passed; Korean checks
also covered English/Chinese source search, eight themes at
desktop/mobile sizes, and the iframe theme bridge. Full-page browser
smoke passed without runtime or local resource failures.
All-active-version CI is still running. Production deployment and live
URL acceptance remain pending. The merged Korean coverage is five
machine translations out of 3,336 required documents; remaining
documents use the existing same-version English fallback and Korean
notices. T12 Korean product pages are still pending.
Korean translation generation remains manual-only; this PR does not add
a schedule. All enabled locales must build and pass strict verification
before the existing OSS/ASF deployment steps. No document URLs,
sidebars, translation state, theme colors, permissions, or secrets are
changed.
---------
Co-authored-by: morningman <[email protected]>
---
.github/workflows/build-check.yml | 1 +
.github/workflows/cron-deploy-website.yml | 1 +
.github/workflows/manual-deploy-website.yml | 1 +
scripts/i18n/__tests__/locale-registry.test.cjs | 4 +-
scripts/i18n/__tests__/marketing-pages.test.cjs | 80 ++++++++++++++++++++++---
scripts/i18n/status-plugin.cjs | 8 ++-
scripts/i18n/verify-static.cjs | 32 +++++++++-
src/theme/SiteMetadata/index.tsx | 18 ++++--
src/utils/localization.ts | 3 +-
src/utils/translation.ts | 10 +++-
10 files changed, 136 insertions(+), 22 deletions(-)
diff --git a/.github/workflows/build-check.yml
b/.github/workflows/build-check.yml
index dc6f3208719..7562b330357 100644
--- a/.github/workflows/build-check.yml
+++ b/.github/workflows/build-check.yml
@@ -108,6 +108,7 @@ jobs:
node --test
scripts/i18n/__tests__/search-verification.test.cjs
node --test scripts/i18n/__tests__/korean-tokenizer.test.cjs
node --test scripts/i18n/__tests__/doc-navigation.test.cjs
+ node --test scripts/i18n/__tests__/marketing-pages.test.cjs
node --test scripts/i18n/__tests__/locale-registry.test.cjs
node --test scripts/i18n/__tests__/multi-locale.test.cjs
node --test scripts/i18n/__tests__/inventory.test.cjs
diff --git a/.github/workflows/cron-deploy-website.yml
b/.github/workflows/cron-deploy-website.yml
index b7b221b4f39..99845ddfecc 100644
--- a/.github/workflows/cron-deploy-website.yml
+++ b/.github/workflows/cron-deploy-website.yml
@@ -25,6 +25,7 @@ jobs:
environment: Production
env:
DORIS_ENABLE_JA: 'true'
+ DORIS_ENABLE_KO: 'true'
steps:
- name: Checkout
# v4 (the pinned @master commit predates the `filter` input) with
diff --git a/.github/workflows/manual-deploy-website.yml
b/.github/workflows/manual-deploy-website.yml
index 0a40bda4915..cd65e786de5 100644
--- a/.github/workflows/manual-deploy-website.yml
+++ b/.github/workflows/manual-deploy-website.yml
@@ -21,6 +21,7 @@ jobs:
environment: Production
env:
DORIS_ENABLE_JA: 'true'
+ DORIS_ENABLE_KO: 'true'
steps:
- name: Checkout
uses: actions/checkout@master
diff --git a/scripts/i18n/__tests__/locale-registry.test.cjs
b/scripts/i18n/__tests__/locale-registry.test.cjs
index 33c8ea46ace..190a03c425d 100644
--- a/scripts/i18n/__tests__/locale-registry.test.cjs
+++ b/scripts/i18n/__tests__/locale-registry.test.cjs
@@ -71,13 +71,15 @@ test('route groups span every managed locale while a
manifest carries one', asyn
default: {translationManifest: {pages: [page('ja', 'a'), page('ja',
'b', false)], reports: [
{kind: 'unsupported_source_route', sourcePath:
'i18n/zh-CN/x.md'},
{kind: 'source_deleted', locale: 'ja', docId: 'gone'}]},
loadedVersions: [{path: '/ja/docs/current'}]},
- }};
+ }, 'docusaurus-plugin-content-pages': {default: [{permalink: '/ja/'},
{permalink: '/ja/download'}]}};
for (const locale of ['en', 'ja']) {
let globalData;
const plugin = translationStatusPlugin({siteDir, i18n: {currentLocale:
locale}});
await plugin.allContentLoaded({allContent, actions: {setGlobalData:
data => {globalData = data;}}});
const manifest = JSON.parse(fs.readFileSync(path.join(siteDir,
'.i18n-build/manifests', `${locale}.json`), 'utf8'));
assert.equal(manifest.locale, locale);
+ assert.deepEqual(globalData.pageRoutes, ['/ja/', '/ja/download/']);
+ assert.deepEqual(manifest.pageRoutes, globalData.pageRoutes);
// Both locales group the indexable page; the non-indexable one
contributes none.
assert.deepEqual(Object.keys(globalData.alternates).sort(),
['/docs/current/a/', '/ja/docs/current/a/',
'/zh-CN/docs/current/a/']);
diff --git a/scripts/i18n/__tests__/marketing-pages.test.cjs
b/scripts/i18n/__tests__/marketing-pages.test.cjs
index 543c9dc3748..d35944a789e 100644
--- a/scripts/i18n/__tests__/marketing-pages.test.cjs
+++ b/scripts/i18n/__tests__/marketing-pages.test.cjs
@@ -12,20 +12,34 @@ const {generate} = require('../../key-features/generate');
const root = path.resolve(__dirname, '../../..');
const pagePaths = localeRegistry.find(locale => locale.code ===
'ja').translatedPages;
-function modules(locale, pathname, enabled = true) {
+function modules(locale, pathname, enabled = true, translatedPages = {ja:
pagePaths}) {
const cache = new Map();
- const i18n = {currentLocale: locale, defaultLocale: 'en', locales: enabled
? ['en', 'zh-CN', 'ja'] : ['en', 'zh-CN'],
- localeConfigs: Object.fromEntries(localeRegistry.map(record =>
[record.code, record]))};
+ const locales = enabled ? ['en', 'zh-CN', 'ja', 'ko'] : ['en', 'zh-CN'];
+ const i18n = {currentLocale: locale, defaultLocale: 'en', locales,
+ localeConfigs: Object.fromEntries(localeRegistry.filter(record =>
locales.includes(record.code)).map(record => [record.code, record]))};
+ const config = {url: 'https://doris.apache.org', trailingSlash: true,
baseUrl: locale === 'en' ? '/' : `/${locale}/`,
+ customFields: {defaultLocalePageLocales: enabled ? ['ja','ko'] : [],
translatedPagePaths: enabled ? translatedPages : {}}};
+ const prefix = locale === 'en' ? '' : `/${locale}`;
const location = {pathname, search: '?from=test', hash: '#tech'};
const imports = {
'@generated/i18n': i18n,
- '@generated/docusaurus.config': {baseUrl: locale === 'en' ? '/' :
`/${locale}/`,
- customFields: {defaultLocalePageLocales: ['ja'],
translatedPagePaths: enabled ? {ja: pagePaths} : {}}},
+ '@generated/docusaurus.config': config,
'@docusaurus/Translate': {translate: ({message}) => message},
- '@docusaurus/useDocusaurusContext': () => ({i18n}),
+ '@docusaurus/useDocusaurusContext': () => ({i18n, siteConfig: config}),
'@docusaurus/useBaseUrl': source => source,
- '@docusaurus/useGlobalData': () => ({}),
+ '@docusaurus/useGlobalData': () => ({'doris-translation-status':
{default: {
+ alternates: {}, locales: enabled ? ['ja','ko'] : [], prefixes:
[`${prefix}/docs/dev`,`${prefix}/community`],
+ pageRoutes: ['/', '/download/', '/community-report/',
...pagePaths.map(page => `${page}/`)].map(page => prefix + page),
+ }}}),
'@docusaurus/router': {useLocation: () => location},
+ '@docusaurus/Head': ({children}) =>
React.createElement(React.Fragment, null, children),
+ '@docusaurus/theme-common': {useThemeConfig: () => ({metadata: []})},
+ '@docusaurus/theme-common/internal': {DEFAULT_SEARCH_TAG: 'default',
keyboardFocusedClassName: 'keyboard',
+ useAlternatePageUtils: () => ({createUrl: ({locale,
fullyQualified}) => {
+ const plain = pathname.replace(/^\/(en|zh-CN|ja|ko)(?=\/|$)/,
'') || '/';
+ return (fullyQualified ? config.url : '') + (locale === 'en' ?
'' : `/${locale}`) + plain;
+ }})},
+ '@theme/SearchMetadata': () => null,
'@docusaurus/Link': React.forwardRef(({to, href, autoAddBaseUrl =
true, children, ...props}, ref) => {
const target = (to || href).replace(/^pathname:\/\//, '');
const baseUrl = locale === 'en' ? '' : `/${locale}`;
@@ -60,6 +74,58 @@ function modules(locale, pathname, enabled = true) {
return load;
}
+function metadata(locale, pathname, enabled = true, translatedPages) {
+ const {default: SiteMetadata} = modules(locale, pathname, enabled,
translatedPages)('src/theme/SiteMetadata/index.tsx');
+ return
require('cheerio').load(renderToStaticMarkup(React.createElement(SiteMetadata)));
+}
+
+test('ordinary English fallbacks declare English and stay out of translated
SEO groups', () => {
+ for (const locale of ['ja','ko']) for (const page of ['/', '/download/',
'/community-report/']) {
+ const $ = metadata(locale, `/${locale}${page}`);
+ assert.equal($('html').attr('lang'), 'en-US');
+ assert.equal($('link[rel="canonical"]').attr('href'),
`https://doris.apache.org${page}`);
+ assert.equal($('meta[property="og:url"]').attr('content'),
`https://doris.apache.org${page}`);
+ assert.equal($('meta[property="og:locale"]').attr('content'), 'en_US');
+ assert.match($('meta[name="robots"]').attr('content'), /noindex/);
+ assert.equal($('link[hreflang="ja"],link[hreflang="ko"]').length, 0);
+ }
+ for (const [locale, pathname, lang] of [['en','/','en-US'],
['zh-CN','/zh-CN/','zh-Hans-CN']]) {
+ const $ = metadata(locale, pathname);
+ assert.equal($('html').attr('lang'), lang);
+ assert.equal($('meta[name="robots"]').length, 0);
+ assert.equal($('link[hreflang="ja"],link[hreflang="ko"]').length, 0);
+ }
+
assert.equal(modules('ko','/ko')('src/utils/localization.ts').defaultLocalePath('/ko'),
'/');
+});
+
+test('translated pages keep their SEO while an untranslated Korean counterpart
points to English', () => {
+ const pathname = '/why-doris/compare/';
+ const ja = metadata('ja', `/ja${pathname}`);
+ assert.equal(ja('html').attr('lang'), 'ja');
+ assert.equal(ja('meta[name="robots"]').length, 0);
+ assert.equal(ja('link[rel="canonical"]').attr('href'),
`https://doris.apache.org/ja${pathname}`);
+ const ko = metadata('ko', `/ko${pathname}`);
+ assert.equal(ko('html').attr('lang'), 'en-US');
+ assert.equal(ko('link[rel="canonical"]').attr('href'),
`https://doris.apache.org${pathname}`);
+ assert.match(ko('meta[name="robots"]').attr('content'), /noindex/);
+ assert.equal(ko('link[hreflang="ko"]').length, 0);
+ assert.equal(ko('link[hreflang="ja"]').attr('href'),
`https://doris.apache.org/ja${pathname}`);
+ const translated = metadata('ko', `/ko${pathname}`, true, {ja: pagePaths,
ko: ['/why-doris/compare']});
+ assert.equal(translated('html').attr('lang'), 'ko');
+ assert.equal(translated('meta[name="robots"]').length, 0);
+ assert.equal(translated('link[hreflang="ko"]').length, 1);
+});
+
+test('native page identification excludes documentation, search and generated
error routes', () => {
+ for (const pathname of ['/ko/docs/dev/guide/',
'/ko/community/join-community/', '/ko/search/', '/ko/404.html']) {
+ const routes = modules('ko',
pathname)('src/utils/translation.ts').useTranslationRoutes();
+ assert.equal(routes.pageFallback, false, pathname);
+ assert.equal(metadata('ko', pathname)('html').attr('lang'), 'ko');
+ }
+ const disabled = metadata('en', '/', false);
+ assert.equal(disabled('link[hreflang="ja"],link[hreflang="ko"]').length,
0);
+});
+
test('Japanese links retain translated pages and suffixes while other ordinary
pages use English', () => {
const {localizedHomeLink, hasTranslatedPage} = modules('ja',
'/')('src/utils/localization.ts');
assert.equal(pagePaths.length, 7);
diff --git a/scripts/i18n/status-plugin.cjs b/scripts/i18n/status-plugin.cjs
index ca92fb057cb..3303086b2f2 100644
--- a/scripts/i18n/status-plugin.cjs
+++ b/scripts/i18n/status-plugin.cjs
@@ -30,7 +30,11 @@ module.exports = function translationStatusPlugin(context) {
const pages = allPages.filter(page => page.locale === locale);
const prefixes =
Object.values(allContent['docusaurus-plugin-content-docs'] ?? {})
.flatMap(content => content.loadedVersions.map(version =>
version.path));
- actions.setGlobalData({alternates, prefixes, locales});
+ // Use native page routes so ordinary-page fallback never captures
+ // localized documentation, search, or other plugin-owned UI.
+ const pageRoutes = [...new
Set(Object.values(allContent['docusaurus-plugin-content-pages'] ?? {})
+ .flatMap(content => content ?? []).map(page =>
page.permalink.replace(/\/$/, '') + '/'))];
+ actions.setGlobalData({alternates, prefixes, locales, pageRoutes});
// Reports that belong to no single locale describe the repository,
// so they stay in every locale's manifest.
const reports = manifests.flatMap(manifest => manifest.reports)
@@ -38,7 +42,7 @@ module.exports = function translationStatusPlugin(context) {
const output = path.join(context.siteDir, '.i18n-build/manifests',
`${locale}.json`);
await fs.mkdir(path.dirname(output), {recursive: true});
await fs.writeFile(output, JSON.stringify({schemaVersion: 1,
locale,
- pages, reports}, null, 2) + '\n');
+ pages, pageRoutes, reports}, null, 2) + '\n');
},
};
};
diff --git a/scripts/i18n/verify-static.cjs b/scripts/i18n/verify-static.cjs
index 56d74e217ec..6d91baafdab 100644
--- a/scripts/i18n/verify-static.cjs
+++ b/scripts/i18n/verify-static.cjs
@@ -92,7 +92,35 @@ function verify({site, manifest, partial = false}) {
}
verified++;
}
- return {verified, skipped, partial, indexedPaths: indexedPaths.size,
+ const ordinaryPages = {verified: 0, fallback: 0};
+ const record = localeRegistry.find(item => item.code === locale);
+ for (const permalink of data.pageRoutes ?? []) {
+ const filename = path.join(site, decodeURIComponent(permalink),
'index.html');
+ assert(fs.existsSync(filename), `Missing ordinary page: ${permalink}`);
+ const $ = cheerio.load(fs.readFileSync(filename, 'utf8'));
+ const source = permalink.startsWith(`/${locale}/`) ?
permalink.slice(locale.length + 1) : permalink;
+ const fallback = record?.ordinaryPages === false &&
!record.translatedPages?.includes(source.replace(/\/+$/, ''));
+ if (fallback) {
+ assert.equal($('html').attr('lang'), htmlLangFor('en'), `Fallback
page language: ${permalink}`);
+ assert.equal($('link[rel="canonical"]').length, 1, `Duplicate
ordinary canonical: ${permalink}`);
+ assert.equal($('link[rel="canonical"]').attr('href'),
`https://doris.apache.org${source}`);
+ assert.equal($('meta[property="og:url"]').attr('content'),
`https://doris.apache.org${source}`);
+ assert.equal($('meta[property="og:locale"]').attr('content'),
'en_US');
+ assert(($('meta[name="robots"]').attr('content') ??
'').includes('noindex'), `Indexed English fallback: ${permalink}`);
+ assert(!sitemapPaths.has(permalink), `Ordinary fallback in
sitemap: ${permalink}`);
+
assert.equal($(`link[rel="alternate"][hreflang="${htmlLang}"]`).length, 0,
`Ordinary fallback alternate: ${permalink}`);
+ for (const sourceRoute of [source, `/zh-CN${source}`]) {
+ const source$ = counterpart(sourceRoute, 'ordinary fallback
alternates');
+ if (source$)
assert.equal(source$(`link[rel="alternate"][hreflang="${htmlLang}"]`).length, 0,
+ `Source advertises ordinary fallback: ${sourceRoute}`);
+ }
+ ordinaryPages.fallback++;
+ } else {
+ assert.equal($('html').attr('lang'), htmlLang, `Translated
ordinary page language: ${permalink}`);
+ }
+ ordinaryPages.verified++;
+ }
+ return {verified, skipped, partial, indexedPaths: indexedPaths.size,
ordinaryPages,
states: Object.fromEntries([...new Set(data.pages.map(page =>
page.status))]
.map(status => [status, data.pages.filter(page => page.status ===
status).length])), reports: data.reports};
}
@@ -104,6 +132,6 @@ if (require.main === module) {
partial: args.includes('--partial')});
if (args.includes('--output')) fs.writeFileSync(option('--output'),
JSON.stringify(result, null, 2) + '\n');
console.log(JSON.stringify({verified: result.verified, skipped:
result.skipped, partial: result.partial,
- states: result.states, reports: result.reports.length}));
+ states: result.states, ordinaryPages: result.ordinaryPages, reports:
result.reports.length}));
}
module.exports = {verify};
diff --git a/src/theme/SiteMetadata/index.tsx b/src/theme/SiteMetadata/index.tsx
index e34214e5a03..b9568ed1988 100644
--- a/src/theme/SiteMetadata/index.tsx
+++ b/src/theme/SiteMetadata/index.tsx
@@ -8,7 +8,7 @@
import React, {type JSX} from 'react';
import Head from '@docusaurus/Head';
import {useTranslationRoutes} from '@site/src/utils/translation';
-import {isDefaultLocaleBlogRoute} from '@site/src/utils/localization';
+import {defaultLocalePath, isDefaultLocaleBlogRoute} from
'@site/src/utils/localization';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import useBaseUrl from '@docusaurus/useBaseUrl';
import {PageMetadata, useThemeConfig} from '@docusaurus/theme-common';
@@ -31,11 +31,11 @@ function AlternateLangHeaders(): JSX.Element {
i18n: {currentLocale, defaultLocale, localeConfigs},
} = useDocusaurusContext();
const alternatePageUtils = useAlternatePageUtils();
- const {group, managed, translatedLocales} = useTranslationRoutes();
+ const {group, managed, translatedLocales, pageFallback} =
useTranslationRoutes();
const availableLocales = Object.entries(localeConfigs).filter(([locale]) =>
group ? !!group[locale] : !managed || !translatedLocales.includes(locale));
- const currentHtmlLang = localeConfigs[currentLocale]!.htmlLang;
+ const currentHtmlLang = localeConfigs[pageFallback ? defaultLocale :
currentLocale]!.htmlLang;
// HTML lang is a BCP 47 tag, but the Open Graph protocol requires
// using underscores instead of dashes.
@@ -123,9 +123,10 @@ function CanonicalUrlHeaders({permalink}: {permalink?:
string}) {
export default function SiteMetadata(): JSX.Element {
const {
- i18n: {currentLocale},
+ i18n: {currentLocale, defaultLocale, localeConfigs},
} = useDocusaurusContext();
const {pathname} = useLocation();
+ const {pageFallback} = useTranslationRoutes();
// TODO maybe move these 2 themeConfig to siteConfig?
// These seems useful for other themes as well
@@ -134,6 +135,7 @@ export default function SiteMetadata(): JSX.Element {
return (
<>
<Head>
+ <html lang={localeConfigs[pageFallback ? defaultLocale :
currentLocale]!.htmlLang} />
<meta name="twitter:card" content="summary_large_image" />
{/* The keyboard focus class name need to be applied when SSR so links
are outlined when JS is disabled */}
@@ -142,7 +144,13 @@ export default function SiteMetadata(): JSX.Element {
{defaultImage && <PageMetadata image={defaultImage} />}
- <CanonicalUrlHeaders />
+ <CanonicalUrlHeaders permalink={pageFallback ?
defaultLocalePath(pathname) : undefined} />
+
+ {pageFallback && (
+ <Head>
+ <meta name="robots" content="noindex, follow" />
+ </Head>
+ )}
{/* A locale without a translated blog serves the default locale's
articles under its own /blog routes. The section stays browsable, but
diff --git a/src/utils/localization.ts b/src/utils/localization.ts
index 7e3b339d7e8..1184fcaef6e 100644
--- a/src/utils/localization.ts
+++ b/src/utils/localization.ts
@@ -61,8 +61,7 @@ export function isDefaultLocaleBlogRoute(pathname: string):
boolean {
* so the page can point search engines at the copy worth ranking.
*/
export function defaultLocalePath(permalink: string): string {
- const prefix = `/${i18n.currentLocale}/`;
- const path = permalink.startsWith(prefix) ?
`/${permalink.slice(prefix.length)}` : permalink;
+ const path = normalizePathname(permalink, [i18n.currentLocale]);
// Permalinks carry no trailing slash; the served URLs do.
return applyTrailingSlash(path, { trailingSlash: siteConfig.trailingSlash,
baseUrl: siteConfig.baseUrl });
}
diff --git a/src/utils/translation.ts b/src/utils/translation.ts
index faa3dd2f32b..2b51ef1a7b1 100644
--- a/src/utils/translation.ts
+++ b/src/utils/translation.ts
@@ -1,7 +1,7 @@
import useGlobalData from '@docusaurus/useGlobalData';
import {useLocation} from '@docusaurus/router';
import i18n from '@generated/i18n';
-import {hasTranslatedPage, translatedPagePaths} from './localization';
+import {hasTranslatedPage, translatedPagePaths, usesDefaultLocalePages} from
'./localization';
import {normalizePathname} from './locale';
export type TranslationState = {
@@ -22,19 +22,22 @@ export function docTranslation(metadata: unknown):
TranslationState | undefined
return (metadata as {translation?: TranslationState}).translation;
}
-type StatusData = {alternates: Record<string, Record<string, string>>;
prefixes: string[]; locales?: string[]};
+type StatusData = {alternates: Record<string, Record<string, string>>;
prefixes: string[]; locales?: string[]; pageRoutes?: string[]};
export function useTranslationRoutes() {
const data = useGlobalData()['doris-translation-status']?.default as
StatusData | undefined;
const {pathname} = useLocation();
const route = pathname.replace(/\/$/, '') + '/';
const pagePath = normalizePathname(pathname, i18n.locales).replace(/\/+$/,
'');
const pageLocales = Object.keys(translatedPagePaths).filter(locale =>
hasTranslatedPage(pagePath, locale));
+ const ordinaryPage = data?.pageRoutes?.includes(route) ?? false;
+ const pageFallback = ordinaryPage && usesDefaultLocalePages() &&
!hasTranslatedPage(pathname);
if (pageLocales.length) {
return {
group: Object.fromEntries([i18n.defaultLocale,
...pageLocales].map(locale => [locale,
`${locale === i18n.defaultLocale ? '' :
`/${locale}`}${pagePath}/`])),
translatedLocales: data?.locales ?? [],
managed: true,
+ pageFallback,
};
}
return {group: data?.alternates[route],
@@ -42,5 +45,6 @@ export function useTranslationRoutes() {
// group must not offer them as an alternate, because the target page
// would only repeat the default locale's content.
translatedLocales: data?.locales ?? [],
- managed: !!data?.prefixes.some(prefix => route === prefix ||
route.startsWith(prefix.replace(/\/$/, '') + '/'))};
+ managed: ordinaryPage || !!data?.prefixes.some(prefix => route ===
prefix || route.startsWith(prefix.replace(/\/$/, '') + '/')),
+ pageFallback};
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]