Skip to content

Commit

Permalink
添加显示国际化选择器
Browse files Browse the repository at this point in the history
  • Loading branch information
qianmoQ committed Jan 1, 2025
1 parent 2d7ea07 commit b90e2dc
Show file tree
Hide file tree
Showing 8 changed files with 267 additions and 41 deletions.
2 changes: 1 addition & 1 deletion docs/content/getting-started/get-started.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
title: 快速上手
title: GetStarted
icon: crosshair
---

Expand Down
59 changes: 59 additions & 0 deletions docs/content/setup/feature.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,62 @@ lucide:
- `enable`: 是否启用 Lucide 图标
- `cdn`: Lucide CDN 链接,默认为 `https://unpkg.com/lucide@latest/dist/umd/lucide.js` ,可以根据自己的需求进行修改,这里要填写完整的 CDN 链接,例如 `https://unpkg.com/lucide@latest/dist/umd/lucide.js`

## 国际化设置

---

PageForge 支持用户自定义的国际化设置,需要启用后才能使用国际化。

```markdown
i18n:
enable: true
```

启用国际化后,需要配置国际化语言

```markdown
i18n:
default: zh-CN
en:
name: English
flag: 🇬🇧
translations:
GetStarted: Getting Started
Setup: Setup
Usage: Usage Docs
zh-CN:
name: 中文
flag: 🇨🇳
translations:
GetStarted: 快速开始
Setup: 设置
Usage: 使用文档
```

- `default`: 默认语言
- `en`、`zh-CN`: 自定义语言
- `translations`: 对应语言的翻译,格式为 `key: value`

系统会通过 `pageforge.yaml` 中配置的语言来自动加载对应的翻译,如果没有找到翻译,则会返回原 key

在 `pageforge.yaml` 中配置国际化后,可以在 `nav` 中使用以下方式来获取翻译的结果。

```markdown
nav:
- GetStarted:
- /getting-started/get-started
```

- `GetStarted`: 要翻译的 key,如果没有找到翻译,则会返回原 key

在页面中使用使用以下方式来获取翻译的结果

```markdown
---
title: GetStarted
icon: crosshair
---
```

只需要将 `title` 中替换为国际化的 key 就可以了
25 changes: 22 additions & 3 deletions docs/pageforge.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,25 @@ repo:
feature:
lucide:
enable: true
i18n:
enable: true

i18n:
default: zh-CN
en:
name: English
flag: 🇬🇧
translations:
GetStarted: Getting Started
Setup: Setup
Usage: Usage Docs
zh-CN:
name: 中文
flag: 🇨🇳
translations:
GetStarted: 快速开始
Setup: 设置
Usage: 使用文档

footer:
copyright: © 2024 PageForge All Rights Reserved. 使用 ❤️ PageForge 构建
Expand Down Expand Up @@ -54,16 +73,16 @@ footer:
href: https://github.com/devlive-community/pageforge/blob/dev/LICENSE

nav:
- 快速开始:
- GetStarted:
- /getting-started/get-started
- /getting-started/create-site/index
- /getting-started/publish-site
- /getting-started/customization
- 设置:
- Setup:
- /setup/site
- /setup/page
- /setup/feature
- 使用文档:
- Usage:
- /usage/href
- /usage/image
- /usage/alert
Expand Down
65 changes: 61 additions & 4 deletions lib/site-generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,16 +109,60 @@ class SiteGenerator {
return path;
}

// 判断特定功能是否启用
isFeatureEnabled(featureName) {
return this.config.feature?.[featureName]?.enable === true;
}

// 获取翻译的方法
getTranslation(key, locale) {
if (!this.isFeatureEnabled('i18n')) {
return key;
}

const i18n = this.config.i18n || {};
// 如果没有指定语言,使用默认语言
const currentLocale = locale || i18n.default;

// 直接从配置中获取对应语言的翻译
const translations = i18n[currentLocale]?.translations;

// 返回翻译,如果没有找到则返回原key
return translations?.[key] || key;
}

// 获取所有配置的语言信息
getAvailableLocales() {
// 如果国际化功能未启用,返回空数组
if (!this.isFeatureEnabled('i18n')) {
return [];
}

const i18n = this.config.i18n || {};
// 过滤掉 default 属性,只返回语言配置
return Object.entries(i18n)
.filter(([key]) => key !== 'default')
.map(([key, value]) => ({
key, // 语言代码 如 'en', 'zh-CN'
name: value.name, // 语言名称
flag: value.flag // 语言图标
}));
}

