Compare commits

..

No commits in common. "main" and "v1.4.0" have entirely different histories.
main ... v1.4.0

35 changed files with 1354 additions and 7527 deletions

1
.gitignore vendored
View file

@ -1 +0,0 @@
assets/.DS_Store

View file

@ -101,14 +101,15 @@
- **不转换**: 指定不需要转换的内容区域 - **不转换**: 指定不需要转换的内容区域


### 短代码支持 ### 短代码支持
- 语言切换器(老短代码,平铺/下拉由“展示形式”设置控制) ```php
``` // 显示语言切换器
[wp-chinese-converter] [wpcc_language_switcher]
```
- 不转换内容(编辑器无法保留注释时作为稳健占位): // 显示转换状态
``` [wpcc_conversion_status]
[wpcc_nc]不转换的内容[/wpcc_nc]
[wpcs_nc]不转换的内容[/wpcs_nc] // 不转换指定内容
[wpcc_no_conversion]不转换的内容[/wpcc_no_conversion]
``` ```


### PHP 函数调用 ### PHP 函数调用
@ -146,25 +147,6 @@ add_action('wpcc_before_conversion', 'before_convert');
add_action('wpcc_after_conversion', 'after_convert'); add_action('wpcc_after_conversion', 'after_convert');
``` ```


## 链接与重写规则行为说明(重要)

- 链接格式与固定链接的关系
- 当 WordPress 启用了固定链接且“URL 链接格式”选择了“前缀/后缀”时:生成 /zh-xx/ 样式链接(或 …/zh-xx/
- 当 WordPress 未启用固定链接(或环境未正确应用 rewrite 规则)时:自动回退为 ?variant=zh-xx避免 404
- 首页根级变体访问行为
- 访问 /zh/ 或 /zh-reset/作为“哨兵”回到不转换首页https://example.com/),并设置 zh 偏好以覆盖浏览器/Cookie 策略
- 访问 /zh-xx/(如 /zh-tw/、/zh-hk/):统一 302 跳转到首页,避免首页重复内容与 404
- zh 哨兵与 rel="nofollow"
- 仅当“不转换”链接为覆盖策略而携带 zh 哨兵URL 含 /zh/ 或 ?variant=zh时自动添加 rel="nofollow"
- 当不需要哨兵(直接是原始 URL不加 nofollow避免影响站内权重传递

## 兼容层与新版内核

- 新内核WPCC_Main / WPCC_Config 等)统一管理重写、变体解析、链接构造、注入脚本等;
- 为了兼容历史主题/插件/短代码调用,保留了 includes/wpcc-core.php 中的“老函数”(如 wpcc_link_conversion、set_wpcc_langs_urls、wpcc_output_navi、短代码 [wp-chinese-converter] 等);
- 老函数的行为已与新内核对齐(例如固定链接未启用时自动降级为 ?variant=xx确保前后端一致
- 建议新项目优先使用区块与新内核能力;对生态依赖的老接口,后续会以 @deprecated 标注与迁移指引逐步过渡。

## 故障排除 ## 故障排除


### 常见问题 ### 常见问题
@ -179,7 +161,7 @@ A: 请检查选择的转换引擎和目标语言是否正确。OpenCC 引擎适
A: 请检查 WordPress 缓存配置,或尝试清除转换缓存:`设置 > WP Chinese Converter > 高级设置 > 清除缓存` A: 请检查 WordPress 缓存配置,或尝试清除转换缓存:`设置 > WP Chinese Converter > 高级设置 > 清除缓存`


**Q: URL 重写不工作?** **Q: URL 重写不工作?**
A: 请确保 WordPress 固定链接已启用并“保存更改”一次;服务器需正确支持 rewrite如 Nginx/Apache 规则)。插件在未启用固定链接时会自动回退为 ?variant=xx。 A: 请确保服务器的 mod_rewrite 模块已启用,并重新保存固定链接设置。


## 性能优化建议 ## 性能优化建议


@ -213,13 +195,6 @@ A: 请确保 WordPress 固定链接已启用并“保存更改”一次;服务


## 版本历史 ## 版本历史


### v1.4.x (2025年稳定版补丁)
- 统一“展示形式”数值语义1=平铺0=下拉;修复短代码展示反向问题
- 单站模式:当 WordPress 未启用固定链接时,链接自动降级为 ?variant=xx避免 /zh-xx/ 404
- 首页根级变体:/zh/ 与 /zh-xx/ 统一 302 到首页;/zh/ 同时设置 zh 偏好覆盖浏览器/Cookie 策略
- 前端切换器:仅在携带 zh 哨兵时为“不转换”链接添加 rel="nofollow",并与新窗口 noopener noreferrer 兼容
- 统一文件命名风格:核心类重命名为 class-wpcc-*.php保留兼容层但与新内核对齐

### v1.3.0 (2025年重构版本) ### v1.3.0 (2025年重构版本)
- 完全重写插件架构,采用现代化 OOP 设计 - 完全重写插件架构,采用现代化 OOP 设计
- 新增 Gutenberg 区块支持 - 新增 Gutenberg 区块支持

BIN
assets/.DS_Store vendored

Binary file not shown.

View file

@ -317,30 +317,6 @@
max-width: 120px; max-width: 120px;
} }


/* Disabled state styling for network-controlled or engine-restricted options */
.wpcc-disabled,
.wpcc-disabled * {
opacity: 0.6 !important;
cursor: not-allowed !important;
pointer-events: none !important;
}

.wpcc-disabled .wpcc-slider {
background-color: #e0e0e0 !important;
}

.wpcc-disabled .wpcc-slider:before {
background-color: #f5f5f5 !important;
}

input[disabled] + .wpcc-slider {
background-color: #e0e0e0 !important;
}

input[disabled] + .wpcc-slider:before {
background-color: #f5f5f5 !important;
}

