Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(i18n): fix locales reading, add site.langs #353

Merged
merged 7 commits into from
Aug 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/client/app/components/Debug.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const el = ref<HTMLElement | null>(null)
const open = ref(false)

watch(open, (value) => {
if (value === false) {
if (!value) {
el.value!.scrollTop = 0
}
})
Expand Down
8 changes: 5 additions & 3 deletions src/client/app/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,11 @@ export function initData(route: Route): VitePressData {
frontmatter: computed(() => route.data.frontmatter),
lang: computed(() => site.value.lang),
localePath: computed(() => {
const { locales, lang } = site.value
const path = Object.keys(locales).find((lp) => locales[lp].lang === lang)
return withBase((locales && path) || '/')
const { langs, lang } = site.value
const path = Object.keys(langs).find(
(langPath) => langs[langPath].lang === lang
)
return withBase(path || '/')
}),
title: computed(() => {
return route.data.title
Expand Down
4 changes: 1 addition & 3 deletions src/client/theme-default/Layout.vue
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,7 @@ const isCustomLayout = computed(() => !!frontmatter.value.customLayout)
const enableHome = computed(() => !!frontmatter.value.home)

// automatic multilang check for AlgoliaSearchBox
const isMultiLang = computed(
() => Object.keys(theme.value.locales || {}).length > 0
)
const isMultiLang = computed(() => Object.keys(site.value.langs).length > 1)

// navbar
const showNavbar = computed(() => {
Expand Down
4 changes: 2 additions & 2 deletions src/client/theme-default/components/NavLinks.vue
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<script setup lang="ts">
import { computed } from 'vue'
import { useData } from 'vitepress'
import { useLocaleLinks } from '../composables/nav'
import { useLanguageLinks } from '../composables/nav'
import { useRepo } from '../composables/repo'
import NavLink from './NavLink.vue'
import NavDropdownLink from './NavDropdownLink.vue'

const { theme } = useData()
const localeLinks = useLocaleLinks()
const localeLinks = useLanguageLinks()
const repo = useRepo()
const show = computed(() => theme.value.nav || repo.value || localeLinks.value)
</script>
Expand Down
57 changes: 15 additions & 42 deletions src/client/theme-default/composables/nav.ts
Original file line number Diff line number Diff line change
@@ -1,57 +1,30 @@
import { computed } from 'vue'
import { useRoute, useData, inBrowser } from 'vitepress'
import { useData, useRoute } from 'vitepress'
import type { DefaultTheme } from '../config'

export function useLocaleLinks() {
const route = useRoute()
const { site } = useData()
export function useLanguageLinks() {
const { site, localePath, theme } = useData()

return computed(() => {
const theme = site.value.themeConfig as DefaultTheme.Config
const locales = theme.locales
const langs = site.value.langs
const localePaths = Object.keys(langs)

if (!locales) {
// one language
if (localePaths.length < 2) {
return null
}

const localeKeys = Object.keys(locales)
const route = useRoute()

if (localeKeys.length <= 1) {
return null
}

// handle site base
const siteBase = inBrowser ? site.value.base : '/'

const siteBaseWithoutSuffix = siteBase.endsWith('/')
? siteBase.slice(0, -1)
: siteBase

// remove site base in browser env
const routerPath = route.path.slice(siteBaseWithoutSuffix.length)

const currentLangBase = localeKeys.find((key) => {
return key === '/' ? false : routerPath.startsWith(key)
})

const currentContentPath = currentLangBase
? routerPath.substring(currentLangBase.length - 1)
: routerPath

const candidates = localeKeys.map((v) => {
const localePath = v.endsWith('/') ? v.slice(0, -1) : v

return {
text: locales[v].label,
link: `${localePath}${currentContentPath}`
}
})
// intentionally remove the leading slash because each locale has one
const currentPath = route.path.replace(localePath.value, '')

const currentLangKey = currentLangBase ? currentLangBase : '/'
const candidates = localePaths.map((localePath) => ({
text: langs[localePath].label,
link: `${localePath}${currentPath}`
}))

const selectText = locales[currentLangKey].selectText
? locales[currentLangKey].selectText
: 'Languages'
const selectText = theme.value.selectText || 'Languages'

return {
text: selectText,
Expand Down
8 changes: 7 additions & 1 deletion src/node/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ import chalk from 'chalk'
import globby from 'globby'
import { AliasOptions, UserConfig as ViteConfig } from 'vite'
import { Options as VuePluginOptions } from '@vitejs/plugin-vue'
import { SiteData, HeadConfig, LocaleConfig } from './shared'
import {
SiteData,
HeadConfig,
LocaleConfig,
createLangDictionary
} from './shared'
import { resolveAliases, APP_PATH, DEFAULT_THEME_PATH } from './alias'
import { MarkdownOptions } from './markdown/markdown'

Expand Down Expand Up @@ -142,6 +147,7 @@ export async function resolveSiteData(
head: userConfig.head || [],
themeConfig: userConfig.themeConfig || {},
locales: userConfig.locales || {},
langs: createLangDictionary(userConfig),
customData: userConfig.customData || {}
}
}
41 changes: 29 additions & 12 deletions src/shared/shared.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { SiteData } from '../../types/shared'
import { LocaleConfig, SiteData } from '../../types/shared'

export type {
SiteData,
Expand All @@ -12,7 +12,7 @@ export const EXTERNAL_URL_RE = /^https?:/i

export const inBrowser = typeof window !== 'undefined'

function findMatchRoot(route: string, roots: string[]) {
function findMatchRoot(route: string, roots: string[]): string | undefined {
// first match to the routes with the most deep level.
roots.sort((a, b) => {
const levelDelta = b.split('/').length - a.split('/').length
Expand All @@ -24,9 +24,8 @@ function findMatchRoot(route: string, roots: string[]) {
})

for (const r of roots) {
if (route.startsWith(r)) return
if (route.startsWith(r)) return r
}
return undefined
}

function resolveLocales<T>(
Expand All @@ -37,19 +36,35 @@ function resolveLocales<T>(
return localeRoot ? locales[localeRoot] : undefined
}

export function createLangDictionary(siteData: {
themeConfig?: any
locales?: Record<string, LocaleConfig>
}) {
const { locales } = siteData.themeConfig
const siteLocales = siteData.locales
return locales && siteLocales
? Object.keys(locales).reduce((langs, path) => {
langs[path] = {
label: locales![path].label,
lang: siteLocales[path].lang
}
return langs
}, {} as Record<string, { lang: string; label: string }>)
: {}
}

// this merges the locales data to the main data by the route
export function resolveSiteDataByRoute(
siteData: SiteData,
route: string
): SiteData {
route = cleanRoute(siteData, route)

const localeData = resolveLocales(siteData.locales || {}, route) || {}
const localeThemeConfig =
resolveLocales<any>(
(siteData.themeConfig && siteData.themeConfig.locales) || {},
route
) || {}
const localeData = resolveLocales(siteData.locales || {}, route)
const localeThemeConfig = resolveLocales<any>(
siteData.themeConfig.locales || {},
route
)

return {
...siteData,
Expand All @@ -60,8 +75,10 @@ export function resolveSiteDataByRoute(
// clean the locales to reduce the bundle size
locales: {}
},
lang: localeThemeConfig.lang || siteData.lang,
locales: {}
lang: (localeData || siteData).lang,
// clean the locales to reduce the bundle size
locales: {},
langs: createLangDictionary(siteData)
}
}

Expand Down
24 changes: 24 additions & 0 deletions types/shared.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,36 @@ export interface LocaleConfig {

export interface SiteData<ThemeConfig = any> {
base: string
/**
* Language of the site as it should be set on the `html` element.
* @example `en-US`, `zh-CN`
*/
lang: string
title: string
description: string
head: HeadConfig[]
themeConfig: ThemeConfig
locales: Record<string, LocaleConfig>
/**
* Available locales for the site when it has defined `locales` in its
* `themeConfig`. This object is otherwise empty. Keys are paths like `/` or
* `/zh/`.
*/
langs: Record<
string,
{
/**
* Lang attribute as set on the `<html>` element.
* @example `en-US`, `zh-CN`
*/
lang: string
/**
* Label to display in the language menu.
* @example `English', `简体中文`
*/
label: string
}
>
customData: any
}

Expand Down