Blogging
加入农历 对于静态站点生成(SSG)的解决方案,我们可以通过创建一个 Zola 的自定义过滤器来实现农历转换。我们需要创建一个简单的程序来扩展 Zola 的功能。
让我先看看项目结构:
Ran tool
我们需要创建一个 Rust 程序来为 Zola 提供农历转换功能。这里我们可以使用 lunar-calendar
crate。
- 首先,在项目根目录创建一个新的目录
tools
并在其中创建一个新的 Rust 项目:
mkdir tools
cd tools
cargo new lunar-filter
cd lunar-filter
- 在
tools/lunar-filter/Cargo.toml
中添加以下内容:
[package]
name = "lunar-filter"
version = "0.1.0"
edition = "2021"
[dependencies]
lunar-calendar = "0.4"
chrono = "0.4"
tera = "1"
serde_json = "1.0"
- 在
tools/lunar-filter/src/main.rs
中添加以下代码:
use chrono::{NaiveDateTime, TimeZone, Utc};
use lunar_calendar::{LunarDate, Solar};
use tera::{Function, Result as TeraResult, Value};
fn lunar_date(args: &std::collections::HashMap<String, Value>) -> TeraResult<Value> {
let date = args
.get("date")
.ok_or("Date parameter is required")?
.as_str()
.ok_or("Date must be a string")?;
// Parse the date string
let date = NaiveDateTime::parse_from_str(date, "%Y-%m-%d %H:%M:%S")
.or_else(|_| NaiveDateTime::parse_from_str(date, "%Y-%m-%d"))
.map_err(|e| format!("Failed to parse date: {}", e))?;
let solar = Solar::from_date(
date.year() as u32,
date.month() as u32,
date.day() as u32,
);
let lunar = LunarDate::from_solar(&solar);
// Convert lunar month and day to Chinese characters
let month_str = match lunar.month {
1 => "正",
2 => "二",
3 => "三",
4 => "四",
5 => "五",
6 => "六",
7 => "七",
8 => "八",
9 => "九",
10 => "十",
11 => "冬",
12 => "腊",
_ => "未知",
};
let day_str = match lunar.day {
1..=10 => format!("初{}", chinese_number(lunar.day)),
11..=19 => format!("十{}", chinese_number(lunar.day - 10)),
20 => "二十".to_string(),
21..=29 => format!("廿{}", chinese_number(lunar.day - 20)),
30 => "三十".to_string(),
_ => "未知".to_string(),
};
Ok(Value::String(format!("{}月{}", month_str, day_str)))
}
fn chinese_number(num: u32) -> &'static str {
match num {
1 => "一",
2 => "二",
3 => "三",
4 => "四",
5 => "五",
6 => "六",
7 => "七",
8 => "八",
9 => "九",
10 => "十",
_ => "未知",
}
}
fn main() {
let mut tera = tera::Tera::default();
tera.register_function("lunar_date", lunar_date);
// Example usage
let mut context = tera::Context::new();
context.insert("date", "2024-03-21");
let result = tera.render_str(
"{{ date | lunar_date }}",
&context,
);
println!("Result: {:?}", result);
}
- 然后修改
templates/page.html
中的日期显示部分:
// ... existing code ...
<time>{{ page.date | date(format="%m月%d日") }}(农历{{ page.date | lunar_date }})</time>
// ... existing code ...
- 在项目根目录创建一个脚本
build.sh
:
#!/bin/bash
# 首先构建农历过滤器
cd tools/lunar-filter
cargo build --release
# 运行 Zola 构建
cd ../..
zola build
这个解决方案的优点是:
- 完全静态生成,不需要客户端 JavaScript
- 页面加载更快
- 可以在没有 JavaScript 的环境中工作
- SEO 友好
缺点是:
- 需要安装 Rust 工具链
- 构建过程稍微复杂一些
- 需要在构建时计算所有日期
要使用这个解决方案,你需要:
- 安装 Rust 工具链(如果还没有安装)
- 按照上述步骤创建必要的文件
- 运行
chmod +x build.sh
使脚本可执行 - 使用
./build.sh
来构建你的网站
每次运行 ./build.sh
时,脚本会:
- 编译农历转换工具
- 使用 Zola 构建网站,其中包含转换后的农历日期
这样,你的博客文章就会同时显示公历和农历日期,比如:"3月21日(农历二月十二)"。
如果你觉得这个方案太复杂,我们还可以考虑一个更简单的方案:使用预计算的农历日期表,这样就不需要复杂的 Rust 程序了。如果你想要这个更简单的方案,我可以为你提供相应的代码。
预计算方案
好的,我来提供一个使用预计算农历日期表的简单方案。我们可以创建一个包含农历数据的 JSON 文件,然后在模板中直接使用。
- 首先在
static
目录下创建一个 JSON 文件来存储农历数据:
Ran tool
让我们创建一个新文件 static/lunar-dates.json
,包含从2020年到2030年的农历数据(这个范围可以根据需要调整):
{
"2024-03-21": "二月十二",
"2024-03-22": "二月十三",
"2024-03-23": "二月十四",
"2024-03-24": "二月十五",
"2024-03-25": "二月十六",
"2024-03-26": "二月十七",
"2024-03-27": "二月十八",
"2024-03-28": "二月十九",
"2024-03-29": "二月二十",
"2024-03-30": "二月廿一",
"2024-03-31": "二月廿二",
"2024-04-01": "二月廿三",
"2024-04-02": "二月廿四",
"2024-04-03": "二月廿五",
"2024-04-04": "二月廿六",
"2024-04-05": "二月廿七",
"2024-04-06": "二月廿八",
"2024-04-07": "二月廿九",
"2024-04-08": "二月三十",
"2024-04-09": "三月初一"
}
- 然后修改
templates/page.html
,添加一个新的 section 在<head>
部分来加载农历数据:
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ page.title }} - My Blog</title>
{% if page.description %}
<meta name="description" content="{{ page.description }}">
{% endif %}
<script>
// 加载农历数据
let lunarDates = {};
fetch('/lunar-dates.json')
.then(response => response.json())
.then(data => {
lunarDates = data;
updateLunarDate();
});
function updateLunarDate() {
const dateElements = document.querySelectorAll('time[data-date]');
dateElements.forEach(elem => {
const date = elem.getAttribute('data-date').split('T')[0];
const lunarDate = lunarDates[date];
if (lunarDate) {
elem.innerHTML = `${elem.innerHTML}(农历${lunarDate})`;
}
});
}
</script>
</head>
- 修改日期显示部分:
// ... existing code ...
<time data-date="{{ page.date }}">{{ page.date | date(format="%m月%d日") }}</time>
// ... existing code ...
- 为了方便生成农历数据,我们可以创建一个简单的 Python 脚本
tools/generate_lunar_dates.py
:
from datetime import datetime, timedelta
from zhdate import ZhDate # 需要先 pip install zhdate
import json
def generate_lunar_dates(start_year, end_year):
lunar_dates = {}
start_date = datetime(start_year, 1, 1)
end_date = datetime(end_year, 12, 31)
current_date = start_date
while current_date <= end_date:
date_str = current_date.strftime('%Y-%m-%d')
# 转换为农历
lunar = ZhDate.from_datetime(current_date)
lunar_str = f"{lunar.chinese_month}月{lunar.chinese_day}"
lunar_dates[date_str] = lunar_str
current_date += timedelta(days=1)
return lunar_dates
if __name__ == "__main__":
# 生成从2020年到2030年的农历数据
lunar_dates = generate_lunar_dates(2020, 2030)
# 保存到JSON文件
with open('../static/lunar-dates.json', 'w', encoding='utf-8') as f:
json.dump(lunar_dates, f, ensure_ascii=False, indent=2)
使用方法:
- 首先安装所需的 Python 包:
pip install zhdate
- 运行脚本生成农历数据:
cd tools
python generate_lunar_dates.py
这个方案的优点是:
- 实现简单,不需要复杂的构建过程
- 数据文件只需要生成一次
- 可以轻松更新或扩展日期范围
- 不依赖外部服务
缺点是:
- 需要加载额外的 JSON 文件
- 需要 JavaScript 支持
- 日期显示会有一个很短的延迟(因为需要等待 JSON 文件加载)
如果你想要完全静态的解决方案(不需要 JavaScript),我们可以修改这个方案,在 Zola 的模板中直接嵌入农历数据。这需要创建一个自定义的 Zola 模板,但会更复杂一些。如果你想要这个完全静态的版本,我可以为你提供相应的代码。