@media (max-width: 768px) { @media (max-width: 768px) {
.wpcc-sync-tabs { .wpcc-sync-tabs {
flex-direction: column; flex-direction: column;
@ -580,193 +556,3 @@ input[disabled] + .wpcc-slider:before {
text-decoration: none; text-decoration: none;
} }


/* 网络管理提示样式 */
.wpcc-network-notice {
margin: 10px 0 20px 0;
padding: 12px 15px;
background: #fff3cd;
border-left: 4px solid #ffc107;
border-radius: 4px;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04);
font-size: 13px;
line-height: 1.4;
}

.wpcc-network-notice p {
margin: 0;
color: #856404;
}

.wpcc-network-notice p:not(:last-child) {
margin-bottom: 8px;
}

.wpcc-network-notice .dashicons {
font-size: 14px;
width: 14px;
height: 14px;
vertical-align: text-top;
margin-right: 4px;
color: #856404;
}

.wpcc-network-notice a {
color: #856404;
text-decoration: underline;
}

.wpcc-network-notice a:hover {
color: #533f03;
text-decoration: none;
}

.wpcc-network-notice strong {
font-weight: 600;
color: #533f03;
}

.wpcc-network-notice em {
font-style: normal;
opacity: 0.9;
}

/* 响应式设计 */
@media (max-width: 768px) {
.wpcc-network-notice {
margin: 15px 0;
padding: 10px 12px;
font-size: 12px;
}
.wpcc-network-notice .dashicons {
font-size: 12px;
width: 12px;
height: 12px;
}
}

/* 深色模式支持 */
@media (prefers-color-scheme: dark) {
.wpcc-network-notice {
background: #2c2611;
border-left-color: #d4a017;
}
.wpcc-network-notice p,
.wpcc-network-notice .dashicons {
color: #f4d03f;
}
.wpcc-network-notice strong {
color: #f7dc6f;
}
.wpcc-network-notice a {
color: #f4d03f;
}
.wpcc-network-notice a:hover {
color: #f7dc6f;
}
}/* Disab
led 字段样式 */
.wpcc-switch.wpcc-disabled {
opacity: 0.6;
pointer-events: none;
cursor: not-allowed;
}

.wpcc-switch.wpcc-disabled .wpcc-slider {
background-color: #ddd;
cursor: not-allowed;
}

.wpcc-switch.wpcc-disabled .wpcc-switch-label {
color: #999;
cursor: not-allowed;
}

.wpcc-radio input[disabled] + .wpcc-radio-label {
color: #999;
cursor: not-allowed;
}

input[disabled].wpcc-input,
select[disabled].wpcc-select,
textarea[disabled].wpcc-textarea {
background-color: #f5f5f5;
color: #999;
cursor: not-allowed;
opacity: 0.6;
}



/* 网络控
制禁用状态样式 */
.wpcc-switch.wpcc-disabled {
opacity: 0.6;
pointer-events: none !important;
cursor: not-allowed !important;
}

.wpcc-switch.wpcc-disabled input[type="checkbox"] {
pointer-events: none !important;
cursor: not-allowed !important;
}

.wpcc-switch.wpcc-disabled .wpcc-slider {
background-color: #ddd !important;
cursor: not-allowed !important;
pointer-events: none !important;
}

.wpcc-switch.wpcc-disabled .wpcc-switch-label {
color: #999 !important;
cursor: not-allowed !important;
pointer-events: none !important;
}

.wpcc-switch.wpcc-disabled input[type="checkbox"]:checked + .wpcc-slider {
background-color: #ccc !important;
}

.wpcc-switch.wpcc-disabled input[type="checkbox"]:checked + .wpcc-slider:before {
background-color: #f0f0f0 !important;
}

/* 网络控制区域样式 */
.wpcc-network-controlled {
opacity: 0.6;
pointer-events: none !important;
}

.wpcc-network-controlled select,
.wpcc-network-controlled input,
.wpcc-network-controlled textarea {
background-color: #f5f5f5 !important;
color: #999 !important;
cursor: not-allowed !important;
pointer-events: none !important;
}

/* 网络控制提示样式 */
.wpcc-network-notice {
margin: 10px 0;
padding: 10px 12px;
background: #fff3cd;
border-left: 4px solid #ffc107;
border-radius: 4px;
}

.wpcc-network-notice p {
margin: 0;
color: #856404;
}

.wpcc-network-notice .dashicons {
font-size: 16px;
width: 16px;
height: 16px;
vertical-align: text-top;
}

View file

@ -24,12 +24,11 @@


.wpcc-lang-item .wpcc-link { .wpcc-lang-item .wpcc-link {
display: inline-block; display: inline-block;
padding: 4px 10px; padding: 4px 8px;
background: #f0f0f0; background: #f0f0f0;
color: #333; color: #333;
text-decoration: none; text-decoration: none;
border: 1px solid #ddd; border: 1px solid #ddd;
border-radius: 5px;
font-size: 12px; font-size: 12px;
} }



View file

@ -26,62 +26,46 @@


.wpcc-lang-item .wpcc-link { .wpcc-lang-item .wpcc-link {
display: inline-block; display: inline-block;
padding: 4px 10px; padding: 4px 8px;
background: #f0f0f0; background: #0073aa;
color: #333; color: #fff;
text-decoration: none; text-decoration: none;
border: 1px solid #ddd; border: 1px solid #0073aa;
border-radius: 5px; font-size: 13px;
font-size: 12px;
transition: background-color 0.2s ease, border-color 0.2s ease;
} }


.wpcc-lang-item .wpcc-link:hover { .wpcc-lang-item .wpcc-link:hover {
background: #e0e0e0;
border-color: #ccc;
}

.wpcc-lang-item.wpcc-current .wpcc-link {
background: #0073aa;
color: #fff;
border-color: #0073aa;
}

.wpcc-lang-item.wpcc-current .wpcc-link:hover {
background: #005a87; background: #005a87;
border-color: #005a87; border-color: #005a87;
} }


.wpcc-lang-item.wpcc-current .wpcc-link {
background: #00a32a;
border-color: #00a32a;
}

.wpcc-lang-item.wpcc-no-conversion .wpcc-link { .wpcc-lang-item.wpcc-no-conversion .wpcc-link {
background: #f9f9f9; background: #666;
color: #666; border-color: #666;
border-color: #ddd;
} }


.wpcc-lang-item.wpcc-no-conversion .wpcc-link:hover { .wpcc-lang-item.wpcc-no-conversion .wpcc-link:hover {
background: #f0f0f0; background: #555;
border-color: #ccc; border-color: #555;
} }


.wpcc-dropdown-switcher { .wpcc-dropdown-switcher {
padding: 6px 8px; padding: 6px 8px;
border: 1px solid #ddd; border: 1px solid #ddd;
background: #fff; background: #fff;
font-size: 12px; font-size: 13px;
cursor: pointer; cursor: pointer;
min-width: 150px; min-width: 150px;
border-radius: 5px;
transition: border-color 0.2s ease;
}

.wpcc-dropdown-switcher:hover {
border-color: #bbb;
} }


.wpcc-dropdown-switcher:focus { .wpcc-dropdown-switcher:focus {
outline: 2px solid #0073aa; outline: 1px solid #0073aa;
outline-offset: 0; outline-offset: 1px;
border-color: #0073aa;
} }


.wpcc-conversion-status { .wpcc-conversion-status {
@ -92,12 +76,11 @@
.wpcc-status-container { .wpcc-status-container {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 6px; gap: 4px;
} }


.wpcc-status-text { .wpcc-status-text {
font-size: 12px; font-size: 13px;
color: #333;
} }


.wpcc-conversion-status.wpcc-format-badge .wpcc-status-text { .wpcc-conversion-status.wpcc-format-badge .wpcc-status-text {
@ -110,7 +93,7 @@


.wpcc-conversion-status.wpcc-format-text .wpcc-status-text { .wpcc-conversion-status.wpcc-format-text .wpcc-status-text {
color: #333; color: #333;
font-size: 12px; font-size: 13px;
} }


.wpcc-conversion-status.wpcc-format-minimal .wpcc-status-text { .wpcc-conversion-status.wpcc-format-minimal .wpcc-status-text {
@ -118,30 +101,18 @@
color: #666; color: #666;
} }


/* 不转换区块在前台不显示任何视觉样式 */
.wpcc-no-conversion-wrapper { .wpcc-no-conversion-wrapper {
/* 仅保留功能性标记,无视觉样式 */ position: relative;
}

.wpcc-no-conversion-header {
display: none; /* 前台隐藏标题 */
}

.wpcc-label {
display: none; /* 前台隐藏标签 */
} }


.wpcc-no-conversion-content { .wpcc-no-conversion-content {
/* 内容正常显示,无额外样式 */ position: relative;
} }


@media (max-width: 768px) { @media (max-width: 768px) {
.wpcc-horizontal-switcher { .wpcc-horizontal-switcher {
display: flex;
align-items: stretch;
flex-direction: column; flex-direction: column;
flex-direction: row; align-items: stretch;
flex-wrap: nowrap;
gap: 6px; gap: 6px;
} }
@ -186,14 +157,13 @@
min-width: 180px; min-width: 180px;
} }


/* 前台所有不转换区块的边框变体都不显示 */ .wpcc-no-conversion-wrapper.wpcc-border-solid {
.wpcc-no-conversion-wrapper.wpcc-border-solid, border-style: solid;
.wpcc-no-conversion-wrapper.wpcc-border-dashed, }

.wpcc-no-conversion-wrapper.wpcc-border-none { .wpcc-no-conversion-wrapper.wpcc-border-none {
border: none; border: none;
background: transparent; background: transparent;
padding: 0;
margin: 0;
} }


.wpcc-conversion-status.wpcc-clickable { .wpcc-conversion-status.wpcc-clickable {

View file

@ -86,10 +86,8 @@ function renderLanguageSwitcher(


if (showNoConversion) { if (showNoConversion) {
const isActive = !currentLang || currentLang === ""; const isActive = !currentLang || currentLang === "";
const noConvUrl = getNoConversionUrl();
const relForNoConv = buildRelAttribute(noConvUrl, openInNewWindow);
html += `<span class="wpcc-lang-item wpcc-no-conversion ${isActive ? "wpcc-current" : ""}"> html += `<span class="wpcc-lang-item wpcc-no-conversion ${isActive ? "wpcc-current" : ""}">
<a href="${noConvUrl}" class="wpcc-link"${target}${relForNoConv}>${noConversionText}</a> <a href="${getLanguageUrl("")}" class="wpcc-link"${target}${rel}>${noConversionText}</a>
</span>`; </span>`;
} }


@ -263,7 +261,7 @@ function getCurrentLanguage() {
return variant; return variant;
} }


const pathMatch = window.location.pathname.match(/\/(zh(?:-[a-z]+)?)\//i); const pathMatch = window.location.pathname.match(/\/(zh-[a-z]+)\//);
if (pathMatch) { if (pathMatch) {
return pathMatch[1]; return pathMatch[1];
} }
@ -286,121 +284,28 @@ function getLanguageUrl(langCode) {
// 优先使用服务端注入的 URL 映射(如可用) // 优先使用服务端注入的 URL 映射(如可用)
try { try {
if (typeof wpcc_langs_urls === "object" && wpcc_langs_urls) { if (typeof wpcc_langs_urls === "object" && wpcc_langs_urls) {
if (langCode && wpcc_langs_urls[langCode]) {
return wpcc_langs_urls[langCode];
}
if (!langCode || langCode === "") { if (!langCode || langCode === "") {
if (typeof wpcc_noconversion_url === "string" && wpcc_noconversion_url) { if (typeof wpcc_noconversion_url === "string" && wpcc_noconversion_url) {
return wpcc_noconversion_url; return wpcc_noconversion_url;
} }
} else if (wpcc_langs_urls[langCode]) {
return wpcc_langs_urls[langCode];
} }
} }
} catch (e) { } catch (e) {
// 忽略映射读取错误,回退到查询参数模式 // 忽略映射读取错误,回退到查询参数模式
} }


// 回退:使用查询参数模式(并避免重复:/zh-xx/ 与 ?variant=zh-xx 共存) // 回退:使用查询参数模式
const currentUrl = new URL(window.location.href); const currentUrl = new URL(window.location.href);
const pathMatch = currentUrl.pathname.match(/^\/(zh(?:-[a-z]+)?)(\b|\/)/i);

if (langCode) { if (langCode) {
// 若当前已处于同一变体路径,则仅移除冗余的 variant 参数,返回干净的漂亮链接
if (pathMatch && pathMatch[1].toLowerCase() === langCode.toLowerCase()) {
currentUrl.searchParams.delete("variant");
return currentUrl.toString();
}
currentUrl.searchParams.set("variant", langCode); currentUrl.searchParams.set("variant", langCode);
return currentUrl.toString();
} else { } else {
// 不转换:尽量去除语言段与冗余参数
currentUrl.searchParams.delete("variant"); currentUrl.searchParams.delete("variant");
if (pathMatch) {
// 去掉开头的 /zh-xx 段,回到原始路径
currentUrl.pathname = currentUrl.pathname.replace(/^\/(zh-[a-z]+)(\/?)/i, "/");
} }

return currentUrl.toString(); return currentUrl.toString();
} }
}

// 构建“不转换”链接:在变体页面时注入 zh 哨兵以覆盖浏览器/Cookie 策略
function getNoConversionUrl() {
// 基于服务端注入的原始 URL 获取基础地址
let baseUrl = (typeof wpcc_noconversion_url === "string" && wpcc_noconversion_url)
? wpcc_noconversion_url
: (function() {
const u = new URL(window.location.href);
// 去除 variant 查询与路径前缀
u.searchParams.delete("variant");
u.pathname = u.pathname.replace(/^\/(zh(?:-[a-z]+)?)(\/?)/i, "/");
return u.toString();
})();

const currentLang = getCurrentLanguage();
if (!currentLang) {
return baseUrl; // 已经是不转换
}

// 检测站点使用的链接风格:查询参数 / 后缀 / 前缀
const detectStyle = () => {
try {
if (typeof wpcc_langs_urls === 'object' && wpcc_langs_urls) {
for (const k in wpcc_langs_urls) {
if (!Object.prototype.hasOwnProperty.call(wpcc_langs_urls, k)) continue;
const href = String(wpcc_langs_urls[k] || '');
if (/([?&])variant=zh-[a-z]+/i.test(href)) return 'query';
const u = new URL(href, window.location.origin);
const path = u.pathname;
// 前缀: /zh-xx/... 后缀: .../zh-xx/
if (/^\/(zh(?:-[a-z]+)?)(\/|$)/i.test(path)) return 'prefix';
if (/(\/)(zh(?:-[a-z]+)?)\/?$/i.test(path)) return 'suffix';
}
}
} catch (e) {}
return 'query';
};

const style = detectStyle();
try {
const u = new URL(baseUrl);
if (style === 'query') {
u.searchParams.set('variant', 'zh');
return u.toString();
}
if (style === 'suffix') {
// 确保末尾带 /zh/
u.pathname = u.pathname.replace(/\/$/, '') + '/zh/';
return u.toString();
}
// prefix
// 将路径改为 /zh/ + 原路径
u.pathname = u.pathname.replace(/^\/+/, '/');
// 避免在已是 /zh/ 的情况下重复添加 /zh
if (!/^\/(zh)(\/|$)/i.test(u.pathname)) {
u.pathname = '/zh' + (u.pathname.startsWith('/') ? '' : '/') + u.pathname;
}
// 合并多余斜杠
u.pathname = u.pathname.replace(/\/{2,}/g, '/');
return u.toString();
} catch (e) {
// 回退:总是可用的查询参数
try {
const u2 = new URL(baseUrl);
u2.searchParams.set('variant', 'zh');
return u2.toString();
} catch (_e) {
return baseUrl;
}
}
}

// 根据链接是否包含 zh 哨兵决定 rel且兼容新窗口的 noopener noreferrer
function buildRelAttribute(url, openInNewWindow) {
const hasZhSentinel = /\/(?:zh)(?:\b|\/)/i.test(url) || /(?:[?&])variant=zh(?:&|$)/i.test(url);
let relParts = [];
if (hasZhSentinel) relParts.push('nofollow');
if (openInNewWindow) relParts.push('noopener', 'noreferrer');
return relParts.length ? ` rel="${Array.from(new Set(relParts)).join(' ')}"` : '';
}


function handleLanguageChange(langCode, selectElement) { function handleLanguageChange(langCode, selectElement) {
const url = getLanguageUrl(langCode); const url = getLanguageUrl(langCode);

View file

@ -1,44 +0,0 @@
(function() {
if (typeof window.tinymce === 'undefined') {
return;
}

tinymce.PluginManager.add('wpcc_nc', function(editor, url) {
function insertShortcode() {
var sel = editor.selection ? editor.selection.getContent({ format: 'text' }) : '';
var before = '[wpcc_nc]';
var after = '[/wpcc_nc]';
var content = before + (sel || '') + after;
editor.insertContent(content);
}

// TinyMCE 5+ API
if (editor.ui && editor.ui.registry && editor.ui.registry.addButton) {
editor.ui.registry.addButton('wpcc_nc', {
text: 'wpcc_NC',
tooltip: '插入不转换包裹 [wpcc_nc]...[/wpcc_nc]',
onAction: insertShortcode
});
editor.ui.registry.addMenuItem('wpcc_nc', {
text: '插入不转换包裹',
onAction: insertShortcode
});
} else if (editor.addButton) {
// TinyMCE 4 API (WordPress Classic Editor)
editor.addButton('wpcc_nc', {
text: 'wpcc_NC',
tooltip: '插入不转换包裹 [wpcc_nc]...[/wpcc_nc]',
onclick: insertShortcode
});
}

return {
getMetadata: function () {
return {
name: 'WPCC No-Conversion Shortcode Helper',
url: 'https://wpcc.net'
};
}
};
});
})();

View file

@ -1,27 +0,0 @@
WP Chinese Converter (WPCC) — 更新日志 / Changelog

Version 1.5.0 (2025-10-11)
- 修复 单站模式下“展示形式”反向的问题:统一语义为 1=平铺、0=下拉,短代码 [wp-chinese-converter] 与区块表现一致
- 修复 单站设置保存问题:确保语言模块、全页面转换、展示形式等选项在单站模式可正确保存
- 优化 zh 哨兵与 rel="nofollow" 规则:仅当“不转换”链接为覆盖策略而携带 zh 哨兵(/zh/ 或 ?variant=zh时添加 nofollow新窗口时追加 noopener noreferrer
- 优化 前端语言切换器:在变体页为“不转换”注入 zh 哨兵;缓存破除(基于文件 mtime 的版本号)
- 链接与重写 统一策略:
* WordPress 未启用固定链接时,所有语言链接自动回退为 ?variant=xx避免 /zh-xx/ 404
* 首页根级变体(/zh/、/zh-xx/)统一 302 到首页;/zh/ 同时设置 zh 偏好
- 内核一致性老函数includes/wpcc-core.php与新内核WPCC_Main策略对齐固定链接检测、链接降级
- 架构与清理:统一核心类文件命名为 class-wpcc-*.php移除旧 url fix 入口;保留兼容层并与新内核对齐
- 文档:更新 README补充链接/重写行为说明与兼容策略

Version 1.4.x (2025)
- 统一 SEO 行为:在不需要 zh 哨兵时不添加 nofollow避免权重传递受影响
- 改善 区块与脚本的加载与配置注入,优化多语言切换体验
- 多站点:网络设置与子站策略协调,增强稳定性

Version 1.3.0 (2025 重构)
- 完整重写插件架构为 OOPWPCC_Main / WPCC_Config / 模块管理器等
- 新增 Gutenberg 区块(语言切换器、转换状态、不转换区块)
- 优化 性能与内存占用OpenCC 与 MediaWiki 双引擎;转换缓存
- 增强 SEOcanonical、hreflang、schema/meta 转换)与 REST API 支持

历史说明
- 本项目分叉自 WP Chinese Conversion2025 年起由文派团队WPCC.NET进行长期维护与重构。

View file

@ -12,10 +12,6 @@ class wpcc_Admin
private string $url = ""; private string $url = "";
private $admin_lang = false; private $admin_lang = false;


private array $network_controlled_options = [];
private bool $is_network_enforced = false;
private array $network_options = [];

// 移除旧式构造函数,只使用 __construct // 移除旧式构造函数,只使用 __construct


private function clean_invalid_langs(): void private function clean_invalid_langs(): void
@ -47,10 +43,6 @@ class wpcc_Admin
} }
$this->langs = &$wpcc_langs; $this->langs = &$wpcc_langs;
$this->options = $wpcc_options; $this->options = $wpcc_options;
// 初始化网络控制
$this->init_network_control();
if (empty($this->options)) { if (empty($this->options)) {
$this->options = [ $this->options = [
// 语言与标签 // 语言与标签
@ -74,7 +66,7 @@ class wpcc_Admin
"wpcc_use_cookie_variant" => 1, "wpcc_use_cookie_variant" => 1,


// 不转换 // 不转换
"wpcc_no_conversion_tag" => "", "wpcc_no_conversion_tag" => "pre,code,pre.wp-block-code,pre.wp-block-preformatted,script,noscript,style,kbd,samp",
"wpcc_no_conversion_ja" => 0, "wpcc_no_conversion_ja" => 0,
"wpcc_no_conversion_qtag" => 0, "wpcc_no_conversion_qtag" => 0,


@ -93,7 +85,6 @@ class wpcc_Admin
"wpcc_enable_schema_conversion" => 1, "wpcc_enable_schema_conversion" => 1,
"wpcc_enable_meta_conversion" => 1, "wpcc_enable_meta_conversion" => 1,



// 其他 // 其他
"wpcc_flag_option" => 1, "wpcc_flag_option" => 1,
"wpcc_trackback_plugin_author" => 0, "wpcc_trackback_plugin_author" => 0,
@ -106,7 +97,6 @@ class wpcc_Admin
} }


$this->clean_invalid_langs(); $this->clean_invalid_langs();

update_wpcc_option("wpcc_options", $this->options); update_wpcc_option("wpcc_options", $this->options);


$this->base = plugin_basename(dirname(__FILE__)) . "/"; $this->base = plugin_basename(dirname(__FILE__)) . "/";
@ -118,29 +108,24 @@ class wpcc_Admin
$this->url = admin_url("options-general.php?page=" . $page_slug); $this->url = admin_url("options-general.php?page=" . $page_slug);
} }


if (is_multisite() && is_network_admin() && wpcc_mobile_exist("network")) { if (is_multisite() && wpcc_mobile_exist("network")) {
// 网络管理界面菜单(实际网络设置页面由 WPCC_Network_Settings 提供,此处保持入口一致)
add_submenu_page( add_submenu_page(
"settings.php", "settings.php",
"文派译词", "WP Chinese Converter",
"文派译词", "WP Chinese Converter",
"manage_network_options", "manage_network_options",
$page_slug, $page_slug,
[&$this, "display_options"], [&$this, "display_options"],
); );
} elseif (!is_network_admin()) { } else {
// 子站点菜单:当未启用强制网络管理时显示
$network_enforced = is_multisite() ? (int) get_site_option('wpcc_network_enforce', 0) : 0;
if (!$network_enforced) {
add_options_page( add_options_page(
"文派译词", "WP Chinese Converter",
"译词", "WP Chinese Converter",
"manage_options", "manage_options",
$page_slug, $page_slug,
[&$this, "display_options"], [&$this, "display_options"],
); );
} }
}


wp_enqueue_script("jquery"); wp_enqueue_script("jquery");


@ -252,7 +237,7 @@ class wpcc_Admin
dirname(dirname(__DIR__)) . "/wp-chinese-converter.php", dirname(dirname(__DIR__)) . "/wp-chinese-converter.php",
) . "assets/admin/admin.css", ) . "assets/admin/admin.css",
[], [],
wpcc_VERSION, "2.0.0",
); );


wp_enqueue_script( wp_enqueue_script(
@ -261,7 +246,7 @@ class wpcc_Admin
dirname(dirname(__DIR__)) . "/wp-chinese-converter.php", dirname(dirname(__DIR__)) . "/wp-chinese-converter.php",
) . "assets/admin/admin.js", ) . "assets/admin/admin.js",
["jquery"], ["jquery"],
wpcc_VERSION, "2.0.0",
true, true,
); );


@ -416,14 +401,12 @@ class wpcc_Admin


// 只更新表单中实际提交的字段 // 只更新表单中实际提交的字段


// 确保关键字段存在默认值,防止未定义键产生警告 // 语言设置
if (!is_array($options)) { $options = []; } if (
if (!isset($options['wpcc_use_permalink'])) { $options['wpcc_use_permalink'] = 0; } isset($_POST["wpcco_variant_zh-cn"]) ||
if (!isset($this->options['wpcc_use_permalink'])) { $this->options['wpcc_use_permalink'] = 0; } isset($_POST["wpcco_variant_zh-tw"]) ||
if (!isset($options['wpcc_used_langs']) || !is_array($options['wpcc_used_langs'])) { $options['wpcc_used_langs'] = []; } isset($_POST["wpcco_variant_zh-hk"])
if (!isset($this->options['wpcc_used_langs']) || !is_array($this->options['wpcc_used_langs'])) { $this->options['wpcc_used_langs'] = []; } ) {

// 语言设置:总是基于提交的 wpcco_variant_* 重建已启用语言,避免部分站点无法保存
$langs = []; $langs = [];
if (is_array($this->langs)) { if (is_array($this->langs)) {
foreach ($this->langs as $key => $value) { foreach ($this->langs as $key => $value) {
@ -433,27 +416,21 @@ class wpcc_Admin
} }
} }
$options["wpcc_used_langs"] = $langs; $options["wpcc_used_langs"] = $langs;
}


// 复选框字段未选中时不会在POST中出现 // 复选框字段未选中时不会在POST中出现
$checkbox_fields = [ $checkbox_fields = [
// 同时兼容两种命名(历史与现用)
"wpcco_use_fullpage_conversion" => "wpcc_use_fullpage_conversion", "wpcco_use_fullpage_conversion" => "wpcc_use_fullpage_conversion",
"wpcc_use_fullpage_conversion" => "wpcc_use_fullpage_conversion",
"wpcco_use_sitemap" => "wpcco_use_sitemap", "wpcco_use_sitemap" => "wpcco_use_sitemap",
// 支持两种命名历史兼容wpcco_* 与 wpcc_*
"wpcco_auto_language_recong" => "wpcc_auto_language_recong", "wpcco_auto_language_recong" => "wpcc_auto_language_recong",
"wpcc_auto_language_recong" => "wpcc_auto_language_recong",
"wpcc_enable_cache_addon" => "wpcc_enable_cache_addon", "wpcc_enable_cache_addon" => "wpcc_enable_cache_addon",
"wpcc_enable_network_module" => "wpcc_enable_network_module", "wpcc_enable_network_module" => "wpcc_enable_network_module",
"wpcc_enable_hreflang_tags" => "wpcc_enable_hreflang_tags", "wpcc_enable_hreflang_tags" => "wpcc_enable_hreflang_tags",
"wpcc_enable_hreflang_x_default" => "wpcc_enable_hreflang_x_default",
"wpcc_enable_schema_conversion" => "wpcc_enable_schema_conversion", "wpcc_enable_schema_conversion" => "wpcc_enable_schema_conversion",
"wpcc_enable_meta_conversion" => "wpcc_enable_meta_conversion", "wpcc_enable_meta_conversion" => "wpcc_enable_meta_conversion",
"wpcc_show_more_langs" => "wpcc_show_more_langs", "wpcc_show_more_langs" => "wpcc_show_more_langs",
"wpcc_no_conversion_qtag" => "wpcc_no_conversion_qtag", "wpcc_no_conversion_qtag" => "wpcc_no_conversion_qtag",
"wpcc_no_conversion_ja" => "wpcc_no_conversion_ja",
"wpcc_enable_post_conversion" => "wpcc_enable_post_conversion", "wpcc_enable_post_conversion" => "wpcc_enable_post_conversion",
"wpcc_first_visit_default" => "wpcc_first_visit_default",
]; ];


foreach ($checkbox_fields as $post_field => $option_field) { foreach ($checkbox_fields as $post_field => $option_field) {
@ -463,19 +440,13 @@ class wpcc_Admin
// 文本字段和下拉选择框 // 文本字段和下拉选择框
$text_fields = [ $text_fields = [
"wpcc_translate_type" => "wpcc_translate_type", "wpcc_translate_type" => "wpcc_translate_type",
// 同时兼容两种命名历史与现用wpcco_no_conversion_tag 与 wpcc_no_conversion_tag
"wpcco_no_conversion_tag" => "wpcc_no_conversion_tag", "wpcco_no_conversion_tag" => "wpcc_no_conversion_tag",
"wpcc_no_conversion_tag" => "wpcc_no_conversion_tag",
"wpcco_no_conversion_tip" => "nctip", "wpcco_no_conversion_tip" => "nctip",
"wpcc_engine" => "wpcc_engine", "wpcc_engine" => "wpcc_engine",
"wpcco_search_conversion" => "wpcc_search_conversion", "wpcco_search_conversion" => "wpcc_search_conversion",
// 支持两种命名历史兼容wpcco_* 与 wpcc_*
"wpcco_browser_redirect" => "wpcc_browser_redirect", "wpcco_browser_redirect" => "wpcc_browser_redirect",
"wpcc_browser_redirect" => "wpcc_browser_redirect",
"wpcco_use_cookie_variant" => "wpcc_use_cookie_variant", "wpcco_use_cookie_variant" => "wpcc_use_cookie_variant",
"wpcc_use_cookie_variant" => "wpcc_use_cookie_variant",
"wpcco_use_permalink" => "wpcc_use_permalink", "wpcco_use_permalink" => "wpcc_use_permalink",
"wpcc_use_permalink" => "wpcc_use_permalink",
"wpcco_sitemap_post_type" => "wpcco_sitemap_post_type", "wpcco_sitemap_post_type" => "wpcco_sitemap_post_type",
"wpcc_hreflang_x_default" => "wpcc_hreflang_x_default", "wpcc_hreflang_x_default" => "wpcc_hreflang_x_default",
"wpcc_post_conversion_target" => "wpcc_post_conversion_target", "wpcc_post_conversion_target" => "wpcc_post_conversion_target",
@ -483,7 +454,7 @@ class wpcc_Admin


foreach ($text_fields as $post_field => $option_field) { foreach ($text_fields as $post_field => $option_field) {
if (isset($_POST[$post_field])) { if (isset($_POST[$post_field])) {
if ($post_field === "wpcco_no_conversion_tag" || $post_field === "wpcc_no_conversion_tag") { if ($post_field === "wpcco_no_conversion_tag") {
$options[$option_field] = trim( $options[$option_field] = trim(
$_POST[$post_field], $_POST[$post_field],
" \t\n\r\0\x0B,|", " \t\n\r\0\x0B,|",
@ -510,11 +481,6 @@ class wpcc_Admin
} }
} }


// 兼容旧字段:当表单提交了 wpcc_flag_option 时,将其映射为 wpcc_translate_type
if (isset($_POST['wpcc_flag_option'])) {
$options['wpcc_translate_type'] = intval($_POST['wpcc_flag_option']);
}

if (is_array($this->langs)) { if (is_array($this->langs)) {
foreach ($this->langs as $lang => $value) { foreach ($this->langs as $lang => $value) {
if ( if (
@ -528,22 +494,6 @@ class wpcc_Admin
} }
} }


// 根据本地“显示更多语言”与引擎裁剪启用语言,防止未启用的扩展语言(如 zh-sg误入站点配置
$enable_more = isset($options['wpcc_show_more_langs']) ? (int)$options['wpcc_show_more_langs'] : 1;
if (! $enable_more) {
$base_langs = array('zh-cn','zh-tw','zh-hk');
$options['wpcc_used_langs'] = array_values(array_intersect($options['wpcc_used_langs'], $base_langs));
}
// 非 OpenCC 引擎移除 zh-jp
$engine = isset($options['wpcc_engine']) ? $options['wpcc_engine'] : 'opencc';
if ($engine !== 'opencc') {
$options['wpcc_used_langs'] = array_values(array_diff($options['wpcc_used_langs'], array('zh-jp')));
}
// 至少保留一种语言
if (empty($options['wpcc_used_langs'])) {
$options['wpcc_used_langs'] = array('zh-cn');
}

if ( if (
$this->get_cache_status() == 2 && $this->get_cache_status() == 2 &&
empty($options["wpcc_browser_redirect"]) && empty($options["wpcc_browser_redirect"]) &&
@ -555,14 +505,12 @@ class wpcc_Admin
$wpcc_options = $options; $wpcc_options = $options;
$need_flush_rules = false; $need_flush_rules = false;


$current_permalink = isset($this->options["wpcc_use_permalink"]) ? (int) $this->options["wpcc_use_permalink"] : 0;
$new_permalink = isset($options["wpcc_use_permalink"]) ? (int) $options["wpcc_use_permalink"] : 0;
$current_langs = isset($this->options["wpcc_used_langs"]) && is_array($this->options["wpcc_used_langs"]) ? $this->options["wpcc_used_langs"] : [];
$new_langs = isset($options["wpcc_used_langs"]) && is_array($options["wpcc_used_langs"]) ? $options["wpcc_used_langs"] : [];

if ( if (
$current_permalink !== $new_permalink || $this->options["wpcc_use_permalink"] !=
($current_permalink !== 0 && $current_langs != $new_langs) $options["wpcc_use_permalink"] ||
($this->options["wpcc_use_permalink"] != 0 &&
$this->options["wpcc_used_langs"] !=
$options["wpcc_used_langs"])
) { ) {
if (!has_filter("rewrite_rules_array", "wpcc_rewrite_rules")) { if (!has_filter("rewrite_rules_array", "wpcc_rewrite_rules")) {
add_filter("rewrite_rules_array", "wpcc_rewrite_rules"); add_filter("rewrite_rules_array", "wpcc_rewrite_rules");
@ -585,8 +533,6 @@ class wpcc_Admin
$wp_rewrite->flush_rules(); $wp_rewrite->flush_rules();
} }


// 网络设置交由 WPCC_Network_Settings 统一管理,此处不再重复保存以避免冲突

update_wpcc_option("wpcc_options", $options); update_wpcc_option("wpcc_options", $options);


$this->options = $options; $this->options = $options;
@ -668,71 +614,4 @@ class wpcc_Admin


wp_send_json_success(["message" => "已优化 {$optimized} 个数据表"]); wp_send_json_success(["message" => "已优化 {$optimized} 个数据表"]);
} }

/**
* 初始化网络控制
*/
private function init_network_control() {
if (!is_multisite()) {
return;
}

// 获取网络控制的选项
$this->network_controlled_options = get_site_option('wpcc_network_controlled_options', []);
$this->is_network_enforced = get_site_option('wpcc_network_enforce', false);
// 如果启用了网络强制模式,获取网络选项值
if ($this->is_network_enforced) {
$this->network_options = get_site_option('wpcc_network_options', []);
$this->apply_network_options();
}
}

/**
* 应用网络选项值
*/
private function apply_network_options() {
foreach ($this->network_controlled_options as $option_name) {
if (isset($this->network_options[$option_name])) {
$this->options[$option_name] = $this->network_options[$option_name];
}
}
}

/**
* 检查选项是否被网络控制
*/
public function is_option_controlled($option_name) {
if (!is_multisite()) {
return false;
}
return in_array($option_name, $this->network_controlled_options);
}

/**
* 获取字段属性(用于禁用网络控制的字段)
*/
public function get_field_attributes($option_name) {
if ($this->is_option_controlled($option_name)) {
return 'disabled="disabled" title="此选项由网络管理员控制"';
}
return '';
}

/**
* 获取网络控制提示
*/
public function get_network_controlled_notice($option_name) {
if ($this->is_option_controlled($option_name)) {
return '<p class="description" style="color: #d63638;"><strong>此选项由网络管理员统一控制,无法在子站点修改。</strong></p>';
}
return '';
}

/**
* 检查是否为网络强制模式
*/
public function is_network_enforced() {
return $this->is_network_enforced;
}
} }

View file

@ -8,28 +8,10 @@ class WPCC_Blocks {
public function __construct() { public function __construct() {
add_action( 'init', array( $this, 'register_blocks' ) ); add_action( 'init', array( $this, 'register_blocks' ) );
add_action( 'init', array( $this, 'register_block_styles' ) );
add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue_block_editor_assets' ) ); add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue_block_editor_assets' ) );
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_frontend_assets' ) ); add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_frontend_assets' ) );
} }


public function register_block_styles() {
// 在 init 阶段注册样式句柄,供 block.json 中的 editorStyle 和 style 字段使用
wp_register_style(
'wpcc-blocks-editor',
plugins_url( 'assets/css/blocks-editor.css', dirname( dirname( __FILE__ ) ) ),
array(),
wpcc_VERSION
);

wp_register_style(
'wpcc-blocks-frontend',
plugins_url( 'assets/css/blocks-frontend.css', dirname( dirname( __FILE__ ) ) ),
array(),
wpcc_VERSION
);
}

public function register_blocks() { public function register_blocks() {
$blocks = array( $blocks = array(
'language-switcher', 'language-switcher',
@ -54,18 +36,22 @@ class WPCC_Blocks {
public function enqueue_block_editor_assets() { public function enqueue_block_editor_assets() {
// 确保wpcc-variant脚本已加载 // 确保wpcc-variant脚本已加载
if ( ! wp_script_is( 'wpcc-variant', 'registered' ) ) { if ( ! wp_script_is( 'wpcc-variant', 'registered' ) ) {
wp_register_script( 'wpcc-variant', plugins_url( 'assets/dist/wpcc-variant.umd.js', dirname( dirname( __FILE__ ) ) ), array(), wpcc_VERSION ); wp_register_script( 'wpcc-variant', plugins_url( 'assets/dist/wpcc-variant.umd.js', dirname( dirname( __FILE__ ) ) ), array(), '1.1.0' );
} }


// 样式已在 init 阶段注册,这里只需要加载即可 wp_enqueue_style(
wp_enqueue_style( 'wpcc-blocks-editor' ); 'wpcc-blocks-editor',
plugins_url( 'assets/css/blocks-editor.css', dirname( dirname( __FILE__ ) ) ),
array(),
'1.0.0'
);


// 兼容层:为编辑器注册旧占位文本的 deprecated 版本,避免校验失败 // 兼容层:为编辑器注册旧占位文本的 deprecated 版本,避免校验失败
wp_enqueue_script( wp_enqueue_script(
'wpcc-block-compat', 'wpcc-block-compat',
plugins_url( 'assets/js/wpcc-block-compat.js', dirname( dirname( __FILE__ ) ) ), plugins_url( 'assets/js/wpcc-block-compat.js', dirname( dirname( __FILE__ ) ) ),
array( 'wp-blocks', 'wp-element', 'wp-block-editor', 'wp-hooks' ), array( 'wp-blocks', 'wp-element', 'wp-block-editor', 'wp-hooks' ),
wpcc_VERSION, '1.0.0',
true true
); );


@ -73,7 +59,7 @@ wpcc_VERSION,
$enabled_languages = $wpcc_options['wpcc_used_langs'] ?? array(); $enabled_languages = $wpcc_options['wpcc_used_langs'] ?? array();


wp_localize_script( wp_localize_script(
'wpcc-block-compat', 'wp-blocks',
'wpccBlockSettings', 'wpccBlockSettings',
array( array(
'enabledLanguages' => $enabled_languages, 'enabledLanguages' => $enabled_languages,
@ -99,23 +85,24 @@ wpcc_VERSION,
public function enqueue_frontend_assets() { public function enqueue_frontend_assets() {
// 确保wpcc-variant脚本已加载 // 确保wpcc-variant脚本已加载
if ( ! wp_script_is( 'wpcc-variant', 'registered' ) ) { if ( ! wp_script_is( 'wpcc-variant', 'registered' ) ) {
wp_register_script( 'wpcc-variant', plugins_url( 'assets/dist/wpcc-variant.umd.js', dirname( dirname( __FILE__ ) ) ), array(), wpcc_VERSION ); wp_register_script( 'wpcc-variant', plugins_url( 'assets/dist/wpcc-variant.umd.js', dirname( dirname( __FILE__ ) ) ), array(), '1.1.0' );
} }


// 为前端状态指示器的 dashicons 图标提供样式支持 // 为前端状态指示器的 dashicons 图标提供样式支持
wp_enqueue_style( 'dashicons' ); wp_enqueue_style( 'dashicons' );


// 样式已在 init 阶段注册,这里只需要加载即可 wp_enqueue_style(
wp_enqueue_style( 'wpcc-blocks-frontend' ); 'wpcc-blocks-frontend',
plugins_url( 'assets/css/blocks-frontend.css', dirname( dirname( __FILE__ ) ) ),
array(),
'1.0.0'
);


// 使用文件修改时间作为版本号,避免浏览器缓存导致逻辑不更新
$blocks_front_js = plugin_dir_path( dirname( __FILE__ ) ) . 'assets/js/blocks-frontend.js';
$blocks_front_ver = file_exists( $blocks_front_js ) ? filemtime( $blocks_front_js ) : wpcc_VERSION;
wp_enqueue_script( wp_enqueue_script(
'wpcc-blocks-frontend', 'wpcc-blocks-frontend',
plugins_url( 'assets/js/blocks-frontend.js', dirname( dirname( __FILE__ ) ) ), plugins_url( 'assets/js/blocks-frontend.js', dirname( dirname( __FILE__ ) ) ),
array( 'wpcc-variant' ), array( 'wpcc-variant' ),
$blocks_front_ver, '1.0.0',
true true
); );

View file

@ -27,12 +27,12 @@ class WPCC_Converter_Factory {
private static function create_converter( $engine ) { private static function create_converter( $engine ) {
switch ( $engine ) { switch ( $engine ) {
case 'opencc': case 'opencc':
require_once dirname( __FILE__ ) . '/class-wpcc-opencc-converter.php'; require_once dirname( __FILE__ ) . '/class-opencc-converter.php';
return new WPCC_OpenCC_Converter(); return new WPCC_OpenCC_Converter();
case 'mediawiki': case 'mediawiki':
default: default:
require_once dirname( __FILE__ ) . '/class-wpcc-mediawiki-converter.php'; require_once dirname( __FILE__ ) . '/class-mediawiki-converter.php';
return new WPCC_MediaWiki_Converter(); return new WPCC_MediaWiki_Converter();
} }
} }

View file

@ -0,0 +1,284 @@
<?php

if ( ! defined( 'ABSPATH' ) ) {
exit;
}

require_once dirname( __FILE__ ) . '/interface-converter.php';

class WPCC_MediaWiki_Converter implements WPCC_Converter_Interface {
private $conversion_tables_loaded = false;
private static $loaded_tables = array();
private $lazy_load_enabled = true;
public function convert( $text, $target_variant ) {
if ( empty( $text ) ) {
return $text;
}
// 检查缓存
$cached_result = WPCC_Conversion_Cache::get_cached_conversion( $text, $target_variant );
if ( $cached_result !== null ) {
return $cached_result;
}
$this->ensure_conversion_tables_loaded( $target_variant );
$result = $text;
switch ( $target_variant ) {
case 'zh-hans':
$result = $this->convert_to_hans( $text );
break;
case 'zh-hant':
$result = $this->convert_to_hant( $text );
break;
case 'zh-cn':
$result = $this->convert_to_cn( $text );
break;
case 'zh-tw':
$result = $this->convert_to_tw( $text );
break;
case 'zh-hk':
$result = $this->convert_to_hk( $text );
break;
case 'zh-sg':
$result = $this->convert_to_sg( $text );
break;
}
// 缓存转换结果
if ( $result !== $text ) {
WPCC_Conversion_Cache::set_cached_conversion( $text, $target_variant, $result );
}
return $result;
}
public function get_supported_variants() {
return array( 'zh-hans', 'zh-hant', 'zh-cn', 'zh-tw', 'zh-hk', 'zh-sg' );
}
public function get_engine_name() {
return 'mediawiki';
}
public function get_engine_info() {
$memory_usage = $this->get_memory_usage_info();
return array(
'name' => 'MediaWiki',
'version' => '1.23.5',
'description' => '基于MediaWiki的字符映射转换引擎',
'features' => array(
'字符级精确映射',
'快速转换速度',
'良好的兼容性',
'支持多地区变体',
'按需加载优化'
),
'memory_usage' => $memory_usage,
'conversion_type' => 'character_mapping',
'lazy_load_enabled' => $this->lazy_load_enabled,
'loaded_tables' => array_keys( array_filter( self::$loaded_tables ) )
);
}
public function get_memory_usage_info() {
$loaded_count = count( array_filter( self::$loaded_tables ) );
$total_count = count( $this->get_supported_variants() );
if ( $this->lazy_load_enabled ) {
$estimated_usage = $loaded_count * 0.25; // MB per table approximately
return sprintf( '约 %.1fMB (%d/%d 表已加载, 按需加载开启)', $estimated_usage, $loaded_count, $total_count );
} else {
return '约 1.5MB (所有转换表已加载)';
}
}
public function enable_lazy_loading( $enable = true ) {
$this->lazy_load_enabled = $enable;
}
public function is_lazy_loading_enabled() {
return $this->lazy_load_enabled;
}
public static function get_loaded_tables_status() {
return self::$loaded_tables;
}
public static function unload_table( $variant ) {
if ( isset( self::$loaded_tables[ $variant ] ) ) {
unset( self::$loaded_tables[ $variant ] );
// 清理对应的全局变量这里只能标记PHP不能真正释放全局变量内存
return true;
}
return false;
}
public function is_available() {
$conversion_file = dirname( dirname( __FILE__ ) ) . '/core/ZhConversion.php';
return file_exists( $conversion_file );
}
private function ensure_conversion_tables_loaded( $target_variant = null ) {
if ( $this->lazy_load_enabled && $target_variant ) {
// 按需加载特定语言变体的转换表
$this->ensure_specific_table_loaded( $target_variant );
} else {
// 传统方式:加载所有转换表
$this->load_all_conversion_tables();
}
}
private function ensure_specific_table_loaded( $target_variant ) {
if ( isset( self::$loaded_tables[ $target_variant ] ) ) {
return; // 已经加载
}
$conversion_file = dirname( dirname( __FILE__ ) ) . '/core/ZhConversion.php';
if ( ! file_exists( $conversion_file ) ) {
return;
}
// 只加载需要的转换表
global $zh2Hans, $zh2Hant, $zh2CN, $zh2TW, $zh2HK, $zh2SG;
switch ( $target_variant ) {
case 'zh-hans':
if ( ! isset( $zh2Hans ) ) {
$this->load_conversion_table_partial( 'zh2Hans' );
}
self::$loaded_tables['zh-hans'] = true;
break;
case 'zh-hant':
if ( ! isset( $zh2Hant ) ) {
$this->load_conversion_table_partial( 'zh2Hant' );
}
self::$loaded_tables['zh-hant'] = true;
break;
case 'zh-cn':
if ( ! isset( $zh2CN ) || ! isset( $zh2Hans ) ) {
$this->load_conversion_table_partial( array( 'zh2CN', 'zh2Hans' ) );
}
self::$loaded_tables['zh-cn'] = true;
break;
case 'zh-tw':
if ( ! isset( $zh2TW ) || ! isset( $zh2Hant ) ) {
$this->load_conversion_table_partial( array( 'zh2TW', 'zh2Hant' ) );
}
self::$loaded_tables['zh-tw'] = true;
break;
case 'zh-hk':
if ( ! isset( $zh2HK ) || ! isset( $zh2Hant ) ) {
$this->load_conversion_table_partial( array( 'zh2HK', 'zh2Hant' ) );
}
self::$loaded_tables['zh-hk'] = true;
break;
case 'zh-sg':
if ( ! isset( $zh2SG ) || ! isset( $zh2Hans ) ) {
$this->load_conversion_table_partial( array( 'zh2SG', 'zh2Hans' ) );
}
self::$loaded_tables['zh-sg'] = true;
break;
default:
$this->load_all_conversion_tables();
break;
}
}
private function load_conversion_table_partial( $table_names ) {
$conversion_file = dirname( dirname( __FILE__ ) ) . '/core/ZhConversion.php';
if ( ! is_array( $table_names ) ) {
$table_names = array( $table_names );
}
// 读取文件内容并解析指定的转换表
$file_content = file_get_contents( $conversion_file );
foreach ( $table_names as $table_name ) {
$pattern = '/\$' . preg_quote( $table_name, '/' ) . '\s*=\s*array\s*\((.*?)\);/s';
if ( preg_match( $pattern, $file_content, $matches ) ) {
$table_code = '$' . $table_name . ' = array(' . $matches[1] . ');';
eval( $table_code );
// 将变量设置为全局
$GLOBALS[ $table_name ] = ${$table_name};
}
}
// 加载额外转换文件
$extra_conversion_file = WP_CONTENT_DIR . '/extra_zhconversion.php';
if ( file_exists( $extra_conversion_file ) ) {
require_once $extra_conversion_file;
}
}
private function load_all_conversion_tables() {
if ( $this->conversion_tables_loaded ) {
return;
}
global $zh2Hans, $zh2Hant, $zh2CN, $zh2TW, $zh2HK, $zh2SG;
if ( ! isset( $zh2Hans ) ) {
require_once dirname( dirname( __FILE__ ) ) . '/core/ZhConversion.php';
$extra_conversion_file = WP_CONTENT_DIR . '/extra_zhconversion.php';
if ( file_exists( $extra_conversion_file ) ) {
require_once $extra_conversion_file;
}
}
// 标记所有表为已加载
self::$loaded_tables = array(
'zh-hans' => true,
'zh-hant' => true,
'zh-cn' => true,
'zh-tw' => true,
'zh-hk' => true,
'zh-sg' => true
);
$this->conversion_tables_loaded = true;
}
private function convert_to_hans( $text ) {
global $zh2Hans;
return strtr( $text, $zh2Hans );
}
private function convert_to_hant( $text ) {
global $zh2Hant;
return strtr( $text, $zh2Hant );
}
private function convert_to_cn( $text ) {
global $zh2Hans, $zh2CN;
return strtr( strtr( $text, $zh2CN ), $zh2Hans );
}
private function convert_to_tw( $text ) {
global $zh2Hant, $zh2TW;
return strtr( strtr( $text, $zh2TW ), $zh2Hant );
}
private function convert_to_hk( $text ) {
global $zh2Hant, $zh2HK;
return strtr( strtr( $text, $zh2HK ), $zh2Hant );
}
private function convert_to_sg( $text ) {
global $zh2Hans, $zh2SG;
return strtr( strtr( $text, $zh2SG ), $zh2Hans );
}
}

View file

@ -0,0 +1,159 @@
<?php

if ( ! defined( 'ABSPATH' ) ) {
exit;
}

require_once dirname( __FILE__ ) . '/interface-converter.php';

use Overtrue\PHPOpenCC\OpenCC;
use Overtrue\PHPOpenCC\Strategy;

class WPCC_OpenCC_Converter implements WPCC_Converter_Interface {
private $strategy_map = array(
'zh-hans' => Strategy::TRADITIONAL_TO_SIMPLIFIED,
'zh-hant' => Strategy::SIMPLIFIED_TO_TRADITIONAL,
'zh-cn' => Strategy::TRADITIONAL_TO_SIMPLIFIED,
'zh-tw' => Strategy::SIMPLIFIED_TO_TAIWAN_WITH_PHRASE,
'zh-hk' => Strategy::SIMPLIFIED_TO_HONGKONG,
'zh-sg' => Strategy::TRADITIONAL_TO_SIMPLIFIED,
'zh-jp' => Strategy::SIMPLIFIED_TO_JAPANESE,
);
public function convert( $text, $target_variant ) {
if ( empty( $text ) ) {
return $text;
}
if ( ! $this->is_available() ) {
throw new Exception( 'OpenCC library is not available' );
}
if ( ! isset( $this->strategy_map[ $target_variant ] ) ) {
return $text;
}
try {
return OpenCC::convert( $text, $this->strategy_map[ $target_variant ] );
} catch ( Exception $e ) {
error_log( 'WPCC OpenCC Conversion Error: ' . $e->getMessage() );
return $text;
}
}
public function get_supported_variants() {
return array_keys( $this->strategy_map );
}
public function get_engine_name() {
return 'opencc';
}
public function get_engine_info() {
return array(
'name' => 'OpenCC',
'version' => '1.2.1',
'description' => '基于 OpenCC 的智能词汇级转换引擎',
'features' => array(
'词汇级别转换',
'异体字转换',
'地区习惯用词转换',
'智能语境分析',
'支持批量转换'
),
'memory_usage' => '按需加载,内存占用较低',
'conversion_type' => 'vocabulary_based'
);
}
public function is_available() {
return class_exists( 'Overtrue\PHPOpenCC\OpenCC' );
}
public function batch_convert( $texts, $target_variant ) {
if ( ! $this->is_available() ) {
return $texts;
}
if ( ! isset( $this->strategy_map[ $target_variant ] ) ) {
return $texts;
}
if ( ! is_array( $texts ) ) {
return $texts;
}
// 检查缓存
$cached_results = array();
$uncached_texts = array();
$uncached_indices = array();
foreach ( $texts as $index => $text ) {
$cached_result = WPCC_Conversion_Cache::get_cached_conversion( $text, $target_variant );
if ( $cached_result !== null ) {
$cached_results[ $index ] = $cached_result;
} else {
$uncached_texts[] = $text;
$uncached_indices[] = $index;
}
}
// 如果所有内容都在缓存中,直接返回
if ( empty( $uncached_texts ) ) {
$results = array();
foreach ( $texts as $index => $text ) {
$results[] = $cached_results[ $index ];
}
return $results;
}
try {
// 转换未缓存的内容
$converted_texts = OpenCC::convert( $uncached_texts, $this->strategy_map[ $target_variant ] );
// 合并结果并缓存新转换的内容
$results = array();
$uncached_index = 0;
foreach ( $texts as $index => $original_text ) {
if ( isset( $cached_results[ $index ] ) ) {
$results[] = $cached_results[ $index ];
} else {
$converted_text = is_array( $converted_texts ) ? $converted_texts[ $uncached_index ] : $converted_texts;
$results[] = $converted_text;
// 缓存转换结果
if ( $converted_text !== $original_text ) {
WPCC_Conversion_Cache::set_cached_conversion( $original_text, $target_variant, $converted_text );
}
$uncached_index++;
}
}
return $results;
} catch ( Exception $e ) {
error_log( 'WPCC OpenCC Batch Conversion Error: ' . $e->getMessage() );
return $texts;
}
}
public function get_available_strategies() {
return $this->strategy_map;
}
public function convert_with_strategy( $text, $strategy ) {
if ( empty( $text ) || ! $this->is_available() ) {
return $text;
}
try {
return OpenCC::convert( $text, $strategy );
} catch ( Exception $e ) {
error_log( 'WPCC OpenCC Strategy Conversion Error: ' . $e->getMessage() );
return $text;
}
}
}

View file

@ -9,8 +9,7 @@ if (! defined('ABSPATH')) {
* *
* 统一管理插件配置,减少全局变量的使用 * 统一管理插件配置,减少全局变量的使用
*/ */
class WPCC_Config class WPCC_Config {
{
private static ?self $instance = null; private static ?self $instance = null;
private array $options = []; private array $options = [];
@ -22,14 +21,12 @@ class WPCC_Config
private array $langs_urls = []; private array $langs_urls = [];
private array $debug_data = []; private array $debug_data = [];
private function __construct() private function __construct() {
{
$this->load_options(); $this->load_options();
$this->init_languages(); $this->init_languages();
} }
public static function get_instance(): self public static function get_instance(): self {
{
if ( null === self::$instance ) { if ( null === self::$instance ) {
self::$instance = new self(); self::$instance = new self();
} }
@ -39,121 +36,17 @@ class WPCC_Config
/** /**
* 加载插件选项 * 加载插件选项
*/ */
private function load_options(): void private function load_options(): void {
{ $this->options = get_wpcc_option( 'wpcc_options' );
$options = $this->get_wpcc_options(); if ( empty( $this->options ) ) {

// 确保返回值是数组类型
if (! is_array($options) || empty($options)) {
$this->options = $this->get_default_options(); $this->options = $this->get_default_options();
} else {
$this->options = $options;
} }
} }
/**
* 获取WPCC选项考虑网络控制
*/
private function get_wpcc_options(): array
{
// 获取本地选项
$local_options = get_option('wpcc_options', []);

// 如果不是多站点环境,直接返回本地选项
if (! is_multisite() || is_network_admin()) {
return $local_options;
}

// 检查网络控制
if (! class_exists('WPCC_Network_Settings')) {
return $local_options;
}

// 获取网络控制的选项列表
$controlled_options = get_site_option('wpcc_network_controlled_options', []);
if (! is_array($controlled_options)) {
$controlled_options = explode(',', $controlled_options);
}

// 如果没有网络控制选项,返回本地选项
if (empty($controlled_options)) {
return $local_options;
}

// 合并网络控制的选项
$network_options = [];

// 映射网络默认选项到本地选项名
$option_mapping = [
'wpcc_default_used_langs' => 'wpcc_used_langs',
'wpcc_default_cntip' => 'cntip',
'wpcc_default_twtip' => 'twtip',
'wpcc_default_hktip' => 'hktip',
'wpcc_default_hanstip' => 'hanstip',
'wpcc_default_hanttip' => 'hanttip',
'wpcc_default_sgtip' => 'sgtip',
'wpcc_default_jptip' => 'jptip',
'wpcc_default_nctip' => 'nctip',
'wpcc_default_flag_option' => 'wpcc_flag_option',
// Harmonize with single-site option key used in UI
'wpcc_default_enable_extended_langs' => 'wpcc_show_more_langs',
'wpcc_default_engine' => 'wpcc_engine',
'wpcc_default_search_conversion' => 'wpcc_search_conversion',
'wpcc_default_use_fullpage_conversion' => 'wpcc_use_fullpage_conversion',
// Quicktags uses wpcc_no_conversion_qtag in single-site
'wpcc_default_no_conversion_qtag' => 'wpcc_no_conversion_qtag',
'wpcc_default_enable_post_conversion' => 'wpcc_enable_post_conversion',
'wpcc_default_post_conversion_target' => 'wpcc_post_conversion_target',
'wpcc_default_use_permalink' => 'wpcc_use_permalink',
'wpcc_default_sitemap_post_type' => 'wpcco_sitemap_post_type',
'wpcc_default_use_sitemap' => 'wpcco_use_sitemap',
'wpcc_default_browser_redirect' => 'wpcc_browser_redirect',
'wpcc_default_auto_language_recong' => 'wpcc_auto_language_recong',
'wpcc_default_use_cookie_variant' => 'wpcc_use_cookie_variant',
'wpcc_default_no_conversion_tag' => 'wpcc_no_conversion_tag',
'wpcc_default_no_conversion_ja' => 'wpcc_no_conversion_ja',
'wpcc_default_hreflang_x_default' => 'wpcc_hreflang_x_default',
'wpcc_default_enable_hreflang_tags' => 'wpcc_enable_hreflang_tags',
'wpcc_default_enable_hreflang_x_default' => 'wpcc_enable_hreflang_x_default',
'wpcc_default_enable_schema_conversion' => 'wpcc_enable_schema_conversion',
'wpcc_default_enable_meta_conversion' => 'wpcc_enable_meta_conversion',
'wpcc_default_enable_cache_addon' => 'wpcc_enable_cache_addon'
];

// 对于每个被控制的选项,使用网络设置
foreach ($controlled_options as $option_name) {
// 使用映射表查找对应的网络选项名
$network_option_name = null;
// 查找映射表中的对应关系
foreach ($option_mapping as $network_key => $local_key) {
if ($local_key === $option_name) {
$network_option_name = $network_key;
break;
}
}
// 如果映射表中没有找到,使用默认规则
if (!$network_option_name) {
$network_option_name = 'wpcc_default_' . str_replace('wpcc_', '', $option_name);
}

// 获取网络设置值
$network_value = get_site_option($network_option_name);
if ($network_value !== false) {
$network_options[$option_name] = $network_value;
}
}

// 合并选项:网络控制的选项覆盖本地选项
return array_merge($local_options, $network_options);
}

/** /**
* 获取默认配置 * 获取默认配置
*/ */
private function get_default_options(): array private function get_default_options(): array {
{
return [ return [
// 语言与标签 // 语言与标签
'wpcc_used_langs' => ['zh-cn','zh-tw'], 'wpcc_used_langs' => ['zh-cn','zh-tw'],
@ -176,7 +69,7 @@ class WPCC_Config
'wpcc_use_cookie_variant' => 1, 'wpcc_use_cookie_variant' => 1,


// 不转换 // 不转换
'wpcc_no_conversion_tag' => '', 'wpcc_no_conversion_tag' => 'pre,code,pre.wp-block-code,pre.wp-block-preformatted,script,noscript,style,kbd,samp',
'wpcc_no_conversion_ja' => 0, 'wpcc_no_conversion_ja' => 0,
'wpcc_no_conversion_qtag' => 0, 'wpcc_no_conversion_qtag' => 0,


@ -191,12 +84,10 @@ class WPCC_Config


// SEO // SEO
'wpcc_enable_hreflang_tags' => 1, 'wpcc_enable_hreflang_tags' => 1,
'wpcc_enable_hreflang_x_default' => 1,
'wpcc_hreflang_x_default' => 'zh-cn', 'wpcc_hreflang_x_default' => 'zh-cn',
'wpcc_enable_schema_conversion' => 1, 'wpcc_enable_schema_conversion' => 1,
'wpcc_enable_meta_conversion' => 1, 'wpcc_enable_meta_conversion' => 1,



// 其他 // 其他
'wpcc_flag_option' => 1, 'wpcc_flag_option' => 1,
'wpcc_trackback_plugin_author' => 0, 'wpcc_trackback_plugin_author' => 0,
@ -211,145 +102,134 @@ class WPCC_Config
/** /**
* 初始化语言配置 * 初始化语言配置
*/ */
private function init_languages(): void private function init_languages(): void {
{ $this->languages = [
// 使用中心化的语言配置 'zh-cn' => [ 'zhconversion_cn', 'cntip', __( '简体中文', 'wp-chinese-converter' ), 'zh-CN' ],
$this->languages = WPCC_Language_Config::get_all_languages(); 'zh-tw' => [ 'zhconversion_tw', 'twtip', __( '台灣正體', 'wp-chinese-converter' ), 'zh-TW' ],
'zh-hk' => [ 'zhconversion_hk', 'hktip', __( '港澳繁體', 'wp-chinese-converter' ), 'zh-HK' ],
'zh-hans' => [ 'zhconversion_hans', 'hanstip', __( '简体中文', 'wp-chinese-converter' ), 'zh-Hans' ],
'zh-hant' => [ 'zhconversion_hant', 'hanttip', __( '繁体中文', 'wp-chinese-converter' ), 'zh-Hant' ],
'zh-sg' => [ 'zhconversion_sg', 'sgtip', __( '马新简体', 'wp-chinese-converter' ), 'zh-SG' ],
'zh-jp' => [ 'zhconversion_jp', 'jptip', __( '日式汉字', 'wp-chinese-converter' ), 'zh-JP' ],
];
} }
/** /**
* 获取配置选项 * 获取配置选项
*/ */
public function get_option(string $key, $default = null) public function get_option( string $key, $default = null ) {
{
return $this->options[$key] ?? $default; return $this->options[$key] ?? $default;
} }
/** /**
* 设置配置选项 * 设置配置选项
*/ */
public function set_option(string $key, $value): void public function set_option( string $key, $value ): void {
{
$this->options[$key] = $value; $this->options[$key] = $value;
} }
/** /**
* 更新配置选项到数据库 * 更新配置选项到数据库
*/ */
public function save_options(): bool public function save_options(): bool {
{ return update_wpcc_option( 'wpcc_options', $this->options );
return update_option('wpcc_options', $this->options);
} }
/** /**
* 获取所有配置选项 * 获取所有配置选项
*/ */
public function get_all_options(): array public function get_all_options(): array {
{
return $this->options; return $this->options;
} }
/** /**
* 获取语言配置 * 获取语言配置
*/ */
public function get_languages(): array public function get_languages(): array {
{
return $this->languages; return $this->languages;
} }
/** /**
* 获取特定语言配置 * 获取特定语言配置
*/ */
public function get_language(string $lang_code): ?array public function get_language( string $lang_code ): ?array {
{
return $this->languages[$lang_code] ?? null; return $this->languages[$lang_code] ?? null;
} }
/** /**
* 设置目标语言 * 设置目标语言
*/ */
public function set_target_lang(string $lang): void public function set_target_lang( string $lang ): void {
{
$this->target_lang = $lang; $this->target_lang = $lang;
} }
/** /**
* 获取目标语言 * 获取目标语言
*/ */
public function get_target_lang(): string public function get_target_lang(): string {
{
return $this->target_lang; return $this->target_lang;
} }
/** /**
* 设置无转换URL * 设置无转换URL
*/ */
public function set_noconversion_url(string $url): void public function set_noconversion_url( string $url ): void {
{
$this->noconversion_url = $url; $this->noconversion_url = $url;
} }
/** /**
* 获取无转换URL * 获取无转换URL
*/ */
public function get_noconversion_url(): string public function get_noconversion_url(): string {
{
return $this->noconversion_url; return $this->noconversion_url;
} }
/** /**
* 设置语言URL映射 * 设置语言URL映射
*/ */
public function set_langs_urls(array $urls): void public function set_langs_urls( array $urls ): void {
{
$this->langs_urls = $urls; $this->langs_urls = $urls;
} }
/** /**
* 获取语言URL映射 * 获取语言URL映射
*/ */
public function get_langs_urls(): array public function get_langs_urls(): array {
{
return $this->langs_urls; return $this->langs_urls;
} }
/** /**
* 设置重定向标志 * 设置重定向标志
*/ */
public function set_redirect_to(bool $redirect): void public function set_redirect_to( bool $redirect ): void {
{
$this->redirect_to = $redirect; $this->redirect_to = $redirect;
} }
/** /**
* 获取重定向标志 * 获取重定向标志
*/ */
public function get_redirect_to(): bool public function get_redirect_to(): bool {
{
return $this->redirect_to; return $this->redirect_to;
} }
/** /**
* 设置直接转换标志 * 设置直接转换标志
*/ */
public function set_direct_conversion_flag(bool $flag): void public function set_direct_conversion_flag( bool $flag ): void {
{
$this->direct_conversion_flag = $flag; $this->direct_conversion_flag = $flag;
} }
/** /**
* 获取直接转换标志 * 获取直接转换标志
*/ */
public function get_direct_conversion_flag(): bool public function get_direct_conversion_flag(): bool {
{
return $this->direct_conversion_flag; return $this->direct_conversion_flag;
} }
/** /**
* 添加调试数据 * 添加调试数据
*/ */
public function add_debug_data(string $key, $data): void public function add_debug_data( string $key, $data ): void {
{
if ( defined( 'wpcc_DEBUG' ) && wpcc_DEBUG ) { if ( defined( 'wpcc_DEBUG' ) && wpcc_DEBUG ) {
$this->debug_data[$key] = $data; $this->debug_data[$key] = $data;
} }
@ -358,48 +238,42 @@ class WPCC_Config
/** /**
* 获取调试数据 * 获取调试数据
*/ */
public function get_debug_data(): array public function get_debug_data(): array {
{
return $this->debug_data; return $this->debug_data;
} }
/** /**
* 检查是否启用了指定功能 * 检查是否启用了指定功能
*/ */
public function is_feature_enabled(string $feature): bool public function is_feature_enabled( string $feature ): bool {
{
return (bool) $this->get_option( $feature, false ); return (bool) $this->get_option( $feature, false );
} }
/** /**
* 获取启用的语言列表 * 获取启用的语言列表
*/ */
public function get_enabled_languages(): array public function get_enabled_languages(): array {
{
return $this->get_option( 'wpcc_used_langs', [] ); return $this->get_option( 'wpcc_used_langs', [] );
} }
/** /**
* 检查语言是否启用 * 检查语言是否启用
*/ */
public function is_language_enabled(string $lang_code): bool public function is_language_enabled( string $lang_code ): bool {
{
return in_array( $lang_code, $this->get_enabled_languages(), true ); return in_array( $lang_code, $this->get_enabled_languages(), true );
} }
/** /**
* 获取转换引擎 * 获取转换引擎
*/ */
public function get_conversion_engine(): string public function get_conversion_engine(): string {
{
return $this->get_option( 'wpcc_engine', 'opencc' ); return $this->get_option( 'wpcc_engine', 'opencc' );
} }
/** /**
* 验证配置完整性 * 验证配置完整性
*/ */
public function validate_config(): array public function validate_config(): array {
{
$errors = []; $errors = [];
if ( empty( $this->get_enabled_languages() ) ) { if ( empty( $this->get_enabled_languages() ) ) {

View file

@ -194,14 +194,8 @@ final class WPCC_Exception_Handler {
return; return;
} }
// 统一异常计数键,保证与 should_suppress_error 使用的键一致 $error_key = get_class( $exception );
if ( $exception instanceof WPCC_Exception ) { self::$error_counts[ $error_key ] = ( self::$error_counts[ $error_key ] ?? 0 ) + 1;
$ekey = 'wpcc_error_' . $exception->get_error_code();
self::$error_counts[ $ekey ] = ( self::$error_counts[ $ekey ] ?? 0 ) + 1;
} else {
$ekey = get_class( $exception );
self::$error_counts[ $ekey ] = ( self::$error_counts[ $ekey ] ?? 0 ) + 1;
}
$log_message = sprintf( $log_message = sprintf(
'WPCC Error in %s: %s [%s:%d] Context: %s', 'WPCC Error in %s: %s [%s:%d] Context: %s',

View file

@ -18,7 +18,7 @@ abstract class WPCC_Extension extends WPCC_Abstract_Module {
protected $update_server_url; protected $update_server_url;
protected $extension_slug; protected $extension_slug;
protected $is_premium = true; protected $is_premium = true;
protected $required_core_version = '1.5'; protected $required_core_version = '1.3.0';
protected $pricing_tier = 'pro'; // free, pro, enterprise, ultimate protected $pricing_tier = 'pro'; // free, pro, enterprise, ultimate
public function __construct() { public function __construct() {

View file

@ -1,165 +0,0 @@
<?php

if ( ! defined( 'ABSPATH' ) ) {
exit;
}

/**
* WPCC语言配置中心
*
* 统一管理所有语言相关配置,避免重复定义
* 这是语言配置的唯一真实来源Single Source of Truth
*/
final class WPCC_Language_Config {
/**
* 语言配置数组
* 格式: [语言代码 => [转换函数名, 选项键名, 默认显示名, BCP47代码]]
*/
private static array $languages = [];
/**
* 默认语言显示名称
*/
private static array $default_names = [
'zh-cn' => '简体中文',
'zh-tw' => '台灣正體',
'zh-hk' => '港澳繁體',
'zh-hans' => '简体中文',
'zh-hant' => '繁体中文',
'zh-sg' => '马新简体',
'zh-jp' => '日式汉字',
];
/**
* 初始化语言配置
*/
public static function init(): void {
if ( ! empty( self::$languages ) ) {
return;
}
self::$languages = [
'zh-cn' => [ 'zhconversion_cn', 'cntip', self::get_translated_name( 'zh-cn' ), 'zh-CN' ],
'zh-tw' => [ 'zhconversion_tw', 'twtip', self::get_translated_name( 'zh-tw' ), 'zh-TW' ],
'zh-hk' => [ 'zhconversion_hk', 'hktip', self::get_translated_name( 'zh-hk' ), 'zh-HK' ],
'zh-hans' => [ 'zhconversion_hans', 'hanstip', self::get_translated_name( 'zh-hans' ), 'zh-Hans' ],
'zh-hant' => [ 'zhconversion_hant', 'hanttip', self::get_translated_name( 'zh-hant' ), 'zh-Hant' ],
'zh-sg' => [ 'zhconversion_sg', 'sgtip', self::get_translated_name( 'zh-sg' ), 'zh-SG' ],
'zh-jp' => [ 'zhconversion_jp', 'jptip', self::get_translated_name( 'zh-jp' ), 'zh-JP' ],
];
}
/**
* 获取翻译后的语言名称
*/
private static function get_translated_name( string $lang_code ): string {
// 只在WordPress完全初始化后才使用翻译函数
if ( function_exists( 'did_action' ) && did_action( 'init' ) && function_exists( '__' ) ) {
return __( self::$default_names[ $lang_code ] ?? $lang_code, 'wp-chinese-converter' );
}
return self::$default_names[ $lang_code ] ?? $lang_code;
}
/**
* 获取所有语言配置
*/
public static function get_all_languages(): array {
self::init();
return self::$languages;
}
/**
* 获取特定语言配置
*/
public static function get_language( string $lang_code ): ?array {
self::init();
return self::$languages[ $lang_code ] ?? null;
}
/**
* 获取语言显示名称
*/
public static function get_language_name( string $lang_code, ?array $custom_names = null ): string {
self::init();
// 优先使用自定义名称
if ( $custom_names && isset( $custom_names[ $lang_code ] ) ) {
return $custom_names[ $lang_code ];
}
// 使用配置中的名称
if ( isset( self::$languages[ $lang_code ][2] ) ) {
return self::$languages[ $lang_code ][2];
}
// 降级到默认名称
return self::$default_names[ $lang_code ] ?? $lang_code;
}
/**
* 获取语言的BCP47代码
*/
public static function get_bcp47_code( string $lang_code ): string {
self::init();
return self::$languages[ $lang_code ][3] ?? $lang_code;
}
/**
* 获取语言的转换函数名
*/
public static function get_conversion_function( string $lang_code ): ?string {
self::init();
return self::$languages[ $lang_code ][0] ?? null;
}
/**
* 获取语言的选项键名
*/
public static function get_option_key( string $lang_code ): ?string {
self::init();
return self::$languages[ $lang_code ][1] ?? null;
}
/**
* 检查语言代码是否有效
*/
public static function is_valid_language( string $lang_code ): bool {
self::init();
return isset( self::$languages[ $lang_code ] );
}
/**
* 获取所有有效的语言代码
*/
public static function get_valid_language_codes(): array {
self::init();
return array_keys( self::$languages );
}
/**
* 获取自定义语言名称配置
*
* @param array $options 插件选项数组
* @return array 自定义名称数组
*/
public static function get_custom_names( array $options ): array {
self::init();
$custom_names = [];
foreach ( self::$languages as $code => $config ) {
$option_key = $config[1];
$default_name = self::$default_names[ $code ];
$custom_names[ $code ] = $options[ $option_key ] ?? $default_name;
}
return $custom_names;
}
/**
* 获取默认语言名称
*/
public static function get_default_names(): array {
return self::$default_names;
}
}

View file

@ -40,14 +40,9 @@ class WPCC_Main {
add_action( 'init', [ $this, 'init' ], 1 ); add_action( 'init', [ $this, 'init' ], 1 );
add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_scripts' ] ); add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_scripts' ] );
add_filter( 'query_vars', [ $this, 'add_query_vars' ] ); add_filter( 'query_vars', [ $this, 'add_query_vars' ] );
// 在 WordPress 基于重写规则生成 query_vars 之后优先级0先行修正 pagename=zh-xx 的场景
add_filter( 'request', [ $this, 'filter_request_vars' ], 0 );
add_action( 'parse_request', [ $this, 'parse_request' ] ); add_action( 'parse_request', [ $this, 'parse_request' ] );
add_action( 'template_redirect', [ $this, 'template_redirect' ], -100 ); add_action( 'template_redirect', [ $this, 'template_redirect' ], -100 );
// 进一步兜底:在主查询生成前修正“纯语言前缀”为首页查询
add_action( 'pre_get_posts', [ $this, 'pre_get_posts_fix' ], 0 );
// 调试模式钩子 // 调试模式钩子
if ( WP_DEBUG || ( defined( 'wpcc_DEBUG' ) && wpcc_DEBUG ) ) { if ( WP_DEBUG || ( defined( 'wpcc_DEBUG' ) && wpcc_DEBUG ) ) {
add_action( 'init', [ $this, 'flush_rewrite_rules' ] ); add_action( 'init', [ $this, 'flush_rewrite_rules' ] );
@ -66,20 +61,12 @@ class WPCC_Main {
return; return;
} }
// 标记 AJAX/REST/wc-ajax 请求为"直接输出",跳过页面级转换
$is_ajax = function_exists('wp_doing_ajax') ? wp_doing_ajax() : ( defined('DOING_AJAX') && DOING_AJAX );
$is_rest = defined('REST_REQUEST') && REST_REQUEST;
$is_wc_ajax = isset($_REQUEST['wc-ajax']) && is_string($_REQUEST['wc-ajax']) && $_REQUEST['wc-ajax'] !== '';
$this->config->set_direct_conversion_flag( (bool) ( $is_ajax || $is_rest || $is_wc_ajax ) );
// 初始化模块 // 初始化模块
$this->init_modules(); $this->init_modules();
// 设置重写规则 // 设置重写规则
if ( $this->config->is_feature_enabled( 'wpcc_use_permalink' ) ) { if ( $this->config->is_feature_enabled( 'wpcc_use_permalink' ) ) {
$this->setup_rewrite_rules(); $this->setup_rewrite_rules();
// 自愈:在启用固定链接模式但尚未生成 WPCC 语言规则时,尝试一次性刷新重写规则,避免 /zh-xx/ 访问 404
$this->maybe_autoflush_rewrite_rules();
} }
// 处理评论提交 // 处理评论提交
@ -121,7 +108,7 @@ class WPCC_Main {
// 注册核心模块 // 注册核心模块
$this->module_manager->register_module( 'WPCC_Cache_Addon', $modules_dir . 'wpcc-cache-addon.php' ); $this->module_manager->register_module( 'WPCC_Cache_Addon', $modules_dir . 'wpcc-cache-addon.php' );
// $this->module_manager->register_module( 'WPCC_Network', $modules_dir . 'wpcc-network.php' ); // 已被新的网络设置模块替代 $this->module_manager->register_module( 'WPCC_Network', $modules_dir . 'wpcc-network.php' );
$this->module_manager->register_module( 'WPCC_Rest_Api', $modules_dir . 'wpcc-rest-api.php' ); $this->module_manager->register_module( 'WPCC_Rest_Api', $modules_dir . 'wpcc-rest-api.php' );
$this->module_manager->register_module( 'WPCC_Modern_Cache', $modules_dir . 'wpcc-modern-cache.php' ); $this->module_manager->register_module( 'WPCC_Modern_Cache', $modules_dir . 'wpcc-modern-cache.php' );
$this->module_manager->register_module( 'WPCC_SEO_Enhancement', $modules_dir . 'wpcc-seo-enhancement.php' ); $this->module_manager->register_module( 'WPCC_SEO_Enhancement', $modules_dir . 'wpcc-seo-enhancement.php' );
@ -175,19 +162,6 @@ class WPCC_Main {
return; return;
} }
// 兜底:若 rewrite 未捕获到 variant但路径以语言前缀开头则从路径中注入 variant
$path = isset( $wp->request ) ? trim( (string) $wp->request, "\r\n\t " ) : '';
if ( empty( $wp->query_vars['variant'] ) && is_string( $path ) && $path !== '' ) {
$enabled = $this->config->get_enabled_languages();
if ( ! empty( $enabled ) && is_array( $enabled ) ) {
$reg = implode( '|', array_map( 'preg_quote', $enabled ) );
if ( preg_match( '/^(' . $reg . '|zh|zh-reset)(?:\/)?$/i', $path, $m ) ) {
// 仅当是“纯前缀”(如 zh-tw/ 或 zh/)时兜底注入;
$wp->query_vars['variant'] = strtolower( $m[1] );
}
}
}
if ( is_404() ) { if ( is_404() ) {
$this->config->set_noconversion_url( home_url( '/' ) ); $this->config->set_noconversion_url( home_url( '/' ) );
$this->config->set_target_lang( '' ); $this->config->set_target_lang( '' );
@ -197,29 +171,10 @@ class WPCC_Main {
// 设置无转换URL // 设置无转换URL
$noconversion_url = $this->get_noconversion_url(); $noconversion_url = $this->get_noconversion_url();
$this->config->set_noconversion_url( $noconversion_url ); $this->config->set_noconversion_url( $noconversion_url );
// 向后兼容:同步全局变量
$GLOBALS['wpcc_noconversion_url'] = $noconversion_url;
// 获取目标语言 // 获取目标语言
$target_lang = $this->determine_target_language( $wp ); $target_lang = $this->determine_target_language( $wp );
$this->config->set_target_lang( $target_lang ); $this->config->set_target_lang( $target_lang );
// 向后兼容:同步全局变量
$GLOBALS['wpcc_target_lang'] = $target_lang;
// 若首页为静态页面且请求为根级变体路径,则直接映射到首页,避免 404
if ( get_option( 'show_on_front' ) === 'page' && get_option( 'page_on_front' ) ) {
$req_path = isset( $wp->request ) ? trim( (string) $wp->request, '/' ) : '';
if ( $req_path !== '' ) {
$enabled = $this->config->get_enabled_languages();
if ( ! empty( $enabled ) ) {
$pattern = '/^(?:' . implode( '|', array_map( 'preg_quote', $enabled ) ) . '|zh|zh-reset)$/i';
if ( preg_match( $pattern, $req_path ) ) {
$wp->query_vars['page_id'] = (int) get_option( 'page_on_front' );
unset( $wp->query_vars['pagename'], $wp->query_vars['name'] );
}
}
}
}
// 处理搜索转换 // 处理搜索转换
$this->handle_search_conversion(); $this->handle_search_conversion();
@ -232,25 +187,20 @@ class WPCC_Main {
* 确定目标语言 * 确定目标语言
*/ */
private function determine_target_language( $wp ): string { private function determine_target_language( $wp ): string {
$request_lang = isset( $wp->query_vars['variant'] ) ? sanitize_text_field( $wp->query_vars['variant'] ) : ''; $request_lang = $wp->query_vars['variant'] ?? '';
$cookie_lang = isset( $_COOKIE[ 'wpcc_variant_' . COOKIEHASH ] ) ? sanitize_text_field( wp_unslash( $_COOKIE[ 'wpcc_variant_' . COOKIEHASH ] ) ) : ''; $cookie_lang = $_COOKIE[ 'wpcc_variant_' . COOKIEHASH ] ?? '';
// 检查URL参数中的语言 // 检查URL参数中的语言
if ( $request_lang && $this->config->is_language_enabled( $request_lang ) ) { if ( $request_lang && $this->config->is_language_enabled( $request_lang ) ) {
return $request_lang; return $request_lang;
} }
// 处理特殊的'zh'重定向(用户明确选择“原始/不转换”) // 处理特殊的'zh'重定向
if ( $request_lang === 'zh' && ! is_admin() ) { if ( $request_lang === 'zh' && ! is_admin() ) {
$this->handle_zh_redirect(); $this->handle_zh_redirect();
return ''; return '';
} }
// 如果Cookie中记录了'zh'哨兵,则优先恢复为“不转换”
if ( $cookie_lang === 'zh' ) {
return '';
}
// 浏览器语言检测 // 浏览器语言检测
if ( $this->config->is_feature_enabled( 'wpcc_browser_redirect' ) ) { if ( $this->config->is_feature_enabled( 'wpcc_browser_redirect' ) ) {
$browser_lang = $this->detect_browser_language(); $browser_lang = $this->detect_browser_language();
@ -276,23 +226,9 @@ class WPCC_Main {
$cookie_key = 'wpcc_variant_' . COOKIEHASH; $cookie_key = 'wpcc_variant_' . COOKIEHASH;
if ( $this->config->is_feature_enabled( 'wpcc_use_cookie_variant' ) ) { if ( $this->config->is_feature_enabled( 'wpcc_use_cookie_variant' ) ) {
setcookie( $cookie_key, 'zh', [ setcookie( $cookie_key, 'zh', time() + 30000000, COOKIEPATH, COOKIE_DOMAIN );
'expires' => time() + 30000000,
'path' => COOKIEPATH,
'domain' => COOKIE_DOMAIN,
'secure' => is_ssl(),
'httponly' => true,
'samesite' => 'Lax',
] );
} else { } else {
setcookie( 'wpcc_is_redirect_' . COOKIEHASH, '1', [ setcookie( 'wpcc_is_redirect_' . COOKIEHASH, '1', 0, COOKIEPATH, COOKIE_DOMAIN );
'expires' => 0,
'path' => COOKIEPATH,
'domain' => COOKIE_DOMAIN,
'secure' => is_ssl(),
'httponly' => true,
'samesite' => 'Lax',
] );
} }
wp_redirect( $this->config->get_noconversion_url() ); wp_redirect( $this->config->get_noconversion_url() );
@ -303,7 +239,7 @@ class WPCC_Main {
* 检测浏览器语言 * 检测浏览器语言
*/ */
private function detect_browser_language(): string { private function detect_browser_language(): string {
$accept_language = isset( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) ) : ''; $accept_language = $_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? '';
if ( empty( $accept_language ) ) { if ( empty( $accept_language ) ) {
return ''; return '';
} }
@ -327,14 +263,7 @@ class WPCC_Main {
$current_cookie = $_COOKIE[$cookie_key] ?? ''; $current_cookie = $_COOKIE[$cookie_key] ?? '';
if ( $current_cookie !== $target_lang ) { if ( $current_cookie !== $target_lang ) {
setcookie( $cookie_key, $target_lang, [ setcookie( $cookie_key, $target_lang, time() + 30000000, COOKIEPATH, COOKIE_DOMAIN );
'expires' => time() + 30000000,
'path' => COOKIEPATH,
'domain' => COOKIE_DOMAIN,
'secure' => is_ssl(),
'httponly' => true,
'samesite' => 'Lax',
] );
} }
} }
@ -355,65 +284,18 @@ class WPCC_Main {
* 模板重定向处理 * 模板重定向处理
*/ */
public function template_redirect(): void { public function template_redirect(): void {
// 排除 WooCommerce 关键页面(结算、购物车、我的账户等),避免转换影响功能
if ( function_exists( 'is_checkout' ) && is_checkout() ) {
return;
}
if ( function_exists( 'is_cart' ) && is_cart() ) {
return;
}
if ( function_exists( 'is_account_page' ) && is_account_page() ) {
return;
}
$this->set_language_urls(); $this->set_language_urls();
// 处理“根级语言前缀”访问:/zh-xx/ 或 /zh/
if ( ! is_admin() ) {
global $wp;
$req = isset( $wp->request ) ? trim( (string) $wp->request, "/" ) : '';
if ( $req !== '' ) {
$enabled = $this->config->get_enabled_languages();
$pattern = '/^(?:' . implode( '|', array_map( 'preg_quote', $enabled ) ) . '|zh|zh-reset)$/i';
if ( preg_match( $pattern, $req ) ) {
// 禁用本次自动重定向(避免将根级变体再次重定向到其它 URL
if ( method_exists( $this->config, 'set_redirect_to' ) ) {
$this->config->set_redirect_to( false );
}
$v = strtolower( $req );

// 仅 zh/zh-reset 使用哨兵重定向到不转换首页;其它根级变体在当前 URL 下渲染(避免缓存都命中 /
if ( $v === 'zh' || $v === 'zh-reset' ) {
$this->handle_zh_redirect();
return;
}

// 在根级变体 URL 下渲染首页:由下游 pre_get_posts/filter_request_vars 修正查询为首页
// 注入头部脚本供前端使用
add_action( 'wp_head', [ $this, 'output_header' ] );
}
}
}
// 处理重定向 // 处理重定向
if ( ! is_404() && $this->config->get_redirect_to() && ! is_admin() ) { if ( ! is_404() && $this->config->get_redirect_to() && ! is_admin() ) {
$redirect_url = $this->config->get_langs_urls()[ $this->config->get_redirect_to() ]; $redirect_url = $this->config->get_langs_urls()[ $this->config->get_redirect_to() ];
setcookie( 'wpcc_is_redirect_' . COOKIEHASH, '1', [ setcookie( 'wpcc_is_redirect_' . COOKIEHASH, '1', 0, COOKIEPATH, COOKIE_DOMAIN );
'expires' => 0,
'path' => COOKIEPATH,
'domain' => COOKIE_DOMAIN,
'secure' => is_ssl(),
'httponly' => true,
'samesite' => 'Lax',
] );
wp_redirect( $redirect_url, 302 ); wp_redirect( $redirect_url, 302 );
exit; exit;
} }
$target_lang = $this->config->get_target_lang(); $target_lang = $this->config->get_target_lang();
if ( ! $target_lang ) { if ( ! $target_lang ) {
// 即使是“未选择语言(不转换)”也输出头部数据,供前端开关生成漂亮链接
add_action( 'wp_head', [ $this, 'output_header' ] );
return; return;
} }
@ -430,36 +312,23 @@ class WPCC_Main {
private function set_language_urls(): void { private function set_language_urls(): void {
$langs_urls = []; $langs_urls = [];
$noconversion_url = $this->config->get_noconversion_url(); $noconversion_url = $this->config->get_noconversion_url();
$style = (int) $this->config->get_option( 'wpcc_use_permalink', 0 ); $use_permalink = $this->config->is_feature_enabled( 'wpcc_use_permalink' );
$style_effective = $this->is_permalinks_enabled() ? $style : 0;
// 语言映射
foreach ( $this->config->get_enabled_languages() as $lang ) { foreach ( $this->config->get_enabled_languages() as $lang ) {
if ( $style_effective !== 0 && $noconversion_url === home_url( '/' ) ) { if ( $noconversion_url === home_url( '/' ) && $use_permalink ) {
$langs_urls[$lang] = $noconversion_url . $lang . '/'; $langs_urls[$lang] = $noconversion_url . $lang . '/';
} else { } else {
$langs_urls[$lang] = $this->convert_link( $noconversion_url, $lang ); $langs_urls[$lang] = $this->convert_link( $noconversion_url, $lang );
} }
} }
// 增加 zh 哨兵映射,便于前端/菜单快速回到“不转换”状态
// 在前缀/后缀模式下输出漂亮链接;在查询参数模式下回退为 ?variant=zh
$langs_urls['zh'] = $this->convert_link( $noconversion_url, 'zh' );
$this->config->set_langs_urls( $langs_urls ); $this->config->set_langs_urls( $langs_urls );
// 向后兼容:同步全局变量
$GLOBALS['wpcc_langs_urls'] = $langs_urls;
} }
/** /**
* 执行转换 * 执行转换
*/ */
private function do_conversion(): void { private function do_conversion(): void {
// 若是 AJAX/REST/wc-ajax 等非 HTML 响应,直接跳过所有转换,避免破坏 JSON/片段
if ( $this->config->get_direct_conversion_flag() ) {
return;
}
// 加载转换表 // 加载转换表
$this->load_conversion_table(); $this->load_conversion_table();
@ -473,14 +342,6 @@ class WPCC_Main {
// 添加链接转换过滤器 // 添加链接转换过滤器
$this->add_link_conversion_filters(); $this->add_link_conversion_filters();

// 导航与 Logo 链接也加入变体(兼容经典菜单与站点 Logo
// 注意:不在 home_url 上挂钩,避免 convert_link 内部调用 home_url 时产生递归
add_filter( 'get_custom_logo', [ $this, 'filter_custom_logo' ], 10, 1 );
add_filter( 'wp_nav_menu', [ $this, 'filter_wp_nav_menu' ], 20, 2 );

// 取消错误的 canonical 跳转,避免语言前缀被 WP 错误重定向
add_filter( 'redirect_canonical', [ $this, 'cancel_incorrect_redirect' ], 10, 2 );
} }
// 内容转换过滤器 // 内容转换过滤器
@ -549,24 +410,16 @@ class WPCC_Main {
// 这里添加其他必要的私有方法... // 这里添加其他必要的私有方法...
/**
* 检查 WordPress 是否启用了固定链接
*/
private function is_permalinks_enabled(): bool {
$structure = get_option( 'permalink_structure' );
return is_string( $structure ) && $structure !== '';
}
/** /**
* 获取无转换URL * 获取无转换URL
*/ */
private function get_noconversion_url(): string { private function get_noconversion_url(): string {
$enabled_langs = $this->config->get_enabled_languages(); $enabled_langs = $this->config->get_enabled_languages();
$reg = implode( '|', array_map( 'preg_quote', $enabled_langs ) ); $reg = implode( '|', $enabled_langs );
$protocol = is_ssl() ? 'https://' : 'http://'; $protocol = is_ssl() ? 'https://' : 'http://';
$host = isset( $_SERVER['HTTP_HOST'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_HOST'] ) ) : ''; $host = $_SERVER['HTTP_HOST'] ?? '';
$uri = isset( $_SERVER['REQUEST_URI'] ) ? esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : ''; $uri = $_SERVER['REQUEST_URI'] ?? '';
$tmp = trim( strtolower( remove_query_arg( 'variant', $protocol . $host . $uri ) ) ); $tmp = trim( strtolower( remove_query_arg( 'variant', $protocol . $host . $uri ) ) );
@ -584,80 +437,7 @@ class WPCC_Main {
* 设置重写规则 * 设置重写规则
*/ */
private function setup_rewrite_rules(): void { private function setup_rewrite_rules(): void {
// 仅在 WP 启用了固定链接 且 插件启用了固定链接格式时设置重写规则
if ( ! $this->is_permalinks_enabled() || ! $this->config->is_feature_enabled( 'wpcc_use_permalink' ) ) {
return;
}
add_filter( 'rewrite_rules_array', [ $this, 'modify_rewrite_rules' ] ); add_filter( 'rewrite_rules_array', [ $this, 'modify_rewrite_rules' ] );

// 显式在规则顶端加入"根级变体"规则,确保 /zh-xx/ 能优先匹配到首页
$enabled = $this->config->get_enabled_languages();
if ( ! empty( $enabled ) && function_exists( 'add_rewrite_rule' ) ) {
$reg = implode( '|', array_map( 'preg_quote', $enabled ) );
add_rewrite_rule( '^(' . $reg . '|zh|zh-reset)/?$', 'index.php?variant=$matches[1]', 'top' );
// 强制在init钩子中添加根级规则确保优先级
add_action( 'init', function() use ( $reg ) {
add_rewrite_rule( '^(' . $reg . '|zh|zh-reset)/?$', 'index.php?variant=$matches[1]', 'top' );
}, 5 ); // 优先级5早于大部分其他规则
}
}

/**
* 如有必要,自动刷新一次重写规则,避免未刷新导致的 404
*/
private function maybe_autoflush_rewrite_rules(): void {
// 仅在启用了固定链接模式且 WP 启用了固定链接时尝试
if ( ! $this->config->is_feature_enabled( 'wpcc_use_permalink' ) || ! $this->is_permalinks_enabled() ) {
return;
}
// 延迟执行,确保所有规则都已加载
add_action( 'wp_loaded', [ $this, 'check_and_flush_rules' ], 10 );
}
/**
* 检查并刷新规则
*/
public function check_and_flush_rules(): void {
// 6 小时内最多尝试一次
$last = (int) get_option( 'wpcc_rewrite_autoflush_ts', 0 );
if ( $last && ( time() - $last ) < 6 * 3600 ) {
return;
}
// 检查当前规则中是否已经包含根级的语言捕获规则
$rules = get_option( 'rewrite_rules' );
if ( ! is_array( $rules ) ) {
$rules = [];
}
$enabled_langs = $this->config->get_enabled_languages();
if ( empty( $enabled_langs ) ) {
return;
}
$reg = implode( '|', $enabled_langs );
$expected = '^(' . $reg . '|zh|zh-reset)/?$';
$has_expected = false;
foreach ( $rules as $regex => $query ) {
if ( $regex === $expected && strpos( $query, 'variant=' ) !== false ) {
$has_expected = true;
break;
}
}
if ( ! $has_expected && function_exists( 'flush_rewrite_rules' ) ) {
// 刷新一次规则
flush_rewrite_rules( false );
update_option( 'wpcc_rewrite_autoflush_ts', time() );
// 记录日志
if ( defined( 'WP_DEBUG_LOG' ) && WP_DEBUG_LOG ) {
error_log( 'WPCC: Auto-flushed rewrite rules for language variants' );
}
}
} }
/** /**
@ -670,10 +450,6 @@ class WPCC_Main {
$use_permalink = $this->config->get_option( 'wpcc_use_permalink', 0 ); $use_permalink = $this->config->get_option( 'wpcc_use_permalink', 0 );
// 首先添加根级语言规则,确保最高优先级
$root_rule_key = '^(' . $reg . '|zh|zh-reset)/?$';
$rules2[ $root_rule_key ] = 'index.php?variant=$matches[1]';
if ( $use_permalink == 1 ) { if ( $use_permalink == 1 ) {
foreach ( $rules as $key => $value ) { foreach ( $rules as $key => $value ) {
if ( strpos( $key, 'trackback' ) !== false || strpos( $key, 'print' ) !== false || strpos( $value, 'lang=' ) !== false ) { if ( strpos( $key, 'trackback' ) !== false || strpos( $key, 'print' ) !== false || strpos( $value, 'lang=' ) !== false ) {
@ -698,6 +474,7 @@ class WPCC_Main {
} }
} }
$rules2[ '^(' . $reg . '|zh|zh-reset)/?$' ] = 'index.php?variant=$matches[1]';
return array_merge( $rules2, $rules ); return array_merge( $rules2, $rules );
} }
@ -712,23 +489,17 @@ class WPCC_Main {
* 处理评论提交 * 处理评论提交
*/ */
private function handle_comment_submission(): void { private function handle_comment_submission(): void {
$php_self = isset( $_SERVER['PHP_SELF'] ) ? sanitize_text_field( wp_unslash( $_SERVER['PHP_SELF'] ) ) : ''; if ( ( isset( $_SERVER['PHP_SELF'] ) && ( strpos( $_SERVER['PHP_SELF'], 'wp-comments-post.php' ) !== false
$request_method = isset( $_SERVER['REQUEST_METHOD'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_METHOD'] ) ) : ''; || strpos( $_SERVER['PHP_SELF'], 'ajax-comments.php' ) !== false
|| strpos( $_SERVER['PHP_SELF'], 'comments-ajax.php' ) !== false )
if ( ( $php_self && ( strpos( $php_self, 'wp-comments-post.php' ) !== false
|| strpos( $php_self, 'ajax-comments.php' ) !== false
|| strpos( $php_self, 'comments-ajax.php' ) !== false )
) && ) &&
$request_method === 'POST' && isset( $_SERVER["REQUEST_METHOD"] ) && $_SERVER["REQUEST_METHOD"] == "POST" &&
isset( $_POST['variant'] ) && ! empty( $_POST['variant'] ) isset( $_POST['variant'] ) && ! empty( $_POST['variant'] ) && $this->config->is_language_enabled( $_POST['variant'] )
) { ) {
$variant = sanitize_text_field( wp_unslash( $_POST['variant'] ) ); $this->config->set_target_lang( sanitize_text_field( $_POST['variant'] ) );
if ( $this->config->is_language_enabled( $variant ) ) {
$this->config->set_target_lang( $variant );
$this->do_conversion(); $this->do_conversion();
} }
} }
}
/** /**
* 修复首页查询 * 修复首页查询
@ -763,8 +534,17 @@ class WPCC_Main {
public function render_no_conversion_block( string $block_content, array $block ): string { public function render_no_conversion_block( string $block_content, array $block ): string {
if ( isset( $block['blockName'] ) && $block['blockName'] === 'wpcc/no-conversion' ) { if ( isset( $block['blockName'] ) && $block['blockName'] === 'wpcc/no-conversion' ) {
$unique_id = uniqid(); $unique_id = uniqid();
return '<!--wpcc_NC' . $unique_id . '_START-->' . $block_content . '<!--wpcc_NC' . $unique_id . '_END-->';
$pattern = '/<div[^>]*class="[^"]*wpcc-no-conversion-content[^"]*"[^>]*>(.*?)<\\/div>/s';
$replacement = function ( $matches ) use ( $unique_id ) {
$content = $matches[1];
return '<div class="wpcc-no-conversion-content"><!--wpcc_NC' . $unique_id . '_START-->' . $content . '<!--wpcc_NC' . $unique_id . '_END--></div>';
};
$block_content = preg_replace_callback( $pattern, $replacement, $block_content );
} }
return $block_content; return $block_content;
} }
@ -787,45 +567,19 @@ class WPCC_Main {
$wp_home = home_url(); $wp_home = home_url();
} }
if ( empty( $variant ) ) { if ( str_contains( $link, $variant ) ) {
return $link; return $link;
} }
// 仅当路径段或查询参数中已包含有效变体时,认为“已包含” if ( str_contains( $link, '?' ) || ! $this->config->is_feature_enabled( 'wpcc_use_permalink' ) ) {
$enabled = $this->config->get_enabled_languages();
$enabled = is_array( $enabled ) ? $enabled : [];
$variant_regex = '#/(?:' . implode( '|', array_map( 'preg_quote', $enabled ) ) . '|zh|zh-reset)(/|$)#i';

$qpos = strpos( $link, '?' );
$path = $qpos !== false ? substr( $link, 0, $qpos ) : $link;
$qs = $qpos !== false ? substr( $link, $qpos ) : '';
$path_only = parse_url( $path, PHP_URL_PATH );
if ( $path_only === null ) { $path_only = $path; }

// 如路径中已有变体,仅清理冗余的 variant 查询参数然后返回
if ( preg_match( $variant_regex, (string) $path_only ) ) {
if ( $qpos !== false ) {
$qs = preg_replace( '/([?&])variant=[^&]*(&|$)/', '$1', $qs );
$qs = rtrim( $qs, '?&' );
if ( $qs && $qs[0] !== '?' ) { $qs = '?' . ltrim( $qs, '?' ); }
}
return $path . $qs;
}

$style = (int) $this->config->get_option( 'wpcc_use_permalink', 0 );

// 查询字符串模式或未启用固定链接WP未启用固定链接时也回退到查询参数
if ( $style === 0 || ! $this->config->is_feature_enabled( 'wpcc_use_permalink' ) || ! $this->is_permalinks_enabled() ) {
return add_query_arg( 'variant', $variant, $link ); return add_query_arg( 'variant', $variant, $link );
} }
if ( $style === 1 ) { if ( $this->config->get_option( 'wpcc_use_permalink' ) == 1 ) {
// 后缀模式: .../permalink/.../zh-xx/ return user_trailingslashit( trailingslashit( $link ) . $variant );
return user_trailingslashit( trailingslashit( $path ) . $variant ) . $qs;
} }
// 前缀模式 (style 2): .../zh-xx/permalink/... return str_replace( $wp_home, "$wp_home/$variant", $link );
return str_replace( $wp_home, "$wp_home/$variant", $path ) . $qs;
} }
/** /**
@ -853,195 +607,15 @@ class WPCC_Main {
add_filter( 'feed_link', [ $this, 'filter_link_conversion' ] ); add_filter( 'feed_link', [ $this, 'filter_link_conversion' ] );
add_filter( 'attachment_link', [ $this, 'filter_link_conversion' ] ); add_filter( 'attachment_link', [ $this, 'filter_link_conversion' ] );
add_filter( 'search_feed_link', [ $this, 'filter_link_conversion' ] ); add_filter( 'search_feed_link', [ $this, 'filter_link_conversion' ] );

// 添加链接修复过滤器(重要!)
if ( function_exists( 'wpcc_fix_link_conversion' ) ) {
add_filter( 'category_feed_link', 'wpcc_fix_link_conversion' );
add_filter( 'tag_feed_link', 'wpcc_fix_link_conversion' );
add_filter( 'author_feed_link', 'wpcc_fix_link_conversion' );
add_filter( 'post_comments_feed_link', 'wpcc_fix_link_conversion' );
add_filter( 'get_comments_pagenum_link', 'wpcc_fix_link_conversion' );
add_filter( 'get_comment_link', 'wpcc_fix_link_conversion' );
}

// 添加取消转换过滤器
if ( function_exists( 'wpcc_cancel_link_conversion' ) ) {
add_filter( 'attachment_link', 'wpcc_cancel_link_conversion' );
add_filter( 'trackback_url', 'wpcc_cancel_link_conversion' );
}

// 添加分页链接修复
if ( function_exists( 'wpcc_pagenum_link_fix' ) ) {
add_filter( 'get_pagenum_link', 'wpcc_pagenum_link_fix' );
}
} }
/** /**
* 过滤链接转换 * 过滤链接转换
*/ */
public function filter_link_conversion( string $link ): string { public function filter_link_conversion( string $link ): string {
// 使用全局函数以保持一致性
if ( function_exists( 'wpcc_link_conversion' ) ) {
return wpcc_link_conversion( $link, $this->config->get_target_lang() );
}
return $this->convert_link( $link, $this->config->get_target_lang() ); return $this->convert_link( $link, $this->config->get_target_lang() );
} }
/**
* 过滤 request 阶段的 query_vars
* - 当 pagename 等于语言前缀zh-xx|zh|zh-reset注入 variant 并指向首页,避免 404
*/
public function filter_request_vars( array $qv ): array {
if ( is_admin() ) { return $qv; }
$enabled = $this->config->get_enabled_languages();
if ( empty( $enabled ) ) { return $qv; }
$candidate = '';
if ( isset( $qv['pagename'] ) && is_string( $qv['pagename'] ) ) {
$candidate = trim( $qv['pagename'], '/' );
} elseif ( isset( $qv['name'] ) && is_string( $qv['name'] ) ) {
$candidate = trim( $qv['name'], '/' );
}
if ( $candidate === '' ) { return $qv; }
$langs = array_map( 'strtolower', $enabled );
$candidate_l = strtolower( $candidate );
if ( in_array( $candidate_l, $langs, true ) || $candidate_l === 'zh' || $candidate_l === 'zh-reset' ) {
// 注入 variant
$qv['variant'] = $candidate_l;
// 指向首页
if ( 'page' === get_option( 'show_on_front' ) && get_option( 'page_on_front' ) ) {
$qv['page_id'] = (int) get_option( 'page_on_front' );
if ( ! empty( $qv['paged'] ) ) { $qv['page'] = $qv['paged']; unset( $qv['paged'] ); }
} else {
// 文章作为首页:移除 pagename/name交由 is_home 处理
unset( $qv['pagename'], $qv['name'] );
}
}
return $qv;
}

/**
* 过滤 home_url使首页链接带上变体仅前台且存在目标语言时
*/
public function filter_home_url( string $url, string $path, ?string $orig_scheme, ?int $blog_id ): string {
if ( is_admin() ) { return $url; }
$target = $this->config->get_target_lang();
if ( ! $target ) { return $url; }
return $this->convert_link( $url, $target );
}

/**
* 调整自定义 Logo 的链接 href仅前台且存在目标语言时
*/
public function filter_custom_logo( string $html ): string {
if ( is_admin() ) { return $html; }
$target = $this->config->get_target_lang();
if ( ! $target ) { return $html; }
$home = home_url();
$that = $this;
$out = preg_replace_callback('/href=(\"|\')(.*?)(\1)/i', function($m) use ($home, $target, $that) {
$href = $m[2];
if ( strpos( $href, $home ) === 0 ) {
$new = $that->convert_link( $href, $target );
return 'href=' . $m[1] . esc_url( $new ) . $m[1];
}
return $m[0];
}, $html );
return $out ?: $html;
}

/**
* 调整经典菜单wp_nav_menu输出中的 href仅前台且存在目标语言时
*/
public function filter_wp_nav_menu( string $nav_menu, $args ): string {
if ( is_admin() ) { return $nav_menu; }
$target = $this->config->get_target_lang();
if ( ! $target ) { return $nav_menu; }
$home = home_url();
$that = $this;
$out = preg_replace_callback('/href=(\"|\')(.*?)(\1)/i', function($m) use ($home, $target, $that) {
$href = html_entity_decode( $m[2] );
// 仅转换本站链接
if ( strpos( $href, $home ) === 0 ) {
$new = $that->convert_link( $href, $target );
return 'href=' . $m[1] . esc_url( $new ) . $m[1];
}
return $m[0];
}, $nav_menu );
return $out ?: $nav_menu;
}

/**
* pre_get_posts 兜底:当请求仅为语言前缀时,强制首页查询,避免 404。
*/
public function pre_get_posts_fix( $q ): void {
if ( is_admin() || ! $q || ! method_exists( $q, 'is_main_query' ) || ! $q->is_main_query() ) {
return;
}
$enabled = $this->config->get_enabled_languages();
if ( empty( $enabled ) ) { return; }
$path = isset( $_SERVER['REQUEST_URI'] ) ? (string) $_SERVER['REQUEST_URI'] : '';
if ( $path === '' ) { return; }
$path = trim( $path, '/' );
$candidate = $path;
// 仅当整个路径就是语言前缀(忽略末尾斜杠和查询串)
if ( strpos( $candidate, '/' ) !== false ) {
// 含有更多段,忽略
return;
}
$langs = array_map( 'strtolower', $enabled );
$candidate_l = strtolower( $candidate );
if ( in_array( $candidate_l, $langs, true ) || $candidate_l === 'zh' || $candidate_l === 'zh-reset' ) {
// 注入 variant
$q->set( 'variant', $candidate_l );
// 修正首页查询
if ( 'page' === get_option( 'show_on_front' ) && get_option( 'page_on_front' ) ) {
$q->set( 'page_id', (int) get_option( 'page_on_front' ) );
if ( $q->get( 'paged' ) ) { $q->set( 'page', $q->get( 'paged' ) ); $q->set( 'paged', 0 ); }
} else {
$q->set( 'pagename', '' );
$q->set( 'name', '' );
}
// 避免被错误判为 404
if ( method_exists( $q, 'is_404' ) ) {
$q->is_404 = false;
}
}
}

/**
* 取消错误 canonical 跳转,参考旧版逻辑
*/
public function cancel_incorrect_redirect( $redirect_to, $redirect_from ) {
if ( ! is_string( $redirect_to ) || ! is_string( $redirect_from ) ) { return $redirect_to; }

// 动态构建允许的语言前缀集合
$langs = method_exists( 'WPCC_Language_Config', 'get_valid_language_codes' ) ? WPCC_Language_Config::get_valid_language_codes() : [ 'zh-cn','zh-tw','zh-hk','zh-hans','zh-hant','zh-sg','zh-jp' ];
$reg = implode( '|', array_map( 'preg_quote', $langs ) );

// 如果来源是变体路径(根级或包含变体段),阻止将其规范化到非变体路径(例如首页 /
if ( preg_match( '/\/(' . $reg . '|zh|zh-reset)(\/|$)/i', $redirect_from ) ) {
// 允许仅修正末尾斜杠
global $wp_rewrite;
if ( ( $wp_rewrite && $wp_rewrite->use_trailing_slashes && substr( $redirect_from, -1 ) != '/' ) ||
( $wp_rewrite && ! $wp_rewrite->use_trailing_slashes && substr( $redirect_from, -1 ) == '/' ) ) {
return user_trailingslashit( $redirect_from );
}
return false; // 阻止从变体路径跳到非变体路径
}

// 如果目标是变体路径,确保不因斜杠规范导致错误跳转
if ( preg_match( '/\/(' . $reg . '|zh|zh-reset)(\/|$)/i', $redirect_to ) ) {
global $wp_rewrite;
if ( ( $wp_rewrite && $wp_rewrite->use_trailing_slashes && substr( $redirect_from, -1 ) != '/' ) ||
( $wp_rewrite && ! $wp_rewrite->use_trailing_slashes && substr( $redirect_from, -1 ) == '/' ) ) {
return user_trailingslashit( $redirect_from );
}
return false; // 阻止多余跳转
}

return $redirect_to;
}
/** /**
* 添加内容转换过滤器 * 添加内容转换过滤器
*/ */
@ -1074,19 +648,11 @@ class WPCC_Main {
$noconversion_url = $this->config->get_noconversion_url(); $noconversion_url = $this->config->get_noconversion_url();
$langs_urls = $this->config->get_langs_urls(); $langs_urls = $this->config->get_langs_urls();
// 当启用了“浏览器语言”或“Cookie偏好”的显示模式时为“不转换”链接注入 zh 哨兵,确保能覆盖浏览器/Cookie策略
$browser_pref = (int) $this->config->get_option( 'wpcc_browser_redirect', 0 );
$cookie_pref = (int) $this->config->get_option( 'wpcc_use_cookie_variant', 0 );
$noconv_forced = $noconversion_url;
if ( ($browser_pref === 2 || $cookie_pref === 2) && $target_lang ) {
$noconv_forced = $this->convert_link( $noconversion_url, 'zh' );
}
echo "\n" . '<!-- WP Chinese Converter Plugin Version ' . esc_html( wpcc_VERSION ) . ' -->'; echo "\n" . '<!-- WP Chinese Converter Plugin Version ' . esc_html( wpcc_VERSION ) . ' -->';
$script_data = [ $script_data = [
'wpcc_target_lang' => $target_lang ? esc_js( $target_lang ) : '', 'wpcc_target_lang' => $target_lang ? esc_js( $target_lang ) : '',
'wpcc_noconversion_url' => $noconv_forced ? esc_url( $noconv_forced ) : '', 'wpcc_noconversion_url' => $noconversion_url ? esc_url( $noconversion_url ) : '',
'wpcc_langs_urls' => [] 'wpcc_langs_urls' => []
]; ];
@ -1133,7 +699,7 @@ class WPCC_Main {
if ( $target_lang && ! $this->config->get_direct_conversion_flag() ) { if ( $target_lang && ! $this->config->get_direct_conversion_flag() ) {
$home_url = $this->convert_link( home_url( '/' ), $target_lang ); $home_url = $this->convert_link( home_url( '/' ), $target_lang );
$home_pattern = preg_quote( esc_url( home_url( '' ) ), '|' ); $home_pattern = preg_quote( esc_url( home_url( '' ) ), '|' );
$buffer = preg_replace( '|(<a\s(?!class=\"wpcc_link\")[^<>]*?href=([\'\"]))' . $home_pattern . '/?(\2[^<>]*?>)|', '${1}' . esc_url( $home_url ) . '${3}', $buffer ); $buffer = preg_replace( '|(<a\s(?!class="wpcc_link")[^<>]*?href=([\'"]]))' . $home_pattern . '/?(\2[^<>]*?>)|', '${1}' . esc_url( $home_url ) . '${3}', $buffer );
} }
return zhconversion2( $buffer ) . "\n" . '<!-- WP Chinese Converter Full Page Converted. Target Lang: ' . $target_lang . ' -->'; return zhconversion2( $buffer ) . "\n" . '<!-- WP Chinese Converter Full Page Converted. Target Lang: ' . $target_lang . ' -->';
} }

View file

@ -1,130 +0,0 @@
<?php

if ( ! defined( 'ABSPATH' ) ) {
exit;
}

require_once dirname( __FILE__ ) . '/interface-converter.php';

class WPCC_MediaWiki_Converter implements WPCC_Converter_Interface {
private $conversion_tables_loaded = false;

public function convert( $text, $target_variant ) {
if ( empty( $text ) ) {
return $text;
}

// 检查缓存
$cached_result = WPCC_Conversion_Cache::get_cached_conversion( $text, $target_variant );
if ( $cached_result !== null ) {
return $cached_result;
}

// 回滚:始终加载所有转换表
$this->load_all_conversion_tables();

$result = $text;
switch ( $target_variant ) {
case 'zh-hans':
$result = $this->convert_to_hans( $text );
break;
case 'zh-hant':
$result = $this->convert_to_hant( $text );
break;
case 'zh-cn':
$result = $this->convert_to_cn( $text );
break;
case 'zh-tw':
$result = $this->convert_to_tw( $text );
break;
case 'zh-hk':
$result = $this->convert_to_hk( $text );
break;
case 'zh-sg':
$result = $this->convert_to_sg( $text );
break;
}

// 缓存转换结果
if ( $result !== $text ) {
WPCC_Conversion_Cache::set_cached_conversion( $text, $target_variant, $result );
}

return $result;
}

public function get_supported_variants() {
return array( 'zh-hans', 'zh-hant', 'zh-cn', 'zh-tw', 'zh-hk', 'zh-sg' );
}

public function get_engine_name() {
return 'mediawiki';
}

public function get_engine_info() {
return array(
'name' => 'MediaWiki',
'version' => '1.23.5',
'description' => '基于MediaWiki的字符映射转换引擎',
'features' => array(
'字符级精确映射',
'快速转换速度',
'良好的兼容性',
'支持多地区变体'
),
'conversion_type' => 'character_mapping'
);
}

public function is_available() {
$conversion_file = dirname( dirname( __FILE__ ) ) . '/core/ZhConversion.php';
return file_exists( $conversion_file );
}

private function load_all_conversion_tables() {
if ( $this->conversion_tables_loaded ) {
return;
}

global $zh2Hans;
if ( ! isset( $zh2Hans ) ) {
require_once dirname( dirname( __FILE__ ) ) . '/core/ZhConversion.php';
$extra_conversion_file = WP_CONTENT_DIR . '/extra_zhconversion.php';
if ( file_exists( $extra_conversion_file ) ) {
require_once $extra_conversion_file;
}
}

$this->conversion_tables_loaded = true;
}

private function convert_to_hans( $text ) {
global $zh2Hans;
return strtr( $text, $zh2Hans );
}

private function convert_to_hant( $text ) {
global $zh2Hant;
return strtr( $text, $zh2Hant );
}

private function convert_to_cn( $text ) {
global $zh2Hans, $zh2CN;
return strtr( strtr( $text, $zh2CN ), $zh2Hans );
}

private function convert_to_tw( $text ) {
global $zh2Hant, $zh2TW;
return strtr( strtr( $text, $zh2TW ), $zh2Hant );
}

private function convert_to_hk( $text ) {
global $zh2Hant, $zh2HK;
return strtr( strtr( $text, $zh2HK ), $zh2Hant );
}

private function convert_to_sg( $text ) {
global $zh2Hans, $zh2SG;
return strtr( strtr( $text, $zh2SG ), $zh2Hans );
}
}

View file

@ -1,233 +0,0 @@
<?php

if ( ! defined( 'ABSPATH' ) ) {
exit;
}

require_once dirname( __FILE__ ) . '/interface-converter.php';

use Overtrue\PHPOpenCC\OpenCC;
use Overtrue\PHPOpenCC\Strategy;
use Overtrue\PHPOpenCC\Dictionary;

class WPCC_OpenCC_Converter implements WPCC_Converter_Interface {
/**
* 预处理并排序后的字典缓存(按策略)
* @var array<string, array<array<string,string>>> 每个策略对应若干个字典映射,保持原有顺序
*/
private $prepared = [];

/**
* 是否包含中文字符的快速检测(避免不必要的 OpenCC 调用)
*/
private function contains_chinese($text) {
return is_string($text) && preg_match('/[\x{4e00}-\x{9fff}]/u', $text) === 1;
}

/**
* 获取预处理后的字典(已展开并按键长度降序排序),仅在本请求内执行一次
*/
private function get_prepared_dictionaries(string $strategy): array {
if (isset($this->prepared[$strategy])) {
return $this->prepared[$strategy];
}

$sets = Dictionary::get($strategy); // 与 upstream 一致的字典结构
$prepared = [];

foreach ($sets as $dictionary) {
// 可能是分组数组(数组的数组),需要先展平
if (is_array(reset($dictionary))) {
$flat = [];
foreach ($dictionary as $dict) {
$flat = array_merge($flat, $dict);
}
// 按键长度降序排序,优先匹配长词
uksort($flat, function ($a, $b) {
return mb_strlen($b) <=> mb_strlen($a);
});
$prepared[] = $flat;
continue;
}

// 普通单字典
$flat = $dictionary;
uksort($flat, function ($a, $b) {
return mb_strlen($b) <=> mb_strlen($a);
});
$prepared[] = $flat;
}

$this->prepared[$strategy] = $prepared;
return $prepared;
}

private $strategy_map = array(
'zh-hans' => Strategy::TRADITIONAL_TO_SIMPLIFIED,
'zh-hant' => Strategy::SIMPLIFIED_TO_TRADITIONAL,
'zh-cn' => Strategy::TRADITIONAL_TO_SIMPLIFIED,
'zh-tw' => Strategy::SIMPLIFIED_TO_TAIWAN_WITH_PHRASE,
'zh-hk' => Strategy::SIMPLIFIED_TO_HONGKONG,
'zh-sg' => Strategy::TRADITIONAL_TO_SIMPLIFIED,
'zh-jp' => Strategy::SIMPLIFIED_TO_JAPANESE,
);
public function convert( $text, $target_variant ) {
if ( empty( $text ) ) {
return $text;
}
// 快速路径:非中文内容不做转换
if ( ! $this->contains_chinese( $text ) ) {
return $text;
}

if ( ! $this->is_available() ) {
throw new Exception( 'OpenCC library is not available' );
}
if ( ! isset( $this->strategy_map[ $target_variant ] ) ) {
return $text;
}
try {
// 使用预处理字典避免每次调用时重复排序
$strategy = $this->strategy_map[ $target_variant ];
$dictionaries = $this->get_prepared_dictionaries($strategy);
$output = $text;
foreach ($dictionaries as $dict) {
$output = strtr($output, $dict);
}
return $output;
} catch ( Exception $e ) {
error_log( 'WPCC OpenCC Conversion Error: ' . $e->getMessage() );
return $text;
}
}
public function get_supported_variants() {
return array_keys( $this->strategy_map );
}
public function get_engine_name() {
return 'opencc';
}
public function get_engine_info() {
return array(
'name' => 'OpenCC',
'version' => '1.2.1',
'description' => '基于 OpenCC 的智能词汇级转换引擎',
'features' => array(
'词汇级别转换',
'异体字转换',
'地区习惯用词转换',
'智能语境分析',
'支持批量转换'
),
'memory_usage' => '按需加载,内存占用较低',
'conversion_type' => 'vocabulary_based'
);
}
public function is_available() {
return class_exists( 'Overtrue\PHPOpenCC\OpenCC' );
}
public function batch_convert( $texts, $target_variant ) {
if ( ! $this->is_available() ) {
return $texts;
}
if ( ! isset( $this->strategy_map[ $target_variant ] ) ) {
return $texts;
}
if ( ! is_array( $texts ) ) {
return $texts;
}
// 检查缓存
$cached_results = array();
$uncached_texts = array();
$uncached_indices = array();
foreach ( $texts as $index => $text ) {
$cached_result = WPCC_Conversion_Cache::get_cached_conversion( $text, $target_variant );
if ( $cached_result !== null ) {
$cached_results[ $index ] = $cached_result;
} else if ( ! $this->contains_chinese( $text ) ) {
// 非中文内容:无需调用 OpenCC直接作为“已处理”内容返回
$cached_results[ $index ] = $text;
} else {
$uncached_texts[] = $text;
$uncached_indices[] = $index;
}
}
// 如果所有内容都在缓存中,直接返回
if ( empty( $uncached_texts ) ) {
$results = array();
foreach ( $texts as $index => $text ) {
$results[] = $cached_results[ $index ];
}
return $results;
}
try {
// 使用预处理字典一次性转换,避免每次排序
$strategy = $this->strategy_map[ $target_variant ];
$dictionaries = $this->get_prepared_dictionaries($strategy);

$converted_texts = $uncached_texts;
foreach ($dictionaries as $dict) {
$converted_texts = array_map(function ($str) use ($dict) {
return strtr($str, $dict);
}, $converted_texts);
}
// 合并结果并缓存新转换的内容
$results = array();
$uncached_index = 0;
foreach ( $texts as $index => $original_text ) {
if ( isset( $cached_results[ $index ] ) ) {
$results[] = $cached_results[ $index ];
} else {
$converted_text = is_array( $converted_texts ) ? $converted_texts[ $uncached_index ] : $converted_texts;
$results[] = $converted_text;
// 缓存转换结果
if ( $converted_text !== $original_text ) {
WPCC_Conversion_Cache::set_cached_conversion( $original_text, $target_variant, $converted_text );
}
$uncached_index++;
}
}
return $results;
} catch ( Exception $e ) {
error_log( 'WPCC OpenCC Batch Conversion Error: ' . $e->getMessage() );
return $texts;
}
}
public function get_available_strategies() {
return $this->strategy_map;
}
public function convert_with_strategy( $text, $strategy ) {
if ( empty( $text ) || ! $this->is_available() ) {
return $text;
}
try {
return OpenCC::convert( $text, $strategy );
} catch ( Exception $e ) {
error_log( 'WPCC OpenCC Strategy Conversion Error: ' . $e->getMessage() );
return $text;
}
}
}

View file

@ -72,7 +72,7 @@ final class WPCC_Presets {


'wpcc_use_fullpage_conversion' => 0, 'wpcc_use_fullpage_conversion' => 0,


'wpcc_no_conversion_tag' => '', 'wpcc_no_conversion_tag' => 'pre,code,pre.wp-block-code,pre.wp-block-preformatted,script,noscript,style,kbd,samp',
'wpcc_no_conversion_ja' => 0, 'wpcc_no_conversion_ja' => 0,
'wpcc_no_conversion_qtag' => 0, 'wpcc_no_conversion_qtag' => 0,



View file

@ -10,7 +10,7 @@ class WPCC_Cache_Addon extends WPCC_Abstract_Module {
public function init() { public function init() {
$this->name = 'Cache Addon'; $this->name = 'Cache Addon';
$this->version = '1.5'; $this->version = '2.0.0';
$this->description = '缓存插件兼容性模块,支持多种缓存插件'; $this->description = '缓存插件兼容性模块,支持多种缓存插件';
$this->dependencies = array(); $this->dependencies = array();

View file

@ -13,7 +13,7 @@ class WPCC_Modern_Cache extends WPCC_Abstract_Module {
public function init() { public function init() {
$this->name = 'Modern Cache'; $this->name = 'Modern Cache';
$this->version = '1.5'; $this->version = '1.0.0';
$this->description = '现代缓存模块支持对象缓存、Redis、Memcached'; $this->description = '现代缓存模块支持对象缓存、Redis、Memcached';
$this->dependencies = array(); $this->dependencies = array();

View file

@ -10,15 +10,14 @@ class WPCC_Network extends WPCC_Abstract_Module {
public function init() { public function init() {
$this->name = 'Network Multisite'; $this->name = 'Network Multisite';
$this->version = '1.5'; $this->version = '1.0.0';
$this->description = '多站点网络支持模块'; $this->description = '多站点网络支持模块';
$this->dependencies = array( $this->dependencies = array(
array( 'type' => 'function', 'name' => 'is_multisite' ) array( 'type' => 'function', 'name' => 'is_multisite' )
); );
if ( $this->is_enabled() && is_multisite() ) { if ( $this->is_enabled() && is_multisite() ) {
// 移除重复的网络管理菜单注册使用主要的wp-chinese-converter菜单 add_action( 'network_admin_menu', array( $this, 'add_network_admin_menu' ) );
// add_action( 'network_admin_menu', array( $this, 'add_network_admin_menu' ) );
add_action( 'wpmu_new_blog', array( $this, 'setup_new_site' ), 10, 6 ); add_action( 'wpmu_new_blog', array( $this, 'setup_new_site' ), 10, 6 );
add_action( 'wp_initialize_site', array( $this, 'initialize_site_settings' ) ); add_action( 'wp_initialize_site', array( $this, 'initialize_site_settings' ) );
} }
@ -139,7 +138,7 @@ $this->version = '1.5';
$this->setup_new_site( $new_site->blog_id, 0, '', '', 0, array() ); $this->setup_new_site( $new_site->blog_id, 0, '', '', 0, array() );
} }
public function display_sites_status() { private function display_sites_status() {
$sites = get_sites( array( 'number' => 50 ) ); $sites = get_sites( array( 'number' => 50 ) );
echo '<table class="wp-list-table widefat fixed striped">'; echo '<table class="wp-list-table widefat fixed striped">';
@ -167,10 +166,19 @@ $this->version = '1.5';
echo '</tbody></table>'; echo '</tbody></table>';
} }
// 同步功能已移至网络设置统一管理 public function sync_settings_to_all_sites( $settings ) {
// public function sync_settings_to_all_sites( $settings ) { $sites = get_sites();
// // 此功能已由网络管理模块统一处理
// } foreach ( $sites as $site ) {
switch_to_blog( $site->blog_id );
$current_options = get_option( 'wpcc_options', array() );
$updated_options = array_merge( $current_options, $settings );
update_option( 'wpcc_options', $updated_options );
restore_current_blog();
}
}
public function get_network_statistics() { public function get_network_statistics() {
$sites = get_sites(); $sites = get_sites();

View file

@ -5,7 +5,7 @@ if ( ! defined( 'ABSPATH' ) ) {
} }


require_once dirname( dirname( __FILE__ ) ) . '/core/abstract-module.php'; require_once dirname( dirname( __FILE__ ) ) . '/core/abstract-module.php';
require_once dirname( dirname( __FILE__ ) ) . '/core/class-wpcc-converter-factory.php'; require_once dirname( dirname( __FILE__ ) ) . '/core/class-converter-factory.php';


class WPCC_Rest_Api extends WPCC_Abstract_Module { class WPCC_Rest_Api extends WPCC_Abstract_Module {
@ -13,7 +13,7 @@ class WPCC_Rest_Api extends WPCC_Abstract_Module {
public function init() { public function init() {
$this->name = 'REST API'; $this->name = 'REST API';
$this->version = '1.5'; $this->version = '1.0.0';
$this->description = 'REST API 接口模块,提供转换服务的 API 端点'; $this->description = 'REST API 接口模块,提供转换服务的 API 端点';
$this->dependencies = array( $this->dependencies = array(
array( 'type' => 'class', 'name' => 'WP_REST_Server' ), array( 'type' => 'class', 'name' => 'WP_REST_Server' ),

View file

@ -13,7 +13,7 @@ class WPCC_SEO_Enhancement extends WPCC_Abstract_Module {
public function init() { public function init() {
$this->name = 'SEO Enhancement'; $this->name = 'SEO Enhancement';
$this->version = '1.5'; $this->version = '1.0.0';
$this->description = 'SEO插件增强模块支持Yoast SEO、RankMath、AIOSEO等主流SEO插件'; $this->description = 'SEO插件增强模块支持Yoast SEO、RankMath、AIOSEO等主流SEO插件';
$this->dependencies = array(); $this->dependencies = array();
@ -22,7 +22,6 @@ $this->version = '1.5';
'enable_rankmath_integration' => true, 'enable_rankmath_integration' => true,
'enable_aioseo_integration' => true, 'enable_aioseo_integration' => true,
'enable_hreflang_tags' => true, 'enable_hreflang_tags' => true,
'enable_hreflang_x_default' => true,
'enable_schema_conversion' => true, 'enable_schema_conversion' => true,
'enable_meta_conversion' => true, 'enable_meta_conversion' => true,
'hreflang_x_default' => 'zh-cn' 'hreflang_x_default' => 'zh-cn'
@ -99,7 +98,6 @@ $this->version = '1.5';
if ( ! empty( $wpcc_options ) ) { if ( ! empty( $wpcc_options ) ) {
$this->settings['enable_hreflang_tags'] = isset( $wpcc_options['wpcc_enable_hreflang_tags'] ) ? $wpcc_options['wpcc_enable_hreflang_tags'] : 1; $this->settings['enable_hreflang_tags'] = isset( $wpcc_options['wpcc_enable_hreflang_tags'] ) ? $wpcc_options['wpcc_enable_hreflang_tags'] : 1;
$this->settings['enable_hreflang_x_default'] = isset( $wpcc_options['wpcc_enable_hreflang_x_default'] ) ? $wpcc_options['wpcc_enable_hreflang_x_default'] : 1;
$this->settings['enable_schema_conversion'] = isset( $wpcc_options['wpcc_enable_schema_conversion'] ) ? $wpcc_options['wpcc_enable_schema_conversion'] : 1; $this->settings['enable_schema_conversion'] = isset( $wpcc_options['wpcc_enable_schema_conversion'] ) ? $wpcc_options['wpcc_enable_schema_conversion'] : 1;
$this->settings['enable_meta_conversion'] = isset( $wpcc_options['wpcc_enable_meta_conversion'] ) ? $wpcc_options['wpcc_enable_meta_conversion'] : 1; $this->settings['enable_meta_conversion'] = isset( $wpcc_options['wpcc_enable_meta_conversion'] ) ? $wpcc_options['wpcc_enable_meta_conversion'] : 1;
$this->settings['hreflang_x_default'] = isset( $wpcc_options['wpcc_hreflang_x_default'] ) ? $wpcc_options['wpcc_hreflang_x_default'] : 'zh-cn'; $this->settings['hreflang_x_default'] = isset( $wpcc_options['wpcc_hreflang_x_default'] ) ? $wpcc_options['wpcc_hreflang_x_default'] : 'zh-cn';
@ -217,16 +215,6 @@ $this->version = '1.5';
} }
public function output_hreflang_tags() { public function output_hreflang_tags() {
// 防抖:确保仅输出一次,避免重复标签
static $wpcc_hreflang_printed = false;
if ( $wpcc_hreflang_printed ) {
return;
}
$wpcc_hreflang_printed = true;
// 确保在输出前同步一次全局设置(避免执行顺序导致读取到默认值)
$this->sync_global_settings();
if ( ! $this->settings['enable_hreflang_tags'] ) { if ( ! $this->settings['enable_hreflang_tags'] ) {
return; return;
} }
@ -237,9 +225,7 @@ $this->version = '1.5';
return; return;
} }
// 需要至少两个语言版本才有意义 if ( count( $wpcc_options['wpcc_used_langs'] ) < 2 ) {
$languages = array_values( array_unique( array_filter( $wpcc_options['wpcc_used_langs'] ) ) );
if ( count( $languages ) < 2 ) {
return; return;
} }
@ -248,18 +234,23 @@ $this->version = '1.5';
return; return;
} }
$printed = []; $languages = $wpcc_options['wpcc_used_langs'];
foreach ( $languages as $lang ) { foreach ( $languages as $lang ) {
if ( empty( $lang ) ) {
continue;
}
$lang_url = $this->get_language_url( $current_url, $lang ); $lang_url = $this->get_language_url( $current_url, $lang );
$hreflang = $this->get_hreflang_code( $lang ); $hreflang = $this->get_hreflang_code( $lang );
if ( $lang_url && $hreflang && empty( $printed[ $hreflang ] ) ) {
$printed[ $hreflang ] = $lang_url; if ( ! empty( $lang_url ) && ! empty( $hreflang ) ) {
echo '<link rel="alternate" hreflang="' . esc_attr( $hreflang ) . '" href="' . esc_url( $lang_url ) . '" />' . "\n"; echo '<link rel="alternate" hreflang="' . esc_attr( $hreflang ) . '" href="' . esc_url( $lang_url ) . '" />' . "\n";
} }
} }
$default_lang = $this->settings['hreflang_x_default']; $default_lang = $this->settings['hreflang_x_default'];
if ( $this->settings['enable_hreflang_x_default'] && ! empty( $default_lang ) ) { if ( ! empty( $default_lang ) ) {
$default_url = $this->get_language_url( $current_url, $default_lang ); $default_url = $this->get_language_url( $current_url, $default_lang );
if ( ! empty( $default_url ) ) { if ( ! empty( $default_url ) ) {
echo '<link rel="alternate" hreflang="x-default" href="' . esc_url( $default_url ) . '" />' . "\n"; echo '<link rel="alternate" hreflang="x-default" href="' . esc_url( $default_url ) . '" />' . "\n";
@ -269,27 +260,17 @@ $this->version = '1.5';
private function get_current_url() { private function get_current_url() {
if ( is_admin() ) { if ( is_admin() ) {
return home_url( '/' ); return home_url();
}
// 优先使用规范链接避免把内部查询变量year/monthnum/day/name 等)拼进 URL
if ( is_singular() ) {
$object_id = get_queried_object_id();
if ( $object_id ) {
$url = get_permalink( $object_id );
return trailingslashit( remove_query_arg( 'variant', $url ) );
}
}
// 搜索页保留搜索参数,其它归一化为当前路径,无附加查询串
if ( is_search() ) {
$url = get_search_link();
return trailingslashit( remove_query_arg( 'variant', $url ) );
} }
global $wp; global $wp;
$url = home_url( $wp->request ); $current_url = home_url( $wp->request );
return trailingslashit( remove_query_arg( 'variant', $url ) );
if ( ! empty( $wp->query_string ) ) {
$current_url .= '?' . $wp->query_string;
}
return trailingslashit( $current_url );
} }
private function get_language_url( $url, $lang ) { private function get_language_url( $url, $lang ) {
@ -297,41 +278,22 @@ $this->version = '1.5';
$clean_url = remove_query_arg( 'variant', $url ); $clean_url = remove_query_arg( 'variant', $url );
// 允许的语言前缀集合(用于剥离已有语言段)
$valid_codes = method_exists( 'WPCC_Language_Config', 'get_valid_language_codes' )
? WPCC_Language_Config::get_valid_language_codes()
: array( 'zh-cn','zh-tw','zh-hk','zh-hans','zh-hant','zh-sg','zh-jp','zh','zh-reset' );
$valid_regex = implode( '|', array_map( 'preg_quote', $valid_codes ) );
// 仅 query 形式(模式 0
if ( empty( $wpcc_options['wpcc_use_permalink'] ) ) { if ( empty( $wpcc_options['wpcc_use_permalink'] ) ) {
return add_query_arg( 'variant', $lang, $clean_url ); return add_query_arg( 'variant', $lang, $clean_url );
} }
$permalink_type = intval( $wpcc_options['wpcc_use_permalink'] ); $permalink_type = intval( $wpcc_options['wpcc_use_permalink'] );
$parsed_url = parse_url( $clean_url );
$scheme = isset( $parsed_url['scheme'] ) ? $parsed_url['scheme'] : ( is_ssl() ? 'https' : 'http' );
$host = isset( $parsed_url['host'] ) ? $parsed_url['host'] : '';
$base = $scheme . '://' . $host;
$path = isset( $parsed_url['path'] ) ? $parsed_url['path'] : '/';
$query = isset( $parsed_url['query'] ) && $parsed_url['query'] !== '' ? '?' . $parsed_url['query'] : '';
// 统一剥离开头的语言前缀
$path = preg_replace( '#^/(?:' . $valid_regex . ')(/|$)#i', '/$1', $path );
if ( $path === '' ) { $path = '/'; }
switch ( $permalink_type ) { switch ( $permalink_type ) {
case 1: case 1:
// 语言段后缀:去掉末尾已存在的语言段再追加 $clean_url = rtrim( $clean_url, '/' );
$path = rtrim( $path, '/' ); return $clean_url . '/' . $lang . '/';
$path = preg_replace( '#/(?:' . $valid_regex . ')/?$#i', '', $path );
return $base . rtrim( $path, '/' ) . '/' . $lang . '/' . $query;
case 2: case 2:
// 语言段前缀:在剥离后的路径前面加上新语言前缀 $parsed_url = parse_url( $clean_url );
$path = '/' . ltrim( $path, '/' ); $base_url = $parsed_url['scheme'] . '://' . $parsed_url['host'];
// 确保不会出现双斜杠 $path = isset( $parsed_url['path'] ) ? $parsed_url['path'] : '/';
$final = rtrim( $base, '/' ) . '/' . $lang . rtrim( $path, '/' ) . '/'; $query = isset( $parsed_url['query'] ) ? '?' . $parsed_url['query'] : '';
return $query ? ( $final . $query ) : $final; return $base_url . '/' . $lang . $path . $query;
default: default:
return add_query_arg( 'variant', $lang, $clean_url ); return add_query_arg( 'variant', $lang, $clean_url );
} }
@ -345,7 +307,7 @@ $this->version = '1.5';
'zh-sg' => 'zh-SG', 'zh-sg' => 'zh-SG',
'zh-hans' => 'zh-Hans', 'zh-hans' => 'zh-Hans',
'zh-hant' => 'zh-Hant', 'zh-hant' => 'zh-Hant',
'zh-jp' => 'zh-JP' 'zh-jp' => 'zh'
); );
return isset( $hreflang_map[ $lang ] ) ? $hreflang_map[ $lang ] : $lang; return isset( $hreflang_map[ $lang ] ) ? $hreflang_map[ $lang ] : $lang;

View file

@ -27,197 +27,56 @@ function wpcc_sitemap_setup() {




function wpcc_sitemap_rewrite_rules( $rules ) { function wpcc_sitemap_rewrite_rules( $rules ) {
global $wpcc_options;
$langs = isset($wpcc_options['wpcc_used_langs']) && is_array($wpcc_options['wpcc_used_langs']) ? $wpcc_options['wpcc_used_langs'] : array('zh-cn','zh-tw','zh-hk');
$langs = array_values(array_unique(array_filter($langs)));
$pattern = implode('|', array_map('preg_quote', $langs));

$new_rules = array(); $new_rules = array();
if ( $pattern !== '' ) { $new_rules['^(zh-tw|zh-cn|zh-hk|zh-sg|zh-hans|zh-hant)/wp-sitemap\.xml/?$'] = 'index.php?wpcc_sitemap_lang=$matches[1]';
// 主网站地图索引 $new_rules['^(zh-tw|zh-cn|zh-hk|zh-sg|zh-hans|zh-hant)/sitemap\.xml/?$'] = 'index.php?wpcc_sitemap_lang=$matches[1]';
$new_rules['^(' . $pattern . ')/wp-sitemap\.xml/?$'] = 'index.php?wpcc_sitemap_lang=$matches[1]'; $new_rules['^sitemap-(zh-tw|zh-cn|zh-hk|zh-sg|zh-hans|zh-hant)-(\d+)\.xml/?$'] = 'index.php?wpcc_sitemap_lang=$matches[1]&wpcc_sitemap_page=$matches[2]';
$new_rules['^(' . $pattern . ')/sitemap\.xml/?$'] = 'index.php?wpcc_sitemap_lang=$matches[1]';
// 文章网站地图
$new_rules['^sitemap-(' . $pattern . ')-(\d+)\.xml/?$'] = 'index.php?wpcc_sitemap_lang=$matches[1]&wpcc_sitemap_page=$matches[2]';
// 分类网站地图
$new_rules['^sitemap-taxonomy-([a-zA-Z0-9_]+)-(' . $pattern . ')-(\d+)\.xml/?$'] = 'index.php?wpcc_sitemap_lang=$matches[2]&wpcc_sitemap_taxonomy=$matches[1]&wpcc_sitemap_page=$matches[3]';
}
return array_merge( $new_rules, $rules ); return array_merge( $new_rules, $rules );
} }


function custom_sitemap_query_vars( $vars ) { function custom_sitemap_query_vars( $vars ) {
$vars[] = 'wpcc_sitemap_lang'; $vars[] = 'wpcc_sitemap_lang';
$vars[] = 'wpcc_sitemap_page'; $vars[] = 'wpcc_sitemap_page';
$vars[] = 'wpcc_sitemap_taxonomy';
return $vars; return $vars;
} }


/**
* 专门用于站点地图的URL转换函数
* 与wpcc_link_conversion不同这个函数会强制转换URL到指定语言即使原URL已包含语言变体
*/
function wpcc_sitemap_link_conversion( $link, $variant ) {
global $wpcc_options;
if ( empty( $variant ) || empty( $link ) ) {
return $link;
}
// 获取原始URL去除所有语言变体
$original_link = wpcc_remove_language_from_url( $link );
$style = (int) ( $wpcc_options['wpcc_use_permalink'] ?? 0 );
$permalinks_enabled = (string) get_option( 'permalink_structure' ) !== '';
// 当 WP 未启用固定链接时,使用查询参数
if ( ! $permalinks_enabled || $style === 0 ) {
return add_query_arg( 'variant', $variant, $original_link );
}
// Split path and query
$qpos = strpos( $original_link, '?' );
$path = $qpos !== false ? substr( $original_link, 0, $qpos ) : $original_link;
$qs = $qpos !== false ? substr( $original_link, $qpos ) : '';
if ( $style === 1 ) {
// suffix style: /postname/zh-xx/
return user_trailingslashit( trailingslashit( $path ) . $variant ) . $qs;
}
// prefix style (2): /zh-xx/postname/
if ( is_multisite() && wpcc_mobile_exist( 'network' ) ) {
$sites = get_sites();
foreach ( $sites as $site ) {
if ( '/' == $site->path ) {
continue;
}
$path_seg = str_replace( '/', '', $site->path );
$sub_url = "$site->domain/$path_seg";
if ( str_contains( $path, $sub_url ) ) {
return str_replace( $sub_url, "$sub_url/$variant", $path ) . $qs;
}
}
}
// 默认前缀样式
$home_url = home_url();
return str_replace( $home_url, $home_url . '/' . $variant, $original_link );
}

/**
* 从URL中移除语言变体
*/
function wpcc_remove_language_from_url( $url ) {
global $wpcc_options;
if ( empty( $url ) ) {
return $url;
}
// 获取启用的语言列表
$enabled = isset( $wpcc_options['wpcc_used_langs'] ) && is_array( $wpcc_options['wpcc_used_langs'] ) ? $wpcc_options['wpcc_used_langs'] : [];
if ( empty( $enabled ) ) {
return $url;
}
// 移除查询参数中的variant
$url = remove_query_arg( 'variant', $url );
// 创建语言变体的正则表达式
$reg = implode( '|', array_map( 'preg_quote', $enabled ) );
$variant_regex = '/\/(' . $reg . '|zh|zh-reset)(\/|$)/i';
// 移除路径中的语言变体
$parsed = parse_url( $url );
if ( isset( $parsed['path'] ) ) {
$path = $parsed['path'];
$path = preg_replace( $variant_regex, '/', $path );
$path = rtrim( $path, '/' );
if ( empty( $path ) ) {
$path = '/';
}
// 重建URL
$scheme = isset( $parsed['scheme'] ) ? $parsed['scheme'] . '://' : '';
$host = $parsed['host'] ?? '';
$port = isset( $parsed['port'] ) ? ':' . $parsed['port'] : '';
$query = isset( $parsed['query'] ) ? '?' . $parsed['query'] : '';
$fragment = isset( $parsed['fragment'] ) ? '#' . $parsed['fragment'] : '';
$url = $scheme . $host . $port . $path . $query . $fragment;
}
return $url;
}

function custom_sitemap_template_redirect() { function custom_sitemap_template_redirect() {
$uri = isset($_SERVER['REQUEST_URI']) ? (string) $_SERVER['REQUEST_URI'] : ''; $uri = $_SERVER['REQUEST_URI'];

// 动态语言模式:仅允许已启用语言
$enabled = isset($GLOBALS['wpcc_options']['wpcc_used_langs']) && is_array($GLOBALS['wpcc_options']['wpcc_used_langs']) ? $GLOBALS['wpcc_options']['wpcc_used_langs'] : array('zh-cn','zh-tw','zh-hk');
$enabled = array_values(array_unique(array_filter($enabled)));
$pat = implode('|', array_map('preg_quote', $enabled));
$lang = ''; $lang = '';
if ( $pat !== '' && preg_match( '#/(' . $pat . ')/sitemap\.xml/?$#i', $uri, $matches ) ) { if ( preg_match( '/\/(zh-tw|zh-cn|zh-hk|zh-sg|zh-hans|zh-hant)\/sitemap\.xml\/?$/', $uri, $matches ) ) {
$lang = strtolower($matches[1]); $lang = $matches[1];
} elseif ( $pat !== '' && preg_match( '#/(' . $pat . ')/wp-sitemap\.xml/?$#i', $uri, $matches ) ) { } elseif ( preg_match( '/\/(zh-tw|zh-cn|zh-hk|zh-sg|zh-hans|zh-hant)\/wp-sitemap\.xml\/?$/', $uri, $matches ) ) {
$lang = strtolower($matches[1]); $lang = $matches[1];
} }
if ( $lang !== '' ) { if ( ! empty( $lang ) ) {
if ( ! in_array( $lang, $enabled, true ) ) {
status_header( 404 );
exit;
}
header( 'Content-Type: application/xml; charset=utf-8' ); header( 'Content-Type: application/xml; charset=utf-8' );
header( 'HTTP/1.1 200 OK' ); header( 'HTTP/1.1 200 OK' );
$content = generate_sitemap_index( $lang ); $content = generate_sitemap_index( $lang );
echo $content !== '' ? $content : '<?xml version="1.0" encoding="UTF-8"?><error>Sitemap not found</error>';
if ( ! empty( $content ) ) {
echo $content;
} else {
echo '<?xml version="1.0" encoding="UTF-8"?><error>Sitemap not found</error>';
}
exit; exit;
} }
// 处理文章网站地图 if ( preg_match( '/\/sitemap-(zh-tw|zh-cn|zh-hk|zh-sg|zh-hans|zh-hant)-(\d+)\.xml\/?$/', $uri, $matches ) ) {
if ( $pat !== '' && preg_match( '#/sitemap-(' . $pat . ')-(\d+)\.xml/?$#i', $uri, $matches ) ) { $lang = $matches[1];
$lang = strtolower($matches[1]);
$page = (int) $matches[2]; $page = (int) $matches[2];
if ( ! in_array( $lang, $enabled, true ) ) {
status_header( 404 );
exit;
}
header( 'Content-Type: application/xml; charset=utf-8' ); header( 'Content-Type: application/xml; charset=utf-8' );
header( 'HTTP/1.1 200 OK' ); header( 'HTTP/1.1 200 OK' );
echo generate_paged_sitemap_content( $lang, $page ); echo generate_paged_sitemap_content( $lang, $page );
exit; exit;
} }
// 处理分类网站地图 if ( preg_match( '/\/sitemap-style\.xsl\/?$/', $uri ) ) {
if ( $pat !== '' && preg_match( '#/sitemap-taxonomy-([a-zA-Z0-9_]+)-(' . $pat . ')-(\d+)\.xml/?$#i', $uri, $matches ) ) {
$taxonomy = sanitize_key($matches[1]);
$lang = strtolower($matches[2]);
$page = (int) $matches[3];
if ( ! in_array( $lang, $enabled, true ) ) {
status_header( 404 );
exit;
}
// 验证分类法是否存在且公开
if ( ! taxonomy_exists( $taxonomy ) || ! is_taxonomy_viewable( $taxonomy ) ) {
status_header( 404 );
exit;
}
header( 'Content-Type: application/xml; charset=utf-8' );
header( 'HTTP/1.1 200 OK' );
echo generate_taxonomy_sitemap_content( $taxonomy, $lang, $page );
exit;
}

if ( preg_match( '#/sitemap-style\.xsl/?$#i', $uri ) ) {
header( 'Content-Type: text/xsl; charset=utf-8' ); header( 'Content-Type: text/xsl; charset=utf-8' );
header( 'HTTP/1.1 200 OK' ); header( 'HTTP/1.1 200 OK' );
@ -229,12 +88,6 @@ function custom_sitemap_template_redirect() {
function generate_sitemap_index( string $lang ) { function generate_sitemap_index( string $lang ) {
global $wpdb, $wpcc_options; global $wpdb, $wpcc_options;
// 仅允许启用语言
$enabled = isset($wpcc_options['wpcc_used_langs']) && is_array($wpcc_options['wpcc_used_langs']) ? $wpcc_options['wpcc_used_langs'] : array('zh-cn','zh-tw');
if ( ! in_array( $lang, $enabled, true ) ) {
return '';
}

$max_urls_per_sitemap = 1000; $max_urls_per_sitemap = 1000;
if ( empty( $wpcc_options['wpcco_sitemap_post_type'] ) ) { if ( empty( $wpcc_options['wpcco_sitemap_post_type'] ) ) {
@ -257,7 +110,6 @@ function generate_sitemap_index( string $lang ) {
$sitemap .= '<?xml-stylesheet type="text/xsl" href="' . site_url( '/sitemap-style.xsl' ) . '"?>'; $sitemap .= '<?xml-stylesheet type="text/xsl" href="' . site_url( '/sitemap-style.xsl' ) . '"?>';
$sitemap .= '<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">'; $sitemap .= '<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">';
// 添加文章网站地图
for ( $i = 1; $i <= $total_sitemaps; $i ++ ) { for ( $i = 1; $i <= $total_sitemaps; $i ++ ) {
$sitemap .= '<sitemap>'; $sitemap .= '<sitemap>';
$sitemap .= '<loc>' . site_url( "/sitemap-{$lang}-{$i}.xml" ) . '</loc>'; $sitemap .= '<loc>' . site_url( "/sitemap-{$lang}-{$i}.xml" ) . '</loc>';
@ -265,35 +117,6 @@ function generate_sitemap_index( string $lang ) {
$sitemap .= '</sitemap>'; $sitemap .= '</sitemap>';
} }
// 添加分类网站地图
$taxonomies = get_taxonomies( array( 'public' => true ), 'objects' );
foreach ( $taxonomies as $taxonomy ) {
// 跳过不需要的分类法
if ( in_array( $taxonomy->name, array( 'post_format', 'nav_menu' ), true ) ) {
continue;
}
// 获取该分类法的条目数量
$term_count = wp_count_terms( array(
'taxonomy' => $taxonomy->name,
'hide_empty' => true,
) );
if ( is_wp_error( $term_count ) || $term_count === 0 ) {
continue;
}
// 计算需要的分页数
$taxonomy_sitemaps = ceil( $term_count / $max_urls_per_sitemap );
for ( $i = 1; $i <= $taxonomy_sitemaps; $i ++ ) {
$sitemap .= '<sitemap>';
$sitemap .= '<loc>' . site_url( "/sitemap-taxonomy-{$taxonomy->name}-{$lang}-{$i}.xml" ) . '</loc>';
$sitemap .= '<lastmod>' . date( 'Y-m-d' ) . '</lastmod>';
$sitemap .= '</sitemap>';
}
}
$sitemap .= '</sitemapindex>'; $sitemap .= '</sitemapindex>';
return $sitemap; return $sitemap;
@ -304,12 +127,6 @@ function generate_sitemap_index( string $lang ) {
function generate_paged_sitemap_content( string $lang, int $page ) { function generate_paged_sitemap_content( string $lang, int $page ) {
global $wpcc_options; global $wpcc_options;
// 仅允许启用语言
$enabled = isset($wpcc_options['wpcc_used_langs']) && is_array($wpcc_options['wpcc_used_langs']) ? $wpcc_options['wpcc_used_langs'] : array('zh-cn','zh-tw');
if ( ! in_array( $lang, $enabled, true ) ) {
return '<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" />';
}

$max_urls_per_sitemap = 1000; $max_urls_per_sitemap = 1000;
if ( empty( $wpcc_options['wpcco_sitemap_post_type'] ) ) { if ( empty( $wpcc_options['wpcco_sitemap_post_type'] ) ) {
@ -338,7 +155,7 @@ function generate_paged_sitemap_content( string $lang, int $page ) {
$postdate = explode( " ", $post->post_modified ); $postdate = explode( " ", $post->post_modified );
$sitemap .= '<url>'; $sitemap .= '<url>';
$sitemap .= '<loc>' . wpcc_sitemap_link_conversion( get_permalink( $post->ID ), $lang ) . '</loc>'; $sitemap .= '<loc>' . wpcc_link_conversion( get_permalink( $post->ID ), $lang ) . '</loc>';
$sitemap .= '<lastmod>' . $postdate[0] . '</lastmod>'; $sitemap .= '<lastmod>' . $postdate[0] . '</lastmod>';
$sitemap .= '<changefreq>weekly</changefreq>'; $sitemap .= '<changefreq>weekly</changefreq>';
$sitemap .= '<priority>0.6</priority>'; $sitemap .= '<priority>0.6</priority>';
@ -495,93 +312,4 @@ function generate_sitemap_styles() {
XSL; XSL;
} }


/**
* 生成分类网站地图内容
*
* @param string $taxonomy 分类法名称
* @param string $lang 语言代码
* @param int $page 页码
* @return string XML内容
*/
function generate_taxonomy_sitemap_content( string $taxonomy, string $lang, int $page ) {
global $wpcc_options;

// 仅允许启用语言
$enabled = isset($wpcc_options['wpcc_used_langs']) && is_array($wpcc_options['wpcc_used_langs']) ? $wpcc_options['wpcc_used_langs'] : array('zh-cn','zh-tw');
if ( ! in_array( $lang, $enabled, true ) ) {
return '<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" />';
}

// 验证分类法
if ( ! taxonomy_exists( $taxonomy ) || ! is_taxonomy_viewable( $taxonomy ) ) {
return '<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" />';
}

$max_urls_per_sitemap = 1000;
$offset = ( $page - 1 ) * $max_urls_per_sitemap;
// 获取分类条目
$terms = get_terms( array(
'taxonomy' => $taxonomy,
'hide_empty' => true,
'number' => $max_urls_per_sitemap,
'offset' => $offset,
'orderby' => 'count',
'order' => 'DESC',
) );
if ( is_wp_error( $terms ) || empty( $terms ) ) {
return '<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" />';
}
$sitemap = '<?xml version="1.0" encoding="UTF-8"?>';
$sitemap .= '<?xml-stylesheet type="text/xsl" href="' . site_url( '/sitemap-style.xsl' ) . '"?>';
$sitemap .= '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">';
foreach ( $terms as $term ) {
// 检查是否应该索引此条目
if ( ! wpcc_should_index_term( $term ) ) {
continue;
}
$term_link = get_term_link( $term, $taxonomy );
if ( is_wp_error( $term_link ) ) {
continue;
}
// 转换链接到指定语言
$converted_link = wpcc_sitemap_link_conversion( $term_link, $lang );
$sitemap .= '<url>';
$sitemap .= '<loc>' . esc_url( $converted_link ) . '</loc>';
$sitemap .= '<lastmod>' . date( 'Y-m-d' ) . '</lastmod>';
$sitemap .= '<changefreq>weekly</changefreq>';
$sitemap .= '<priority>0.5</priority>';
$sitemap .= '</url>';
}
$sitemap .= '</urlset>';
return $sitemap;
}

/**
* 检查是否应该索引分类条目
*
* @param WP_Term $term 分类条目
* @return bool
*/
function wpcc_should_index_term( $term ) {
// 可以在这里添加更多的检查逻辑
// 例如检查条目的元数据中是否有 noindex 标记
// 基本检查:确保条目有内容
if ( $term->count === 0 ) {
return false;
}
// 可以添加自定义过滤器让其他插件或主题控制
return apply_filters( 'wpcc_should_index_term', true, $term );
}

?> ?>

File diff suppressed because it is too large Load diff

View file

@ -1,101 +1,14 @@
<?php <?php


/** /**
* WP Chinese Converter Admin Panel Functions * WP Chinese Converter - Admin Functions
* *
* 管理后台相关功能 * 包含所有后台管理相关功能
* *
* @package WPChineseConverter * @package WPChineseConverter
* @version 1.4 * @version 1.2.0
*/ */


// 添加管理员功能:手动刷新重写规则
add_action( 'wp_ajax_wpcc_flush_rewrite_rules', 'wpcc_ajax_flush_rewrite_rules' );
function wpcc_ajax_flush_rewrite_rules() {
// 权限检查
if ( ! current_user_can( 'manage_options' ) ) {
wp_die( '无权限操作' );
}
// 验证nonce
if ( ! wp_verify_nonce( $_POST['nonce'], 'wpcc_admin_nonce' ) ) {
wp_die( '安全验证失败' );
}
// 刷新重写规则
flush_rewrite_rules( false );
update_option( 'wpcc_rewrite_autoflush_ts', time() );
wp_send_json_success( '重写规则已刷新' );
}

// 添加管理面板提示
add_action( 'admin_notices', 'wpcc_rewrite_rules_notice' );
function wpcc_rewrite_rules_notice() {
// 仅在WPCC设置页面显示
$screen = get_current_screen();
if ( ! $screen || strpos( $screen->id, 'wpcc' ) === false ) {
return;
}
global $wpcc_options;
if ( empty( $wpcc_options['wpcc_use_permalink'] ) ) {
return;
}
// 检查是否存在语言规则
$rules = get_option( 'rewrite_rules', [] );
$enabled_langs = $wpcc_options['wpcc_used_langs'] ?? [];
if ( empty( $enabled_langs ) ) {
return;
}
$reg = implode( '|', $enabled_langs );
$expected = '^(' . $reg . '|zh|zh-reset)/?$';
$has_rule = false;
foreach ( $rules as $regex => $query ) {
if ( $regex === $expected && strpos( $query, 'variant=' ) !== false ) {
$has_rule = true;
break;
}
}
if ( ! $has_rule ) {
echo '<div class="notice notice-warning is-dismissible">';
echo '<p><strong>WP Chinese Converter:</strong> 检测到语言重写规则可能未正确配置这可能导致语言主页404错误。 ';
echo '<a href="#" id="wpcc-flush-rules" class="button button-secondary">刷新重写规则</a></p>';
echo '</div>';
// 添加JavaScript
echo '<script type="text/javascript">
jQuery(document).ready(function($) {
$("#wpcc-flush-rules").on("click", function(e) {
e.preventDefault();
var button = $(this);
button.prop("disabled", true).text("刷新中...");
$.post(ajaxurl, {
action: "wpcc_flush_rewrite_rules",
nonce: "' . wp_create_nonce( 'wpcc_admin_nonce' ) . '"
}, function(response) {
if (response.success) {
button.closest(".notice").fadeOut();
$(".wrap h1").after("<div class=\"notice notice-success is-dismissible\"><p>重写规则已成功刷新!请测试语言主页是否能正常访问。</p></div>");
} else {
alert("刷新失败:" + response.data);
button.prop("disabled", false).text("刷新重写规则");
}
}).fail(function() {
alert("请求失败,请稍后再试");
button.prop("disabled", false).text("刷新重写规则");
});
});
});
</script>';
}
}

// 防止直接访问 // 防止直接访问
if ( ! defined( 'ABSPATH' ) ) { if ( ! defined( 'ABSPATH' ) ) {
exit; exit;
@ -106,7 +19,7 @@ if ( ! defined( 'ABSPATH' ) ) {
*/ */
function wpcc_admin_init() { function wpcc_admin_init() {
global $wpcc_admin; global $wpcc_admin;
require_once __DIR__ . '/admin/class-wpcc-admin.php'; require_once __DIR__ . '/admin/wp-chinese-converter-admin.php';
$wpcc_admin = new wpcc_Admin(); $wpcc_admin = new wpcc_Admin();
add_filter( 'plugin_action_links', array( $wpcc_admin, 'action_links' ), 10, 2 ); add_filter( 'plugin_action_links', array( $wpcc_admin, 'action_links' ), 10, 2 );
} }
@ -167,17 +80,17 @@ function wpcc_activate() {
*/ */
function wpcc_appthemes_add_quicktags() { function wpcc_appthemes_add_quicktags() {
global $wpcc_options; global $wpcc_options;
if ( ! empty( $wpcc_options ) && ! empty( $wpcc_options['wpcc_no_conversion_qtag'] ) ) { if ( ! empty( $wpcc_options ) && ! empty( $wpcc_options['wpcc_no_conversion_qtag'] ) && wp_script_is( 'quicktags', 'enqueued' ) ) {
?> ?>
<script type="text/javascript"> <script type="text/javascript">
//<![CDATA[ //<![CDATA[
jQuery(document).ready(function($) { jQuery(document).ready(function($) {
if (typeof QTags !== 'undefined' && QTags.addButton) { if (typeof QTags !== 'undefined' && QTags.addButton) {
QTags.addButton('eg_wpcc_nc', 'wpcc_NC', '[wpcc_nc]', '[/wpcc_nc]', null, 'WP Chinese Converter: Insert no-convert markers', 120); QTags.addButton('eg_wpcc_nc', 'wpcc_NC', '<!--wpcc_NC_START-->', '<!--wpcc_NC_END-->', null, 'WP Chinese Converter DO-NOT Convert Tag', 120);
} else { } else {
setTimeout(function() { setTimeout(function() {
if (typeof QTags !== 'undefined' && QTags.addButton) { if (typeof QTags !== 'undefined' && QTags.addButton) {
QTags.addButton('eg_wpcc_nc', 'wpcc_NC', '[wpcc_nc]', '[/wpcc_nc]', null, 'WP Chinese Converter: Insert no-convert markers', 120); QTags.addButton('eg_wpcc_nc', 'wpcc_NC', '<!--wpcc_NC_START-->', '<!--wpcc_NC_END-->', null, 'WP Chinese Converter DO-NOT Convert Tag', 120);
} }
}, 100); }, 100);
} }
@ -298,13 +211,31 @@ function wpcc_add_conversion_meta_box() {


/** /**
* 获取语言模块配置 * 获取语言模块配置
* 使用中心化的语言配置管理
*/ */
function wpcc_get_language_config() { function wpcc_get_language_config() {
global $wpcc_options; global $wpcc_options;
// 使用中心化的语言配置 $default_names = array(
return WPCC_Language_Config::get_custom_names( $wpcc_options ); 'zh-cn' => '中国大陆',
'zh-tw' => '台湾正体',
'zh-hk' => '港澳繁体',
'zh-hans' => '简体中文',
'zh-hant' => '繁体中文',
'zh-sg' => '马新简体',
'zh-jp' => '日式汉字'
);
$custom_names = array(
'zh-cn' => $wpcc_options['cntip'] ?? $default_names['zh-cn'],
'zh-tw' => $wpcc_options['twtip'] ?? $default_names['zh-tw'],
'zh-hk' => $wpcc_options['hktip'] ?? $default_names['zh-hk'],
'zh-hans' => $wpcc_options['hanstip'] ?? $default_names['zh-hans'],
'zh-hant' => $wpcc_options['hanttip'] ?? $default_names['zh-hant'],
'zh-sg' => $wpcc_options['sgtip'] ?? $default_names['zh-sg'],
'zh-jp' => $wpcc_options['jptip'] ?? $default_names['zh-jp']
);
return $custom_names;
} }


/** /**
@ -430,10 +361,8 @@ function my_ajax_clear_cache_handler() {




// 注册管理钩子 // 注册管理钩子
// 注意:网络管理菜单现在由 wpcc-network-settings.php 处理
if ( is_multisite() && wpcc_mobile_exist( 'network' ) ) { if ( is_multisite() && wpcc_mobile_exist( 'network' ) ) {
// add_action( 'network_admin_menu', 'wpcc_admin_init' ); // 已被新的网络设置模块替代 add_action( 'network_admin_menu', 'wpcc_admin_init' );
add_action( 'admin_menu', 'wpcc_admin_init' );
} else { } else {
add_action( 'admin_menu', 'wpcc_admin_init' ); add_action( 'admin_menu', 'wpcc_admin_init' );
} }
@ -444,31 +373,6 @@ register_activation_hook( dirname( __DIR__ ) . '/wp-chinese-converter.php', 'wpc
// 注册编辑器增强钩子 // 注册编辑器增强钩子
add_action( 'admin_print_footer_scripts', 'wpcc_appthemes_add_quicktags' ); add_action( 'admin_print_footer_scripts', 'wpcc_appthemes_add_quicktags' );


// TinyMCE 可视化编辑器按钮(仅在开启“快速标签”选项时添加按钮;短代码始终可用)
function wpcc_register_tinymce_plugin( $plugins ) {
global $wpcc_options;
if ( empty( $wpcc_options ) ) {
$wpcc_options = get_wpcc_option( 'wpcc_options' );
}
// 仅当设置开启时添加按钮脚本
if ( ! empty( $wpcc_options['wpcc_no_conversion_qtag'] ) ) {
$plugins['wpcc_nc'] = wpcc_DIR_URL . 'assets/js/tinymce-wpcc-nc.js';
}
return $plugins;
}
function wpcc_add_tinymce_button( $buttons ) {
global $wpcc_options;
if ( empty( $wpcc_options ) ) {
$wpcc_options = get_wpcc_option( 'wpcc_options' );
}
if ( ! empty( $wpcc_options['wpcc_no_conversion_qtag'] ) ) {
$buttons[] = 'wpcc_nc';
}
return $buttons;
}
add_filter( 'mce_external_plugins', 'wpcc_register_tinymce_plugin' );
add_filter( 'mce_buttons', 'wpcc_add_tinymce_button' );

// 注册文章转换钩子 // 注册文章转换钩子
add_action( 'init', 'wpcc_init_post_conversion' ); add_action( 'init', 'wpcc_init_post_conversion' );



View file

@ -6,7 +6,7 @@
* 包含所有前台转换相关功能 * 包含所有前台转换相关功能
* *
* @package WPChineseConverter * @package WPChineseConverter
* @version 1.4 * @version 1.2.0
*/ */


// 防止直接访问 // 防止直接访问
@ -17,25 +17,22 @@ if ( ! defined( 'ABSPATH' ) ) {
// 全局变量初始化 // 全局变量初始化
$wpcc_langs = array(); $wpcc_langs = array();


// 短代码:提供 [wpcc_nc]...[/wpcc_nc] 与 [wpcs_nc]...[/wpcs_nc],在编辑器无法保留注释时作为稳健的占位
add_shortcode( 'wpcc_nc', function( $atts, $content = '' ) {
return '<span data-wpcc-no-conversion="true">' . $content . '</span>';
} );
add_shortcode( 'wpcs_nc', function( $atts, $content = '' ) {
return '<span data-wpcc-no-conversion="true">' . $content . '</span>';
} );


/** /**
* 初始化语言配置 * 初始化语言配置
* 使用中心化的语言配置管理
*/ */
function wpcc_init_languages(): void { function wpcc_init_languages(): void {
global $wpcc_langs; global $wpcc_langs;


if ( empty( $wpcc_langs ) ) { if ( empty( $wpcc_langs ) ) {
// 使用中心化的语言配置 $wpcc_langs = [
$wpcc_langs = WPCC_Language_Config::get_all_languages(); 'zh-cn' => [ 'zhconversion_cn', 'cntip', __( '简体中文', 'wp-chinese-converter' ), 'zh-CN' ],
'zh-tw' => [ 'zhconversion_tw', 'twtip', __( '台灣正體', 'wp-chinese-converter' ), 'zh-TW' ],
'zh-hk' => [ 'zhconversion_hk', 'hktip', __( '港澳繁體', 'wp-chinese-converter' ), 'zh-HK' ],
'zh-hans' => [ 'zhconversion_hans', 'hanstip', __( '简体中文', 'wp-chinese-converter' ), 'zh-Hans' ],
'zh-hant' => [ 'zhconversion_hant', 'hanttip', __( '繁体中文', 'wp-chinese-converter' ), 'zh-Hant' ],
'zh-sg' => [ 'zhconversion_sg', 'sgtip', __( '马新简体', 'wp-chinese-converter' ), 'zh-SG' ],
'zh-jp' => [ 'zhconversion_jp', 'jptip', __( '日式汉字', 'wp-chinese-converter' ), 'zh-JP' ],
];
} }
} }


@ -43,10 +40,6 @@ function wpcc_init_languages(): void {
* 插件核心初始化 * 插件核心初始化
*/ */
function wpcc_init() { function wpcc_init() {
// 当新版核心 WPCC_Main 存在时,完全交由新内核处理
if ( class_exists( 'WPCC_Main' ) ) {
return;
}
global $wpcc_options, $wp_rewrite; global $wpcc_options, $wp_rewrite;


if ( isset( $wpcc_options['wpcc_use_permalink'] ) && $wpcc_options['wpcc_use_permalink'] != 0 && empty( $wp_rewrite->permalink_structure ) ) { if ( isset( $wpcc_options['wpcc_use_permalink'] ) && $wpcc_options['wpcc_use_permalink'] != 0 && empty( $wp_rewrite->permalink_structure ) ) {
@ -59,24 +52,18 @@ function wpcc_init() {
} }


// 处理评论提交的转换 // 处理评论提交的转换
$php_self = isset( $_SERVER['PHP_SELF'] ) ? sanitize_text_field( wp_unslash( $_SERVER['PHP_SELF'] ) ) : ''; if ( ( isset( $_SERVER['PHP_SELF'] ) && ( strpos( $_SERVER['PHP_SELF'], 'wp-comments-post.php' ) !== false
$request_method = isset( $_SERVER['REQUEST_METHOD'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_METHOD'] ) ) : ''; || strpos( $_SERVER['PHP_SELF'], 'ajax-comments.php' ) !== false
|| strpos( $_SERVER['PHP_SELF'], 'comments-ajax.php' ) !== false )
if ( ( $php_self && ( strpos( $php_self, 'wp-comments-post.php' ) !== false
|| strpos( $php_self, 'ajax-comments.php' ) !== false
|| strpos( $php_self, 'comments-ajax.php' ) !== false )
) && ) &&
$request_method === 'POST' && isset( $_SERVER["REQUEST_METHOD"] ) && $_SERVER["REQUEST_METHOD"] == "POST" &&
isset( $_POST['variant'] ) && ! empty( $_POST['variant'] ) isset( $_POST['variant'] ) && ! empty( $_POST['variant'] ) && in_array( $_POST['variant'], $wpcc_options['wpcc_used_langs'] )
) { ) {
$variant = sanitize_text_field( wp_unslash( $_POST['variant'] ) );
if ( in_array( $variant, $wpcc_options['wpcc_used_langs'], true ) ) {
global $wpcc_target_lang; global $wpcc_target_lang;
$wpcc_target_lang = $variant; $wpcc_target_lang = $_POST['variant'];
wpcc_do_conversion(); wpcc_do_conversion();
return; return;
} }
}


// 修复首页显示Page时的问题 // 修复首页显示Page时的问题
if ( 'page' == get_option( 'show_on_front' ) && get_option( 'page_on_front' ) ) { if ( 'page' == get_option( 'show_on_front' ) && get_option( 'page_on_front' ) ) {
@ -139,12 +126,6 @@ function wpcc_parse_query( $query ) {


global $wpcc_target_lang, $wpcc_redirect_to, $wpcc_noconversion_url, $wpcc_options, $wpcc_direct_conversion_flag; global $wpcc_target_lang, $wpcc_redirect_to, $wpcc_noconversion_url, $wpcc_options, $wpcc_direct_conversion_flag;


// 标记 AJAX/REST/wc-ajax 请求,跳过转换避免干扰 JSON 响应
$is_ajax = function_exists('wp_doing_ajax') ? wp_doing_ajax() : ( defined('DOING_AJAX') && DOING_AJAX );
$is_rest = defined('REST_REQUEST') && REST_REQUEST;
$is_wc_ajax = isset($_REQUEST['wc-ajax']) && is_string($_REQUEST['wc-ajax']) && $_REQUEST['wc-ajax'] !== '';
$wpcc_direct_conversion_flag = (bool) ( $is_ajax || $is_rest || $is_wc_ajax );

if ( ! is_404() ) { if ( ! is_404() ) {
$wpcc_noconversion_url = wpcc_get_noconversion_url(); $wpcc_noconversion_url = wpcc_get_noconversion_url();
} else { } else {
@ -153,10 +134,10 @@ function wpcc_parse_query( $query ) {
return; return;
} }


$request_lang = isset( $query->query_vars['variant'] ) ? sanitize_text_field( $query->query_vars['variant'] ) : ''; $request_lang = isset( $query->query_vars['variant'] ) ? $query->query_vars['variant'] : '';
$cookie_lang = isset( $_COOKIE[ 'wpcc_variant_' . COOKIEHASH ] ) ? sanitize_text_field( wp_unslash( $_COOKIE[ 'wpcc_variant_' . COOKIEHASH ] ) ) : ''; $cookie_lang = isset( $_COOKIE[ 'wpcc_variant_' . COOKIEHASH ] ) ? $_COOKIE[ 'wpcc_variant_' . COOKIEHASH ] : '';


if ( $request_lang && in_array( $request_lang, $wpcc_options['wpcc_used_langs'], true ) ) { if ( $request_lang && in_array( $request_lang, $wpcc_options['wpcc_used_langs'] ) ) {
$wpcc_target_lang = $request_lang; $wpcc_target_lang = $request_lang;
} else { } else {
$wpcc_target_lang = false; $wpcc_target_lang = false;
@ -166,23 +147,9 @@ function wpcc_parse_query( $query ) {
if ( ! $wpcc_target_lang ) { if ( ! $wpcc_target_lang ) {
if ( $request_lang == 'zh' && ! is_admin() ) { if ( $request_lang == 'zh' && ! is_admin() ) {
if ( $wpcc_options['wpcc_use_cookie_variant'] != 0 ) { if ( $wpcc_options['wpcc_use_cookie_variant'] != 0 ) {
setcookie( 'wpcc_variant_' . COOKIEHASH, 'zh', [ setcookie( 'wpcc_variant_' . COOKIEHASH, 'zh', time() + 30000000, COOKIEPATH, COOKIE_DOMAIN );
'expires' => time() + 30000000,
'path' => COOKIEPATH,
'domain' => COOKIE_DOMAIN,
'secure' => is_ssl(),
'httponly' => true,
'samesite' => 'Lax',
] );
} else { } else {
setcookie( 'wpcc_is_redirect_' . COOKIEHASH, '1', [ setcookie( 'wpcc_is_redirect_' . COOKIEHASH, '1', 0, COOKIEPATH, COOKIE_DOMAIN );
'expires' => 0,
'path' => COOKIEPATH,
'domain' => COOKIE_DOMAIN,
'secure' => is_ssl(),
'httponly' => true,
'samesite' => 'Lax',
] );
} }
header( 'Location: ' . $wpcc_noconversion_url ); header( 'Location: ' . $wpcc_noconversion_url );
die(); die();
@ -200,14 +167,7 @@ function wpcc_parse_query( $query ) {


// 设置Cookie // 设置Cookie
if ( $wpcc_target_lang && $wpcc_options['wpcc_use_cookie_variant'] != 0 && $cookie_lang != $wpcc_target_lang ) { if ( $wpcc_target_lang && $wpcc_options['wpcc_use_cookie_variant'] != 0 && $cookie_lang != $wpcc_target_lang ) {
setcookie( 'wpcc_variant_' . COOKIEHASH, $wpcc_target_lang, [ setcookie( 'wpcc_variant_' . COOKIEHASH, $wpcc_target_lang, time() + 30000000, COOKIEPATH, COOKIE_DOMAIN );
'expires' => time() + 30000000,
'path' => COOKIEPATH,
'domain' => COOKIE_DOMAIN,
'secure' => is_ssl(),
'httponly' => true,
'samesite' => 'Lax',
] );
} }
} }


@ -215,24 +175,12 @@ function wpcc_parse_query( $query ) {
* 模板重定向处理 - 核心转换逻辑 * 模板重定向处理 - 核心转换逻辑
*/ */
function wpcc_template_redirect() { function wpcc_template_redirect() {
global $wpcc_noconversion_url, $wpcc_langs_urls, $wpcc_options, $wpcc_target_lang, $wpcc_redirect_to, $wpcc_direct_conversion_flag; global $wpcc_noconversion_url, $wpcc_langs_urls, $wpcc_options, $wpcc_target_lang, $wpcc_redirect_to;

// 若是 AJAX/REST/wc-ajax 等非 HTML 响应,直接跳过所有转换,避免破坏 JSON/片段
if ( $wpcc_direct_conversion_flag ) {
return;
}


set_wpcc_langs_urls(); set_wpcc_langs_urls();


if ( ! is_404() && $wpcc_redirect_to && ! is_admin() ) { if ( ! is_404() && $wpcc_redirect_to && ! is_admin() ) {
setcookie( 'wpcc_is_redirect_' . COOKIEHASH, '1', [ setcookie( 'wpcc_is_redirect_' . COOKIEHASH, '1', 0, COOKIEPATH, COOKIE_DOMAIN );
'expires' => 0,
'path' => COOKIEPATH,
'domain' => COOKIE_DOMAIN,
'secure' => is_ssl(),
'httponly' => true,
'samesite' => 'Lax',
] );
wp_redirect( $wpcc_langs_urls[ $wpcc_redirect_to ], 302 ); wp_redirect( $wpcc_langs_urls[ $wpcc_redirect_to ], 302 );
} }


@ -257,13 +205,9 @@ function set_wpcc_langs_urls() {
global $wpcc_langs_urls, $wpcc_options, $wpcc_noconversion_url; global $wpcc_langs_urls, $wpcc_options, $wpcc_noconversion_url;


if ( ! $wpcc_langs_urls ) { if ( ! $wpcc_langs_urls ) {
$permalinks_enabled = (string) get_option( 'permalink_structure' ) !== ''; if ( $wpcc_noconversion_url == get_option( 'home' ) . '/' && $wpcc_options['wpcc_use_permalink'] ) {
$style = (int) ( $wpcc_options['wpcc_use_permalink'] ?? 0 );
$style_effective = $permalinks_enabled ? $style : 0;
if ( $wpcc_noconversion_url == get_option( 'home' ) . '/' && $style_effective ) {
foreach ( $wpcc_options['wpcc_used_langs'] as $value ) { foreach ( $wpcc_options['wpcc_used_langs'] as $value ) {
$wpcc_langs_urls[ $value ] = trailingslashit( $wpcc_noconversion_url . $value ); $wpcc_langs_urls[ $value ] = $wpcc_noconversion_url . $value . '/';
} }
} else { } else {
foreach ( $wpcc_options['wpcc_used_langs'] as $value ) { foreach ( $wpcc_options['wpcc_used_langs'] as $value ) {
@ -278,13 +222,11 @@ function set_wpcc_langs_urls() {
*/ */
function wpcc_get_noconversion_url() { function wpcc_get_noconversion_url() {
global $wpcc_options; global $wpcc_options;
$reg = implode( '|', array_map( 'preg_quote', $wpcc_options['wpcc_used_langs'] ) ); $reg = implode( '|', $wpcc_options['wpcc_used_langs'] );
$tmp = ( is_ssl() ? 'https://' : 'http://' ) .
$protocol = is_ssl() ? 'https://' : 'http://'; ( isset( $_SERVER['HTTP_HOST'] ) ? $_SERVER['HTTP_HOST'] : '' ) .
$host = isset( $_SERVER['HTTP_HOST'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_HOST'] ) ) : ''; ( isset( $_SERVER['REQUEST_URI'] ) ? $_SERVER['REQUEST_URI'] : '' );
$uri = isset( $_SERVER['REQUEST_URI'] ) ? esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : ''; $tmp = trim( strtolower( remove_query_arg( 'variant', $tmp ) ) );
$tmp = trim( strtolower( remove_query_arg( 'variant', $protocol . $host . $uri ) ) );


if ( preg_match( '/^(.*)\/(' . $reg . '|zh|zh-reset)(\/.*)?$/', $tmp, $matches ) ) { if ( preg_match( '/^(.*)\/(' . $reg . '|zh|zh-reset)(\/.*)?$/', $tmp, $matches ) ) {
$tmp = user_trailingslashit( trailingslashit( $matches[1] ) . ltrim( $matches[3] ?? '', '/' ) ); $tmp = user_trailingslashit( trailingslashit( $matches[1] ) . ltrim( $matches[3] ?? '', '/' ) );
@ -313,55 +255,32 @@ function wpcc_link_conversion( $link, $variant = null ) {
if ( $variant == false ) { if ( $variant == false ) {
return $link; return $link;
} }
if ( str_contains( $link, $variant ) ) {
$style = (int) ( $wpcc_options['wpcc_use_permalink'] ?? 0 ); return $link;
$permalinks_enabled = (string) get_option( 'permalink_structure' ) !== '';
// Split path and query
$qpos = strpos( $link, '?' );
$path = $qpos !== false ? substr( $link, 0, $qpos ) : $link;
$qs = $qpos !== false ? substr( $link, $qpos ) : '';
// Detect existing variant in path; if present, strip duplicate query param if any
$enabled = isset( $wpcc_options['wpcc_used_langs'] ) && is_array( $wpcc_options['wpcc_used_langs'] ) ? $wpcc_options['wpcc_used_langs'] : [];
$variant_regex = '#/(?:' . implode( '|', array_map( 'preg_quote', $enabled ) ) . '|zh|zh-reset)(/|$)#i';
$path_only = parse_url( $path, PHP_URL_PATH );
if ( $path_only === null ) { $path_only = $path; }
if ( preg_match( $variant_regex, $path_only ) ) {
if ( $qpos !== false ) {
$qs = preg_replace( '/([?&])variant=[^&]*(&|$)/', '$1', $qs );
$qs = rtrim( $qs, '?&' );
if ( $qs && $qs[0] !== '?' ) { $qs = '?' . ltrim( $qs, '?' ); }
}
return $path . $qs;
} }


// 当 WP 未启用固定链接时,强制使用查询参数,避免 /zh-xx/ 404 if ( str_contains( $link, '?' ) || ! ($wpcc_options['wpcc_use_permalink'] ?? 0) ) {
if ( ! $permalinks_enabled || $style === 0 ) {
return add_query_arg( 'variant', $variant, $link ); return add_query_arg( 'variant', $variant, $link );
} }


if ( $style === 1 ) { if ( $wpcc_options['wpcc_use_permalink'] == 1 ) {
// suffix style return user_trailingslashit( trailingslashit( $link ) . $variant );
return user_trailingslashit( trailingslashit( $path ) . $variant ) . $qs;
} }
// prefix style (2)
if ( is_multisite() && wpcc_mobile_exist( 'network' ) ) { if ( is_multisite() && wpcc_mobile_exist( 'network' ) ) {
$sites = get_sites(); $sites = get_sites();
foreach ( $sites as $site ) { foreach ( $sites as $site ) {
if ( '/' == $site->path ) { if ( '/' == $site->path ) {
continue; continue;
} }
$path_seg = str_replace( '/', '', $site->path ); $path = str_replace( '/', '', $site->path );
$sub_url = "$site->domain/$path_seg"; $sub_url = "$site->domain/$path";
if ( str_contains( $path, $sub_url ) ) { if ( str_contains( $link, $sub_url ) ) {
return str_replace( $sub_url, "$sub_url/$variant", $path ) . $qs; return str_replace( $sub_url, "$sub_url/$variant", $link );
} }
} }
} }


return str_replace( $wpcc_wp_home, "$wpcc_wp_home/$variant", $path ) . $qs; return str_replace( $wpcc_wp_home, "$wpcc_wp_home/$variant", $link );
} }


/** /**
@ -472,9 +391,6 @@ function zhconversion2( $str, $variant = null ) {
return $str; return $str;
} }


// 兜底:如未加保护标记,则在转换前为“不转换内容”区块添加保护标记
$str = wpcc_protect_no_conversion_blocks( $str );

return limit_zhconversion( $str, $wpcc_langs[ $variant ][0] ); return limit_zhconversion( $str, $wpcc_langs[ $variant ][0] );
} }


@ -580,45 +496,11 @@ function zhconversion_deep( $value ) {
return $value; return $value;
} }


/**
* 兜底保护 - 为“不转换内容”区块添加注释标记
*/
function wpcc_protect_no_conversion_blocks( $str ) {
// 若内容中不存在可识别的不转换占位data 属性或指定类),则无需处理
$has_data_attr = preg_match( '/data-wpcc-no-conversion=("|\')true\1/i', $str );
$has_nc_class = preg_match( '/wpcc-no-conversion-(content|wrapper)/i', $str );
if ( ! $has_data_attr && ! $has_nc_class ) {
return $str;
}
$wrap_whole_callback = function( $matches ) {
$id = wpcc_id();
return '<!--wpcc_NC' . $id . '_START-->' . $matches[0] . '<!--wpcc_NC' . $id . '_END-->';
};
$wrap_inner_callback = function( $matches ) {
$id = wpcc_id();
$open = $matches[1];
$inner = $matches[2];
$close = $matches[3];
return $open . '<!--wpcc_NC' . $id . '_START-->' . $inner . '<!--wpcc_NC' . $id . '_END-->' . $close;
};
// 优先:包裹 .wpcc-no-conversion-content 内部
$str = preg_replace_callback( '/(<div[^>]*class="[^"]*wpcc-no-conversion-content[^"]*"[^>]*>)([\s\S]*?)(<\/div>)/i', $wrap_inner_callback, $str );
// 兜底:包裹外层 wrapper 或 data 属性
$str = preg_replace_callback( '/<div[^>]*class="[^"]*wpcc-no-conversion-wrapper[^"]*"[^>]*>[\s\S]*?<\/div>/i', $wrap_whole_callback, $str );
$str = preg_replace_callback( '/<[^>]*data-wpcc-no-conversion="true"[^>]*>[\s\S]*?<\/[a-zA-Z0-9]+>/i', $wrap_whole_callback, $str );
return $str;
}

/** /**
* 有限转换函数 - 不转换指定标签内的内容 * 有限转换函数 - 不转换指定标签内的内容
*/ */

function limit_zhconversion( $str, $function ) { function limit_zhconversion( $str, $function ) {
if ( $m = preg_split( '/(<!--wpc(?:c|s)_NC([a-zA-Z0-9]*)_START-->)(.*?)(<!--wpc(?:c|s)_NC\2_END-->)/s', $str, - 1, PREG_SPLIT_DELIM_CAPTURE ) ) { if ( $m = preg_split( '/(<!--wpcc_NC([a-zA-Z0-9]*)_START-->)(.*?)(<!--wpcc_NC\2_END-->)/s', $str, - 1, PREG_SPLIT_DELIM_CAPTURE ) ) {
$r = ''; $r = '';
$count = 0; $count = 0;
foreach ( $m as $v ) { foreach ( $m as $v ) {
@ -666,75 +548,73 @@ function wpcc_output_navi( $args = '', $isReturn = false ) {
return; return;
} }


// 计算“不转换”标签
if ( ! empty( $wpcc_options['nctip'] ) ) { if ( ! empty( $wpcc_options['nctip'] ) ) {
$noconverttip = $wpcc_options['nctip']; $noconverttip = $wpcc_options['nctip'];
} else { } else {
$locale = str_replace( '_', '-', strtolower( get_locale() ) ); $locale = str_replace( '_', '-', strtolower( get_locale() ) );
$noconverttip = in_array( $locale, array( 'zh-hant', 'zh-tw', 'zh-hk', 'zh-mo' ) ) ? '不转换' : '不转换'; if ( in_array( $locale, array( 'zh-hant', 'zh-tw', 'zh-hk', 'zh-mo' ) ) ) {
$noconverttip = '不转换';
} else {
$noconverttip = '不转换';
}
} }
if ( $wpcc_target_lang ) { if ( $wpcc_target_lang ) {
$noconverttip = zhconversion( $noconverttip ); $noconverttip = zhconversion( $noconverttip );
} }


// 计算“不转换”链接(必要时注入 zh 哨兵以覆盖浏览器/Cookie 策略) if ( ( $wpcc_options['wpcc_browser_redirect'] == 2 || $wpcc_options['wpcc_use_cookie_variant'] == 2 ) &&
if ( ( ! empty($wpcc_options['wpcc_browser_redirect']) && $wpcc_options['wpcc_browser_redirect'] == 2 ) || $wpcc_target_lang
( ! empty($wpcc_options['wpcc_use_cookie_variant']) && $wpcc_options['wpcc_use_cookie_variant'] == 2 ) ) { ) {
$default_url = $wpcc_target_lang ? wpcc_link_conversion( $wpcc_noconversion_url, 'zh' ) : $wpcc_noconversion_url; $default_url = wpcc_link_conversion( $wpcc_noconversion_url, 'zh' );
if ( ! empty($wpcc_options['wpcc_use_permalink']) && is_home() && ! is_paged() ) { if ( $wpcc_options['wpcc_use_permalink'] != 0 && is_home() && ! is_paged() ) {
$default_url = trailingslashit( $default_url ); $default_url = trailingslashit( $default_url );
} }
} else { } else {
$default_url = $wpcc_noconversion_url; $default_url = $wpcc_noconversion_url;
} }


// 展示形式:优先使用新字段 wpcc_translate_type兼容旧字段 wpcc_flag_option $wpcc_translate_type = $wpcc_options['wpcc_translate_type'] ?? 0;
$wpcc_translate_type = $wpcc_options['wpcc_translate_type'] ?? ($wpcc_options['wpcc_flag_option'] ?? 0);


$html = "\n" . '<div id="wpcc_widget_inner"><!--wpcc_NC_START-->' . "\n"; $output = "\n" . '<div id="wpcc_widget_inner"><!--wpcc_NC_START-->' . "\n";

if ( $wpcc_translate_type == 0 ) {
// 统一语义1 = 平铺0 = 下拉 $output .= ' <span id="wpcc_original_link" class="' . ( $wpcc_target_lang == false ? 'wpcc_current_lang' : 'wpcc_lang' ) . '" ><a class="wpcc_link" href="' . esc_url( $default_url ) . '" title="' . esc_html( $noconverttip ) . '" langvar="">' . esc_html( $noconverttip ) . '</a></span>' . "\n";
if ( $wpcc_translate_type == 1 ) {
$__nofollow = ( preg_match( '/\/zh\//i', $default_url ) || preg_match( '/(?:[?&])variant=zh(?:&|$)/i', $default_url ) ) ? ' rel="nofollow"' : '';
$html .= '<span id="wpcc_original_link" class="' . ( $wpcc_target_lang == false ? 'wpcc_current_lang' : 'wpcc_lang' ) . '"><a class="wpcc_link"' . $__nofollow . ' href="' . esc_url( $default_url ) . '" title="' . esc_attr( $noconverttip ) . '" langvar="">' . esc_html( $noconverttip ) . '</a></span>' . "\n";


foreach ( $wpcc_langs_urls as $key => $value ) { foreach ( $wpcc_langs_urls as $key => $value ) {
if ( !isset( $wpcc_langs[ $key ] ) || !isset( $wpcc_langs[ $key ][1] ) || !isset( $wpcc_langs[ $key ][2] ) ) { if ( !isset( $wpcc_langs[ $key ] ) || !isset( $wpcc_langs[ $key ][1] ) || !isset( $wpcc_langs[ $key ][2] ) ) {
continue; continue;
} }
$tip = ! empty( $wpcc_options[ $wpcc_langs[ $key ][1] ] ) ? $wpcc_options[ $wpcc_langs[ $key ][1] ] : $wpcc_langs[ $key ][2]; $tip = ! empty( $wpcc_options[ $wpcc_langs[ $key ][1] ] ) ? esc_html( $wpcc_options[ $wpcc_langs[ $key ][1] ] ) : $wpcc_langs[ $key ][2];
if ( $wpcc_target_lang ) { if ( $wpcc_target_lang ) {
$tip = zhconversion( $tip ); $tip = zhconversion( $tip );
} }
$safe_key = esc_attr( $key ); $safe_key = esc_attr( $key );
$html .= '<span id="wpcc_' . $safe_key . '_link" class="' . ( $wpcc_target_lang == $key ? 'wpcc_current_lang' : 'wpcc_lang' ) . '"><a class="wpcc_link" rel="nofollow" href="' . esc_url( $value ) . '" title="' . esc_attr( $tip ) . '" langvar="' . $safe_key . '">' . esc_html( $tip ) . '</a></span>' . "\n"; $output .= ' <span id="wpcc_' . $safe_key . '_link" class="' . ( $wpcc_target_lang == $key ? 'wpcc_current_lang' : 'wpcc_lang' ) . '" ><a class="wpcc_link" rel="nofollow" href="' . esc_url( $value ) . '" title="' . esc_attr( $tip ) . '" langvar="' . $safe_key . '">' . esc_html( $tip ) . '</a></span>' . "\n";
} }
} else if ( $wpcc_translate_type == 0 ) { } else if ( $wpcc_translate_type == 1 ) {
$checkSelected = function ( $selected_lang ) use ( $wpcc_target_lang ) { $checkSelected = function ( $selected_lang ) use ( $wpcc_target_lang ) {
return $selected_lang == $wpcc_target_lang ? 'selected' : ''; return $selected_lang == $wpcc_target_lang ? "selected" : "";
}; };
$html .= '<select id="wpcc_translate_type" value="' . esc_attr( (string) $wpcc_translate_type ) . '" onchange="wpccRedirectToPage(this)">'; $output .= sprintf( '<select id="wpcc_translate_type" value="%s" onchange="wpccRedirectToPage(this)">', $wpcc_translate_type );
$html .= '<option id="wpcc_original_link" value="" ' . $checkSelected( '' ) . '>' . esc_html( $noconverttip ) . '</option>'; $output .= sprintf( '<option id="wpcc_original_link" value="" %s>%s</option>', $checkSelected( '' ), esc_html( $noconverttip ) );
foreach ( $wpcc_langs_urls as $key => $value ) { foreach ( $wpcc_langs_urls as $key => $value ) {
if ( !isset( $wpcc_langs[ $key ] ) || !isset( $wpcc_langs[ $key ][1] ) || !isset( $wpcc_langs[ $key ][2] ) ) { if ( !isset( $wpcc_langs[ $key ] ) || !isset( $wpcc_langs[ $key ][1] ) || !isset( $wpcc_langs[ $key ][2] ) ) {
continue; continue;
} }
$tip = ! empty( $wpcc_options[ $wpcc_langs[ $key ][1] ] ) ? $wpcc_options[ $wpcc_langs[ $key ][1] ] : $wpcc_langs[ $key ][2]; $tip = ! empty( $wpcc_options[ $wpcc_langs[ $key ][1] ] ) ? esc_html( $wpcc_options[ $wpcc_langs[ $key ][1] ] ) : $wpcc_langs[ $key ][2];
if ( $wpcc_target_lang ) { if ( $wpcc_target_lang ) {
$tip = zhconversion( $tip ); $tip = zhconversion( $tip );
} }
$safe_key = esc_attr( $key ); $safe_key = esc_attr( $key );
$html .= '<option id="wpcc_' . $safe_key . '_link" class="' . esc_attr( $wpcc_target_lang == $key ? 'wpcc_current_lang' : 'wpcc_lang' ) . '" value="' . $safe_key . '" ' . $checkSelected( $key ) . '>' . esc_html( $tip ) . '</option>'; $output .= sprintf( '<option id="wpcc_%s_link" class="%s" value="%s" %s>%s</option>', $safe_key, esc_attr( $wpcc_target_lang == $key ? 'wpcc_current_lang' : 'wpcc_lang' ), $safe_key, $checkSelected( $key ), esc_html( $tip ) );
} }
$html .= '</select>'; $output .= sprintf( '</select>' );
} }

$output .= '<!--wpcc_NC_END--></div>' . "\n";
$html .= '<!--wpcc_NC_END--></div>' . "\n";


if ( ! $echo || $isReturn ) { if ( ! $echo || $isReturn ) {
return $html; return $output;
} }
echo $html; echo $output;
} }


/** /**
@ -743,23 +623,23 @@ function wpcc_output_navi( $args = '', $isReturn = false ) {
function wpcc_output_navi2() { function wpcc_output_navi2() {
global $wpcc_target_lang, $wpcc_noconversion_url, $wpcc_langs_urls, $wpcc_options; global $wpcc_target_lang, $wpcc_noconversion_url, $wpcc_langs_urls, $wpcc_options;


if ( ( ! empty($wpcc_options['wpcc_browser_redirect']) && $wpcc_options['wpcc_browser_redirect'] == 2 ) && $wpcc_target_lang || if ( ( $wpcc_options['wpcc_browser_redirect'] == 2 || $wpcc_options['wpcc_use_cookie_variant'] == 2 ) &&
( ! empty($wpcc_options['wpcc_use_cookie_variant']) && $wpcc_options['wpcc_use_cookie_variant'] == 2 ) && $wpcc_target_lang ) { $wpcc_target_lang
) {
$default_url = wpcc_link_conversion( $wpcc_noconversion_url, 'zh' ); $default_url = wpcc_link_conversion( $wpcc_noconversion_url, 'zh' );
if ( ! empty($wpcc_options['wpcc_use_permalink']) && is_home() && ! is_paged() ) { if ( $wpcc_options['wpcc_use_permalink'] != 0 && is_home() && ! is_paged() ) {
$default_url = trailingslashit( $default_url ); $default_url = trailingslashit( $default_url );
} }
} else { } else {
$default_url = $wpcc_noconversion_url; $default_url = $wpcc_noconversion_url;
} }


$html = "\n" . '<div id="wpcc_widget_inner"><!--wpcc_NC_START-->' . "\n"; $output = "\n" . '<div id="wpcc_widget_inner"><!--wpcc_NC_START-->' . "\n";
$__nofollow2 = ( preg_match( '/\/zh\//i', $default_url ) || preg_match( '/(?:[?&])variant=zh(?:&|$)/i', $default_url ) ) ? ' rel="nofollow"' : ''; $output .= ' <span id="wpcc_original_link" class="' . ( $wpcc_target_lang == false ? 'wpcc_current_lang' : 'wpcc_lang' ) . '" ><a class="wpcc_link" href="' . esc_url( $default_url ) . '" title="' . esc_html( '不转换' ) . '">' . esc_html( '不转换' ) . '</a></span>' . "\n";
$html .= '<span id="wpcc_original_link" class="' . ( $wpcc_target_lang == false ? 'wpcc_current_lang' : 'wpcc_lang' ) . '"><a class="wpcc_link"' . $__nofollow2 . ' href="' . esc_url( $default_url ) . '" title="' . esc_html( '不转换' ) . '">' . esc_html( '不转换' ) . '</a></span>' . "\n"; $output .= ' <span id="wpcc_cn_link" class="' . ( $wpcc_target_lang == 'zh-cn' ? 'wpcc_current_lang' : 'wpcc_lang' ) . '" ><a class="wpcc_link" rel="nofollow" href="' . esc_url( $wpcc_langs_urls['zh-cn'] ) . '" title="' . esc_html( '大陆简体' ) . '" >' . esc_html( '大陆简体' ) . '</a></span>' . "\n";
$html .= '<span id="wpcc_cn_link" class="' . ( $wpcc_target_lang == 'zh-cn' ? 'wpcc_current_lang' : 'wpcc_lang' ) . '"><a class="wpcc_link" rel="nofollow" href="' . esc_url( $wpcc_langs_urls['zh-cn'] ) . '" title="' . esc_html( '大陆简体' ) . '">' . esc_html( '大陆简体' ) . '</a></span>' . "\n"; $output .= ' <span id="wpcc_tw_link" class="' . ( $wpcc_target_lang == 'zh-tw' ? 'wpcc_current_lang' : 'wpcc_lang' ) . '"><a class="wpcc_link" rel="nofollow" href="' . esc_url( $wpcc_langs_urls['zh-tw'] ) . '" title="' . esc_html( '台湾正体' ) . '" >' . esc_html( '台湾正体' ) . '</a></span>' . "\n";
$html .= '<span id="wpcc_tw_link" class="' . ( $wpcc_target_lang == 'zh-tw' ? 'wpcc_current_lang' : 'wpcc_lang' ) . '"><a class="wpcc_link" rel="nofollow" href="' . esc_url( $wpcc_langs_urls['zh-tw'] ) . '" title="' . esc_html( '台湾正体' ) . '">' . esc_html( '台湾正体' ) . '</a></span>' . "\n"; $output .= '<!--wpcc_NC_END--></div>' . "\n";
$html .= '<!--wpcc_NC_END--></div>' . "\n"; echo $output;
echo $html;
} }


/** /**
@ -1007,22 +887,11 @@ function wpcc_load_conversion_table() {
* 执行转换 * 执行转换
*/ */
function wpcc_do_conversion() { function wpcc_do_conversion() {
// 当新版核心 WPCC_Main 存在时,跳过旧版全页面转换与过滤器,避免重复执行
if ( class_exists( 'WPCC_Main' ) ) {
return;
}
global $wpcc_direct_conversion_flag, $wpcc_options; global $wpcc_direct_conversion_flag, $wpcc_options;
// 若是 AJAX/REST/wc-ajax 等非 HTML 响应,直接跳过所有转换,避免破坏 JSON/片段
if ( $wpcc_direct_conversion_flag ) {
return;
}
wpcc_load_conversion_table(); wpcc_load_conversion_table();


add_action( 'wp_head', 'wpcc_header' ); add_action( 'wp_head', 'wpcc_header' );



if ( ! $wpcc_direct_conversion_flag ) { if ( ! $wpcc_direct_conversion_flag ) {
remove_action( 'wp_head', 'rel_canonical' ); remove_action( 'wp_head', 'rel_canonical' );
add_action( 'wp_head', 'wpcc_rel_canonical' ); add_action( 'wp_head', 'wpcc_rel_canonical' );
@ -1058,10 +927,6 @@ function wpcc_do_conversion() {
add_filter( 'the_content_rss', 'wpcc_no_conversion_filter', 15 ); add_filter( 'the_content_rss', 'wpcc_no_conversion_filter', 15 );
} }


// 为了让“全页面转换”模式也能正确排除“不转换”片段,先把注释标记包裹的内容替换为持久性包装(不被压缩器移除)
add_filter( 'the_content', 'wpcc_wrap_nc_markers', 1 );
add_filter( 'the_excerpt', 'wpcc_wrap_nc_markers', 1 );
if ( $wpcc_options['wpcc_use_fullpage_conversion'] == 1 ) { if ( $wpcc_options['wpcc_use_fullpage_conversion'] == 1 ) {
@ob_start( 'wpcc_ob_callback' ); @ob_start( 'wpcc_ob_callback' );
return; return;
@ -1120,7 +985,7 @@ function wpcc_header() {
echo '</script>'; echo '</script>';


if ( ! $wpcc_direct_conversion_flag ) { if ( ! $wpcc_direct_conversion_flag ) {
wp_enqueue_script( 'wpcc-search-js', wpcc_DIR_URL . 'assets/js/search-variant.min.js', array(), wpcc_VERSION, false ); wp_enqueue_script( 'wpcc-search-js', wpcc_DIR_URL . 'assets/js/search-variant.min.js', array(), '1.1', false );
} }


if ( $wpcc_direct_conversion_flag || if ( $wpcc_direct_conversion_flag ||
@ -1143,20 +1008,6 @@ function wpcc_ob_callback( $buffer ) {
return zhconversion2( $buffer ) . "\n" . '<!-- WP WP Chinese Converter Full Page Converted. Target Lang: ' . $wpcc_target_lang . ' -->'; return zhconversion2( $buffer ) . "\n" . '<!-- WP WP Chinese Converter Full Page Converted. Target Lang: ' . $wpcc_target_lang . ' -->';
} }


/**
* 将注释标记包裹的区域转换为持久性 data 包裹避免被HTML压缩器移除注释
*/
function wpcc_wrap_nc_markers( $content ) {
if ( empty( $content ) || strpos( $content, 'wpc' ) === false ) {
return $content;
}
$pattern = '/<!--wpc(?:c|s)_NC([a-zA-Z0-9]*)_START-->([\\s\\S]*?)<!--wpc(?:c|s)_NC\1_END-->/i';
return preg_replace_callback( $pattern, function( $m ) {
$inner = $m[2];
return '<span data-wpcc-no-conversion="true">' . $inner . '</span>';
}, $content );
}

/** /**
* 过滤器 - 不转换指定标签内容 * 过滤器 - 不转换指定标签内容
*/ */
@ -1204,7 +1055,6 @@ function wpcc_id() {
return $_wpcc_id ++; return $_wpcc_id ++;
} }



/** /**
* 修复链接转换 * 修复链接转换
*/ */
@ -1214,10 +1064,7 @@ function wpcc_fix_link_conversion( $link ) {
if ( $flag = strstr( $link, '#' ) ) { if ( $flag = strstr( $link, '#' ) ) {
$link = substr( $link, 0, - strlen( $flag ) ); $link = substr( $link, 0, - strlen( $flag ) );
} }
// 动态语言集 if ( preg_match( '/^(.*\/)(zh-tw|zh-cn|zh-sg|zh-hant|zh-hans|zh-my|zh-mo|zh-hk|zh|zh-reset)\/(.+)$/', $link, $tmp ) ) {
$langs = method_exists( 'WPCC_Language_Config', 'get_valid_language_codes' ) ? WPCC_Language_Config::get_valid_language_codes() : array( 'zh-cn','zh-tw','zh-hk','zh-hans','zh-hant','zh-sg','zh-jp' );
$reg = implode( '|', array_map( 'preg_quote', $langs ) );
if ( preg_match( '/^(.*\/)(' . $reg . '|zh|zh-reset)\/(.+)$/', $link, $tmp ) ) {
return user_trailingslashit( $tmp[1] . trailingslashit( $tmp[3] ) . $tmp[2] ) . $flag; return user_trailingslashit( $tmp[1] . trailingslashit( $tmp[3] ) . $tmp[2] ) . $flag;
} }
return $link . $flag; return $link . $flag;
@ -1237,9 +1084,7 @@ function wpcc_fix_link_conversion( $link ) {
function wpcc_cancel_link_conversion( $link ) { function wpcc_cancel_link_conversion( $link ) {
global $wpcc_options; global $wpcc_options;
if ( $wpcc_options['wpcc_use_permalink'] ) { if ( $wpcc_options['wpcc_use_permalink'] ) {
$langs = method_exists( 'WPCC_Language_Config', 'get_valid_language_codes' ) ? WPCC_Language_Config::get_valid_language_codes() : array( 'zh-cn','zh-tw','zh-hk','zh-hans','zh-hant','zh-sg','zh-jp' ); if ( preg_match( '/^(.*\/)(zh-tw|zh-cn|zh-sg|zh-hant|zh-hans|zh-my|zh-mo|zh-hk|zh|zh-reset)\/(.+)$/', $link, $tmp ) ) {
$reg = implode( '|', array_map( 'preg_quote', $langs ) );
if ( preg_match( '/^(.*\/)(' . $reg . '|zh|zh-reset)\/(.+)$/', $link, $tmp ) ) {
return $tmp[1] . $tmp[3]; return $tmp[1] . $tmp[3];
} }
return $link; return $link;
@ -1257,9 +1102,7 @@ function wpcc_cancel_link_conversion( $link ) {
function wpcc_pagenum_link_fix( $link ) { function wpcc_pagenum_link_fix( $link ) {
global $wpcc_target_lang, $wpcc_options; global $wpcc_target_lang, $wpcc_options;
global $paged; global $paged;
if ( $wpcc_options['wpcc_use_permalink'] != 1 ) {
// 检查配置是否存在
if ( ! is_array( $wpcc_options ) || ! isset( $wpcc_options['wpcc_use_permalink'] ) || $wpcc_options['wpcc_use_permalink'] != 1 ) {
return $link; return $link;
} }


@ -1280,10 +1123,7 @@ function wpcc_pagenum_link_fix( $link ) {
*/ */
function wpcc_cancel_incorrect_redirect( $redirect_to, $redirect_from ) { function wpcc_cancel_incorrect_redirect( $redirect_to, $redirect_from ) {
global $wp_rewrite; global $wp_rewrite;
// 动态语言集 if ( preg_match( '/^.*\/(zh-tw|zh-cn|zh-sg|zh-hant|zh-hans|zh-my|zh-mo|zh-hk|zh|zh-reset)\/?.+$/', $redirect_to ) ) {
$langs = method_exists( 'WPCC_Language_Config', 'get_valid_language_codes' ) ? WPCC_Language_Config::get_valid_language_codes() : array( 'zh-cn','zh-tw','zh-hk','zh-hans','zh-hant','zh-sg','zh-jp' );
$reg = implode( '|', array_map( 'preg_quote', $langs ) );
if ( preg_match( '/^.*\/(?:' . $reg . '|zh|zh-reset)\/? .+$/x', $redirect_to ) ) {
if ( ( $wp_rewrite->use_trailing_slashes && substr( $redirect_from, - 1 ) != '/' ) || if ( ( $wp_rewrite->use_trailing_slashes && substr( $redirect_from, - 1 ) != '/' ) ||
( ! $wp_rewrite->use_trailing_slashes && substr( $redirect_from, - 1 ) == '/' ) ( ! $wp_rewrite->use_trailing_slashes && substr( $redirect_from, - 1 ) == '/' )
) { ) {
@ -1341,33 +1181,14 @@ function wpcc_body_class( $classes ) {
* 语言属性过滤器 * 语言属性过滤器
*/ */
function wpcc_locale( $output, $doctype = 'html' ) { function wpcc_locale( $output, $doctype = 'html' ) {
global $wpcc_target_lang, $wpcc_langs, $wp; global $wpcc_target_lang, $wpcc_langs;


wpcc_init_languages(); wpcc_init_languages();


$lang = get_bloginfo( 'language' ); $lang = get_bloginfo( 'language' );

if ( $wpcc_target_lang && strpos( $lang, 'zh-' ) === 0 && isset( $wpcc_langs[ $wpcc_target_lang ] ) && isset( $wpcc_langs[ $wpcc_target_lang ][3] ) ) {
// 仅在“确实处于变体页面”时才覆盖 <html lang>
$variant_from_query = '';
if ( isset( $wp->query_vars['variant'] ) ) {
$variant_from_query = sanitize_text_field( $wp->query_vars['variant'] );
}
$variant_from_path = '';
$path = isset( $wp->request ) ? $wp->request : '';
if ( $path && preg_match( '/^(zh-cn|zh-tw|zh-hk|zh-hans|zh-hant|zh-sg|zh|zh-reset)\b/i', $path, $m ) ) {
$variant_from_path = strtolower( $m[1] );
}
$is_variant_request = ( $variant_from_query !== '' ) || ( $variant_from_path !== '' );

if (
$wpcc_target_lang &&
$is_variant_request &&
strpos( $lang, 'zh-' ) === 0 &&
isset( $wpcc_langs[ $wpcc_target_lang ] ) &&
isset( $wpcc_langs[ $wpcc_target_lang ][3] )
) {
$lang = $wpcc_langs[ $wpcc_target_lang ][3]; $lang = $wpcc_langs[ $wpcc_target_lang ][3];
$output = preg_replace( '/lang=\"[^\"]+\"/', "lang=\"{$lang}\"", $output ); $output = preg_replace( '/lang="[^"]+"/', "lang=\"{$lang}\"", $output );
} }
return $output; return $output;
} }
@ -1378,7 +1199,15 @@ function wpcc_locale( $output, $doctype = 'html' ) {
function wpcc_render_no_conversion_block( $block_content, $block ) { function wpcc_render_no_conversion_block( $block_content, $block ) {
if ( isset( $block['blockName'] ) && $block['blockName'] === 'wpcc/no-conversion' ) { if ( isset( $block['blockName'] ) && $block['blockName'] === 'wpcc/no-conversion' ) {
$unique_id = uniqid(); $unique_id = uniqid();
return '<!--wpcc_NC' . $unique_id . '_START-->' . $block_content . '<!--wpcc_NC' . $unique_id . '_END-->';
$pattern = '/<div[^>]*class="[^"]*wpcc-no-conversion-content[^"]*"[^>]*>(.*?)<\/div>/s';

$replacement = function ( $matches ) use ( $unique_id ) {
$content = $matches[1];
return '<div class="wpcc-no-conversion-content"><!--wpcc_NC' . $unique_id . '_START-->' . $content . '<!--wpcc_NC' . $unique_id . '_END--></div>';
};

$block_content = preg_replace_callback( $pattern, $replacement, $block_content );
} }


return $block_content; return $block_content;
@ -1391,7 +1220,7 @@ function wpcc_init_modules() {
$module_manager = WPCC_Module_Manager::get_instance(); $module_manager = WPCC_Module_Manager::get_instance();


$module_manager->register_module( 'WPCC_Cache_Addon', dirname( __FILE__ ) . '/../modules/wpcc-cache-addon.php' ); $module_manager->register_module( 'WPCC_Cache_Addon', dirname( __FILE__ ) . '/../modules/wpcc-cache-addon.php' );
// $module_manager->register_module( 'WPCC_Network', dirname( __FILE__ ) . '/../modules/wpcc-network.php' ); // 已被新的网络设置模块替代 $module_manager->register_module( 'WPCC_Network', dirname( __FILE__ ) . '/../modules/wpcc-network.php' );
$module_manager->register_module( 'WPCC_Rest_Api', dirname( __FILE__ ) . '/../modules/wpcc-rest-api.php' ); $module_manager->register_module( 'WPCC_Rest_Api', dirname( __FILE__ ) . '/../modules/wpcc-rest-api.php' );
$module_manager->register_module( 'WPCC_Modern_Cache', dirname( __FILE__ ) . '/../modules/wpcc-modern-cache.php' ); $module_manager->register_module( 'WPCC_Modern_Cache', dirname( __FILE__ ) . '/../modules/wpcc-modern-cache.php' );
$module_manager->register_module( 'WPCC_SEO_Enhancement', dirname( __FILE__ ) . '/../modules/wpcc-seo-enhancement.php' ); $module_manager->register_module( 'WPCC_SEO_Enhancement', dirname( __FILE__ ) . '/../modules/wpcc-seo-enhancement.php' );
@ -1437,33 +1266,6 @@ add_shortcode( 'wp-chinese-converter', 'wp_chinese_converter_shortcode' );
add_filter( "body_class", "wpcc_body_class" ); add_filter( "body_class", "wpcc_body_class" );
add_filter( 'language_attributes', 'wpcc_locale' ); add_filter( 'language_attributes', 'wpcc_locale' );


// 兜底层兜底:在未启用新内核时,显式将根级变体规则置于顶端,避免 /zh-xx/ 404
if ( ! class_exists( 'WPCC_Main' ) ) {
add_action( 'init', function() {
global $wpcc_options;
if ( empty( $wpcc_options ) || empty( $wpcc_options['wpcc_use_permalink'] ) ) {
return;
}
$enabled = isset( $wpcc_options['wpcc_used_langs'] ) && is_array( $wpcc_options['wpcc_used_langs'] ) ? $wpcc_options['wpcc_used_langs'] : [];
if ( empty( $enabled ) ) {
return;
}
$reg = implode( '|', array_map( 'preg_quote', $enabled ) );
if ( function_exists( 'add_rewrite_rule' ) ) {
// 多次尝试添加规则,确保优先级
add_rewrite_rule( '^(' . $reg . '|zh|zh-reset)/?$', 'index.php?variant=$matches[1]', 'top' );
// 添加钩子确保规则被正确处理
add_filter( 'rewrite_rules_array', function( $rules ) use ( $reg ) {
$new_rules = [];
$root_rule = '^(' . $reg . '|zh|zh-reset)/?$';
$new_rules[ $root_rule ] = 'index.php?variant=$matches[1]';
return array_merge( $new_rules, $rules );
}, 1 ); // 最高优先级
}
}, 1 );
}

// 初始化小部件 // 初始化小部件
if ( ! empty( $wpcc_options ) && is_array( $wpcc_options ) && is_array( $wpcc_options['wpcc_used_langs'] ) ) { if ( ! empty( $wpcc_options ) && is_array( $wpcc_options ) && is_array( $wpcc_options['wpcc_used_langs'] ) ) {
add_action( 'widgets_init', function () { add_action( 'widgets_init', function () {

File diff suppressed because it is too large Load diff

View file

@ -6,7 +6,7 @@
* Author URI: https://wpcc.net * Author URI: https://wpcc.net
* Text Domain: wp-chinese-converter * Text Domain: wp-chinese-converter
* Domain Path: /languages * Domain Path: /languages
* Version: 1.5 * Version: 1.4.0
* License: GPLv3 or later * License: GPLv3 or later
* License URI: http://www.gnu.org/licenses/gpl-3.0.html * License URI: http://www.gnu.org/licenses/gpl-3.0.html
*/ */
@ -25,7 +25,7 @@


// 基础常量定义 // 基础常量定义
define("wpcc_DEBUG", false); define("wpcc_DEBUG", false);
define("wpcc_VERSION", "1.5"); define("wpcc_VERSION", "1.4.0");


// 插件URL常量 // 插件URL常量
if (defined("WP_PLUGIN_URL")) { if (defined("WP_PLUGIN_URL")) {
@ -60,14 +60,11 @@ use Overtrue\PHPOpenCC\OpenCC;
use Overtrue\PHPOpenCC\Strategy; use Overtrue\PHPOpenCC\Strategy;


// 载入核心依赖 // 载入核心依赖
require_once dirname(__FILE__) . require_once dirname(__FILE__) . "/includes/core/class-wpcc-exception-handler.php";
"/includes/core/class-wpcc-exception-handler.php";
require_once dirname(__FILE__) . "/includes/core/class-wpcc-utils.php"; require_once dirname(__FILE__) . "/includes/core/class-wpcc-utils.php";
require_once dirname(__FILE__) . require_once dirname(__FILE__) . "/includes/core/class-converter-factory.php";
"/includes/core/class-wpcc-language-config.php"; require_once dirname(__FILE__) . "/includes/core/class-module-manager.php";
require_once dirname(__FILE__) . "/includes/core/class-wpcc-converter-factory.php"; require_once dirname(__FILE__) . "/includes/core/class-conversion-cache.php";
require_once dirname(__FILE__) . "/includes/core/class-wpcc-module-manager.php";
require_once dirname(__FILE__) . "/includes/core/class-wpcc-conversion-cache.php";
require_once dirname(__FILE__) . "/includes/core/class-wpcc-config.php"; require_once dirname(__FILE__) . "/includes/core/class-wpcc-config.php";
require_once dirname(__FILE__) . "/includes/core/class-wpcc-presets.php"; require_once dirname(__FILE__) . "/includes/core/class-wpcc-presets.php";
require_once dirname(__FILE__) . "/includes/core/class-wpcc-main.php"; require_once dirname(__FILE__) . "/includes/core/class-wpcc-main.php";
@ -82,22 +79,18 @@ function wpcc_add_global_js()
"wpcc-variant", "wpcc-variant",
plugins_url("/assets/dist/wpcc-variant.umd.js", __FILE__), plugins_url("/assets/dist/wpcc-variant.umd.js", __FILE__),
[], [],
wpcc_VERSION, "1.1.0",
); );
wp_register_script( wp_register_script(
"wpcc-block-script-ok", "wpcc-block-script-ok",
plugins_url("/assets/js/wpcc-block-script-ok.js", __FILE__), plugins_url("/assets/js/wpcc-block-script-ok.js", __FILE__),
["wp-blocks", "wp-element", "wpcc-variant"], ["wp-blocks", "wp-element", "wpcc-variant"],
wpcc_VERSION, "1.3.0",
); );


wp_enqueue_script(["wpcc-variant", "wpcc-block-script-ok"]); wp_enqueue_script(["wpcc-variant", "wpcc-block-script-ok"]);
$use_permalink_type = 0;
if (is_array($wpcc_options) && isset($wpcc_options["wpcc_use_permalink"])) {
$use_permalink_type = (int) $wpcc_options["wpcc_use_permalink"];
}
wp_localize_script("wpcc-block-script-ok", "wpc_switcher_use_permalink", [ wp_localize_script("wpcc-block-script-ok", "wpc_switcher_use_permalink", [
"type" => $use_permalink_type, "type" => $wpcc_options["wpcc_use_permalink"],
]); ]);
} }


@ -114,16 +107,6 @@ if (file_exists($modules_dir . "wpcc-sitemap.php")) {
require_once $modules_dir . "wpcc-sitemap.php"; require_once $modules_dir . "wpcc-sitemap.php";
} }


// 加载网络设置模块
if (
is_multisite() &&
file_exists(__DIR__ . "/includes/network/wpcc-network-settings.php")
) {
require_once __DIR__ . "/includes/network/wpcc-network-settings.php";
add_action("init", ["WPCC_Network_Settings", "init"]);
}


// 容错处理 - 只有配置正确时才加载功能 // 容错处理 - 只有配置正确时才加载功能
if ( if (
$wpcc_options != false && $wpcc_options != false &&
@ -133,18 +116,13 @@ if (
// 加载遗留的核心功能模块(为了向后兼容) // 加载遗留的核心功能模块(为了向后兼容)
require_once dirname(__FILE__) . "/includes/wpcc-core.php"; require_once dirname(__FILE__) . "/includes/wpcc-core.php";


// 确保核心初始化与查询变量已注册(保证变体参数与重写规则生效)
if (!has_action("init", "wpcc_init")) {
add_action("init", "wpcc_init");
}
if ( ! class_exists( 'WPCC_Main' ) ) {
add_filter("query_vars", "wpcc_insert_query_vars");
}

// 加载管理后台模块(仅在后台时加载) // 加载管理后台模块(仅在后台时加载)
if (is_admin()) { if (is_admin()) {
require_once dirname(__FILE__) . "/includes/wpcc-admin.php"; require_once dirname(__FILE__) . "/includes/wpcc-admin.php";
} }

// 新架构已经处理了大部分初始化;移除旧版全局脚本入列以避免重复/冲突
// add_action("wp_enqueue_scripts", "wpcc_add_global_js");
} }


/** /**
@ -161,26 +139,21 @@ function wpcc_mobile_exist($name)
*/ */
function get_wpcc_option($option_name, $default = false) function get_wpcc_option($option_name, $default = false)
{ {
// 使用现代化的配置管理器 if (is_multisite() && wpcc_mobile_exist("network")) {
global $wpcc_main; return get_site_option($option_name, $default);
if ($wpcc_main && method_exists($wpcc_main, "get_config")) { } else {
$config = $wpcc_main->get_config();
if ($option_name === "wpcc_options") {
return $config->get_all_options();
}
return $config->get_option($option_name, $default);
}

// 向后兼容
return get_option($option_name, $default); return get_option($option_name, $default);
} }
}


/** /**
* 封装更新配置的函数 * 封装更新配置的函数
*/ */
function update_wpcc_option($option_name, $option_value) function update_wpcc_option($option_name, $option_value)
{ {
// 始终将站点级设置保存到当前站点的 options if (is_multisite() && wpcc_mobile_exist("network")) {
// 网络级设置在网络设置模块中使用 update_site_option 专门处理。 return update_site_option($option_name, $option_value);
} else {
return update_option($option_name, $option_value); return update_option($option_name, $option_value);
} }
}