Skip to content

Commit

Permalink
fix(i18n): fix locales reading, add site.langs (vuejs#353)
Browse files Browse the repository at this point in the history
It works with the same config as Vuepress 1: https://vuepress.vuejs.org/guide/i18n.html#site-level-i18n-config

We could probably adapt the `label` property like Vuepress 2 https://v2.vuepress.vuejs.org/guide/i18n.html#site-i18n-config in a next release
  • Loading branch information
posva authored and YunYouJun committed Aug 30, 2021
1 parent 2b4faee commit f2ef9f2
Show file tree
Hide file tree
Showing 8 changed files with 84 additions and 64 deletions.
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

0 comments on commit f2ef9f2

Please sign in to comment.