Skip to content

Commit

Permalink
New options, fixes and improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
babakfp committed Apr 25, 2024
1 parent 2bd4666 commit 19524cd
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 74 deletions.
129 changes: 56 additions & 73 deletions src/client/app/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,14 +77,7 @@ export function createRouter(

let latestPendingPath: string | null = null

async function loadPage(
href: string,
scrollPosition = 0,
isRetry = false,
alreadyTriedLoadingRootFallback = false
) {
let fallbackLoaded = false

async function loadPage(href: string, scrollPosition = 0, isRetry = false) {
if ((await router.onBeforePageLoad?.(href)) === false) return
const targetLoc = new URL(href, fakeHost)
const pendingPath = (latestPendingPath = targetLoc.pathname)
Expand Down Expand Up @@ -159,96 +152,86 @@ export function createRouter(
} catch (e) {}
}

if (!alreadyTriedLoadingRootFallback) {
fallbackLoaded = await loadPageFallback()
if (siteDataRef.value.localesFallback) {
await loadFallback()
}

if (!fallbackLoaded && latestPendingPath === pendingPath) {
if (latestPendingPath === pendingPath) {
latestPendingPath = null
route.path = inBrowser ? pendingPath : withBase(pendingPath)
route.component = fallbackComponent ? markRaw(fallbackComponent) : null
route.data = notFoundPageData
}
}

// If failed to find the page, maybe it's not translated yet! if so, please fallback :)
async function loadPageFallback() {
async function loadFallback() {
const locales = siteDataRef.value.locales
if (!locales) return fallbackLoaded
const namedLocales = Object.fromEntries(
Object.entries(siteDataRef.value.locales).filter(
([name]) => name !== 'root'
)
)
if (!Object.entries(namedLocales).length) return fallbackLoaded

const langNames = Object.keys(namedLocales)

const failedLang = langNames.find(
(lang) =>
pendingPath === `/${lang}` || pendingPath.startsWith(`/${lang}/`)
)

if (failedLang) {
const fallbackLang =
getFailedLangFallbackLang() ?? getCustomFallbackLang()

if (fallbackLang) {
await loadPage(
pendingPath.replace(`/${failedLang}`, `/${fallbackLang}`)
for (const [key, value] of Object.entries(locales)) {
if (!value.fallback) continue
if (value.fallback === 'root') {
throw new Error(
`Invalid Vitepress Config: A locale (${key}), cannot fallback to (root).`
)
return fallbackLoaded
} else {
await loadPage(pendingPath.replace(`/${failedLang}`, ''))
return fallbackLoaded
}
} else {
const rootRouteFallbackPath = getRootRouteFallbackPath()

if (rootRouteFallbackPath) {
await loadPage(rootRouteFallbackPath, 0, true, true)
if (key === value.fallback) {
throw new Error(
`Invalid Vitepress Config: A locale (${key}), cannot have a fallback to itself.`
)
}
return fallbackLoaded
}

function getFailedLangFallbackLang() {
const failedLangFallbackLang = locales[failedLang!]?.fallback
if (!failedLangFallbackLang) return
if (!langNames.includes(failedLangFallbackLang)) {
console.warn(
`Invalid value received in "VitePress Config" > "locales.${failedLang}.fallback". "${failedLangFallbackLang}" is not a valid value.`
if (!Object.keys(locales).includes(value.fallback)) {
throw new Error(
`Invalid Vitepress Config: A locale (${key}), cannot have a fallback to a non existing locale.`
)
return
}
}

return failedLangFallbackLang
// If the length is less than 2, it means there are no alternative locales to fallback to.
if (!locales || Object.keys(locales).length < 2) {
return
}

const nonRootLocales = Object.fromEntries(
Object.entries(locales).filter(([name]) => name !== 'root')
)

const failedLocaleKey =
Object.keys(nonRootLocales).find(
(lang) =>
pendingPath === `/${lang}` || pendingPath.startsWith(`/${lang}/`)
) || 'root'

if (failedLocaleKey !== 'root') {
const fallbackLang =
locales[failedLocaleKey].fallback ?? getCustomFallbackLang()

await loadPage(
pendingPath.replace(
`/${failedLocaleKey}`,
fallbackLang ? `/${fallbackLang}` : ''
),
scrollPosition,
true
)
} else {
const fallbackPath = getRootLocaleFallbackPath()
if (!fallbackPath) return
await loadPage(fallbackPath, scrollPosition, true)
}

function getCustomFallbackLang() {
const customFallbackLang = Object.entries(namedLocales).filter(
([_, values]) => values.useAsFallback
)?.[0]?.[0]
if (customFallbackLang && customFallbackLang !== failedLang) {
const customFallbackLang = siteDataRef.value.localesDefaultFallback
if (customFallbackLang && customFallbackLang !== failedLocaleKey) {
return customFallbackLang
}
}

function getRootRouteFallbackPath() {
const fallbackLang = locales['root']?.fallback

function getRootLocaleFallbackPath() {
const fallbackLang = locales['root'].fallback
if (!fallbackLang) return
if (!langNames.includes(fallbackLang)) {
console.warn(
`Invalid value received in "VitePress Config" > "locales.root.fallback". "${fallbackLang}" is not a valid value.`
)
return
}

return pendingPath === '/'
? `/${fallbackLang}`
: `/${fallbackLang}${
pendingPath.startsWith('/') ? pendingPath : `/${pendingPath}`
}`
if (pendingPath === '/') return `/${fallbackLang}`
const pathDivider = pendingPath.startsWith('/') ? '' : '/'
return `/${fallbackLang}${pathDivider}${pendingPath}`
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/node/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,8 @@ export async function resolveSiteData(
appearance: userConfig.appearance ?? true,
themeConfig: userConfig.themeConfig || {},
locales: userConfig.locales || {},
localesFallback: userConfig.localesFallback ?? true,
localesDefaultFallback: userConfig.localesDefaultFallback,
scrollOffset: userConfig.scrollOffset ?? 134,
cleanUrls: !!userConfig.cleanUrls,
contentProps: userConfig.contentProps
Expand Down
10 changes: 10 additions & 0 deletions src/node/siteConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,16 @@ export interface UserConfig<ThemeConfig = any>

locales?: LocaleConfig<ThemeConfig>

/**
* If a page isn't found in the current language, allow switching to another language as a backup.
*/
localesFallback?: boolean

/**
* Use a custom locale key to be used as a default fallback for all locales. Default is root.
*/
localesDefaultFallback?: string

router?: {
prefetchLinks?: boolean
}
Expand Down
12 changes: 11 additions & 1 deletion types/shared.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,14 @@ export interface SiteData<ThemeConfig = any> {
| string[]
| { selector: string | string[]; padding: number }
locales: LocaleConfig<ThemeConfig>
/**
* If a page isn't found in the current language, allow switching to another language as a backup.
*/
localesFallback?: boolean
/**
* Use a custom locale key to be used as a default fallback for all locales. Default is root.
*/
localesDefaultFallback?: string
localeIndex?: string
contentProps?: Record<string, any>
router: {
Expand Down Expand Up @@ -159,8 +167,10 @@ export type LocaleConfig<ThemeConfig = any> = Record<
LocaleSpecificConfig<ThemeConfig> & {
label: string
link?: string
/**
* If the requested page isn't found in this language, switch to the same page in the specified language as a backup.
*/
fallback?: string
useAsFallback?: boolean
}
>

Expand Down

0 comments on commit 19524cd

Please sign in to comment.