时隔多年,本站终于进行了一次较大的更新,从 Wordpress 迁移到了 Hugo。这篇文章记录了迁移的原因和过程。
缘由
想从 Wordpress 迁移到 Hugo 已经有五六年了,但一直没有进行。想迁移的原因非常简单:
- Wordpress 越来越臃肿。尤其是最近几年,我几乎都不想登录到 Wordpress 后台,太多的插件年久失修,也不敢乱动,甚至 Wordpress 自身升级后连新建页面都打不开了;
- Wordpress 对 Markdown 的支持是非原生的;虽然使用插件后看似效果不错,但从数据源的角度来说,本质上它还是存储为
HTML
格式,这导致文章的结构化和批量处理效果一般。 - 网站的访问中有相当比例来源于繁体中文使用者,因此想更好的支持繁体中文的阅读和使用。
关键步骤
从 Wordpress 备份提取 Markdown
这是看似很简单的步骤,因为有好几个插件都支持相关的功能,也有一些命令行的脚本。但是,最要命的是转换并不完美。
我使用的是基于 NodeJS 的 blog2md 脚本。
这个脚本的使用很简单,只需要在 Wordpress 后台导出备份的 .xml
文件,然后执行脚本就行了。
你的计算机需要有 Node
环境。
要注意的是,一定按照其 README 中的说明,添加 paragraph-fix
参数,否则很多换行和段落会出现问题。例如:
node index.js w your-wordpress-backup-export.xml out m paragraph-fix
但是即使你采用了上面的参数,转换出的文章很多依然是有问题的。主要表现在:
- 部分段落丢失,即缺少换行;
- 很多之前的代码高亮的内容出现了多余的转义,例如
*
变成了 \*
、]
变成了 \]
。
我的理解是当初为了在 Wordpress 中存储代码高亮,相关插件本身做了转义,但在导出数据时就很痛苦了。 - (更要命的)以
<
开头的内容丢失。可能因为和导出格式为 XML 有关,出现了语义上的冲突。这部分可能只能自己修了。
例如一个 C++ 程序:
template<typename T>
class ...
可能会变成
template typename T\>
class ...
所以,我需要写脚本来解决这个问题。
使用脚本处理多余的转义
下面是一个 NodeJS 脚本,用来批量将指定目录下的 md 文件的正文部分包裹的代码块中的多余转义进行修复。
例如,它将 \[
修复成 [
,\_
修复成 _
。
const fs = require('fs');
const path = require('path');
// 指定目录路径
const DIRECTORY_PATH = 'path/to/content';
// 递归地查找.md文件
function findMarkdownFiles(dirPath) {
let filesToProcess = [];
const files = fs.readdirSync(dirPath);
for (const file of files) {
const filePath = path.join(dirPath, file);
const stat = fs.statSync(filePath);
if (stat.isDirectory()) {
filesToProcess = filesToProcess.concat(findMarkdownFiles(filePath));
} else if (path.extname(filePath) === '.md') {
filesToProcess.push(filePath);
}
}
return filesToProcess;
}
// 处理.md文件中的代码块
function processMarkdownFile(filePath) {
const content = fs.readFileSync(filePath, 'utf8');
// 使用正则表达式匹配所有代码块
const newContent = content.replace(/```[\s\S]*?```/g, (match) => {
// 替换代码块中的反转义字符
return match.replace(/\\\[/g, '[')
.replace(/\\\]/g, ']')
.replace(/\\\*/g, '*')
.replace(/\\_/g, '_')
.replace(/\\\\/g, '\\')
});
// 覆盖原文件
fs.writeFileSync(filePath, newContent, 'utf8');
}
// 主函数
function main() {
const markdownFiles = findMarkdownFiles(DIRECTORY_PATH);
for (const filePath of markdownFiles) {
processMarkdownFile(filePath);
}
console.log('所有.md文件中的代码块已处理完毕。');
}
// 运行主函数
main();
警告
- 请修改
DIRECTORY_PATH
的值,并注意转义。 - 这个脚本会覆盖原文件,请提前备份。而且,由于其做了批量替换,可能会影响代码正常运行。需要手工检查。
移除不必要的 TOC(文章目录)
很多 Hugo 主题使用 toc
这个 Front Matter 来标记是否展开文章目录。对于老文章,
我写了一个脚本,可以根据这个文章是否有实际的二级标题来判断是否启用 toc。
也就是说,如果一个文章的 Markdown 里没有两个以上的二级标题,它会把:
+++
title = '本站从 Wordpress 迁移到 Hugo'
date = 2024-04-01T16:38:34+08:00
draft = false
+++
修改为
+++
title = '本站从 Wordpress 迁移到 Hugo'
date = 2024-04-01T16:38:34+08:00
draft = false
toc = false
+++
这样渲染出的文件就不会有目录了,并且不需要前端使用 Javascript 动态实现。
自动翻译简体中文到繁体中文(或相反)
因为这次升级的一个目的是更好地支持简繁两种汉字的使用者,因此我使用了一个脚本,
它可以将所有 Markdown 自动进行简繁转换。
Hugo
支持两种多语言模式,我使用的是文件模式,也就是简体、繁体的文章放在一起,
繁体文章增加 .zh-hant
后缀名。
简繁转换不能简单的字字对应,因为存在很多「一对多」的问题和各地习惯用语的问题。
Opencc
是一个不错的开源工具,可以帮我们实现以词汇为单位的简繁转换,提高转换的准确率。
根据统计,本站大部分繁体用户来自台湾,因此我在简体转换繁体时,使用了台湾的惯用词。
下面的脚本使用 opencc
的 NodeJS
版本进行简繁转换。
这个脚本会把转换后的结果放在 DIRECTORY_OUTPATH
目录下,文件名增加 .zh-hant
后缀。
它只转换文章的标题和正文,不会转换文章的元数据,例如 tag
和 url
,因此比较安全。
使用前,你需要根据自己的情况对脚本进行适当的修改,并且需要安装 opencc
:
你需要提前修改脚本中的 DIRECTORY_PATH
和 DIRECTORY_OUTPATH
。
如果你使用 posts/xxx/index.md
、posts/xxx/index.zh-hant.md
这种形式,
你可以将两个目录设置相同。
脚本内容如下:
const fs = require("fs");
const path = require("path");
const OpenCC = require("opencc");
const converter = new OpenCC("s2twp.json");
const matter = require('gray-matter');
// 指定目录路径
const DIRECTORY_PATH = "path/to/blog/content";
const DIRECTORY_OUTPATH = "path/to/blog/content-hant";
// 递归地查找.md文件
function findMarkdownFiles(dirPath) {
let filesToProcess = [];
const files = fs.readdirSync(dirPath);
for (const file of files) {
const filePath = path.join(dirPath, file);
const stat = fs.statSync(filePath);
if (stat.isDirectory()) {
filesToProcess = filesToProcess.concat(findMarkdownFiles(filePath));
} else if (path.extname(filePath) === ".md") {
filesToProcess.push(filePath);
}
}
return filesToProcess;
}
// 处理.md文件中的代码块
async function processMarkdownFile(filePath) {
if (filePath.indexOf("zh-tw") >= 0) {
return;
}
// 解析Markdown文件的front matter
const content = matter(fs.readFileSync(filePath, "utf8"));
// 翻译正文
content.content = await converter.convertPromise(content.content)
// 翻译标题
if (content.data.title && content.data.title.length > 0) {
content.data.title = await converter.convertPromise(content.data.title)
}
// 构造输出文件路径
const outFilePath = path.join(DIRECTORY_OUTPATH, path.basename(filePath, ".md") + ".zh-hant.md");
console.log(outFilePath)
console.log(content.content)
// 如果文件存在则不覆盖
if (fs.existsSync(outFilePath)) {
return;
}
const newMarkdownContent = matter.stringify(content);
fs.writeFileSync(outFilePath, newMarkdownContent)
}
// 主函数
function main() {
const markdownFiles = findMarkdownFiles(DIRECTORY_PATH);
for (const filePath of markdownFiles) {
processMarkdownFile(filePath);
}
console.log("所有.md文件已处理完毕。");
}
// 运行主函数
main();
警告
- 不要使用
zh-tw
、zh-hk
、zh-cn
等作为语言标识。因为他们代表的是地区。作为一个小站点,你估计不太可能会为香港、台湾、新加坡各做一个不同的站点。 - 使用
zh-hant
代表传统汉字,zh-hans
代表简体中文是明显更加通用的选择。这也方便浏览器、搜索引擎判断你的目标用户。比如,你使用了 zh-hant
,香港访问者不会被排除在外。 - 你可以访问
opencc
的 gihhub 页面,查找不同的配置文件。不同的配置文件会使用不同的惯用词,请根据你实际的需求进行修改。我使用的是 s2tw.json
文件,代表使用台湾惯用词进行简体到繁体的转换。
你也可以通过修改 s2twp.json
配置文件的名字实现繁体->简体的转换。
手工检查所有文章
虽然做了自动化处理,但很多文章依然有问题,需要手工检查一遍。
选择合适的评论系统
我选择的是 Waline
,但你可以选择任何你喜欢的评论系统。唯一的建议是,
评论数据需要掌握在自己手里,因此必须选择可以备份评论数据的评论系统。
否则,一旦你的托管方式发生变化,你可能会追悔莫及。
Waline
支持多种数据源,我选择的是 sqlite
,因为我有
自己的服务器,并且我希望一个节约内存的轻量化解决方案。
从 Wordpress 评论迁移到 Waline
目前网络上似乎还没有从 Wordpress 评论迁移到 Waline 的现成方案。
本文提供了一种方案,可以将 Wordpress 所有评论无损地迁移到 Waline。
首先从 Wordpress 后台,导出所有内容的 xml 文件。
然后执行我写的脚本(你需要有 NodeJS),对 xml 文件进行分析,生成可以导入
到 Waline 的导入格式(注意修改 WORDPRESS_XML_FILE
变量)。
这个脚本耗费了我不少心血,你可以选择花不到一块钱来给你节约时间:
PGRpdiBjbGFzcz0iaGlnaGxpZ2h0Ij48cHJlIHRhYmluZGV4PSIwIiBjbGFzcz0iY2hyb21hIj48Y29kZSBjbGFzcz0ibGFuZ3VhZ2UtanMiIGRhdGEtbGFuZz0ianMiPjxzcGFuIGNsYXNzPSJsaW5lIj48c3BhbiBjbGFzcz0iY2wiPjxzcGFuIGNsYXNzPSJrciI+Y29uc3Q8L3NwYW4+IDxzcGFuIGNsYXNzPSJueCI+ZnM8L3NwYW4+IDxzcGFuIGNsYXNzPSJvIj49PC9zcGFuPiA8c3BhbiBjbGFzcz0ibngiPnJlcXVpcmU8L3NwYW4+PHNwYW4gY2xhc3M9InAiPig8L3NwYW4+PHNwYW4gY2xhc3M9InMxIj4mIzM5O2ZzJiMzOTs8L3NwYW4+PHNwYW4gY2xhc3M9InAiPik7PC9zcGFuPgo8L3NwYW4+PC9zcGFuPjxzcGFuIGNsYXNzPSJsaW5lIj48c3BhbiBjbGFzcz0iY2wiPjxzcGFuIGNsYXNzPSJrciI+Y29uc3Q8L3NwYW4+IDxzcGFuIGNsYXNzPSJueCI+eG1sMmpzPC9zcGFuPiA8c3BhbiBjbGFzcz0ibyI+PTwvc3Bhbj4gPHNwYW4gY2xhc3M9Im54Ij5yZXF1aXJlPC9zcGFuPjxzcGFuIGNsYXNzPSJwIj4oPC9zcGFuPjxzcGFuIGNsYXNzPSJzMSI+JiMzOTt4bWwyanMmIzM5Ozwvc3Bhbj48c3BhbiBjbGFzcz0icCI+KTs8L3NwYW4+Cjwvc3Bhbj48L3NwYW4+PHNwYW4gY2xhc3M9ImxpbmUiPjxzcGFuIGNsYXNzPSJjbCI+PHNwYW4gY2xhc3M9ImtyIj5jb25zdDwvc3Bhbj4gPHNwYW4gY2xhc3M9Im54Ij5wYXJzZXI8L3NwYW4+IDxzcGFuIGNsYXNzPSJvIj49PC9zcGFuPiA8c3BhbiBjbGFzcz0iayI+bmV3PC9zcGFuPiA8c3BhbiBjbGFzcz0ibngiPnhtbDJqczwvc3Bhbj48c3BhbiBjbGFzcz0icCI+Ljwvc3Bhbj48c3BhbiBjbGFzcz0ibngiPlBhcnNlcjwvc3Bhbj48c3BhbiBjbGFzcz0icCI+KCk7PC9zcGFuPgo8L3NwYW4+PC9zcGFuPjxzcGFuIGNsYXNzPSJsaW5lIj48c3BhbiBjbGFzcz0iY2wiPgo8L3NwYW4+PC9zcGFuPjxzcGFuIGNsYXNzPSJsaW5lIj48c3BhbiBjbGFzcz0iY2wiPjxzcGFuIGNsYXNzPSJjMSI+Ly8gV29yZFByZXNz5aSH5Lu95paH5Lu255qE6Lev5b6ECjwvc3Bhbj48L3NwYW4+PC9zcGFuPjxzcGFuIGNsYXNzPSJsaW5lIj48c3BhbiBjbGFzcz0iY2wiPjxzcGFuIGNsYXNzPSJjMSI+PC9zcGFuPjxzcGFuIGNsYXNzPSJrciI+Y29uc3Q8L3NwYW4+IDxzcGFuIGNsYXNzPSJueCI+V09SRFBSRVNTX1hNTF9GSUxFPC9zcGFuPiA8c3BhbiBjbGFzcz0ibyI+PTwvc3Bhbj4gPHNwYW4gY2xhc3M9InMxIj4mIzM5O1dvcmRQcmVzcy4yMDI0LTAzLTI4LnhtbCYjMzk7PC9zcGFuPjxzcGFuIGNsYXNzPSJwIj47PC9zcGFuPgo8L3NwYW4+PC9zcGFuPjxzcGFuIGNsYXNzPSJsaW5lIj48c3BhbiBjbGFzcz0iY2wiPgo8L3NwYW4+PC9zcGFuPjxzcGFuIGNsYXNzPSJsaW5lIj48c3BhbiBjbGFzcz0iY2wiPjxzcGFuIGNsYXNzPSJjMSI+Ly8g6K+75Y+WWE1M5paH5Lu2Cjwvc3Bhbj48L3NwYW4+PC9zcGFuPjxzcGFuIGNsYXNzPSJsaW5lIj48c3BhbiBjbGFzcz0iY2wiPjxzcGFuIGNsYXNzPSJjMSI+PC9zcGFuPjxzcGFuIGNsYXNzPSJueCI+ZnM8L3NwYW4+PHNwYW4gY2xhc3M9InAiPi48L3NwYW4+PHNwYW4gY2xhc3M9Im54Ij5yZWFkRmlsZTwvc3Bhbj48c3BhbiBjbGFzcz0icCI+KDwvc3Bhbj48c3BhbiBjbGFzcz0ibngiPldPUkRQUkVTU19YTUxfRklMRTwvc3Bhbj48c3BhbiBjbGFzcz0icCI+LDwvc3Bhbj4gPHNwYW4gY2xhc3M9InAiPig8L3NwYW4+PHNwYW4gY2xhc3M9Im54Ij5lcnI8L3NwYW4+PHNwYW4gY2xhc3M9InAiPiw8L3NwYW4+IDxzcGFuIGNsYXNzPSJueCI+ZGF0YTwvc3Bhbj48c3BhbiBjbGFzcz0icCI+KTwvc3Bhbj4gPHNwYW4gY2xhc3M9InAiPj0mZ3Q7PC9zcGFuPiA8c3BhbiBjbGFzcz0icCI+ezwvc3Bhbj4KPC9zcGFuPjwvc3Bhbj48c3BhbiBjbGFzcz0ibGluZSI+PHNwYW4gY2xhc3M9ImNsIj4gIDxzcGFuIGNsYXNzPSJrIj5pZjwvc3Bhbj4gPHNwYW4gY2xhc3M9InAiPig8L3NwYW4+PHNwYW4gY2xhc3M9Im54Ij5lcnI8L3NwYW4+PHNwYW4gY2xhc3M9InAiPik8L3NwYW4+IDxzcGFuIGNsYXNzPSJwIj57PC9zcGFuPgo8L3NwYW4+PC9zcGFuPjxzcGFuIGNsYXNzPSJsaW5lIj48c3BhbiBjbGFzcz0iY2wiPiAgICA8c3BhbiBjbGFzcz0ibngiPmNvbnNvbGU8L3NwYW4+PHNwYW4gY2xhc3M9InAiPi48L3NwYW4+PHNwYW4gY2xhc3M9Im54Ij5lcnJvcjwvc3Bhbj48c3BhbiBjbGFzcz0icCI+KDwvc3Bhbj48c3BhbiBjbGFzcz0iczEiPiYjMzk7RXJyb3IgcmVhZGluZyBYTUwgZmlsZTomIzM5Ozwvc3Bhbj48c3BhbiBjbGFzcz0icCI+LDwvc3Bhbj4gPHNwYW4gY2xhc3M9Im54Ij5lcnI8L3NwYW4+PHNwYW4gY2xhc3M9InAiPik7PC9zcGFuPgo8L3NwYW4+PC9zcGFuPjxzcGFuIGNsYXNzPSJsaW5lIj48c3BhbiBjbGFzcz0iY2wiPiAgICA8c3BhbiBjbGFzcz0iayI+cmV0dXJuPC9zcGFuPjxzcGFuIGNsYXNzPSJwIj47PC9zcGFuPgo8L3NwYW4+PC9zcGFuPjxzcGFuIGNsYXNzPSJsaW5lIj48c3BhbiBjbGFzcz0iY2wiPiAgPHNwYW4gY2xhc3M9InAiPn08L3NwYW4+Cjwvc3Bhbj48L3NwYW4+PHNwYW4gY2xhc3M9ImxpbmUiPjxzcGFuIGNsYXNzPSJjbCI+Cjwvc3Bhbj48L3NwYW4+PHNwYW4gY2xhc3M9ImxpbmUiPjxzcGFuIGNsYXNzPSJjbCI+ICA8c3BhbiBjbGFzcz0iYzEiPi8vIOino+aekFhNTOaVsOaNrgo8L3NwYW4+PC9zcGFuPjwvc3Bhbj48c3BhbiBjbGFzcz0ibGluZSI+PHNwYW4gY2xhc3M9ImNsIj48c3BhbiBjbGFzcz0iYzEiPjwvc3Bhbj4gIDxzcGFuIGNsYXNzPSJueCI+cGFyc2VyPC9zcGFuPjxzcGFuIGNsYXNzPSJwIj4uPC9zcGFuPjxzcGFuIGNsYXNzPSJueCI+cGFyc2VTdHJpbmc8L3NwYW4+PHNwYW4gY2xhc3M9InAiPig8L3NwYW4+PHNwYW4gY2xhc3M9Im54Ij5kYXRhPC9zcGFuPjxzcGFuIGNsYXNzPSJwIj4sPC9zcGFuPiA8c3BhbiBjbGFzcz0icCI+KDwvc3Bhbj48c3BhbiBjbGFzcz0ibngiPmVycjwvc3Bhbj48c3BhbiBjbGFzcz0icCI+LDwvc3Bhbj4gPHNwYW4gY2xhc3M9Im54Ij5yZXN1bHQ8L3NwYW4+PHNwYW4gY2xhc3M9InAiPik8L3NwYW4+IDxzcGFuIGNsYXNzPSJwIj49Jmd0Ozwvc3Bhbj4gPHNwYW4gY2xhc3M9InAiPns8L3NwYW4+Cjwvc3Bhbj48L3NwYW4+PHNwYW4gY2xhc3M9ImxpbmUiPjxzcGFuIGNsYXNzPSJjbCI+ICAgIDxzcGFuIGNsYXNzPSJrIj5pZjwvc3Bhbj4gPHNwYW4gY2xhc3M9InAiPig8L3NwYW4+PHNwYW4gY2xhc3M9Im54Ij5lcnI8L3NwYW4+PHNwYW4gY2xhc3M9InAiPik8L3NwYW4+IDxzcGFuIGNsYXNzPSJwIj57PC9zcGFuPgo8L3NwYW4+PC9zcGFuPjxzcGFuIGNsYXNzPSJsaW5lIj48c3BhbiBjbGFzcz0iY2wiPiAgICAgIDxzcGFuIGNsYXNzPSJueCI+Y29uc29sZTwvc3Bhbj48c3BhbiBjbGFzcz0icCI+Ljwvc3Bhbj48c3BhbiBjbGFzcz0ibngiPmVycm9yPC9zcGFuPjxzcGFuIGNsYXNzPSJwIj4oPC9zcGFuPjxzcGFuIGNsYXNzPSJzMSI+JiMzOTtFcnJvciBwYXJzaW5nIFhNTCBkYXRhOiYjMzk7PC9zcGFuPjxzcGFuIGNsYXNzPSJwIj4sPC9zcGFuPiA8c3BhbiBjbGFzcz0ibngiPmVycjwvc3Bhbj48c3BhbiBjbGFzcz0icCI+KTs8L3NwYW4+Cjwvc3Bhbj48L3NwYW4+PHNwYW4gY2xhc3M9ImxpbmUiPjxzcGFuIGNsYXNzPSJjbCI+ICAgICAgPHNwYW4gY2xhc3M9ImsiPnJldHVybjwvc3Bhbj48c3BhbiBjbGFzcz0icCI+Ozwvc3Bhbj4KPC9zcGFuPjwvc3Bhbj48c3BhbiBjbGFzcz0ibGluZSI+PHNwYW4gY2xhc3M9ImNsIj4gICAgPHNwYW4gY2xhc3M9InAiPn08L3NwYW4+Cjwvc3Bhbj48L3NwYW4+PHNwYW4gY2xhc3M9ImxpbmUiPjxzcGFuIGNsYXNzPSJjbCI+Cjwvc3Bhbj48L3NwYW4+PHNwYW4gY2xhc3M9ImxpbmUiPjxzcGFuIGNsYXNzPSJjbCI+ICAgIDxzcGFuIGNsYXNzPSJjMSI+Ly8g5Yid5aeL5YyW6K+E6K665pWw57uECjwvc3Bhbj48L3NwYW4+PC9zcGFuPjxzcGFuIGNsYXNzPSJsaW5lIj48c3BhbiBjbGFzcz0iY2wiPjxzcGFuIGNsYXNzPSJjMSI+PC9zcGFuPiAgICA8c3BhbiBjbGFzcz0ia2QiPmxldDwvc3Bhbj4gPHNwYW4gY2xhc3M9Im54Ij5jb21tZW50c0FycmF5PC9zcGFuPiA8c3BhbiBjbGFzcz0ibyI+PTwvc3Bhbj4gPHNwYW4gY2xhc3M9InAiPltdOzwvc3Bhbj4KPC9zcGFuPjwvc3Bhbj48c3BhbiBjbGFzcz0ibGluZSI+PHNwYW4gY2xhc3M9ImNsIj4KPC9zcGFuPjwvc3Bhbj48c3BhbiBjbGFzcz0ibGluZSI+PHNwYW4gY2xhc3M9ImNsIj4gICAgPHNwYW4gY2xhc3M9ImMxIj4vLyDpgY3ljobmr4/kuKppdGVt77yI5paH56ug5oiW6aG16Z2i77yJCjwvc3Bhbj48L3NwYW4+PC9zcGFuPjxzcGFuIGNsYXNzPSJsaW5lIj48c3BhbiBjbGFzcz0iY2wiPjxzcGFuIGNsYXNzPSJjMSI+PC9zcGFuPiAgICA8c3BhbiBjbGFzcz0ibngiPnJlc3VsdDwvc3Bhbj48c3BhbiBjbGFzcz0icCI+Ljwvc3Bhbj48c3BhbiBjbGFzcz0ibngiPnJzczwvc3Bhbj48c3BhbiBjbGFzcz0icCI+Ljwvc3Bhbj48c3BhbiBjbGFzcz0ibngiPmNoYW5uZWw8L3NwYW4+PHNwYW4gY2xhc3M9InAiPls8L3NwYW4+PHNwYW4gY2xhc3M9Im1pIj4wPC9zcGFuPjxzcGFuIGNsYXNzPSJwIj5dLjwvc3Bhbj48c3BhbiBjbGFzcz0ibngiPml0ZW08L3NwYW4+PHNwYW4gY2xhc3M9InAiPi48L3NwYW4+PHNwYW4gY2xhc3M9Im54Ij5mb3JFYWNoPC9zcGFuPjxzcGFuIGNsYXNzPSJwIj4oKDwvc3Bhbj48c3BhbiBjbGFzcz0ibngiPnBvc3Q8L3NwYW4+PHNwYW4gY2xhc3M9InAiPik8L3NwYW4+IDxzcGFuIGNsYXNzPSJwIj49Jmd0Ozwvc3Bhbj4gPHNwYW4gY2xhc3M9InAiPns8L3NwYW4+Cjwvc3Bhbj48L3NwYW4+PHNwYW4gY2xhc3M9ImxpbmUiPjxzcGFuIGNsYXNzPSJjbCI+ICAgICAgPHNwYW4gY2xhc3M9ImMxIj4vLyDliKTmlq3mmK/lkKbmnInor4TorroKPC9zcGFuPjwvc3Bhbj48L3NwYW4+PHNwYW4gY2xhc3M9ImxpbmUiPjxzcGFuIGNsYXNzPSJjbCI+PHNwYW4gY2xhc3M9ImMxIj48L3NwYW4+ICAgICAgPHNwYW4gY2xhc3M9ImsiPmlmPC9zcGFuPiA8c3BhbiBjbGFzcz0icCI+KDwvc3Bhbj48c3BhbiBjbGFzcz0ibngiPnBvc3Q8L3NwYW4+PHNwYW4gY2xhc3M9InAiPls8L3NwYW4+PHNwYW4gY2xhc3M9InMxIj4mIzM5O3dwOmNvbW1lbnQmIzM5Ozwvc3Bhbj48c3BhbiBjbGFzcz0icCI+XSk8L3NwYW4+IDxzcGFuIGNsYXNzPSJwIj57PC9zcGFuPgo8L3NwYW4+PC9zcGFuPjxzcGFuIGNsYXNzPSJsaW5lIj48c3BhbiBjbGFzcz0iY2wiPiAgICAgICAgPHNwYW4gY2xhc3M9ImMxIj4vLyDpgY3ljobmr4/mnaHor4TorroKPC9zcGFuPjwvc3Bhbj48L3NwYW4+PHNwYW4gY2xhc3M9ImxpbmUiPjxzcGFuIGNsYXNzPSJjbCI+PHNwYW4gY2xhc3M9ImMxIj48L3NwYW4+ICAgICAgICA8c3BhbiBjbGFzcz0ibngiPnBvc3Q8L3NwYW4+PHNwYW4gY2xhc3M9InAiPls8L3NwYW4+PHNwYW4gY2xhc3M9InMxIj4mIzM5O3dwOmNvbW1lbnQmIzM5Ozwvc3Bhbj48c3BhbiBjbGFzcz0icCI+XS48L3NwYW4+PHNwYW4gY2xhc3M9Im54Ij5mb3JFYWNoPC9zcGFuPjxzcGFuIGNsYXNzPSJwIj4oKDwvc3Bhbj48c3BhbiBjbGFzcz0ibngiPmNvbW1lbnQ8L3NwYW4+PHNwYW4gY2xhc3M9InAiPik8L3NwYW4+IDxzcGFuIGNsYXNzPSJwIj49Jmd0Ozwvc3Bhbj4gPHNwYW4gY2xhc3M9InAiPns8L3NwYW4+Cjwvc3Bhbj48L3NwYW4+PHNwYW4gY2xhc3M9ImxpbmUiPjxzcGFuIGNsYXNzPSJjbCI+ICAgICAgICAgIDxzcGFuIGNsYXNzPSJrIj5pZjwvc3Bhbj4gPHNwYW4gY2xhc3M9InAiPig8L3NwYW4+PHNwYW4gY2xhc3M9Im54Ij5jb21tZW50PC9zcGFuPjxzcGFuIGNsYXNzPSJwIj5bPC9zcGFuPjxzcGFuIGNsYXNzPSJzMSI+JiMzOTt3cDpjb21tZW50X2FwcHJvdmVkJiMzOTs8L3NwYW4+PHNwYW4gY2xhc3M9InAiPl08L3NwYW4+IDxzcGFuIGNsYXNzPSJvIj4rPC9zcGFuPiA8c3BhbiBjbGFzcz0iczEiPiYjMzk7JiMzOTs8L3NwYW4+IDxzcGFuIGNsYXNzPSJvIj49PTwvc3Bhbj4gPHNwYW4gY2xhc3M9InMyIj4mIzM0OzEmIzM0Ozwvc3Bhbj4gPHNwYW4gY2xhc3M9Im8iPiZhbXA7JmFtcDs8L3NwYW4+IDxzcGFuIGNsYXNzPSJueCI+Y29tbWVudDwvc3Bhbj48c3BhbiBjbGFzcz0icCI+Wzwvc3Bhbj48c3BhbiBjbGFzcz0iczEiPiYjMzk7d3A6Y29tbWVudF90eXBlJiMzOTs8L3NwYW4+PHNwYW4gY2xhc3M9InAiPl08L3NwYW4+IDxzcGFuIGNsYXNzPSJvIj4hPTwvc3Bhbj4gPHNwYW4gY2xhc3M9InMxIj4mIzM5O3BpbmdiYWNrJiMzOTs8L3NwYW4+PHNwYW4gY2xhc3M9InAiPik8L3NwYW4+IDxzcGFuIGNsYXNzPSJwIj57PC9zcGFuPgo8L3NwYW4+PC9zcGFuPjxzcGFuIGNsYXNzPSJsaW5lIj48c3BhbiBjbGFzcz0iY2wiPiAgICAgICAgICAgIDxzcGFuIGNsYXNzPSJrZCI+bGV0PC9zcGFuPiA8c3BhbiBjbGFzcz0ibngiPmNvbW1lbnRPYmo8L3NwYW4+IDxzcGFuIGNsYXNzPSJvIj49PC9zcGFuPiA8c3BhbiBjbGFzcz0icCI+ezwvc3Bhbj4KPC9zcGFuPjwvc3Bhbj48c3BhbiBjbGFzcz0ibGluZSI+PHNwYW4gY2xhc3M9ImNsIj4gICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9Im54Ij51c2VyX2lkPC9zcGFuPjxzcGFuIGNsYXNzPSJvIj46PC9zcGFuPiA8c3BhbiBjbGFzcz0ia2MiPm51bGw8L3NwYW4+PHNwYW4gY2xhc3M9InAiPiw8L3NwYW4+Cjwvc3Bhbj48L3NwYW4+PHNwYW4gY2xhc3M9ImxpbmUiPjxzcGFuIGNsYXNzPSJjbCI+ICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzPSJueCI+Y29tbWVudDwvc3Bhbj48c3BhbiBjbGFzcz0ibyI+Ojwvc3Bhbj4gPHNwYW4gY2xhc3M9Im54Ij5jb21tZW50PC9zcGFuPjxzcGFuIGNsYXNzPSJwIj5bPC9zcGFuPjxzcGFuIGNsYXNzPSJzMSI+JiMzOTt3cDpjb21tZW50X2NvbnRlbnQmIzM5Ozwvc3Bhbj48c3BhbiBjbGFzcz0icCI+XTwvc3Bhbj4gPHNwYW4gY2xhc3M9Im8iPj88L3NwYW4+IDxzcGFuIGNsYXNzPSJueCI+Y29tbWVudDwvc3Bhbj48c3BhbiBjbGFzcz0icCI+Wzwvc3Bhbj48c3BhbiBjbGFzcz0iczEiPiYjMzk7d3A6Y29tbWVudF9jb250ZW50JiMzOTs8L3NwYW4+PHNwYW4gY2xhc3M9InAiPl1bPC9zcGFuPjxzcGFuIGNsYXNzPSJtaSI+MDwvc3Bhbj48c3BhbiBjbGFzcz0icCI+XTwvc3Bhbj4gPHNwYW4gY2xhc3M9Im8iPjo8L3NwYW4+IDxzcGFuIGNsYXNzPSJzMSI+JiMzOTsmIzM5Ozwvc3Bhbj48c3BhbiBjbGFzcz0icCI+LDwvc3Bhbj4KPC9zcGFuPjwvc3Bhbj48c3BhbiBjbGFzcz0ibGluZSI+PHNwYW4gY2xhc3M9ImNsIj4gICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9Im54Ij5pbnNlcnRlZEF0PC9zcGFuPjxzcGFuIGNsYXNzPSJvIj46PC9zcGFuPiA8c3BhbiBjbGFzcz0ibngiPmNvbW1lbnQ8L3NwYW4+PHNwYW4gY2xhc3M9InAiPls8L3NwYW4+PHNwYW4gY2xhc3M9InMxIj4mIzM5O3dwOmNvbW1lbnRfZGF0ZSYjMzk7PC9zcGFuPjxzcGFuIGNsYXNzPSJwIj5dPC9zcGFuPiA8c3BhbiBjbGFzcz0ibyI+Pzwvc3Bhbj4gPHNwYW4gY2xhc3M9Im54Ij5jb21tZW50PC9zcGFuPjxzcGFuIGNsYXNzPSJwIj5bPC9zcGFuPjxzcGFuIGNsYXNzPSJzMSI+JiMzOTt3cDpjb21tZW50X2RhdGUmIzM5Ozwvc3Bhbj48c3BhbiBjbGFzcz0icCI+XVs8L3NwYW4+PHNwYW4gY2xhc3M9Im1pIj4wPC9zcGFuPjxzcGFuIGNsYXNzPSJwIj5dPC9zcGFuPiA8c3BhbiBjbGFzcz0ibyI+Ojwvc3Bhbj4gPHNwYW4gY2xhc3M9InMxIj4mIzM5OyYjMzk7PC9zcGFuPjxzcGFuIGNsYXNzPSJwIj4sPC9zcGFuPgo8L3NwYW4+PC9zcGFuPjxzcGFuIGNsYXNzPSJsaW5lIj48c3BhbiBjbGFzcz0iY2wiPiAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz0ibngiPmlwPC9zcGFuPjxzcGFuIGNsYXNzPSJvIj46PC9zcGFuPiA8c3BhbiBjbGFzcz0ibngiPmNvbW1lbnQ8L3NwYW4+PHNwYW4gY2xhc3M9InAiPls8L3NwYW4+PHNwYW4gY2xhc3M9InMxIj4mIzM5O3dwOmNvbW1lbnRfYXV0aG9yX0lQJiMzOTs8L3NwYW4+PHNwYW4gY2xhc3M9InAiPl08L3NwYW4+IDxzcGFuIGNsYXNzPSJvIj4/PC9zcGFuPiA8c3BhbiBjbGFzcz0ibngiPmNvbW1lbnQ8L3NwYW4+PHNwYW4gY2xhc3M9InAiPls8L3NwYW4+PHNwYW4gY2xhc3M9InMxIj4mIzM5O3dwOmNvbW1lbnRfYXV0aG9yX0lQJiMzOTs8L3NwYW4+PHNwYW4gY2xhc3M9InAiPl1bPC9zcGFuPjxzcGFuIGNsYXNzPSJtaSI+MDwvc3Bhbj48c3BhbiBjbGFzcz0icCI+XTwvc3Bhbj4gPHNwYW4gY2xhc3M9Im8iPjo8L3NwYW4+IDxzcGFuIGNsYXNzPSJzMSI+JiMzOTsmIzM5Ozwvc3Bhbj48c3BhbiBjbGFzcz0icCI+LDwvc3Bhbj4KPC9zcGFuPjwvc3Bhbj48c3BhbiBjbGFzcz0ibGluZSI+PHNwYW4gY2xhc3M9ImNsIj4gICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9Im54Ij5saW5rPC9zcGFuPjxzcGFuIGNsYXNzPSJvIj46PC9zcGFuPiA8c3BhbiBjbGFzcz0ibngiPmNvbW1lbnQ8L3NwYW4+PHNwYW4gY2xhc3M9InAiPls8L3NwYW4+PHNwYW4gY2xhc3M9InMxIj4mIzM5O3dwOmNvbW1lbnRfYXV0aG9yX3VybCYjMzk7PC9zcGFuPjxzcGFuIGNsYXNzPSJwIj5dPC9zcGFuPiA8c3BhbiBjbGFzcz0ibyI+Pzwvc3Bhbj4gPHNwYW4gY2xhc3M9Im54Ij5jb21tZW50PC9zcGFuPjxzcGFuIGNsYXNzPSJwIj5bPC9zcGFuPjxzcGFuIGNsYXNzPSJzMSI+JiMzOTt3cDpjb21tZW50X2F1dGhvcl91cmwmIzM5Ozwvc3Bhbj48c3BhbiBjbGFzcz0icCI+XVs8L3NwYW4+PHNwYW4gY2xhc3M9Im1pIj4wPC9zcGFuPjxzcGFuIGNsYXNzPSJwIj5dPC9zcGFuPiA8c3BhbiBjbGFzcz0ibyI+Ojwvc3Bhbj4gPHNwYW4gY2xhc3M9InMxIj4mIzM5OyYjMzk7PC9zcGFuPjxzcGFuIGNsYXNzPSJwIj4sPC9zcGFuPgo8L3NwYW4+PC9zcGFuPjxzcGFuIGNsYXNzPSJsaW5lIj48c3BhbiBjbGFzcz0iY2wiPiAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz0ibngiPm1haWw8L3NwYW4+PHNwYW4gY2xhc3M9Im8iPjo8L3NwYW4+IDxzcGFuIGNsYXNzPSJueCI+Y29tbWVudDwvc3Bhbj48c3BhbiBjbGFzcz0icCI+Wzwvc3Bhbj48c3BhbiBjbGFzcz0iczEiPiYjMzk7d3A6Y29tbWVudF9hdXRob3JfZW1haWwmIzM5Ozwvc3Bhbj48c3BhbiBjbGFzcz0icCI+XTwvc3Bhbj4gPHNwYW4gY2xhc3M9Im8iPj88L3NwYW4+IDxzcGFuIGNsYXNzPSJueCI+Y29tbWVudDwvc3Bhbj48c3BhbiBjbGFzcz0icCI+Wzwvc3Bhbj48c3BhbiBjbGFzcz0iczEiPiYjMzk7d3A6Y29tbWVudF9hdXRob3JfZW1haWwmIzM5Ozwvc3Bhbj48c3BhbiBjbGFzcz0icCI+XVs8L3NwYW4+PHNwYW4gY2xhc3M9Im1pIj4wPC9zcGFuPjxzcGFuIGNsYXNzPSJwIj5dPC9zcGFuPiA8c3BhbiBjbGFzcz0ibyI+Ojwvc3Bhbj4gPHNwYW4gY2xhc3M9InMxIj4mIzM5OyYjMzk7PC9zcGFuPjxzcGFuIGNsYXNzPSJwIj4sPC9zcGFuPgo8L3NwYW4+PC9zcGFuPjxzcGFuIGNsYXNzPSJsaW5lIj48c3BhbiBjbGFzcz0iY2wiPiAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz0ibngiPm5pY2s8L3NwYW4+PHNwYW4gY2xhc3M9Im8iPjo8L3NwYW4+IDxzcGFuIGNsYXNzPSJueCI+Y29tbWVudDwvc3Bhbj48c3BhbiBjbGFzcz0icCI+Wzwvc3Bhbj48c3BhbiBjbGFzcz0iczEiPiYjMzk7d3A6Y29tbWVudF9hdXRob3ImIzM5Ozwvc3Bhbj48c3BhbiBjbGFzcz0icCI+XTwvc3Bhbj4gPHNwYW4gY2xhc3M9Im8iPj88L3NwYW4+IDxzcGFuIGNsYXNzPSJueCI+Y29tbWVudDwvc3Bhbj48c3BhbiBjbGFzcz0icCI+Wzwvc3Bhbj48c3BhbiBjbGFzcz0iczEiPiYjMzk7d3A6Y29tbWVudF9hdXRob3ImIzM5Ozwvc3Bhbj48c3BhbiBjbGFzcz0icCI+XVs8L3NwYW4+PHNwYW4gY2xhc3M9Im1pIj4wPC9zcGFuPjxzcGFuIGNsYXNzPSJwIj5dPC9zcGFuPiA8c3BhbiBjbGFzcz0ibyI+Ojwvc3Bhbj4gPHNwYW4gY2xhc3M9InMxIj4mIzM5OyYjMzk7PC9zcGFuPjxzcGFuIGNsYXNzPSJwIj4sPC9zcGFuPgo8L3NwYW4+PC9zcGFuPjxzcGFuIGNsYXNzPSJsaW5lIj48c3BhbiBjbGFzcz0iY2wiPiAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz0ibngiPnJpZDwvc3Bhbj48c3BhbiBjbGFzcz0ibyI+Ojwvc3Bhbj4gPHNwYW4gY2xhc3M9Im54Ij5jb21tZW50PC9zcGFuPjxzcGFuIGNsYXNzPSJwIj5bPC9zcGFuPjxzcGFuIGNsYXNzPSJzMSI+JiMzOTt3cDpjb21tZW50X3BhcmVudCYjMzk7PC9zcGFuPjxzcGFuIGNsYXNzPSJwIj5dPC9zcGFuPiA8c3BhbiBjbGFzcz0ibyI+JmFtcDsmYW1wOzwvc3Bhbj4gPHNwYW4gY2xhc3M9Im54Ij5jb21tZW50PC9zcGFuPjxzcGFuIGNsYXNzPSJwIj5bPC9zcGFuPjxzcGFuIGNsYXNzPSJzMSI+JiMzOTt3cDpjb21tZW50X3BhcmVudCYjMzk7PC9zcGFuPjxzcGFuIGNsYXNzPSJwIj5dWzwvc3Bhbj48c3BhbiBjbGFzcz0ibWkiPjA8L3NwYW4+PHNwYW4gY2xhc3M9InAiPl08L3NwYW4+IDxzcGFuIGNsYXNzPSJvIj4hPT08L3NwYW4+IDxzcGFuIGNsYXNzPSJzMSI+JiMzOTswJiMzOTs8L3NwYW4+IDxzcGFuIGNsYXNzPSJvIj4/PC9zcGFuPiA8c3BhbiBjbGFzcz0ibmIiPnBhcnNlSW50PC9zcGFuPjxzcGFuIGNsYXNzPSJwIj4oPC9zcGFuPjxzcGFuIGNsYXNzPSJueCI+Y29tbWVudDwvc3Bhbj48c3BhbiBjbGFzcz0icCI+Wzwvc3Bhbj48c3BhbiBjbGFzcz0iczEiPiYjMzk7d3A6Y29tbWVudF9wYXJlbnQmIzM5Ozwvc3Bhbj48c3BhbiBjbGFzcz0icCI+XVs8L3NwYW4+PHNwYW4gY2xhc3M9Im1pIj4wPC9zcGFuPjxzcGFuIGNsYXNzPSJwIj5dKTwvc3Bhbj4gPHNwYW4gY2xhc3M9Im8iPjo8L3NwYW4+IDxzcGFuIGNsYXNzPSJrYyI+bnVsbDwvc3Bhbj48c3BhbiBjbGFzcz0icCI+LDwvc3Bhbj4KPC9zcGFuPjwvc3Bhbj48c3BhbiBjbGFzcz0ibGluZSI+PHNwYW4gY2xhc3M9ImNsIj4gICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9Im54Ij5waWQ8L3NwYW4+PHNwYW4gY2xhc3M9Im8iPjo8L3NwYW4+IDxzcGFuIGNsYXNzPSJrYyI+bnVsbDwvc3Bhbj48c3BhbiBjbGFzcz0icCI+LDwvc3Bhbj4KPC9zcGFuPjwvc3Bhbj48c3BhbiBjbGFzcz0ibGluZSI+PHNwYW4gY2xhc3M9ImNsIj4gICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9Im54Ij5zdGlja3k8L3NwYW4+PHNwYW4gY2xhc3M9Im8iPjo8L3NwYW4+IDxzcGFuIGNsYXNzPSJrYyI+bnVsbDwvc3Bhbj48c3BhbiBjbGFzcz0icCI+LDwvc3Bhbj4KPC9zcGFuPjwvc3Bhbj48c3BhbiBjbGFzcz0ibGluZSI+PHNwYW4gY2xhc3M9ImNsIj4gICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9Im54Ij5zdGF0dXM8L3NwYW4+PHNwYW4gY2xhc3M9Im8iPjo8L3NwYW4+IDxzcGFuIGNsYXNzPSJzMSI+JiMzOTthcHByb3ZlZCYjMzk7PC9zcGFuPjxzcGFuIGNsYXNzPSJwIj4sPC9zcGFuPgo8L3NwYW4+PC9zcGFuPjxzcGFuIGNsYXNzPSJsaW5lIj48c3BhbiBjbGFzcz0iY2wiPiAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz0ibngiPmxpa2U8L3NwYW4+PHNwYW4gY2xhc3M9Im8iPjo8L3NwYW4+IDxzcGFuIGNsYXNzPSJrYyI+bnVsbDwvc3Bhbj48c3BhbiBjbGFzcz0icCI+LDwvc3Bhbj4KPC9zcGFuPjwvc3Bhbj48c3BhbiBjbGFzcz0ibGluZSI+PHNwYW4gY2xhc3M9ImNsIj4gICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9Im54Ij51YTwvc3Bhbj48c3BhbiBjbGFzcz0ibyI+Ojwvc3Bhbj4gPHNwYW4gY2xhc3M9Im54Ij5jb21tZW50PC9zcGFuPjxzcGFuIGNsYXNzPSJwIj5bPC9zcGFuPjxzcGFuIGNsYXNzPSJzMSI+JiMzOTt3cDpjb21tZW50X2FnZW50JiMzOTs8L3NwYW4+PHNwYW4gY2xhc3M9InAiPl08L3NwYW4+IDxzcGFuIGNsYXNzPSJvIj4/PC9zcGFuPiA8c3BhbiBjbGFzcz0ibngiPmNvbW1lbnQ8L3NwYW4+PHNwYW4gY2xhc3M9InAiPls8L3NwYW4+PHNwYW4gY2xhc3M9InMxIj4mIzM5O3dwOmNvbW1lbnRfYWdlbnQmIzM5Ozwvc3Bhbj48c3BhbiBjbGFzcz0icCI+XVs8L3NwYW4+PHNwYW4gY2xhc3M9Im1pIj4wPC9zcGFuPjxzcGFuIGNsYXNzPSJwIj5dPC9zcGFuPiA8c3BhbiBjbGFzcz0ibyI+Ojwvc3Bhbj4gPHNwYW4gY2xhc3M9InMxIj4mIzM5OyYjMzk7PC9zcGFuPjxzcGFuIGNsYXNzPSJwIj4sPC9zcGFuPgo8L3NwYW4+PC9zcGFuPjxzcGFuIGNsYXNzPSJsaW5lIj48c3BhbiBjbGFzcz0iY2wiPiAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz0ibngiPnVybDwvc3Bhbj48c3BhbiBjbGFzcz0ibyI+Ojwvc3Bhbj4gPHNwYW4gY2xhc3M9InAiPig8L3NwYW4+PHNwYW4gY2xhc3M9Im54Ij5wb3N0PC9zcGFuPjxzcGFuIGNsYXNzPSJwIj5bPC9zcGFuPjxzcGFuIGNsYXNzPSJzMSI+JiMzOTtsaW5rJiMzOTs8L3NwYW4+PHNwYW4gY2xhc3M9InAiPl08L3NwYW4+IDxzcGFuIGNsYXNzPSJvIj4/PC9zcGFuPiA8c3BhbiBjbGFzcz0ibngiPnBvc3Q8L3NwYW4+PHNwYW4gY2xhc3M9InAiPls8L3NwYW4+PHNwYW4gY2xhc3M9InMxIj4mIzM5O2xpbmsmIzM5Ozwvc3Bhbj48c3BhbiBjbGFzcz0icCI+XVs8L3NwYW4+PHNwYW4gY2xhc3M9Im1pIj4wPC9zcGFuPjxzcGFuIGNsYXNzPSJwIj5dPC9zcGFuPiA8c3BhbiBjbGFzcz0ibyI+Ojwvc3Bhbj4gPHNwYW4gY2xhc3M9InMxIj4mIzM5OyYjMzk7PC9zcGFuPjxzcGFuIGNsYXNzPSJwIj4pLjwvc3Bhbj48c3BhbiBjbGFzcz0ibngiPnJlcGxhY2U8L3NwYW4+PHNwYW4gY2xhc3M9InAiPig8L3NwYW4+PHNwYW4gY2xhc3M9InMyIj4mIzM0O2h0dHBzOi8vY2VlamkubmV0JiMzNDs8L3NwYW4+PHNwYW4gY2xhc3M9InAiPiw8L3NwYW4+IDxzcGFuIGNsYXNzPSJzMiI+JiMzNDsmIzM0Ozwvc3Bhbj48c3BhbiBjbGFzcz0icCI+KSw8L3NwYW4+Cjwvc3Bhbj48L3NwYW4+PHNwYW4gY2xhc3M9ImxpbmUiPjxzcGFuIGNsYXNzPSJjbCI+ICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzPSJueCI+Y3JlYXRlZEF0PC9zcGFuPjxzcGFuIGNsYXNzPSJvIj46PC9zcGFuPiA8c3BhbiBjbGFzcz0ibngiPmNvbW1lbnQ8L3NwYW4+PHNwYW4gY2xhc3M9InAiPls8L3NwYW4+PHNwYW4gY2xhc3M9InMxIj4mIzM5O3dwOmNvbW1lbnRfZGF0ZSYjMzk7PC9zcGFuPjxzcGFuIGNsYXNzPSJwIj5dPC9zcGFuPiA8c3BhbiBjbGFzcz0ibyI+Pzwvc3Bhbj4gPHNwYW4gY2xhc3M9Im54Ij5jb21tZW50PC9zcGFuPjxzcGFuIGNsYXNzPSJwIj5bPC9zcGFuPjxzcGFuIGNsYXNzPSJzMSI+JiMzOTt3cDpjb21tZW50X2RhdGUmIzM5Ozwvc3Bhbj48c3BhbiBjbGFzcz0icCI+XVs8L3NwYW4+PHNwYW4gY2xhc3M9Im1pIj4wPC9zcGFuPjxzcGFuIGNsYXNzPSJwIj5dPC9zcGFuPiA8c3BhbiBjbGFzcz0ibyI+Ojwvc3Bhbj4gPHNwYW4gY2xhc3M9InMxIj4mIzM5OyYjMzk7PC9zcGFuPjxzcGFuIGNsYXNzPSJwIj4sPC9zcGFuPgo8L3NwYW4+PC9zcGFuPjxzcGFuIGNsYXNzPSJsaW5lIj48c3BhbiBjbGFzcz0iY2wiPiAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz0ibngiPnVwZGF0ZWRBdDwvc3Bhbj48c3BhbiBjbGFzcz0ibyI+Ojwvc3Bhbj4gPHNwYW4gY2xhc3M9Im54Ij5jb21tZW50PC9zcGFuPjxzcGFuIGNsYXNzPSJwIj5bPC9zcGFuPjxzcGFuIGNsYXNzPSJzMSI+JiMzOTt3cDpjb21tZW50X2RhdGUmIzM5Ozwvc3Bhbj48c3BhbiBjbGFzcz0icCI+XTwvc3Bhbj4gPHNwYW4gY2xhc3M9Im8iPj88L3NwYW4+IDxzcGFuIGNsYXNzPSJueCI+Y29tbWVudDwvc3Bhbj48c3BhbiBjbGFzcz0icCI+Wzwvc3Bhbj48c3BhbiBjbGFzcz0iczEiPiYjMzk7d3A6Y29tbWVudF9kYXRlJiMzOTs8L3NwYW4+PHNwYW4gY2xhc3M9InAiPl1bPC9zcGFuPjxzcGFuIGNsYXNzPSJtaSI+MDwvc3Bhbj48c3BhbiBjbGFzcz0icCI+XTwvc3Bhbj4gPHNwYW4gY2xhc3M9Im8iPjo8L3NwYW4+IDxzcGFuIGNsYXNzPSJzMSI+JiMzOTsmIzM5Ozwvc3Bhbj48c3BhbiBjbGFzcz0icCI+LDwvc3Bhbj4KPC9zcGFuPjwvc3Bhbj48c3BhbiBjbGFzcz0ibGluZSI+PHNwYW4gY2xhc3M9ImNsIj4gICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9Im54Ij5vYmplY3RJZDwvc3Bhbj48c3BhbiBjbGFzcz0ibyI+Ojwvc3Bhbj4gPHNwYW4gY2xhc3M9Im5iIj5wYXJzZUludDwvc3Bhbj48c3BhbiBjbGFzcz0icCI+KDwvc3Bhbj48c3BhbiBjbGFzcz0ibngiPmNvbW1lbnQ8L3NwYW4+PHNwYW4gY2xhc3M9InAiPls8L3NwYW4+PHNwYW4gY2xhc3M9InMxIj4mIzM5O3dwOmNvbW1lbnRfaWQmIzM5Ozwvc3Bhbj48c3BhbiBjbGFzcz0icCI+XVs8L3NwYW4+PHNwYW4gY2xhc3M9Im1pIj4wPC9zcGFuPjxzcGFuIGNsYXNzPSJwIj5dKTwvc3Bhbj4KPC9zcGFuPjwvc3Bhbj48c3BhbiBjbGFzcz0ibGluZSI+PHNwYW4gY2xhc3M9ImNsIj4gICAgICAgICAgICA8c3BhbiBjbGFzcz0icCI+fTs8L3NwYW4+Cjwvc3Bhbj48L3NwYW4+PHNwYW4gY2xhc3M9ImxpbmUiPjxzcGFuIGNsYXNzPSJjbCI+ICAgICAgICAgICAgPHNwYW4gY2xhc3M9Im54Ij5jb21tZW50c0FycmF5PC9zcGFuPjxzcGFuIGNsYXNzPSJwIj4uPC9zcGFuPjxzcGFuIGNsYXNzPSJueCI+cHVzaDwvc3Bhbj48c3BhbiBjbGFzcz0icCI+KDwvc3Bhbj48c3BhbiBjbGFzcz0ibngiPmNvbW1lbnRPYmo8L3NwYW4+PHNwYW4gY2xhc3M9InAiPik7PC9zcGFuPgo8L3NwYW4+PC9zcGFuPjxzcGFuIGNsYXNzPSJsaW5lIj48c3BhbiBjbGFzcz0iY2wiPiAgICAgICAgICAgIDxzcGFuIGNsYXNzPSJwIj59PC9zcGFuPiA8c3BhbiBjbGFzcz0iayI+ZWxzZTwvc3Bhbj4gPHNwYW4gY2xhc3M9InAiPns8L3NwYW4+Cjwvc3Bhbj48L3NwYW4+PHNwYW4gY2xhc3M9ImxpbmUiPjxzcGFuIGNsYXNzPSJjbCI+ICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzPSJueCI+Y29uc29sZTwvc3Bhbj48c3BhbiBjbGFzcz0icCI+Ljwvc3Bhbj48c3BhbiBjbGFzcz0ibngiPmxvZzwvc3Bhbj48c3BhbiBjbGFzcz0icCI+KDwvc3Bhbj48c3BhbiBjbGFzcz0iczIiPiYjMzQ76Lez6L+H5rKh5om55YeG55qE6K+E6K66JiMzNDs8L3NwYW4+PHNwYW4gY2xhc3M9InAiPik8L3NwYW4+Cjwvc3Bhbj48L3NwYW4+PHNwYW4gY2xhc3M9ImxpbmUiPjxzcGFuIGNsYXNzPSJjbCI+ICAgICAgICAgICAgPHNwYW4gY2xhc3M9InAiPn08L3NwYW4+Cjwvc3Bhbj48L3NwYW4+PHNwYW4gY2xhc3M9ImxpbmUiPjxzcGFuIGNsYXNzPSJjbCI+ICAgICAgICA8c3BhbiBjbGFzcz0icCI+fSk7PC9zcGFuPgo8L3NwYW4+PC9zcGFuPjxzcGFuIGNsYXNzPSJsaW5lIj48c3BhbiBjbGFzcz0iY2wiPiAgICAgIDxzcGFuIGNsYXNzPSJwIj59PC9zcGFuPgo8L3NwYW4+PC9zcGFuPjxzcGFuIGNsYXNzPSJsaW5lIj48c3BhbiBjbGFzcz0iY2wiPiAgICA8c3BhbiBjbGFzcz0icCI+fSk7PC9zcGFuPgo8L3NwYW4+PC9zcGFuPjxzcGFuIGNsYXNzPSJsaW5lIj48c3BhbiBjbGFzcz0iY2wiPgo8L3NwYW4+PC9zcGFuPjxzcGFuIGNsYXNzPSJsaW5lIj48c3BhbiBjbGFzcz0iY2wiPiAgICA8c3BhbiBjbGFzcz0iYzEiPi8vIOWwhuivhOiuuuaVsOe7hOi9rOaNouS4ukpTT07lubbkv53lrZjliLDmlofku7YKPC9zcGFuPjwvc3Bhbj48L3NwYW4+PHNwYW4gY2xhc3M9ImxpbmUiPjxzcGFuIGNsYXNzPSJjbCI+PHNwYW4gY2xhc3M9ImMxIj48L3NwYW4+ICAgIDxzcGFuIGNsYXNzPSJrciI+Y29uc3Q8L3NwYW4+IDxzcGFuIGNsYXNzPSJueCI+anNvbkNvbnRlbnQ8L3NwYW4+IDxzcGFuIGNsYXNzPSJvIj49PC9zcGFuPiA8c3BhbiBjbGFzcz0ibngiPkpTT048L3NwYW4+PHNwYW4gY2xhc3M9InAiPi48L3NwYW4+PHNwYW4gY2xhc3M9Im54Ij5zdHJpbmdpZnk8L3NwYW4+PHNwYW4gY2xhc3M9InAiPig8L3NwYW4+PHNwYW4gY2xhc3M9Im54Ij5jb21tZW50c0FycmF5PC9zcGFuPjxzcGFuIGNsYXNzPSJwIj4sPC9zcGFuPiA8c3BhbiBjbGFzcz0ia2MiPm51bGw8L3NwYW4+PHNwYW4gY2xhc3M9InAiPiw8L3NwYW4+IDxzcGFuIGNsYXNzPSJtaSI+Mjwvc3Bhbj48c3BhbiBjbGFzcz0icCI+KTs8L3NwYW4+Cjwvc3Bhbj48L3NwYW4+PHNwYW4gY2xhc3M9ImxpbmUiPjxzcGFuIGNsYXNzPSJjbCI+ICAgIDxzcGFuIGNsYXNzPSJueCI+ZnM8L3NwYW4+PHNwYW4gY2xhc3M9InAiPi48L3NwYW4+PHNwYW4gY2xhc3M9Im54Ij53cml0ZUZpbGU8L3NwYW4+PHNwYW4gY2xhc3M9InAiPig8L3NwYW4+PHNwYW4gY2xhc3M9InMxIj4mIzM5O2NvbW1lbnRzLmpzb24mIzM5Ozwvc3Bhbj48c3BhbiBjbGFzcz0icCI+LDwvc3Bhbj4gPHNwYW4gY2xhc3M9Im54Ij5qc29uQ29udGVudDwvc3Bhbj48c3BhbiBjbGFzcz0icCI+LDwvc3Bhbj4gPHNwYW4gY2xhc3M9InMxIj4mIzM5O3V0ZjgmIzM5Ozwvc3Bhbj48c3BhbiBjbGFzcz0icCI+LDwvc3Bhbj4gPHNwYW4gY2xhc3M9InAiPig8L3NwYW4+PHNwYW4gY2xhc3M9Im54Ij5lcnI8L3NwYW4+PHNwYW4gY2xhc3M9InAiPik8L3NwYW4+IDxzcGFuIGNsYXNzPSJwIj49Jmd0Ozwvc3Bhbj4gPHNwYW4gY2xhc3M9InAiPns8L3NwYW4+Cjwvc3Bhbj48L3NwYW4+PHNwYW4gY2xhc3M9ImxpbmUiPjxzcGFuIGNsYXNzPSJjbCI+ICAgICAgPHNwYW4gY2xhc3M9ImsiPmlmPC9zcGFuPiA8c3BhbiBjbGFzcz0icCI+KDwvc3Bhbj48c3BhbiBjbGFzcz0ibngiPmVycjwvc3Bhbj48c3BhbiBjbGFzcz0icCI+KTwvc3Bhbj4gPHNwYW4gY2xhc3M9InAiPns8L3NwYW4+Cjwvc3Bhbj48L3NwYW4+PHNwYW4gY2xhc3M9ImxpbmUiPjxzcGFuIGNsYXNzPSJjbCI+ICAgICAgICA8c3BhbiBjbGFzcz0ibngiPmNvbnNvbGU8L3NwYW4+PHNwYW4gY2xhc3M9InAiPi48L3NwYW4+PHNwYW4gY2xhc3M9Im54Ij5lcnJvcjwvc3Bhbj48c3BhbiBjbGFzcz0icCI+KDwvc3Bhbj48c3BhbiBjbGFzcz0iczEiPiYjMzk7RXJyb3Igd3JpdGluZyBKU09OIGZpbGU6JiMzOTs8L3NwYW4+PHNwYW4gY2xhc3M9InAiPiw8L3NwYW4+IDxzcGFuIGNsYXNzPSJueCI+ZXJyPC9zcGFuPjxzcGFuIGNsYXNzPSJwIj4pOzwvc3Bhbj4KPC9zcGFuPjwvc3Bhbj48c3BhbiBjbGFzcz0ibGluZSI+PHNwYW4gY2xhc3M9ImNsIj4gICAgICA8c3BhbiBjbGFzcz0icCI+fTwvc3Bhbj4gPHNwYW4gY2xhc3M9ImsiPmVsc2U8L3NwYW4+IDxzcGFuIGNsYXNzPSJwIj57PC9zcGFuPgo8L3NwYW4+PC9zcGFuPjxzcGFuIGNsYXNzPSJsaW5lIj48c3BhbiBjbGFzcz0iY2wiPiAgICAgICAgPHNwYW4gY2xhc3M9Im54Ij5jb25zb2xlPC9zcGFuPjxzcGFuIGNsYXNzPSJwIj4uPC9zcGFuPjxzcGFuIGNsYXNzPSJueCI+bG9nPC9zcGFuPjxzcGFuIGNsYXNzPSJwIj4oPC9zcGFuPjxzcGFuIGNsYXNzPSJzMSI+JiMzOTtDb21tZW50cyBoYXZlIGJlZW4gZXhwb3J0ZWQgdG8gY29tbWVudHMuanNvbiYjMzk7PC9zcGFuPjxzcGFuIGNsYXNzPSJwIj4pOzwvc3Bhbj4KPC9zcGFuPjwvc3Bhbj48c3BhbiBjbGFzcz0ibGluZSI+PHNwYW4gY2xhc3M9ImNsIj4gICAgICA8c3BhbiBjbGFzcz0icCI+fTwvc3Bhbj4KPC9zcGFuPjwvc3Bhbj48c3BhbiBjbGFzcz0ibGluZSI+PHNwYW4gY2xhc3M9ImNsIj4gICAgPHNwYW4gY2xhc3M9InAiPn0pOzwvc3Bhbj4KPC9zcGFuPjwvc3Bhbj48c3BhbiBjbGFzcz0ibGluZSI+PHNwYW4gY2xhc3M9ImNsIj4gIDxzcGFuIGNsYXNzPSJwIj59KTs8L3NwYW4+Cjwvc3Bhbj48L3NwYW4+PHNwYW4gY2xhc3M9ImxpbmUiPjxzcGFuIGNsYXNzPSJjbCI+PHNwYW4gY2xhc3M9InAiPn0pOzwvc3Bhbj4KPC9zcGFuPjwvc3Bhbj48L2NvZGU+PC9wcmU+PC9kaXY+最后,在 Waline 的管理后台,选择导入导出功能:
先选择导出,打开导出后的 JSON 文件,格式类似如下:
{
"__version": "1.31.13",
"type": "waline",
"version": 1,
"time": 1711963109933,
"tables": [
"Comment",
"Counter",
"Users"
],
"data": {
"Comment": [] // 替换此处的数据
}
}
把 Comment 处的内容替换为脚本生成的内容,即可进行导入。注意,导入会丢失所有现有数据,
如原来有数据的,需要手工合并或提前备份。
实现代码高亮的黑暗模式自适应
Hugo
自带的代码高亮功能,一般只能设置一个样式。如果你的主题支持自适应黑暗模式,可能会导致
在切换黑暗模式开关的时候,代码高亮不能跟着切换。
最新版本的 Hugo 使用 chroma
进行语法高亮。
首先,确定你想要使用的样式。比如我们在亮色模式下使用 monokailight
样式,在暗色模式下使用 onedark
样式。
接下来,创建对应的 .css
文件供 Hugo 使用:
hugo gen chromastyles --style=monokailight > syntax_light.css
hugo gen chromastyles --style=onedark > syntax_dark.css
将这两个文件移动到你的主题 assets
文件夹中(例如 ./themes/paper/assets/
)。
接下来,确保我们的 Hugo 主题加载这些样式。
为此,编辑 ./themes/paper/layout/partials/head.html
(或者负责在你的主题中添加头部部分的文件),添加以下内容:
{{ $syntax_dark_css := resources.Get "syntax_dark.css" | minify }}
{{ $syntax_light_css := resources.Get "syntax_light.css" | minify }}
<link rel="preload stylesheet" as="style" href="{{ $syntax_dark_css.Permalink }}" />
<link rel="preload stylesheet" as="style" href="{{ $syntax_light_css.Permalink }}" />
这将在使用 Hugo 构建站点时使 .css
文件可用,并允许页面加载它们。
顺序很重要:默认情况下,应用最后引用的文件中的样式,因为它会覆盖之前的样式!
为了让 Hugo 使用我们的 .css
文件作为 chroma
样式,我们需要在 Hugo 的 hugo.toml
文件中明确指定以下内容:
[markup.highlight]
noClasses = false
确保一切正常后,检查你的代码是否已使用设置的样式进行高亮显示。当你切换到暗色模式时,目前应该还不会有任何变化。
然后你需要写动态切换应用于代码片段的 .css
文件的脚本。
在 ./themes/papers/partials/header.html
(或者负责在你的 Hugo 主题中切换模式的文件)中,找到切换模式的代码。
每个主题都不一样,你需要自己去找。
例如它可能类似这样:
const setDark = (isDark) => {
metaTheme.setAttribute('content', isDark ? '#000' : lightBg);
htmlClass[isDark ? 'add' : 'remove']('dark');
localStorage.setItem('dark', isDark);
};
我们在这个函数调用中添加一个方法:
const setDark = (isDark) => {
metaTheme.setAttribute('content', isDark ? '#000' : lightBg);
htmlClass[isDark ? 'add' : 'remove']('dark');
localStorage.setItem('dark', isDark);
setSyntaxDark(isDark); // 添加的方法
};
定义 setSyntaxDark
函数和一个额外的辅助函数:
function getStyleSheet(file_name) {
for (var i = 0; i < document.styleSheets.length; i++) {
var sheet = document.styleSheets[i];
if (sheet.href.includes(file_name)) {
return sheet;
}
}
}
function setSyntaxDark(isDark) {
let sheet_light = getStyleSheet("syntax_light")
let sheet_dark = getStyleSheet("syntax_dark")
sheet_light.disabled = isDark ? true : false
sheet_dark.disabled = isDark ? false : true
}
随后,使用 Hugo 重新构建你的网站,确保一切正常。
简繁中文使用相同的评论数据,并自动翻译
简繁语言本就没什么大差距,不需要分开不同的评论。在 Waline 中,
你可以通过移除 url 中的语言部分使得不同语言版本共享同一个评论。
修改你的 waline
初始化代码:
init({
el: "#xxx",
serverURL: "xxx",
locale: locale['{{ .Page.Lang }}'], // 注意这一行
dark: 'html.dark', // 可实现黑暗模式自适应
// path 中使用正则移除简繁部分的网址
path: window.location.pathname.replace(/zh-(hans|hant)\//, '')
});
同时我们需要定义一个 locale
变量实现 waline
的本地化,例如:
const locale = {
"zh-hans": {
nick: '昵称',
nickError: '昵称不能少于3个字符',
mail: '邮箱',
mailError: '请填写正确的邮件地址',
link: '网址',
optional: '可选',
placeholder: '欢迎评论',
sofa: '来发评论吧~',
submit: '提交',
like: '喜欢',
cancelLike: '取消喜欢',
reply: '回复',
cancelReply: '取消回复',
comment: '评论',
refresh: '刷新',
more: '加载更多...',
preview: '预览',
emoji: '表情',
uploadImage: '上传图片',
seconds: '秒前',
minutes: '分钟前',
hours: '小时前',
days: '天前',
now: '刚刚',
uploading: '正在上传',
login: '登录',
logout: '退出',
admin: '博主',
sticky: '置顶',
word: '字',
wordHint: '评论字数应在 $0 到 $1 字之间!\n当前字数:$2',
anonymous: '匿名',
level0: '潜水',
level1: '冒泡',
level2: '吐槽',
level3: '活跃',
level4: '话痨',
level5: '传说',
gif: '表情包',
gifSearchPlaceholder: '搜索表情包',
profile: '个人资料',
approved: '通过',
waiting: '待审核',
spam: '垃圾',
unsticky: '取消置顶',
oldest: '按倒序',
latest: '按正序',
hottest: '按热度',
reactionTitle: '你认为这篇文章怎么样?'
},
"zh-hant": {
nick: '暱稱',
nickError: '暱稱不能少於3個字元',
mail: '郵箱',
mailError: '請填寫正確的郵件地址',
link: '網址',
optional: '可選',
placeholder: '歡迎評論',
sofa: '來發評論吧~',
submit: '提交',
like: '喜歡',
cancelLike: '取消喜歡',
reply: '回覆',
cancelReply: '取消回覆',
comment: '評論',
refresh: '重新整理',
more: '載入更多...',
preview: '預覽',
emoji: '表情',
uploadImage: '上傳圖片',
seconds: '秒前',
minutes: '分鐘前',
hours: '小時前',
days: '天前',
now: '剛剛',
uploading: '正在上傳',
login: '登入',
logout: '退出',
admin: '博主',
sticky: '置頂',
word: '字',
wordHint: '評論字數應在 $0 到 $1 字之間!\n當前字數:$2',
anonymous: '匿名',
level0: '潛水',
level1: '冒泡',
level2: '吐槽',
level3: '活躍',
level4: '話癆',
level5: '傳說',
gif: '表情包',
gifSearchPlaceholder: '搜尋表情包',
profile: '個人資料',
approved: '透過',
waiting: '待稽核',
spam: '垃圾',
unsticky: '取消置頂',
oldest: '按倒序',
latest: '按正序',
hottest: '按熱度',
reactionTitle: '你認為這篇文章怎麼樣?'
}
}
我的网站虽然是个小站,但依然有些人在订阅。为了避免他们走丢,
我需要能够保持原有的 Feed 订阅地址。如果你使用的 OSS 或其他
无服务器(Serverless)的方式进行托管,你可能需要在你的 CDN
页面使用 rewrite 来实现。
因为我是有服务器的,我使用 nginx
来实现了这一点。
Hugo 默认将 RSS/Atom 发布在根目录下的 index.xml
或类似的文件中。
找到这个文件的地址,然后你可以进行重定向:
location ~ ^/blog/feed/?$ {
rewrite ^ /index.xml last;
}
上面的这段 nginx
配置可以把 /blog/feed 和 /blog/feed/ 指向 index.xml
,
同时保持对外访问地址不变。
提示
一个小技巧:在你的 nginx
访问日志中搜索 feed,你可能可以看到有多少人订阅了你的网站。
除了解决 Feed 网址问题外,如果你和我一样通过 nginx 部署,建议你还要做两件事:
- 迁移早期定期访问 access log,查看是否有旧网址出现了 404 错误,这不利于 SEO;
- 在 nginx 配置文件里,设置正确的 404 页面。
图片管理
强力建议你采用下列的文件结构来存放图片:
posts /
article /
index.md
1.jpg
这种方式管理方便,而且在 Markdown 编辑器中不会出现图片看不到的问题。
然后,你可以通过 render-image
hook 实现图片的 CDN 化,提升全球访问速度。
具体可进行相关搜索。
题外话:我帮你整理了包括 AI 写作、绘画、视频(自媒体制作)零门槛 AI 课程 + 国内可直接顺畅使用的软件。想让自己快速用上 AI 工具来降本增效,辅助工作和生活?限时报名。