引言
在将博客主题从 Hueman 切换到 Twenty Twenty-Five 后,我遇到了一个棘手的问题:日历区块在多语言环境下显示异常。在英文页面(如 https://www.shuijingwanwq.com/en/)中,日历上某些日期(如 22 号)明明没有发布过英文文章,却依然显示链接,点击后返回 404 错误页面。而中文页面则正常显示对应语言的链接。经过 1 天的排查与修复,最终通过 PHP 代码片段解决了问题,现将过程与方案整理如下。
原始问题:英文页面日历链接缺少语言前缀
问题描述:在英文页面 https://www.shuijingwanwq.com/en/ 中,日历上每个日期点击后跳转的链接仍然是 https://www.shuijingwanwq.com/2026/06/08/ 的形式,而不是预期的 https://www.shuijingwanwq.com/en/2026/06/08/。这导致用户点击日历链接时,因缺少 /en/ 前缀而跳转至错误页面,相当于语言切换了。
初步解决方案:替换 URL 前缀(及新问题)
初步思路
最初,我尝试通过替换 URL 前缀来解决原始问题,参考了博客文章《修复日历在侧边栏”占不满”的问题:WordPress 2025 主题日历样式优化》中的解决方案。
新问题出现
尽管 URL 前缀问题暂时解决,但新的问题随之而来:在英文页面(https://www.shuijingwanwq.com/en/)中,日历上 22 号显示蓝色链接,但该日期未发布英文文章,点击后返回 404。这表明仅替换 URL 前缀无法解决数据过滤问题——日历仍显示所有语言的文章日期,而非仅当前语言的文章。
方案对比:完全重写 vs. 基于 HTML 修改
针对上述问题,我对比了两种解决方案:
方案一:完全重写日历区块(架构清晰,但易引入 Bug)
思路:废弃 WordPress 原生日历的 get_calendar() 函数,通过 WP_Query 查询当前语言的文章数据,手动生成 HTML 结构。
优点:
- 架构清晰,从数据层直接控制日历显示,避免依赖原生函数的潜在问题。
- 性能更好,因为直接查询数据而不需要解析 HTML。
缺点:日历逻辑复杂(跨月、周首日对齐等),完全重写易引入排版 Bug,且需确保与主题 CSS 兼容,维护成本高。
方案二:基于原生 HTML 结构修改(保留原生日历逻辑,风险低)
思路:让 WordPress 先生成原生日历 HTML,再通过 DOMDocument 解析并修正:
- 查询当前语言当月有文章的日期集合。
- 遍历日历表格中的每个
<td>,核对日期是否有文章:
- 有文章但无链接:创建
<a>标签并添加正确语言前缀。 - 无文章但有链接:移除
<a>标签,保留纯文本。
- 重建月份导航链接,确保基于当前语言数据生成。
优点:保留原生日历的排版逻辑,避免引入新 Bug,兼容主题 CSS,风险低。
缺点:需额外解析 HTML,性能稍受影响,但可接受。
最终选择与效果展示
考虑到时间有限且主题切换已耗时 4 天,我选择方案二,通过 WPCode 插件添加 PHP 代码片段实现修复。以下是修复前后的效果对比:
修复前:英文页面 22 号错误显示链接
在英文页面 https://www.shuijingwanwq.com/en/ 中,22 号显示蓝色链接,但该日期未发布英文文章,点击后返回 404。

https://www.shuijingwanwq.com/en/ 中,22 号显示蓝色链接
修复后:英文页面 22 号无链接
修复后,英文页面 22 号仅显示纯文本,无链接,符合预期。

插件兼容性考虑:Calendas 插件
期间曾考虑使用 Calendas 插件(明确支持 Polylang),但提示 PHP 版本不兼容(需 8.2 或更高),升级 PHP 需额外时间,因此放弃。

代码实现(基于方案二)
以下是通过 WPCode 插件添加的 PHP 代码片段,核心逻辑为:
- 查询当前语言当月有文章的日期。
- 使用
DOMDocument遍历日历<td>,修正链接或移除错误链接。 - 重建导航链接,确保语言前缀正确。
<?php
function fix_polylang_calendar_dom_patch_v2( $block_content, $block ) {
if ( 'core/calendar' !== $block['blockName'] || ! function_exists( 'pll_current_language' ) ) {
return $block_content;
}
global $wp_locale;
$current_lang = pll_current_language( 'slug' );
$default_lang = pll_default_language( 'slug' );
$home_url = home_url( '/' );
// 修正 URL 前缀
$fix_url = function( $url ) use ( $current_lang, $default_lang, $home_url ) {
if ( $current_lang === $default_lang ) return $url;
if ( strpos( $url, $home_url . $current_lang . '/' ) === false ) {
$relative = str_replace( $home_url, '', $url );
return $home_url . $current_lang . '/' . $relative;
}
return $url;
};
// 查询当前语言当月有文章的日期
$year = get_query_var( 'year' ) ?: (int) gmdate( 'Y' );
$month = get_query_var( 'monthnum' ) ?: (int) gmdate( 'm' );
$args = [
'post_type' => 'post', 'post_status' => 'publish', 'posts_per_page' => -1,
'fields' => 'ids', 'lang' => $current_lang, 'year' => $year, 'monthnum' => $month
];
$query = new WP_Query( $args );
$valid_days = array_map( 'intval', array_column( $query->posts, 'ID' ) );
// 解析原生 HTML 并修正
$dom = new DOMDocument();
libxml_use_internal_errors( true );
$dom->loadHTML( '<?xml encoding="utf-8" ?>' . $block_content, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD );
$xpath = new DOMXPath( $dom );
// 遍历每个日期单元格
$td_nodes = $xpath->query( '//table[contains(@class, "wp-calendar-table")]/tbody/tr/td' );
foreach ( $td_nodes as $td ) {
if ( $td->getAttribute('class') === 'pad' ) continue;
$day_text = trim( $td->nodeValue );
if ( ! preg_match('/^\d{1,2}$/', $day_text) ) continue;
$day_num = (int) $day_text;
$has_post = in_array( $day_num, $valid_days );
$existing_a = $xpath->query( './a', $td )->item(0);
if ( $has_post ) {
if ( $existing_a ) $existing_a->setAttribute( 'href', $fix_url( get_day_link( $year, $month, $day_num ) ) );
else {
$td->nodeValue = '';
$new_a = $dom->createElement('a', $day_num);
$new_a->setAttribute( 'href', $fix_url( get_day_link( $year, $month, $day_num ) ) );
$td->appendChild( $new_a );
}
} else {
if ( $existing_a ) $td->removeChild( $existing_a );
}
}
// 重建导航链接(略,逻辑与正文一致)
// 输出修正后的 HTML
$modified_html = $dom->saveHTML();
return trim( preg_replace( '/<!DOCTYPE[^>]*>|<html[^>]*>|<\/html>|<body[^>]*>|<\/body>/', '', $modified_html ) );
}
add_filter( 'render_block_core/calendar', 'fix_polylang_calendar_dom_patch_v2', 20, 2 );总结
在 WordPress 中处理历史遗留问题时,需权衡“完全重写”与“后置修补”的利弊。本次修复通过保留原生日历逻辑,仅修改数据与链接,成功解决了多语言兼容问题,避免了新 Bug 的引入。对于类似问题,建议优先考虑基于原生结构的修改方案,确保稳定性和兼容性。

发表回复