feat: 增强多语言支持

This commit is contained in:
pengzhanbo 2024-01-05 01:37:45 +08:00
parent 530797d61a
commit 5ff9570018
6 changed files with 128 additions and 88 deletions

View File

@ -3,6 +3,7 @@ import { computed } from 'vue'
import type { PlumeThemePageData } from '../../shared/index.js'
import { ensureStartingSlash } from '../utils/index.js'
import { useThemeData } from './themeData.js'
import { normalizePath } from './sidebar.js'
export function useLangs({
removeCurrent = true,
@ -20,18 +21,34 @@ export function useLangs({
}
})
const addPath = computed(() => {
if (page.value.frontmatter.home || (page.value.type && page.value.type !== 'friends'))
return true
return correspondingLink
})
const getBlogLink = (locale: string) => {
const blog = theme.value.locales?.[`/${locale}/`]?.blog
const defaultBlog = theme.value.locales?.['/']?.blog ?? theme.value.blog
const link = blog?.link ? blog.link : normalizePath(`${locale}${defaultBlog?.link || 'blog/'}`)
return link
}
const localeLinks = computed(() =>
Object.entries(theme.value.locales || {}).flatMap(([key, value]) =>
removeCurrent && currentLang.value.label === value.selectLanguageName
? []
: {
text: value.selectLanguageName,
link: normalizeLink(
key,
correspondingLink,
page.value.path.slice(currentLang.value.link.length - 1),
true,
),
link: page.value.isBlogPost
? getBlogLink(key)
: normalizeLink(
key,
addPath.value,
page.value.path.slice(currentLang.value.link.length - 1),
true,
),
},
),
)

View File

@ -13,6 +13,8 @@ import type {
PlumeThemePluginOptions,
} from '../shared/index.js'
import { getCurrentDirname, getPackage, nanoid, pathJoin } from './utils.js'
import { resolveNotesList } from './resolveNotesList.js'
import { resolveLocaleOptions } from './resolveLocaleOptions.js'
export default function autoFrontmatter(
app: App,
@ -21,18 +23,12 @@ export default function autoFrontmatter(
): AutoFrontmatterOptions {
const sourceDir = app.dir.source()
const pkg = getPackage()
const { locales = {}, avatar, article: articlePrefix = '/article/' } = localeOption
const { locales = {}, article: articlePrefix = '/article/' } = localeOption
const { frontmatter } = options
const localesNotesDirs = Object.keys(app.siteData.locales || {})
.map((locale) => {
// fixed: #15
const notes = locales[locale]?.notes
if (!notes)
return ''
return notes.dir ? pathJoin(locale, notes.dir).replace(/^\//, '') : ''
})
const avatar = resolveLocaleOptions(localeOption, 'avatar')
const notesList = resolveNotesList(localeOption)
const localesNotesDirs = notesList
.map(notes => notes.dir?.replace(/^\//, ''))
.filter(Boolean)
const baseFrontmatter: FrontmatterObject = {
@ -58,7 +54,7 @@ export default function autoFrontmatter(
return resolveLocalePath(localeOption.locales!, file)
}
const notesByLocale = (locale: string) => {
const notes = locales[locale]?.notes || localeOption.notes
const notes = resolveLocaleOptions(localeOption, 'notes', locale)
if (notes === false)
return undefined
return notes
@ -162,7 +158,13 @@ export default function autoFrontmatter(
if (permalink)
return permalink
const locale = resolveLocale(filepath)
return pathJoin(locale, articlePrefix, nanoid(), '/')
const prefix = resolveLocaleOptions(localeOption, 'article', locale, false)
const args: string[] = []
prefix
? args.push(prefix)
: args.push(locale, articlePrefix)
return pathJoin(...args, nanoid(), '/')
},
},
},

View File

@ -1,4 +1,3 @@
import path from 'node:path'
import type { App, PluginConfig } from '@vuepress/core'
import { activeHeaderLinksPlugin } from '@vuepress/plugin-active-header-links'
import { docsearchPlugin } from '@vuepress/plugin-docsearch'
@ -28,62 +27,56 @@ import type {
PlumeThemePluginOptions,
} from '../shared/index.js'
import autoFrontmatter from './autoFrontmatter.js'
import { resolveLocaleOptions } from './resolveLocaleOptions.js'
import { pathJoin } from './utils.js'
import { resolveNotesList } from './resolveNotesList.js'
export function setupPlugins(app: App, options: PlumeThemePluginOptions, localeOptions: PlumeThemeLocaleOptions): PluginConfig {
export function setupPlugins(
app: App,
options: PlumeThemePluginOptions,
localeOptions: PlumeThemeLocaleOptions,
): PluginConfig {
const isProd = !app.env.isDev
const locales = (localeOptions.locales || {}) as PlumeThemeLocaleOptions
const localeNotesDirs = Object.keys(locales)
.map((locale) => {
const dir = locales[locale].notes?.dir || ''
return dir
? path.join(locale, dir, '**').replace(/\\+/g, '/').replace(/^\//, '')
: ''
})
const notesList = resolveNotesList(localeOptions)
const notesDirList = notesList
.map(notes => notes.dir && pathJoin(notes.dir, '**').replace(/^\//, ''))
.filter(Boolean)
const blog = resolveLocaleOptions(localeOptions, 'blog')
const plugins: PluginConfig = [
palettePlugin({ preset: 'sass' }),
themeDataPlugin({
themeData: {
...localeOptions,
notes: localeOptions.notes
? { dir: localeOptions.notes.dir, link: localeOptions.notes.link }
: undefined,
} as any,
}),
themeDataPlugin({ themeData: localeOptions }),
autoFrontmatterPlugin(autoFrontmatter(app, options, localeOptions)),
blogDataPlugin({
include: localeOptions.blog?.include ?? ['**/*.md'],
include: blog?.include ?? ['**/*.md'],
exclude: [
'**/{README,readme,index}.md',
'.vuepress/',
'node_modules/',
...(localeOptions.blog?.exclude ?? []),
...localeNotesDirs,
...(blog?.exclude ?? []),
...notesDirList,
].filter(Boolean),
sortBy: 'createTime',
excerpt: true,
pageFilter(page: any) {
if (page.frontmatter.article !== undefined)
return !!page.frontmatter.article
return true
},
extendBlogData(page: any) {
return {
categoryList: page.data.categoryList,
tags: page.frontmatter.tags,
sticky: page.frontmatter.sticky,
createTime: page.data.frontmatter.createTime,
lang: page.lang,
}
},
pageFilter: (page: any) => page.frontmatter.article !== undefined
? !!page.frontmatter.article
: true,
extendBlogData: (page: any) => ({
categoryList: page.data.categoryList,
tags: page.frontmatter.tags,
sticky: page.frontmatter.sticky,
createTime: page.data.frontmatter.createTime,
lang: page.lang,
}),
}),
notesDataPlugin(notesList),
iconifyPlugin(),
contentUpdatePlugin(),
@ -99,37 +92,27 @@ export function setupPlugins(app: App, options: PlumeThemePluginOptions, localeO
if (options.readingTime !== false)
plugins.push(readingTimePlugin(options.readingTime || {}))
if (localeOptions.notes)
plugins.push(notesDataPlugin(localeOptions.notes))
if (options.nprogress !== false)
plugins.push(nprogressPlugin())
if (options.git !== false) {
plugins.push(gitPlugin({
createdTime: false,
updatedTime: localeOptions.lastUpdated !== false,
contributors: localeOptions.contributors !== false,
updatedTime: resolveLocaleOptions(localeOptions, 'lastUpdated') !== false,
contributors: resolveLocaleOptions(localeOptions, 'contributors') !== false,
}))
}
if (options.mediumZoom !== false) {
plugins.push(mediumZoomPlugin({
selector: '.plume-content > img, .plume-content :not(a) > img',
zoomOptions: {
background: 'var(--vp-c-bg)',
},
zoomOptions: { background: 'var(--vp-c-bg)' },
delay: 300,
}))
}
if (options.caniuse !== false) {
plugins.push(caniusePlugin(
options.caniuse || {
mode: 'embed',
},
))
}
if (options.caniuse !== false)
plugins.push(caniusePlugin(options.caniuse || { mode: 'embed' }))
if (options.externalLinkIcon !== false) {
plugins.push(externalLinkIconPlugin({
@ -150,14 +133,11 @@ export function setupPlugins(app: App, options: PlumeThemePluginOptions, localeO
plugins.push(searchPlugin(options.search))
if (options.docsearch !== false && !options.search) {
if (options.docsearch?.appId && options.docsearch?.apiKey) {
if (options.docsearch?.appId && options.docsearch?.apiKey)
plugins.push(docsearchPlugin(options.docsearch))
}
else {
console.error(
'docsearch plugin: appId and apiKey are both required',
)
}
else
console.error('docsearch plugin: appId and apiKey are both required')
}
if (options.shikiji !== false) {
@ -180,7 +160,6 @@ export function setupPlugins(app: App, options: PlumeThemePluginOptions, localeO
{
hint: true, // info note tip warning danger details
codetabs: true,
tabs: true,
align: true,
mark: true,
tasklist: true,
@ -188,6 +167,7 @@ export function setupPlugins(app: App, options: PlumeThemePluginOptions, localeO
attrs: true,
sup: true,
sub: true,
katex: true,
} as MarkdownEnhanceOptions,
options.markdownEnhance || {},
),
@ -200,16 +180,15 @@ export function setupPlugins(app: App, options: PlumeThemePluginOptions, localeO
if (options.baiduTongji !== false && options.baiduTongji?.key)
plugins.push(baiduTongjiPlugin(options.baiduTongji))
if (options.sitemap !== false && localeOptions.hostname && isProd) {
plugins.push(sitemapPlugin({
hostname: localeOptions.hostname,
}))
}
const hostname = resolveLocaleOptions(localeOptions, 'hostname')
if (options.seo !== false && localeOptions.hostname && isProd) {
if (options.sitemap !== false && hostname && isProd)
plugins.push(sitemapPlugin({ hostname }))
if (options.seo !== false && hostname && isProd) {
plugins.push(seoPlugin({
hostname: localeOptions.hostname || '',
author: localeOptions.avatar?.name,
hostname,
author: resolveLocaleOptions(localeOptions, 'avatar')?.name,
}))
}

View File

@ -0,0 +1,26 @@
import type { PlumeThemeLocaleOptions } from '../shared/index.js'
import { normalizePath } from './utils.js'
export function resolveLocaleOptions<
T extends PlumeThemeLocaleOptions = PlumeThemeLocaleOptions,
K extends Exclude<keyof T, 'locales'> = Exclude<keyof T, 'locales'>,
>(options: T, key: K, locale = '', fallback = true): T[K] | undefined {
const locales = options.locales
if (!locales)
return options[key]
locale = !locale || locale === '/' ? '/' : normalizePath(`/${locale}/`)
const localeOptions = locales[locale]
const fallbackLocaleOptions = locales['/']
if (!localeOptions)
return fallback ? options[key] : undefined
const _key = key as keyof typeof localeOptions
const fallbackData = (fallbackLocaleOptions[_key] ?? options[key]) as T[K]
const value = localeOptions[_key] as T[K]
return value ?? (fallback ? fallbackData : undefined)
}

View File

@ -0,0 +1,15 @@
import type { NotesDataOptions } from '@vuepress-plume/plugin-notes-data'
import type { PlumeThemeLocaleOptions } from '../shared/index.js'
import { resolveLocaleOptions } from './resolveLocaleOptions.js'
export function resolveNotesList(options: PlumeThemeLocaleOptions) {
const locales = options.locales || {}
const notesList: NotesDataOptions[] = []
for (const locale of Object.keys(locales)) {
const notes = resolveLocaleOptions(options, 'notes', locale, false)
notes && notesList.push(notes)
}
return notesList
}

View File

@ -7,15 +7,16 @@ import type {
PlumeThemePageData,
} from '../shared/index.js'
import { pathJoin } from './utils.js'
import { resolveLocaleOptions } from './resolveLocaleOptions.js'
export async function setupPage(
app: App,
localeOption: PlumeThemeLocaleOptions,
) {
const locales = Object.keys(app.siteData.locales || {})
const defaultBlog = resolveLocaleOptions(localeOption, 'blog')
for (const [, locale] of locales.entries()) {
const blog = localeOption.locales?.[locale]?.blog
const defaultBlog = localeOption.blog
const blog = resolveLocaleOptions(localeOption, 'blog', locale, false)
const link = blog?.link
? blog.link
: pathJoin('/', locale, defaultBlog?.link || '/blog/')