diff --git a/docs/.vuepress/notes/zh/theme-config.ts b/docs/.vuepress/notes/zh/theme-config.ts
index 13e1793c..c0bbb750 100644
--- a/docs/.vuepress/notes/zh/theme-config.ts
+++ b/docs/.vuepress/notes/zh/theme-config.ts
@@ -9,8 +9,8 @@ export const themeConfig = defineNoteConfig({
collapsed: false,
items: [
'配置说明',
- '多语言配置',
'主题配置',
+ '多语言配置',
'导航栏配置',
'notes配置',
'侧边栏配置',
diff --git a/docs/notes/theme/config/主题配置.md b/docs/notes/theme/config/主题配置.md
index b833a9c4..8e99d288 100644
--- a/docs/notes/theme/config/主题配置.md
+++ b/docs/notes/theme/config/主题配置.md
@@ -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
-
-弃用,请使用 [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
-}
-```
diff --git a/docs/notes/theme/config/多语言配置.md b/docs/notes/theme/config/多语言配置.md
index 8c8e35ec..51d73505 100644
--- a/docs/notes/theme/config/多语言配置.md
+++ b/docs/notes/theme/config/多语言配置.md
@@ -1,155 +1,279 @@
---
-title: 多语言
+title: 多语言配置
author: pengzhanbo
createTime: 2024/03/02 10:07:15
permalink: /config/locales/
---
-## 设置语言 重要
+这些选项用于配置与语言相关的文本。
-你需要为每个语言设置 `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
+}
+```
diff --git a/docs/notes/theme/guide/国际化.md b/docs/notes/theme/guide/国际化.md
index e5eb709b..420234e7 100644
--- a/docs/notes/theme/guide/国际化.md
+++ b/docs/notes/theme/guide/国际化.md
@@ -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) - 配置与语言相关的文本。
diff --git a/theme/src/client/components/VPDocBreadcrumbs.vue b/theme/src/client/components/VPDocBreadcrumbs.vue
index a20a7089..3b489be2 100644
--- a/theme/src/client/components/VPDocBreadcrumbs.vue
+++ b/theme/src/client/components/VPDocBreadcrumbs.vue
@@ -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(() => {
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) {
diff --git a/theme/src/client/components/VPDocMeta.vue b/theme/src/client/components/VPDocMeta.vue
index 4be3831e..4bc5baf6 100644
--- a/theme/src/client/components/VPDocMeta.vue
+++ b/theme/src/client/components/VPDocMeta.vue
@@ -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,
diff --git a/theme/src/client/composables/blog-archives.ts b/theme/src/client/composables/blog-archives.ts
index 5bbf766c..9cb05ffe 100644
--- a/theme/src/client/composables/blog-archives.ts
+++ b/theme/src/client/composables/blog-archives.ts
@@ -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
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
diff --git a/theme/src/client/composables/internal-link.ts b/theme/src/client/composables/internal-link.ts
index 0e5329f0..27e4912b 100644
--- a/theme/src/client/composables/internal-link.ts
+++ b/theme/src/client/composables/internal-link.ts
@@ -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`],
}
}
diff --git a/theme/src/node/config/resolveLocaleOptions.ts b/theme/src/node/config/resolveLocaleOptions.ts
index 0762a18d..fc14cf87 100644
--- a/theme/src/node/config/resolveLocaleOptions.ts
+++ b/theme/src/node/config/resolveLocaleOptions.ts
@@ -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
+}
diff --git a/theme/src/node/config/resolveThemeData.ts b/theme/src/node/config/resolveThemeData.ts
index 1f9fa685..32e68692 100644
--- a/theme/src/node/config/resolveThemeData.ts
+++ b/theme/src/node/config/resolveThemeData.ts
@@ -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),
})
}
diff --git a/theme/src/node/index.ts b/theme/src/node/index.ts
index a37691df..df8b9d04 100644
--- a/theme/src/node/index.ts
+++ b/theme/src/node/index.ts
@@ -2,6 +2,7 @@ import { plumeTheme } from './theme.js'
export * from '../shared/index.js'
export * from './defineConfig.js'
+export * from './types.js'
export { plumeTheme }
diff --git a/theme/src/node/locales/de.ts b/theme/src/node/locales/de.ts
index 49d10a65..1f4b2248 100644
--- a/theme/src/node/locales/de.ts
+++ b/theme/src/node/locales/de.ts
@@ -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',
diff --git a/theme/src/node/locales/en.ts b/theme/src/node/locales/en.ts
index fdeaacd4..664d772a 100644
--- a/theme/src/node/locales/en.ts
+++ b/theme/src/node/locales/en.ts
@@ -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 VuePress & vuepress-theme-plume',
@@ -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',
diff --git a/theme/src/node/locales/fr.ts b/theme/src/node/locales/fr.ts
index 14835eb5..6317f4cd 100644
--- a/theme/src/node/locales/fr.ts
+++ b/theme/src/node/locales/fr.ts
@@ -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',
diff --git a/theme/src/node/locales/ja.ts b/theme/src/node/locales/ja.ts
index d74b4d82..78a2f9f2 100644
--- a/theme/src/node/locales/ja.ts
+++ b/theme/src/node/locales/ja.ts
@@ -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 国際',
diff --git a/theme/src/node/locales/ru.ts b/theme/src/node/locales/ru.ts
index 04d956b7..72856e64 100644
--- a/theme/src/node/locales/ru.ts
+++ b/theme/src/node/locales/ru.ts
@@ -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 Международный',
diff --git a/theme/src/node/locales/zh-tw.ts b/theme/src/node/locales/zh-tw.ts
index 540686af..007a5e84 100644
--- a/theme/src/node/locales/zh-tw.ts
+++ b/theme/src/node/locales/zh-tw.ts
@@ -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 國際',
diff --git a/theme/src/node/locales/zh.ts b/theme/src/node/locales/zh.ts
index c5e7b343..6083b3a2 100644
--- a/theme/src/node/locales/zh.ts
+++ b/theme/src/node/locales/zh.ts
@@ -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 国际',
diff --git a/theme/src/node/pages/createPages.ts b/theme/src/node/pages/createPages.ts
index eb9c189b..4bc61c87 100644
--- a/theme/src/node/pages/createPages.ts
+++ b/theme/src/node/pages/createPages.ts
@@ -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[] = []
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' },
}))
}
}
diff --git a/theme/src/shared/base.ts b/theme/src/shared/base.ts
index 5ab6f4bd..85988967 100644
--- a/theme/src/shared/base.ts
+++ b/theme/src/shared/base.ts
@@ -50,14 +50,7 @@ export type SocialLinkIconUnion =
export type SocialLinkIcon = SocialLinkIconUnion | { svg: string, name?: string }
-export interface PresetLocale extends Record {
- home: string
- blog: string
- tag: string
- archive: string
- category: string
- archiveTotal: string
-}
+export interface PresetLocale extends Record {}
export type KnownCopyrightLicense =
| 'CC-BY-4.0'
diff --git a/theme/src/shared/options/locale.ts b/theme/src/shared/options/locale.ts
index 40a7978f..e9a33841 100644
--- a/theme/src/shared/options/locale.ts
+++ b/theme/src/shared/options/locale.ts
@@ -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
+
/**
* 全站加密时的提示
*/