transformNavigation(navigation) {
const processItem = (item) => {
// 如果是字符串类型的链接(如 "/docs/get-started")
if (typeof item === 'string') {
// 直接使用原始 URL 作为 href
const pageData = this.pages.get(item);
if (pageData) {
const title = pageData.title
? this.getTranslation(pageData.title)
: pageData.title;

return {
...pageData,
title: pageData.title,
title,
href: this.appendHtml(item)
};
}
Expand All @@ -132,8 +176,10 @@ class SiteGenerator {
// 如果是对象(有子项的导航)
if (typeof item === 'object') {
const [[title, items]] = Object.entries(item);
const translatedTitle = title ? this.getTranslation(title) : title;

return {
title,
title: translatedTitle,
href: this.appendHtml(items[0]),
items: Array.isArray(items) ? items.map(processItem) : []
};
Expand Down Expand Up @@ -176,21 +222,32 @@ class SiteGenerator {
// 计算当前页面的路径
const pagePath = this.getPagePath(relativePath);

// 尝试翻译 metadata.title
const translatedTitle = metadata.title
? this.getTranslation(metadata.title)
: metadata.title;
metadata.title = translatedTitle;

// 配置的国际化
this.config.languages = this.getAvailableLocales()

// 准备页面数据
const pageData = {
title: metadata.title,
title: translatedTitle,
content: content,
site: this.config.site || {},
page: {
...metadata,
// // 添加国际化信息
// i18n: this.config.i18n,
// 添加路径信息
path: this.appendHtml(pagePath),
// 原始文件路径
sourcePath: relativePath,
// 输出路径
outputPath: path.join(baseDir, file.replace('.md', '.html'))
},
nav: this.config.nav || this.defaultNav,
nav: this.config.nav,
config: this.config,
// 计算当前页面相对于根目录的路径
basePath: this.getRelativeBasePath(baseDir),
Expand Down
57 changes: 29 additions & 28 deletions templates/assets/js/pageforge.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ const GitHubStats = {

// 更新所有容器为加载状态
['.tag-container', '.stars-container', '.forks-container'].forEach(selector => {
const container = document.querySelector(selector);
if (container) {
const containers = document.querySelectorAll(selector);
containers.forEach(container => {
container.innerHTML = '<div class="skeleton-loader"></div>';
}
});
});
},

Expand Down Expand Up @@ -113,33 +113,34 @@ const GitHubStats = {
return;
}

const tagContainer = document.querySelector('.tag-container');
const starsContainer = document.querySelector('.stars-container');
const forksContainer = document.querySelector('.forks-container');

if (tagContainer) {
tagContainer.innerHTML = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16" fill="currentColor">
<path d="M1 7.775V2.75C1 1.784 1.784 1 2.75 1h5.025c.464 0 .91.184 1.238.513l6.25 6.25a1.75 1.75 0 0 1 0 2.474l-5.026 5.026a1.75 1.75 0 0 1-2.474 0l-6.25-6.25A1.75 1.75 0 0 1 1 7.775Zm1.5 0c0 .066.026.13.073.177l6.25 6.25a.25.25 0 0 0 .354 0l5.025-5.025a.25.25 0 0 0 0-.354l-6.25-6.25a.25.25 0 0 0-.177-.073H2.75a.25.25 0 0 0-.25.25ZM6 5a1 1 0 1 1 0 2 1 1 0 0 1 0-2Z"></path>
</svg>
${stats.tag}`;
}
// 修改为更新所有匹配的容器
const tagContainers = document.querySelectorAll('.tag-container');
const starsContainers = document.querySelectorAll('.stars-container');
const forksContainers = document.querySelectorAll('.forks-container');

tagContainers.forEach(container => {
container.innerHTML = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16" fill="currentColor">
<path d="M1 7.775V2.75C1 1.784 1.784 1 2.75 1h5.025c.464 0 .91.184 1.238.513l6.25 6.25a1.75 1.75 0 0 1 0 2.474l-5.026 5.026a1.75 1.75 0 0 1-2.474 0l-6.25-6.25A1.75 1.75 0 0 1 1 7.775Zm1.5 0c0 .066.026.13.073.177l6.25 6.25a.25.25 0 0 0 .354 0l5.025-5.025a.25.25 0 0 0 0-.354l-6.25-6.25a.25.25 0 0 0-.177-.073H2.75a.25.25 0 0 0-.25.25ZM6 5a1 1 0 1 1 0 2 1 1 0 0 1 0-2Z"></path>
</svg>
${stats.tag}`;
});

if (starsContainer) {
starsContainer.innerHTML = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16" fill="currentColor">
<path d="M8 .25a.75.75 0 0 1 .673.418l1.882 3.815 4.21.612a.75.75 0 0 1 .416 1.279l-3.046 2.97.719 4.192a.751.751 0 0 1-1.088.791L8 12.347l-3.766 1.98a.75.75 0 0 1-1.088-.79l.72-4.194L.818 6.374a.75.75 0 0 1 .416-1.28l4.21-.611L7.327.668A.75.75 0 0 1 8 .25Z"/>
</svg>
${stats.stars}`;
}
starsContainers.forEach(container => {
container.innerHTML = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16" fill="currentColor">
<path d="M8 .25a.75.75 0 0 1 .673.418l1.882 3.815 4.21.612a.75.75 0 0 1 .416 1.279l-3.046 2.97.719 4.192a.751.751 0 0 1-1.088.791L8 12.347l-3.766 1.98a.75.75 0 0 1-1.088-.79l.72-4.194L.818 6.374a.75.75 0 0 1 .416-1.28l4.21-.611L7.327.668A.75.75 0 0 1 8 .25Z"/>
</svg>
${stats.stars}`;
});

if (forksContainer) {
forksContainer.innerHTML = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16" fill="currentColor">
<path d="M5 5.372v.878c0 .414.336.75.75.75h4.5a.75.75 0 0 0 .75-.75v-.878a2.25 2.25 0 1 1 1.5 0v.878a2.25 2.25 0 0 1-2.25 2.25h-1.5v2.128a2.251 2.251 0 1 1-1.5 0V8.5h-1.5A2.25 2.25 0 0 1 3.5 6.25v-.878a2.25 2.25 0 1 1 1.5 0ZM5 3.25a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Zm6.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Zm-3 8.75a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Z"/>
</svg>
${stats.forks}`;
}
forksContainers.forEach(container => {
container.innerHTML = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16" fill="currentColor">
<path d="M5 5.372v.878c0 .414.336.75.75.75h4.5a.75.75 0 0 0 .75-.75v-.878a2.25 2.25 0 1 1 1.5 0v.878a2.25 2.25 0 0 1-2.25 2.25h-1.5v2.128a2.251 2.251 0 1 1-1.5 0V8.5h-1.5A2.25 2.25 0 0 1 3.5 6.25v-.878a2.25 2.25 0 1 1 1.5 0ZM5 3.25a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Zm6.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Zm-3 8.75a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Z"/>
</svg>
${stats.forks}`;
});
},

init(owner, repo) {
Expand Down
2 changes: 1 addition & 1 deletion templates/includes/footer.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
<hr class="my-6 border-gray-200 sm:mx-auto dark:border-gray-700 lg:my-8"/>

<!-- 底部版权和社交链接 -->
<div class="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
<div class="flex flex-col items-center sm:flex-row sm:items-center justify-between gap-4">
<span class="text-sm text-gray-500 dark:text-gray-400">
<%= locals.footer && locals.footer.copyright %>
</span>
Expand Down
51 changes: 49 additions & 2 deletions templates/includes/header.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,38 @@ function isPathInNavItem(currentPath, navItem) {
<% } %>
</ul>

<!-- 语言切换器 -->
<% if (locals.page.config.feature?.i18n?.enable && locals.page.config?.languages?.length > 0) { %>
<div class="hidden lg:flex items-center ml-4">
<div class="relative">
<button type="button"
onclick="document.getElementById('language-menu').classList.toggle('hidden')"
class="flex items-center gap-2 px-3 py-2 text-sm font-medium text-gray-700 dark:text-gray-200 hover:text-gray-900 dark:hover:text-white rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700">
<!-- 当前语言 -->
<% const currentLocale = locals.page.config.languages.find(locale => locale.key === locals.page.config.i18n.default) %>
<span><%= currentLocale?.flag %></span>
<span><%= currentLocale?.name %></span>
<!-- 下拉箭头 -->
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
</svg>
</button>
<!-- 语言选择下拉菜单 -->
<div id="language-menu"
class="hidden absolute right-0 mt-2 py-2 w-36 bg-white dark:bg-gray-800 rounded-lg shadow-lg ring-1 ring-black ring-opacity-5">
<% locals.page.config.languages.forEach(locale => { %>
<a href="?lang=<%= locale.key %>"
class="flex items-center gap-2 px-4 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-700
<%= locale.key === locals.page.config.i18n.default ? 'bg-gray-50 dark:bg-gray-700' : '' %>">
<span><%= locale.flag %></span>
<span><%= locale.name %></span>
</a>
<% }) %>
</div>
</div>
</div>
<% } %>
<!-- 仓库链接 -->
<div class="hidden lg:block">
<%- include('./repo') %>
Expand All @@ -90,7 +122,7 @@ function isPathInNavItem(currentPath, navItem) {
<!-- 移动端菜单 -->
<div class="hidden lg:hidden" id="mobile-menu">
<div class="px-2 pt-2 pb-3 space-y-1 border-t dark:border-gray-700">
<div class="px-2 pt-2 pb-3 space-y-2 border-t dark:border-gray-700">
<% for(let item of nav) { %>
<a href="<%= item.href %>"
class="block px-3 py-2 rounded-md text-base font-medium
Expand All @@ -101,8 +133,23 @@ function isPathInNavItem(currentPath, navItem) {
<%= item.title %>
</a>
<% } %>
<!-- 移动端语言切换 -->
<% if (locals.page.config.feature?.i18n?.enable && locals.page.config?.languages?.length > 0) { %>
<div class="px-3 py-2 space-y-1 border-t dark:border-gray-700">
<% locals.page.config.languages.forEach(locale => { %>
<a href="?lang=<%= locale.key %>"
class="flex items-center gap-2 px-4 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-700
<%= locale.key === locals.page.config.i18n.default ? 'bg-gray-50 dark:bg-gray-700' : '' %>">
<span><%= locale.flag %></span>
<span><%= locale.name %></span>
</a>
<% }) %>
</div>
<% } %>
<!-- 移动端的仓库链接 -->
<div class="px-3 py-2">
<div class="px-3 py-2 border-t dark:border-gray-700">
<%- include('./repo') %>
</div>
</div>
Expand Down
Loading

0 comments on commit b90e2dc

Please sign in to comment.