feat(theme): improve locales (#366)

* chore: tweak

* feat(theme): improve locales
This commit is contained in:
pengzhanbo 2024-11-30 05:31:14 +08:00 committed by GitHub
parent 8b39248f96
commit 9906f1a4e2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 438 additions and 400 deletions

View File

@ -9,8 +9,8 @@ export const themeConfig = defineNoteConfig({
collapsed: false,
items: [
'配置说明',
'多语言配置',
'主题配置',
'多语言配置',
'导航栏配置',
'notes配置',
'侧边栏配置',

View File

@ -344,9 +344,9 @@ interface LastUpdatedOptions {
- 默认值: `{}`
- 详情: 多语言配置
多语言配置,参考 [此文档](./多语言配置.md)
不同语言的文本配置,参考 [此文档](./多语言配置.md)
多语言配置支持以下 [Locale](#locale-配置) 所有配置选项
多语言配置支持以下 [Locale](#locale-配置) 所有配置选项以控制不同语言下的主题行为。
## Locale 配置
@ -385,16 +385,6 @@ interface LastUpdatedOptions {
此选项注入一个内联脚本,从本地存储恢复用户设置。这确保在呈现页面之前应用 `[data-theme="dark"]` 以避免闪烁。
### appearanceText
- 类型: `string`
- 默认值: `'Appearance'`
- 详情: 导航栏中的主题切换按钮的文本。
### avatar <Badge type="danger" text="弃用" />
弃用,请使用 [profile](#profile)。
### profile
- 类型: `ProfileOptions`
@ -740,66 +730,6 @@ interface SidebarItem {
详情请参考 [公告板](../guide/功能/公告板.md)
### selectLanguageName
- 类型: `string`
- 默认值: `''`
- 详情:
Locale 的语言名称。
该配置项 **仅能在主题配置的 [locales](#locales) 的内部生效** 。它将被用作 locale 的语言名称,展示在 _选择语言菜单_ 内。
### selectLanguageText
- 类型: `string`
- 默认值: `''`
- 详情:
_选择语言菜单_ 的文本。
如果你在站点配置中设置了多个 [locales](#locales) ,那么 _选择语言菜单_ 就会显示在导航栏中仓库按钮的旁边。
### selectLanguageAriaLabel
- 类型: `string`
- 默认值: `''`
- 详情:
_选择语言菜单 的 `aria-label` 属性。_
它主要是为了站点的可访问性 (a11y) 。
### sidebarMenuLabel
- 类型: `string`
- 默认值: `'Menu'`
- 详情:
移动设备下的导航栏中 菜单选项的文本。
### returnToTopLabel
- 类型: `string`
- 默认值: `'return to top'`
- 详情:
移动设备下的导航栏中返回顶部的文本。
### outlineLabel
- 类型: `string`
- 默认值: `'On this page'`
- 详情:
移动设备下的导航栏中大纲标题的文本
### editLinkText
- 类型: `string`
- 默认值: `'Edit this page'`
- 详情: 编辑链接文本
### editLinkPattern
- 类型: `string`
@ -808,36 +738,6 @@ interface SidebarItem {
示例: `':repo/edit/:branch/:path'`
### latestUpdatedText
- 类型: `string`
- 默认值: `'Latest Updated'`
- 详情: 最近更新时间 的文本
### contributorsText
- 类型: `string`
- 默认值: `'Contributors'`
- 详情: 贡献者的文本
### changelogText
- 类型: `string`
- 默认值: `'Changelog'`
- 详情: 变更记录的文本
### changelogOnText
- 类型: `string`
- 默认值: `'On'`
- 详情: 单次变更记录的时间文本
### changelogButtonText
- 类型: `string`
- 默认值: `'View All Changelog'`
- 详情: 变更记录的按钮文本
### copyright
- 类型: `boolean | CopyrightLicense | CopyrightOptions`
@ -846,66 +746,18 @@ interface SidebarItem {
详情请参考 [版权所有](../guide/功能/文章版权所有.md)
### copyrightText
- 类型: `string`
- 默认值: `'Copyright'`
- 详情: 版权所有的文本
### copyrightAuthorText
- 类型: `string`
- 默认值: `'Copyright Ownership:'`
- 详情: 版权所有者的文本
### copyrightCreationOriginalText
- 类型: `string`
- 默认值: `'This article link:'`
- 详情: 本文链接的文本
### copyrightCreationTranslateText
- 类型: `string`
- 默认值: `'This article translated from:'`
- 详情: 本文翻译的文本
### copyrightCreationReprintText
- 类型: `string`
- 默认值: `'This article reprint from:'`
- 详情: 本文转载的文本
### copyrightLicenseText
- 类型: `string`
- 默认值: `'License under:'`
- 详情: 版权许可证的文本
### prevPage
- 类型: `boolean`
- 默认值: `true`
- 详情: 是否显示上一页
### prevPageLabel
- 类型: `string`
- 默认值: `'Previous Page'`
- 详情: 上一页的文本
### nextPage
- 类型: `boolean`
- 默认值: `true`
- 详情: 是否显示下一页
### nextPageLabel
- 类型: `string`
- 默认值: `'Next Page'`
- 详情: 下一页的文本
### createTime
- 类型: `boolean | 'only-blog'`
@ -915,19 +767,3 @@ interface SidebarItem {
- `false` - 不显示
- `'only-blog'` - 只显示在博客文章页面
- `true` - 显示在所有文章页面
### notFound
- 类型: `NotFound | undefined`
- 默认值: `undefined`
- 详情: 404 页面配置
```ts
interface NotFound {
code?: string
title?: string
quote?: string
linkLabel?: string
linkText?: string
}
```

View File

@ -1,155 +1,279 @@
---
title: 多语言
title: 多语言配置
author: pengzhanbo
createTime: 2024/03/02 10:07:15
permalink: /config/locales/
---
## 设置语言 <Badge type="danger">重要</Badge>
这些选项用于配置与语言相关的文本。
你需要为每个语言设置 `lang` 选项。即使你只在使用单个语言,你也必须在 `.vuepress/config.{js,ts}` 中设置 `lang`
如果你的站点是以非内置语言支持以外的其他语言提供服务的,你应该为每个语言设置这些选项来提供翻译
::: tip 为什么要这样做?
要提供正确的语言环境文本,主题需要知道根文件夹以及每个多语言文件夹正在使用哪种语言。
:::
## 内置语言支持
主题内置了以下语言支持,包括:
- 简体中文 (`zh-CN`) - `/zh/`
- 繁体中文 (`zh-TW`) - `/zh-tw/`
- 英语 (`en-US`) - `/en/`
- 法语 (`fr-FR`) - `/fr/`
- 德语 (`de-DE`) - `/de/`
- 俄语 (`ru-RU`) - `/ru/`
- 日语 (`ja-JP`) - `/ja/`
## 配置
您应该将配置写入到 `theme.locales` 中。
您可以在 `.vuepress/config.ts` ,或者在 `.vuepress/plume.config.ts` 中进行配置:
::: code-tabs
@tab 单语言
```ts
import { defineUserConfig } from 'vuepress'
export default defineUserConfig({
// 设置正在使用的语言
lang: 'zh-CN',
})
```
@tab 多语言
```ts
import { defineUserConfig } from 'vuepress'
export default defineUserConfig({
locales: {
'/': {
// 设置正在使用的语言
lang: 'zh-CN',
},
'/en/': {
// 设置正在使用的语言
lang: 'en-US',
},
},
})
```
:::
## 多语言配置
`locales` 是一个对象,其键为每个语言的路径前缀,值为该语言的配置,可以包含 `title`, `description`, `lang` 等。
你应当为每个语言设置 `lang` 选项,以便主题和插件能够正确的处理它们。
如果站点和主题配置中的 `locales` 对象只包含 "/" 一个键,则主题不会显示语言切换菜单。当你通过 `locales` 设置多个键,即存在多个语言的时候,我们会在导航栏显示语言切换菜单。
## 语言适配
主题默认适配了以下语言
- 简体中文 (zh-CN)
- 英文(美国) (en-US)
::: tip
如果您希望支持更多语言,欢迎通过
[PR](https://github.com/pengzhanbo/vuepress-theme-plume/pulls?q=sort%3Aupdated-desc+is%3Apr+is%3Aopen) 在 主题仓库的 `/theme/src/node/locales` 目录中按照相同的方式添加语言。
:::
## 为每个语言设置主题选项
与站点配置和 `@vuepress/theme-default` 的主题配置相同,`vuepress-theme-plume` 也支持你在主题选项中设置 locale 选项,并为每种语言设置不同的配置。
::: code-tabs
@tab .vuepress/config.ts
```ts :no-line-numbers
```ts
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
locales: {
'/': {
lang: 'en-US',
},
'/zh/': {
lang: 'zh-CN',
},
},
theme: plumeTheme({
// 通用配置
// ...
locales: {
'/': {
// 英文配置
// ...
},
'/zh/': {
// 中文配置
// ...
},
},
}),
})
```
:::
**使用主题配置文件:**
::: code-tabs
@tab .vuepress/config.ts
```ts
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
locales: {
'/': {
lang: 'en-US',
},
'/zh/': {
lang: 'zh-CN',
},
},
theme: plumeTheme(),
// 非内置语言的语言代码
'/xxx/': {
// 语言配置
}
}
})
})
```
@tab .vuepress/plume.config.ts
```ts
import { defineThemeConfig } from 'vuepress-theme-plume'
import { definePlumeConfig } from 'vuepress-theme-plume'
export default defineThemeConfig({
// 通用配置
// ...
export default definePlumeConfig({
locales: {
'/': {
// 英文配置
// ...
},
'/zh/': {
// 中文配置
// ...
},
},
// 非内置语言的语言代码
'/xxx/': {
// 语言配置
}
}
})
```
:::
### appearanceText
- 类型: `string`
- 默认值: `'Appearance'`
- 详情: 导航栏中的主题切换按钮的文本。
### selectLanguageName
- 类型: `string`
- 默认值: `''`
- 详情:
Locale 的语言名称。
该配置项 **仅能在主题配置的 [locales](./主题配置.md#locales) 的内部生效** 。它将被用作 locale 的语言名称,展示在 _选择语言菜单_ 内。
### selectLanguageText
- 类型: `string`
- 默认值: `''`
- 详情:
_选择语言菜单_ 的文本。
如果你在站点配置中设置了多个 [locales](./主题配置.md#locales) ,那么 _选择语言菜单_ 就会显示在导航栏中仓库按钮的旁边。
### selectLanguageAriaLabel
- 类型: `string`
- 默认值: `''`
- 详情:
_选择语言菜单 的 `aria-label` 属性。_
它主要是为了站点的可访问性 (a11y) 。
### homeText
- 类型: `string`
- 默认值: `'Home'`
- 详情: 主页链接的文本。
- 主题默认导航栏中的首页链接的文本。
- 面包屑导航中的首页链接的文本。
### blogText
- 类型: `string`
- 默认值: `'Blog'`
- 详情: 博客链接的文本。
- 主题默认导航栏中的博客链接的文本。
- 面包屑导航中的博客链接的文本。
### tagText
- 类型: `string`
- 默认值: `'Tags'`
- 详情: 标签链接的文本。
- 主题默认导航栏中的标签链接的文本。
- 博客页中的标签链接的文本。
- 博客标签页中的标题。
### categoryText
- 类型: `string`
- 默认值: `'Categories'`
- 详情: 分类链接的文本。
- 主题默认导航栏中的分类链接的文本。
- 博客页中的分类链接的文本。
- 博客分类页中的标题。
### archiveText
- 类型: `string`
- 默认值: `'Archives'`
- 详情: 归档链接的文本。
- 主题默认导航栏中的归档链接的文本。
- 博客页中的归档链接的文本。
- 博客归档页中的标题。
### archiveTotalText
- 类型: `string`
- 默认值: `'{count} articles'`
- 详情: 归档页中的总文章数的文本。
### sidebarMenuLabel
- 类型: `string`
- 默认值: `'Menu'`
- 详情:
移动设备下的导航栏中 菜单选项的文本。
### returnToTopLabel
- 类型: `string`
- 默认值: `'return to top'`
- 详情:
移动设备下的导航栏中返回顶部的文本。
### outlineLabel
- 类型: `string`
- 默认值: `'On this page'`
- 详情:
移动设备下的导航栏中大纲标题的文本
### editLinkText
- 类型: `string`
- 默认值: `'Edit this page'`
- 详情: 编辑链接文本
### latestUpdatedText
- 类型: `string`
- 默认值: `'Latest Updated'`
- 详情: 最近更新时间 的文本
### contributorsText
- 类型: `string`
- 默认值: `'Contributors'`
- 详情: 贡献者的文本
### changelogText
- 类型: `string`
- 默认值: `'Changelog'`
- 详情: 变更记录的文本
### changelogOnText
- 类型: `string`
- 默认值: `'On'`
- 详情: 单次变更记录的时间文本
### changelogButtonText
- 类型: `string`
- 默认值: `'View All Changelog'`
- 详情: 变更记录的按钮文本
### copyrightText
- 类型: `string`
- 默认值: `'Copyright'`
- 详情: 版权所有的文本
### copyrightAuthorText
- 类型: `string`
- 默认值: `'Copyright Ownership:'`
- 详情: 版权所有者的文本
### copyrightCreationOriginalText
- 类型: `string`
- 默认值: `'This article link:'`
- 详情: 本文链接的文本
### copyrightCreationTranslateText
- 类型: `string`
- 默认值: `'This article translated from:'`
- 详情: 本文翻译的文本
### copyrightCreationReprintText
- 类型: `string`
- 默认值: `'This article reprint from:'`
- 详情: 本文转载的文本
### copyrightLicenseText
- 类型: `string`
- 默认值: `'License under:'`
- 详情: 版权许可证的文本
### prevPageLabel
- 类型: `string`
- 默认值: `'Previous Page'`
- 详情: 上一页的文本
### nextPageLabel
- 类型: `string`
- 默认值: `'Next Page'`
- 详情: 下一页的文本
### notFound
- 类型: `NotFound | undefined`
- 默认值: `undefined`
- 详情: 404 页面配置
```ts
interface NotFound {
code?: string
title?: string
quote?: string
linkLabel?: string
linkText?: string
}
```

View File

@ -20,10 +20,10 @@ tags:
::: file-tree
- docs
- en
- **en** \# 英文目录
- foo.md
- README.md \# 英文首页
- fr
- **fr** \# 法文目录
- foo.md
- README.md \# 法文首页
- foo.md
@ -32,19 +32,49 @@ tags:
其中,`docs/en/` 用于保存 **英文** 文档,`docs/fr/` 保存 **法文** 文档,而 **中文** 则直接保存在 `docs/` 下。
::: important
::: important 不同语言下的目录结构与文件名保持一致
在不同的语言目录下,请尽量保持文件名和目录名的一致。这有助于在 切换语言时,主题能够正确的导航到 文章
的不同的语言版本。
:::
## vuepress 配置
接下来,在 `.vuepress/config.ts` 中配置:
### 默认语言
`.vuepress/config.ts` 中,声明默认的语言:
::: code-tabs
@tab .vuepress/config.ts
```js
```ts
import { defineUserConfig } from 'vuepress'
export default defineUserConfig({
// 声明默认的语言
lang: 'zh-CN', // [!code ++]
// 多语言下支持的各种语言 locales
locales: {
'/': { lang: 'zh-CN', title: '博客' }, // 默认语言 为 简体中文
}
})
```
:::
### 添加其他语言
你需要为每个语言设置 `lang` 选项。即使你只在使用单个语言,你也必须在 `.vuepress/config.{js,ts}` 中设置 `lang`
::: tip 为什么要这样做?
要提供正确的语言环境文本,主题需要知道根文件夹以及每个多语言文件夹正在使用哪种语言。
:::
`.vuepress/config.ts` 中配置:
::: code-tabs
@tab .vuepress/config.ts
```ts
import { defineUserConfig } from 'vuepress'
export default defineUserConfig({
@ -54,15 +84,24 @@ export default defineUserConfig({
locales: {
// 配置 不同 path 下的语言
'/': { lang: 'zh-CN', title: '博客' }, // 默认语言 为 简体中文
'/en/': { lang: 'en-US', title: 'Blog' }, // 英文
'/fr/': { lang: 'fr-FR', title: 'Le blog' }, // 法语
'/en/': { lang: 'en-US', title: 'Blog' }, // 英文 // [!code ++]
'/fr/': { lang: 'fr-FR', title: 'Le blog' }, // 法语 // [!code ++]
}
})
```
:::
`locales` 中的 `key` 对应着 `docs` 目录下的语言路径,同时,`key` 也将作为 不同语言的页面访问链接的前缀。
**`locales` 配置中的 `key` 作为 `localPath`, 对应着 `docs` 目录下的语言路径,应该保证它们具有相同的命名。**
**同时,`key` (`localPath`) 也将作为 不同语言的页面访问链接的前缀。**
::: important 语言代码
`locales` 配置中的 `key``localePath` 需要符合 [ISO 639](https://zh.wikipedia.org/wiki/ISO_639-1) 规范,
对于非默认语言的,如 `英语` 应该为 `/en/`
`locales[localePath]` 中, `lang` 为当前语言的 **语言代码**,请使用标准的 [BCP47](https://www.ietf.org/rfc/bcp/bcp47.txt) 语言代码。如, `英语` 应该为 `en-US`,(表示英语(美国))。
:::
## 主题配置
@ -87,7 +126,7 @@ export default defineUserConfig({
theme: plumeTheme({
// 主题内的多语言配置
locales: {
'/': {
'/': { // [!code hl]
// 当前语言显示在导航栏多语言下拉菜单的文本
selectLanguageName: '简体中文',
navbar: [
@ -95,14 +134,14 @@ export default defineUserConfig({
{ text: '博客', link: '/blog/' },
]
},
'/en/': {
'/en/': { // [!code hl]
selectLanguageName: 'English',
navbar: [
{ text: 'Home', link: '/en/' },
{ text: 'Blog', link: '/en/blog/' },
]
},
'/fr/': {
'/fr/': { // [!code hl]
selectLanguageName: 'Français',
navbar: [
{ text: 'Accueil', link: '/fr/' },
@ -116,4 +155,11 @@ export default defineUserConfig({
:::
更多 `locales` 配置请查看 [主题配置 > Locales 配置](../config/主题配置.md#locale-配置)
主题 `theme.locales` 配置中的 `key` 应与 `vuepress` 配置中的 `locales` 配置中的 `key` 一致。
您应该为 `theme.locales[localePath]` 配置 `selectLanguageName` 用于在导航栏多语言下拉菜单中显示当前语言的名称。
更多 `locales` 配置请查看
- [主题配置 > Locales 配置](../config/主题配置.md#locale-配置) - 配置主题在不同语言下的行为。
- [主题配置 > 多语言配置](../config/多语言配置.md) - 配置与语言相关的文本。

View File

@ -15,9 +15,9 @@ interface Breadcrumb {
current?: boolean
}
const { page, theme } = useData<'post'>()
const { page, blog } = useData<'post'>()
const { isBlogPost } = useBlogPageData()
const { home, blog, categories } = useInternalLink()
const { home, blog: blogLink, categories } = useInternalLink()
const sidebar = useSidebarData()
const hasBreadcrumb = computed(() => {
@ -32,9 +32,8 @@ const breadcrumbList = computed<Breadcrumb[]>(() => {
const list: Breadcrumb[] = [{ text: home.value.text, link: home.value.link }]
if (isBlogPost.value) {
const blogConf = theme.value.blog || {}
if (blogConf.postList ?? true)
list.push({ text: blog.value.text, link: blog.value.link })
if (blog.value.postList ?? true)
list.push({ text: blogLink.value.text, link: blogLink.value.link })
const categoryList = page.value.categoryList ?? []
for (const category of categoryList) {

View File

@ -5,7 +5,7 @@ import { useReadingTimeLocale } from '@vuepress/plugin-reading-time/client'
import { computed } from 'vue'
import { useBlogPageData, useData, useInternalLink, useTagColors } from '../composables/index.js'
const { page, frontmatter: matter, theme } = useData<'post'>()
const { page, frontmatter: matter, theme, blog } = useData<'post'>()
const colors = useTagColors()
const readingTime = useReadingTimeLocale()
const { tags: tagsLink } = useInternalLink()
@ -22,8 +22,7 @@ const createTime = computed(() => {
})
const tags = computed(() => {
const blog = theme.value.blog || {}
const tagTheme = blog.tagsTheme ?? 'colored'
const tagTheme = blog.value.tagsTheme ?? 'colored'
if (matter.value.tags) {
return matter.value.tags.slice(0, 4).map(tag => ({
name: tag,

View File

@ -1,18 +1,19 @@
import type { PlumeThemeBlogPostItem } from '../../shared/index.js'
import { computed } from 'vue'
import { useRouteLocale } from 'vuepress/client'
import { useLocalePostList } from './blog-data.js'
import { getPresetLocaleData } from './preset-locales.js'
import { useData } from './data.js'
import { useThemeData } from './theme-data.js'
export type ShortPostItem = Pick<PlumeThemeBlogPostItem, 'title' | 'path' | 'createTime'>
export function useArchives() {
const locale = useRouteLocale()
const themeData = useThemeData()
const list = useLocalePostList()
const { theme } = useData()
const archives = computed(() => {
const archives: { title: string, label: string, list: ShortPostItem[] }[] = []
const countLocale = getPresetLocaleData(locale.value, 'archiveTotal')
const countLocale = theme.value.archiveTotalText || themeData.value.archiveTotalText
list.value.forEach((item) => {
const createTime = item.createTime?.split(/\s|T/)[0] || ''
@ -30,7 +31,7 @@ export function useArchives() {
})
archives.forEach((item) => {
item.label = countLocale.replace('{count}', item.list.length.toString())
item.label = countLocale?.replace('{count}', item.list.length.toString()) || ''
})
return archives

View File

@ -2,7 +2,7 @@ import type { PresetLocale } from '../../shared/index.js'
import { computed } from 'vue'
import { useRouteLocale } from 'vuepress/client'
import { useData } from './data.js'
import { getPresetLocaleData } from './preset-locales.js'
import { useThemeData } from './theme-data.js'
export interface InternalLink {
text: string
@ -10,13 +10,14 @@ export interface InternalLink {
}
export function useInternalLink() {
const { blog } = useData()
const { blog, theme } = useData()
const themeData = useThemeData()
const routeLocale = useRouteLocale()
function resolveLink(name: keyof PresetLocale, link: string): InternalLink {
return {
link: (routeLocale.value + link).replace(/\/+/g, '/'),
text: getPresetLocaleData(routeLocale.value, name),
text: theme.value[`${name}Text`] || themeData.value[`${name}Text`],
}
}

View File

@ -1,6 +1,7 @@
import type { App } from 'vuepress'
import type { PlumeThemeData, PlumeThemeLocaleOptions } from '../../shared/index.js'
import { entries, fromEntries, getLocaleConfig } from '@vuepress/helper'
import { hasOwn, uniq } from '@pengzhanbo/utils'
import { entries, fromEntries, getLocaleConfig, isPlainObject } from '@vuepress/helper'
import { LOCALE_OPTIONS } from '../locales/index.js'
import { THEME_NAME } from '../utils/index.js'
@ -41,8 +42,7 @@ const FALLBACK_OPTIONS: PlumeThemeData = {
export function resolveLocaleOptions(app: App, { locales, ...options }: PlumeThemeLocaleOptions): PlumeThemeLocaleOptions {
const resolvedOptions: PlumeThemeLocaleOptions = {
...FALLBACK_OPTIONS,
...options,
...mergeLocaleOptions(FALLBACK_OPTIONS, options),
locales: getLocaleConfig({
app,
name: THEME_NAME,
@ -53,10 +53,31 @@ export function resolveLocaleOptions(app: App, { locales, ...options }: PlumeThe
...locales,
}).map(([locale, opt]) => [
locale,
{ ...options, ...opt },
mergeLocaleOptions(options, opt),
]),
),
}),
}
return resolvedOptions
}
function mergeLocaleOptions(target: PlumeThemeData, source: PlumeThemeData): PlumeThemeData {
const res: PlumeThemeData = {}
const keys = uniq([...Object.keys(target), ...Object.keys(source)]) as (keyof PlumeThemeData)[]
for (const key of keys) {
if (hasOwn(source, key)) {
const value = source[key]
const targetValue = target[key]
if (isPlainObject(targetValue) && isPlainObject(value)) {
res[key] = Object.assign({}, targetValue, value) as any
}
else {
res[key] = value as any
}
}
else {
res[key] = target[key]
}
}
return res
}

View File

@ -1,7 +1,6 @@
import type { App } from 'vuepress'
import type { NavItem, PlumeThemeLocaleOptions } from '../../shared/index.js'
import { entries, getRootLangPath, isBoolean, isPlainObject } from '@vuepress/helper'
import { PRESET_LOCALES } from '../locales/index.js'
import { entries, isBoolean, isPlainObject } from '@vuepress/helper'
import { withBase } from '../utils/index.js'
const EXCLUDE_LIST = ['locales', 'sidebar', 'navbar', 'notes', 'sidebar', 'article', 'avatar']
@ -10,7 +9,6 @@ const EXCLUDE_LOCALE_LIST = [...EXCLUDE_LIST, 'blog', 'appearance']
export function resolveThemeData(app: App, options: PlumeThemeLocaleOptions): PlumeThemeLocaleOptions {
const themeData: PlumeThemeLocaleOptions = { locales: {} }
const root = getRootLangPath(app)
entries(options).forEach(([key, value]) => {
if (!EXCLUDE_LIST.includes(key))
@ -61,27 +59,26 @@ export function resolveThemeData(app: App, options: PlumeThemeLocaleOptions): Pl
// home | blog | tags | archives
if (opt.navbar !== false && (!opt.navbar || opt.navbar.length === 0)) {
// fallback navbar option
const localePath = locale === '/' ? root : locale
const navbar: NavItem[] = [{
text: PRESET_LOCALES[localePath].home,
text: opt.homeText || options.homeText || 'Home',
link: locale,
}]
if (options.blog !== false) {
const blog = options.blog || {}
const blogLink = blog.link || '/blog/'
navbar.push({
text: PRESET_LOCALES[localePath].blog,
text: opt.blogText || options.blogText || 'Blog',
link: withBase(blogLink, locale),
})
if (blog.tags !== false) {
navbar.push({
text: PRESET_LOCALES[localePath].tag,
text: opt.tagText || options.tagText || 'Tags',
link: withBase(blog.tagsLink || `${blogLink}/tags/`, locale),
})
}
if (blog.archives !== false) {
navbar.push({
text: PRESET_LOCALES[localePath].archive,
text: opt.archiveText || options.archiveText || 'Archives',
link: withBase(blog.archivesLink || `${blogLink}/archives/`, locale),
})
}

View File

@ -2,6 +2,7 @@ import { plumeTheme } from './theme.js'
export * from '../shared/index.js'
export * from './defineConfig.js'
export * from './types.js'
export { plumeTheme }

View File

@ -37,6 +37,13 @@ export const deLocale: PlumeThemeLocaleData = {
linkText: 'Zur Startseite',
},
homeText: 'Startseite',
blogText: 'Blog',
tagText: 'Tag',
archiveText: 'Archiv',
categoryText: 'Kategorie',
archiveTotalText: '{count} Beiträge',
encryptButtonText: 'Bestätigen',
encryptPlaceholder: 'Bitte Passwort eingeben',
encryptGlobalText: 'Diese Website ist nur mit Passwort zugänglich',
@ -49,12 +56,6 @@ export const deLocale: PlumeThemeLocaleData = {
}
export const dePresetLocale: PresetLocale = {
'home': 'Startseite',
'blog': 'Blog',
'tag': 'Tag',
'archive': 'Archiv',
'category': 'Kategorie',
'archiveTotal': '{count} Beiträge',
// ------ copyright license ------
'CC0': 'CC0 1.0 Universell',

View File

@ -28,6 +28,13 @@ export const enLocale: PlumeThemeLocaleData = {
encryptGlobalText: 'Only password can access this site',
encryptPageText: 'Only password can access this page',
homeText: 'Home',
blogText: 'Blog',
tagText: 'Tags',
archiveText: 'Archives',
categoryText: 'Categories',
archiveTotalText: '{count} articles',
footer: {
message:
'Powered by <a target="_blank" href="https://v2.vuepress.vuejs.org/">VuePress</a> & <a target="_blank" href="https://theme-plume.vuejs.press">vuepress-theme-plume</a>',
@ -35,13 +42,6 @@ export const enLocale: PlumeThemeLocaleData = {
}
export const enPresetLocale: PresetLocale = {
'home': 'Home',
'blog': 'Blog',
'tag': 'Tags',
'archive': 'Archives',
'category': 'Categories',
'archiveTotal': '{count} articles',
// ------ copyright license ------
'CC0': 'CC0 1.0 Universal',
'CC-BY-4.0': 'Attribution 4.0 International',

View File

@ -37,6 +37,13 @@ export const frLocale: PlumeThemeLocaleData = {
linkText: 'Retour à l\'accueil',
},
homeText: 'Accueil',
blogText: 'Blog',
tagText: 'Étiquette',
archiveText: 'Archives',
categoryText: 'Catégorie',
archiveTotalText: '{count} articles',
encryptButtonText: 'Confirmer',
encryptPlaceholder: 'Veuillez entrer le mot de passe',
encryptGlobalText: 'Ce site n\'est accessible qu\'avec un mot de passe',
@ -49,13 +56,6 @@ export const frLocale: PlumeThemeLocaleData = {
}
export const frPresetLocale: PresetLocale = {
'home': 'Accueil',
'blog': 'Blog',
'tag': 'Étiquette',
'archive': 'Archives',
'category': 'Catégorie',
'archiveTotal': '{count} articles',
// ------ copyright license ------
'CC0': 'CC0 1.0 Universel',
'CC-BY-4.0': 'Attribution 4.0 International',

View File

@ -37,6 +37,13 @@ export const jaLocale: PlumeThemeLocaleData = {
linkText: 'ホームに戻る',
},
homeText: 'ホーム',
blogText: 'ブログ',
tagText: 'タグ',
archiveText: 'アーカイブ',
categoryText: 'カテゴリー',
archiveTotalText: '{count} 件',
encryptButtonText: '確認',
encryptPlaceholder: 'パスワードを入力してください',
encryptGlobalText: 'このサイトはパスワードでのみアクセス可能です',
@ -49,13 +56,6 @@ export const jaLocale: PlumeThemeLocaleData = {
}
export const jaPresetLocale: PresetLocale = {
'home': 'ホーム',
'blog': 'ブログ',
'tag': 'タグ',
'archive': 'アーカイブ',
'category': 'カテゴリー',
'archiveTotal': '{count} 件',
// ------ copyright license ------
'CC0': 'CC0 1.0 パブリックドメイン',
'CC-BY-4.0': '表示 4.0 国際',

View File

@ -37,6 +37,13 @@ export const ruLocale: PlumeThemeLocaleData = {
linkText: 'Вернуться на главную',
},
homeText: 'Главная',
blogText: 'Блог',
tagText: 'Теги',
archiveText: 'Архив',
categoryText: 'Категории',
archiveTotalText: '{count} статей',
encryptButtonText: 'Подтвердить',
encryptPlaceholder: 'Введите пароль',
encryptGlobalText: 'Доступ к сайту только по паролю',
@ -49,13 +56,6 @@ export const ruLocale: PlumeThemeLocaleData = {
}
export const ruPresetLocale: PresetLocale = {
'home': 'Главная',
'blog': 'Блог',
'tag': 'Теги',
'archive': 'Архив',
'category': 'Категории',
'archiveTotal': '{count} статей',
// ------ copyright license ------
'CC0': 'CC0 1.0 Универсальная',
'CC-BY-4.0': 'Атрибуция 4.0 Международный',

View File

@ -37,6 +37,13 @@ export const zhTwLocale: PlumeThemeLocaleData = {
linkText: '返回首頁',
},
homeText: '首頁',
blogText: '博客',
tagText: '標籤',
archiveText: '歸檔',
categoryText: '分類',
archiveTotalText: '{count} 篇',
encryptButtonText: '確認',
encryptPlaceholder: '請輸入密碼',
encryptGlobalText: '本站只允許密碼訪問',
@ -49,13 +56,6 @@ export const zhTwLocale: PlumeThemeLocaleData = {
}
export const zhTwPresetLocale: PresetLocale = {
'home': '首頁',
'blog': '博客',
'tag': '標籤',
'archive': '歸檔',
'category': '分類',
'archiveTotal': '{count} 篇',
// ------ copyright license ------
'CC0': 'CC0 1.0 通用',
'CC-BY-4.0': '署名 4.0 國際',

View File

@ -36,6 +36,13 @@ export const zhLocale: PlumeThemeLocaleData = {
linkText: '返回首页',
},
homeText: '首页',
blogText: '博客',
tagText: '标签',
archiveText: '归档',
categoryText: '分类',
archiveTotalText: '{count} 篇',
encryptButtonText: '确认',
encryptPlaceholder: '请输入密码',
encryptGlobalText: '本站只允许密码访问',
@ -48,13 +55,6 @@ export const zhLocale: PlumeThemeLocaleData = {
}
export const zhPresetLocale: PresetLocale = {
'home': '首页',
'blog': '博客',
'tag': '标签',
'archive': '归档',
'category': '分类',
'archiveTotal': '{count} 篇',
// ------ copyright license ------
'CC0': 'CC0 1.0 通用',
'CC-BY-4.0': '署名 4.0 国际',

View File

@ -1,8 +1,7 @@
import type { App, Page } from 'vuepress/core'
import type { PlumeThemeLocaleOptions } from '../../shared/index.js'
import { getRootLang, getRootLangPath } from '@vuepress/helper'
import { getRootLang } from '@vuepress/helper'
import { createPage } from 'vuepress/core'
import { PRESET_LOCALES } from '../locales/index.js'
import { withBase } from '../utils/index.js'
export async function createPages(app: App, localeOption: PlumeThemeLocaleOptions) {
@ -11,26 +10,20 @@ export async function createPages(app: App, localeOption: PlumeThemeLocaleOption
const pageList: Promise<Page>[] = []
const locales = localeOption.locales || {}
const rootPath = getRootLangPath(app)
const rootLang = getRootLang(app)
const blog = localeOption.blog || {}
const link = blog.link || '/blog/'
const getTitle = (locale: string, key: string) => {
const opt = PRESET_LOCALES[locale] || PRESET_LOCALES[rootPath] || {}
return opt[key] || ''
}
for (const localePath of Object.keys(locales)) {
const lang = app.siteData.locales?.[localePath]?.lang || rootLang
const locale = localePath === '/' ? rootPath : localePath
const opt = locales[localePath]
// 添加 博客页面
if (blog.postList !== false) {
pageList.push(createPage(app, {
path: withBase(link, localePath),
frontmatter: { lang, _pageLayout: 'blog', title: getTitle(locale, 'blog') },
frontmatter: { lang, _pageLayout: 'blog', title: opt.blogText || localeOption.blogText || 'Blog' },
}))
}
@ -38,7 +31,7 @@ export async function createPages(app: App, localeOption: PlumeThemeLocaleOption
if (blog.tags !== false) {
pageList.push(createPage(app, {
path: withBase(blog.tagsLink || `${link}/tags/`, localePath),
frontmatter: { lang, _pageLayout: 'blog-tags', title: getTitle(locale, 'tag') },
frontmatter: { lang, _pageLayout: 'blog-tags', title: opt.tagText || localeOption.tagText || 'Tags' },
}))
}
@ -46,7 +39,7 @@ export async function createPages(app: App, localeOption: PlumeThemeLocaleOption
if (blog.archives !== false) {
pageList.push(createPage(app, {
path: withBase(blog.archivesLink || `${link}/archives/`, localePath),
frontmatter: { lang, _pageLayout: 'blog-archives', title: getTitle(locale, 'archive') },
frontmatter: { lang, _pageLayout: 'blog-archives', title: opt.archiveText || localeOption.archiveText || 'Archives' },
}))
}
@ -54,7 +47,7 @@ export async function createPages(app: App, localeOption: PlumeThemeLocaleOption
if (blog.categories !== false) {
pageList.push(createPage(app, {
path: withBase(blog.categoriesLink || `${link}/categories/`, localePath),
frontmatter: { lang, _pageLayout: 'blog-categories', title: getTitle(locale, 'category') },
frontmatter: { lang, _pageLayout: 'blog-categories', title: opt.categoryText || localeOption.categoryText || 'Categories' },
}))
}
}

View File

@ -50,14 +50,7 @@ export type SocialLinkIconUnion =
export type SocialLinkIcon = SocialLinkIconUnion | { svg: string, name?: string }
export interface PresetLocale extends Record<CopyrightLicense, string> {
home: string
blog: string
tag: string
archive: string
category: string
archiveTotal: string
}
export interface PresetLocale extends Record<CopyrightLicense, string> {}
export type KnownCopyrightLicense =
| 'CC-BY-4.0'

View File

@ -299,6 +299,32 @@ export interface PlumeThemeLocaleData extends LocaleData {
linkText?: string
}
/**
*
*/
homeText?: string
/**
*
*/
blogText?: string
/**
*
*/
tagText?: string
/**
*
*/
archiveText?: string
/**
*
*/
categoryText?: string
/**
*
* @default '{count} articles''
*/
archiveTotalText?: string
/**
*
*/