Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a1f7933e90 | ||
|
|
becb4ae3d6 | ||
|
|
7aead0fb97 | ||
|
|
06591a0b24 | ||
|
|
f6f6a4cbb6 | ||
|
|
ae9d1cad4c | ||
|
|
3fa50cb126 | ||
|
|
67c897256f | ||
|
|
b51c35b9da | ||
|
|
424c6eba32 | ||
|
|
ad7f7d498d | ||
|
|
2f2046a809 | ||
|
|
7ad51fb281 | ||
|
|
9a865912c0 | ||
|
|
460a70fc3c | ||
|
|
ea65c7b93b | ||
|
|
156502ba77 | ||
|
|
c253efda07 | ||
|
|
1792d785c0 | ||
|
|
5ceaa907d8 | ||
|
|
96c49503ee | ||
|
|
e526dd95fd | ||
|
|
1703da84b7 | ||
|
|
f4e0a30ef1 | ||
|
|
dcf96cd108 | ||
|
|
97648d798c |
@@ -1,5 +1,5 @@
|
||||
name: Bug Report and Feature Request
|
||||
description: Report a Btranslate issue or suggest a feature or documentation improvement
|
||||
description: Report a HTBD issue or suggest a feature or documentation improvement
|
||||
title: "[Issue]: "
|
||||
body:
|
||||
- type: markdown
|
||||
@@ -32,8 +32,8 @@ body:
|
||||
- type: input
|
||||
id: plugin-version
|
||||
attributes:
|
||||
label: Btranslate version
|
||||
placeholder: "For example: 0.2.2"
|
||||
label: HTBD version
|
||||
placeholder: "For example: 0.3.1"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: 问题反馈与功能建议
|
||||
description: 反馈 Btranslate 的问题,或提出功能和文档改进建议
|
||||
description: 反馈 HTBD 的问题,或提出功能和文档改进建议
|
||||
title: "[Issue]: "
|
||||
body:
|
||||
- type: markdown
|
||||
@@ -32,8 +32,8 @@ body:
|
||||
- type: input
|
||||
id: plugin-version
|
||||
attributes:
|
||||
label: Btranslate 版本
|
||||
placeholder: 例如:0.2.2
|
||||
label: HTBD 版本
|
||||
placeholder: 例如:0.3.1
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
name: Deploy to WordPress.org SVN
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
release_tag:
|
||||
description: GitHub Release tag to deploy (for example, v0.3.1)
|
||||
required: true
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: wordpress-svn-deploy
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
name: Deploy release to WordPress.org
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
env:
|
||||
PLUGIN_SLUG: hyx-translator-for-baidu-translate
|
||||
SVN_URL: https://plugins.svn.wordpress.org/hyx-translator-for-baidu-translate
|
||||
SVN_USERNAME: ${{ secrets.SVN_USERNAME }}
|
||||
SVN_PASSWORD: ${{ secrets.SVN_PASSWORD }}
|
||||
|
||||
steps:
|
||||
- name: Resolve release tag and archive name
|
||||
id: release
|
||||
env:
|
||||
MANUAL_TAG: ${{ inputs.release_tag }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
release_tag="${MANUAL_TAG}"
|
||||
|
||||
if [[ ! "${release_tag}" =~ ^v?([0-9]+\.[0-9]+\.[0-9]+)$ ]]; then
|
||||
printf 'Release tag must be vX.Y.Z or X.Y.Z, got: %s\n' "${release_tag}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
version="${BASH_REMATCH[1]}"
|
||||
archive="${PLUGIN_SLUG}-${version}.zip"
|
||||
printf 'tag=%s\nversion=%s\narchive=%s\n' \
|
||||
"${release_tag}" "${version}" "${archive}" >> "${GITHUB_OUTPUT}"
|
||||
|
||||
- name: Install deployment tools
|
||||
run: sudo apt-get update && sudo apt-get install --yes rsync subversion unzip
|
||||
|
||||
- name: Download ZIP from GitHub Release
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
RELEASE_TAG: ${{ steps.release.outputs.tag }}
|
||||
ARCHIVE: ${{ steps.release.outputs.archive }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir -p release
|
||||
gh release download "${RELEASE_TAG}" \
|
||||
--repo "${GITHUB_REPOSITORY}" \
|
||||
--pattern "${ARCHIVE}" \
|
||||
--dir release
|
||||
test -f "release/${ARCHIVE}"
|
||||
|
||||
- name: Extract and validate plugin package
|
||||
env:
|
||||
ARCHIVE: ${{ steps.release.outputs.archive }}
|
||||
VERSION: ${{ steps.release.outputs.version }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir -p package
|
||||
unzip -q "release/${ARCHIVE}" -d package
|
||||
|
||||
plugin_dir="package/${PLUGIN_SLUG}"
|
||||
plugin_file="${plugin_dir}/${PLUGIN_SLUG}.php"
|
||||
test -f "${plugin_file}"
|
||||
test -f "${plugin_dir}/readme.txt"
|
||||
|
||||
package_version="$(sed -nE 's/^ \* Version: *([^ ]+) *$/\1/p' "${plugin_file}")"
|
||||
stable_tag="$(sed -nE 's/^Stable tag: *([^ ]+) *$/\1/p' "${plugin_dir}/readme.txt")"
|
||||
if [[ "${package_version}" != "${VERSION}" || "${stable_tag}" != "${VERSION}" ]]; then
|
||||
printf 'Version mismatch: release=%s, plugin=%s, stable tag=%s\n' \
|
||||
"${VERSION}" "${package_version}" "${stable_tag}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Check out WordPress.org SVN repository
|
||||
run: |
|
||||
set -euo pipefail
|
||||
svn checkout "${SVN_URL}" wordpress-svn \
|
||||
--non-interactive \
|
||||
--no-auth-cache \
|
||||
--username "${SVN_USERNAME}" \
|
||||
--password "${SVN_PASSWORD}"
|
||||
|
||||
- name: Update trunk and create SVN tag
|
||||
env:
|
||||
VERSION: ${{ steps.release.outputs.version }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
source_dir="${GITHUB_WORKSPACE}/package/${PLUGIN_SLUG}/"
|
||||
trunk_dir="${GITHUB_WORKSPACE}/wordpress-svn/trunk"
|
||||
tag_dir="${GITHUB_WORKSPACE}/wordpress-svn/tags/${VERSION}"
|
||||
|
||||
if svn info "${SVN_URL}/tags/${VERSION}" \
|
||||
--non-interactive \
|
||||
--no-auth-cache \
|
||||
--username "${SVN_USERNAME}" \
|
||||
--password "${SVN_PASSWORD}" >/dev/null 2>&1; then
|
||||
printf 'SVN tag %s already exists; refusing to overwrite it.\n' "${VERSION}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
rsync -a --delete --exclude='.svn' "${source_dir}" "${trunk_dir}/"
|
||||
svn add --force "${trunk_dir}" --parents
|
||||
|
||||
svn status "${trunk_dir}" | while IFS= read -r status_line; do
|
||||
if [[ "${status_line:0:1}" == '!' ]]; then
|
||||
svn rm --force "${status_line:8}"
|
||||
fi
|
||||
done
|
||||
|
||||
svn copy "${trunk_dir}" "${tag_dir}"
|
||||
|
||||
- name: Commit release to WordPress.org SVN
|
||||
env:
|
||||
VERSION: ${{ steps.release.outputs.version }}
|
||||
working-directory: wordpress-svn
|
||||
run: |
|
||||
set -euo pipefail
|
||||
svn status
|
||||
svn commit . \
|
||||
--message "Release ${VERSION}" \
|
||||
--non-interactive \
|
||||
--no-auth-cache \
|
||||
--username "${SVN_USERNAME}" \
|
||||
--password "${SVN_PASSWORD}"
|
||||
@@ -2,8 +2,6 @@ name: Package Plugin
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
tags:
|
||||
- 'v*'
|
||||
pull_request:
|
||||
@@ -12,6 +10,7 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
actions: write
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
@@ -30,15 +29,15 @@ jobs:
|
||||
id: package
|
||||
run: |
|
||||
set -eu
|
||||
version="$(grep -E '^ \* Version:' btranslate.php | sed -E 's/^ \* Version: *//')"
|
||||
archive_name="btranslate-${version}.zip"
|
||||
version="$(grep -E '^ \* Version:' hyx-translator-for-baidu-translate.php | sed -E 's/^ \* Version: *//')"
|
||||
archive_name="hyx-translator-for-baidu-translate-${version}.zip"
|
||||
./build.sh build
|
||||
printf 'archive=%s\n' "${archive_name}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Upload plugin archive
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: btranslate-plugin
|
||||
name: hyx-translator-for-baidu-translate-plugin
|
||||
path: ${{ steps.package.outputs.archive }}
|
||||
if-no-files-found: error
|
||||
retention-days: 30
|
||||
@@ -47,4 +46,14 @@ jobs:
|
||||
if: startsWith( github.ref, 'refs/tags/v' )
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: ${{ steps.package.outputs.archive }}
|
||||
files: ${{ steps.package.outputs.archive }}
|
||||
|
||||
- name: Deploy GitHub Release to WordPress.org SVN
|
||||
if: startsWith( github.ref, 'refs/tags/v' )
|
||||
env:
|
||||
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
gh workflow run deploy-wordpress-svn.yml \
|
||||
--ref "${DEFAULT_BRANCH}" \
|
||||
--field release_tag="${GITHUB_REF_NAME}"
|
||||
+2
-1
@@ -11,4 +11,5 @@
|
||||
/wp-content/
|
||||
.DS_Store
|
||||
*.zip
|
||||
/languages/*.mo
|
||||
/languages/*.mo
|
||||
/svn/
|
||||
@@ -1,4 +1,4 @@
|
||||
# btranslate Agent Guide
|
||||
# hyx-translator-for-baidu-translate Agent Guide
|
||||
|
||||
## Project Scope
|
||||
|
||||
@@ -45,6 +45,15 @@ Unless a user explicitly requests a refresh, each translatable value must be tra
|
||||
- Mock Baidu API calls in every test. Tests must not require live credentials or outbound network access.
|
||||
- Keep changes narrow and avoid unrelated refactors or formatting churn.
|
||||
- After every file change, review `.github/workflows/package-plugin.yml` and ensure the automated packaging Action includes all runtime files required by the change. Validate the packaging commands locally when practical.
|
||||
- Plugin slug and PHP prefix: `btranslate` and `btranslate_`/`BTRANSLATE_`. The current minimum supported PHP version is 8.1 and the current minimum WordPress version is 6.4.
|
||||
- Plugin slug and PHP prefix: `hyx-translator-for-baidu-translate` and `htbd_`/`HTBD_`. The custom translation table is `{$wpdb->prefix}hyx_bd_translations`. The current minimum supported PHP version is 8.1 and the current minimum WordPress version is 6.4.
|
||||
- The repository has no Composer-managed development dependencies or bundled automated test suite. Validate PHP syntax with `find . -path './.git' -prune -o -type f -name '*.php' -print0 | xargs -0 -n1 php -l`. Any future automated tests must mock WordPress API calls and must not require live credentials, external network access, or a WordPress database.
|
||||
- Document supported languages, routing modes, data retention, translation lifecycle, and known limitations in the README whenever the implementation changes.
|
||||
- Whenever any README or readme file is updated, update the corresponding PO translation file under `languages/` in the same change so the translated documentation remains synchronized.
|
||||
|
||||
## Release Workflow
|
||||
|
||||
When the user issues `release x.x.x` or `release vx.x.x`:
|
||||
|
||||
1. Determine the previous version from the latest Git tag reachable from the current commit and output that tag/version before making release changes. Accept the optional leading `v` in the requested version and preserve the repository's existing version/tag convention.
|
||||
2. Search the entire repository, including hidden files such as `.github` and excluding only Git internals and ignored/generated artifacts, for every occurrence of the previous version. Replace all release-version occurrences with the new version, including but not limited to PHP headers/constants, readme files, documentation, workflows, and issue templates. Report the files changed and verify that no stale release-version occurrence remains where the current release version is expected.
|
||||
3. Generate release notes from the previous tag through the current release state, using the commits and resulting diff in that range. Output both an English release log and a Chinese release log in Markdown format, with each complete language version enclosed in its own fenced code block. Keep the two logs semantically aligned and organize notable changes, fixes, compatibility notes, and upgrade considerations when applicable.
|
||||
|
||||
@@ -1,21 +1,24 @@
|
||||
# Btranslate: A Free and Open-Source WordPress Multilingual Plugin Powered by the Baidu Translate API
|
||||
# HTBD: A Free and Open-Source WordPress Multilingual Plugin Powered by the Baidu Translate API
|
||||
|
||||
BTranslate is a multilingual translation plugin for WordPress. It uses the Baidu Translate API to translate posts, pages, and other website content into different languages, with a dedicated URL for each translated version.
|
||||
[](https://github.com/hanyixuanten/HTBD)
|
||||
|
||||
Translations are stored on the server. Unless a user explicitly refreshes a translation, the same content is translated only once for each target language. This avoids calling the translation API on every page visit, reducing API usage and improving page load performance.
|
||||
HTBD is a multilingual translation plugin for WordPress. It uses the Baidu Translate API to translate posts, pages, and other website content into different languages, with a dedicated URL for each translated version.
|
||||
|
||||
Translations are stored on the server in `{$wpdb->prefix}hyx_bd_translations`. Unless a user explicitly refreshes a translation, the same content is translated only once for each target language. Existing `{$wpdb->prefix}btranslate_translations` data is migrated when the renamed plugin is activated. This avoids calling the translation API on every page visit, reducing API usage and improving page load performance.
|
||||
|
||||
## Project Links
|
||||
|
||||
- GitHub repository: [https://github.com/hanyixuanten/btranslate](https://github.com/hanyixuanten/btranslate)
|
||||
- WordPress plugin page: [https://wordpress.org/plugins/btranslate/](https://wordpress.org/plugins/btranslate/)
|
||||
- Plugin homepage: [https://www.vblg.top/index.php/archives/147](https://www.vblg.top/index.php/archives/147)
|
||||
- GitHub repository: [https://github.com/hanyixuanten/HTBD](https://github.com/hanyixuanten/HTBD)
|
||||
- WordPress plugin page: [https://wordpress.org/plugins/hyx-translator-for-baidu-translate/](https://wordpress.org/plugins/hyx-translator-for-baidu-translate/)
|
||||
|
||||
> **Review status:** BTranslate has not yet been approved for the WordPress.org Plugin Directory, so it cannot currently be installed directly from the WordPress admin plugin marketplace. For now, download it from GitHub and install it manually.
|
||||
> **Review status:** HTBD has not yet been approved for the WordPress.org Plugin Directory, so it cannot currently be installed directly from the WordPress admin plugin marketplace. For now, download it from GitHub and install it manually.
|
||||
|
||||
## Key Features
|
||||
|
||||
### Baidu Translate API Integration
|
||||
|
||||
BTranslate uses the translation API provided by the Baidu Translate Open Platform. Users can configure their own App ID and secret key in WordPress Admin, and the plugin handles request signing, API calls, and error handling.
|
||||
HTBD uses the translation API provided by the Baidu Translate Open Platform. Users can configure their own App ID and secret key in WordPress Admin, and the plugin handles request signing, API calls, and error handling.
|
||||
|
||||
### Post and Page Translation
|
||||
|
||||
@@ -32,9 +35,9 @@ The plugin supports translating the main content of WordPress posts and pages, i
|
||||
|
||||
### Multilingual URLs
|
||||
|
||||
BTranslate can generate dedicated URLs for different languages, with support for:
|
||||
HTBD can generate dedicated URLs for different languages, with support for:
|
||||
|
||||
- Language subdirectories such as `/en/` and `/ja/`
|
||||
- Language subdirectories such as `/en/` and `/jp/`
|
||||
- A separate domain for each language
|
||||
- Language-specific permalinks based on the configured routing mode
|
||||
|
||||
@@ -53,11 +56,12 @@ When translating content, the plugin makes every effort to preserve:
|
||||
- Shortcodes
|
||||
- Placeholders
|
||||
- URLs
|
||||
- Content inside `<pre>` and `<code>` elements, which is left untranslated
|
||||
- Protected special content
|
||||
|
||||
### Multilingual SEO
|
||||
|
||||
BTranslate provides essential SEO support for multilingual pages, including:
|
||||
HTBD provides essential SEO support for multilingual pages, including:
|
||||
|
||||
- Multilingual permalinks
|
||||
- Canonical URLs
|
||||
@@ -93,42 +97,44 @@ Because the plugin has not yet been approved for the WordPress.org Plugin Direct
|
||||
|
||||
#### Method 1: Upload the ZIP Package
|
||||
|
||||
1. Visit the [BTranslate GitHub repository](https://github.com/hanyixuanten/btranslate).
|
||||
1. Visit the [HTBD GitHub repository](https://github.com/hanyixuanten/HTBD).
|
||||
2. Select **Releases**.
|
||||
3. Download the **btranslate-*.zip** file from the latest release.
|
||||
3. Download the **hyx-translator-for-baidu-translate-*.zip** file from the latest release.
|
||||
4. Sign in to WordPress Admin.
|
||||
5. Open **Plugins > Add New Plugin**.
|
||||
6. Select **Upload Plugin**.
|
||||
7. Choose the downloaded ZIP file and start the installation.
|
||||
8. Activate BTranslate after installation is complete.
|
||||
8. Activate HTBD after installation is complete.
|
||||
|
||||
#### Method 2: Upload to the Server
|
||||
|
||||
1. Visit the [BTranslate GitHub repository](https://github.com/hanyixuanten/btranslate).
|
||||
1. Visit the [HTBD GitHub repository](https://github.com/hanyixuanten/HTBD).
|
||||
2. Select **Releases**.
|
||||
3. Download the **btranslate-*.zip** file from the latest release.
|
||||
4. Upload the `btranslate` directory to the WordPress plugin directory: `wp-content/plugins/btranslate/`.
|
||||
3. Download the **hyx-translator-for-baidu-translate-*.zip** file from the latest release.
|
||||
4. Upload the `hyx-translator-for-baidu-translate` directory to the WordPress plugin directory: `wp-content/plugins/hyx-translator-for-baidu-translate/`.
|
||||
5. Sign in to WordPress Admin.
|
||||
6. Open **Plugins > Installed Plugins**.
|
||||
7. Find BTranslate and select **Activate**.
|
||||
7. Find HTBD and select **Activate**.
|
||||
|
||||
### 3. Configure the Plugin
|
||||
|
||||
1. Sign in to WordPress Admin.
|
||||
2. Open the BTranslate settings page.
|
||||
2. Open the HTBD settings page.
|
||||
3. Enter the **APP ID** and **secret key** provided by the Baidu Translate Open Platform.
|
||||
4. Set the website's source language.
|
||||
4. Set the website's source language, or enable **Automatically detect the source language** to use Baidu's `auto` source language mode.
|
||||
5. Add the target languages you want to enable.
|
||||
6. Select a language URL mode, such as language subdirectories or domain binding.
|
||||
7. Save the settings.
|
||||
|
||||
After changing language routing settings, open **Settings > Permalinks** in WordPress Admin and confirm that the permalink configuration is working correctly.
|
||||
|
||||
Automatic detection is stored as a separate setting. Entering `auto` in the source language field without enabling automatic detection remains a distinct configuration state.
|
||||
|
||||
### 4. Start Translating
|
||||
|
||||
1. Open the post or page you want to translate in WordPress Admin.
|
||||
2. Save or update the content.
|
||||
3. Use the translation feature provided by BTranslate to generate translations for the target languages.
|
||||
3. Use the translation feature provided by HTBD to generate translations for the target languages.
|
||||
4. Wait for the translation tasks to finish.
|
||||
5. Check each translation at its language-specific URL.
|
||||
|
||||
@@ -144,12 +150,14 @@ When English uses the `/en/` subdirectory, its translated URL is:
|
||||
https://example.com/en/about/
|
||||
```
|
||||
|
||||
When Japanese uses the `/ja/` subdirectory, its translated URL is:
|
||||
When Japanese uses the `/jp/` subdirectory, its translated URL is:
|
||||
|
||||
```txt
|
||||
https://example.com/ja/about/
|
||||
https://example.com/jp/about/
|
||||
```
|
||||
|
||||
Requests for `wp-login.php` or `wp-admin` through a target-language subdirectory or bound domain are redirected to the corresponding source-site URL. Query parameters are preserved.
|
||||
|
||||
The plugin prioritizes saved translations. It calls the translation API again only when the source content changes, a translation becomes invalid, or a user explicitly requests a refresh.
|
||||
|
||||
## Recommendations
|
||||
@@ -173,13 +181,13 @@ The plugin prioritizes saved translations. It calls the translation API again on
|
||||
|
||||
To report a problem or suggest a feature, visit the GitHub repository:
|
||||
|
||||
[https://github.com/hanyixuanten/btranslate](https://github.com/hanyixuanten/btranslate)
|
||||
[https://github.com/hanyixuanten/HTBD](https://github.com/hanyixuanten/HTBD)
|
||||
|
||||
When opening an issue, consider including the following information:
|
||||
|
||||
- WordPress version
|
||||
- PHP version
|
||||
- BTranslate version
|
||||
- HTBD version
|
||||
- Languages and routing mode in use
|
||||
- Steps to reproduce the problem
|
||||
- Error logs with sensitive information removed
|
||||
|
||||
+33
-25
@@ -1,21 +1,24 @@
|
||||
# Btranslate:基于百度翻译 API 的免费开源 Wordpress 多语言插件
|
||||
# HTBD:基于百度翻译 API 的免费开源 Wordpress 多语言插件
|
||||
|
||||
BTranslate 是一款 WordPress 多语言翻译插件,通过百度翻译开放平台 API,将网站中的文章、页面及其他内容翻译为不同语言,并为译文生成独立的语言访问地址。
|
||||
[](https://github.com/hanyixuanten/HTBD)
|
||||
|
||||
插件会将翻译结果保存在服务器中。除非用户主动刷新翻译,否则同一内容在同一目标语言下只需翻译一次,无需在每次访问页面时重复调用翻译 API,有助于减少 API 请求量并提高页面加载速度。
|
||||
HTBD 是一款 WordPress 多语言翻译插件,通过百度翻译开放平台 API,将网站中的文章、页面及其他内容翻译为不同语言,并为译文生成独立的语言访问地址。
|
||||
|
||||
插件会将翻译结果保存在服务器的 `{$wpdb->prefix}hyx_bd_translations` 表中。除非用户主动刷新翻译,否则同一内容在同一目标语言下只需翻译一次。启用改名后的插件时,已有 `{$wpdb->prefix}btranslate_translations` 数据会自动迁移,无需在每次访问页面时重复调用翻译 API,有助于减少 API 请求量并提高页面加载速度。
|
||||
|
||||
## 项目地址
|
||||
|
||||
- GitHub 仓库:[https://github.com/hanyixuanten/btranslate](https://github.com/hanyixuanten/btranslate)
|
||||
- WordPress 插件主页:[https://wordpress.org/plugins/btranslate/](https://wordpress.org/plugins/btranslate/)
|
||||
- 插件主页:[https://www.vblg.top/index.php/archives/147](https://www.vblg.top/index.php/archives/147)
|
||||
- GitHub 仓库:[https://github.com/hanyixuanten/HTBD](https://github.com/hanyixuanten/HTBD)
|
||||
- WordPress 插件主页:[https://wordpress.org/plugins/hyx-translator-for-baidu-translate/](https://wordpress.org/plugins/hyx-translator-for-baidu-translate/)
|
||||
|
||||
> **审核状态:** BTranslate 目前尚未通过 WordPress.org 插件目录审核,因此暂时无法直接通过 WordPress 后台插件市场安装。现阶段请从 GitHub 仓库下载并手动安装。
|
||||
> **审核状态:** HTBD 目前尚未通过 WordPress.org 插件目录审核,因此暂时无法直接通过 WordPress 后台插件市场安装。现阶段请从 GitHub 仓库下载并手动安装。
|
||||
|
||||
## 主要功能
|
||||
|
||||
### 百度翻译 API 集成
|
||||
|
||||
BTranslate 使用百度翻译开放平台提供的翻译 API。用户可以在 WordPress 后台配置自己的 App ID 和密钥,由插件完成请求签名、翻译调用及错误处理。
|
||||
HTBD 使用百度翻译开放平台提供的翻译 API。用户可以在 WordPress 后台配置自己的 App ID 和密钥,由插件完成请求签名、翻译调用及错误处理。
|
||||
|
||||
### 文章和页面翻译
|
||||
|
||||
@@ -32,9 +35,9 @@ BTranslate 使用百度翻译开放平台提供的翻译 API。用户可以在 W
|
||||
|
||||
### 多语言访问地址
|
||||
|
||||
BTranslate 可以为不同语言生成独立的访问地址,支持:
|
||||
HTBD 可以为不同语言生成独立的访问地址,支持:
|
||||
|
||||
- 语言子目录,例如 `/en/`、`/ja/`
|
||||
- 语言子目录,例如 `/en/`、`/jp/`
|
||||
- 不同语言绑定不同域名
|
||||
- 根据配置生成对应语言的固定链接
|
||||
|
||||
@@ -53,11 +56,12 @@ BTranslate 可以为不同语言生成独立的访问地址,支持:
|
||||
- Shortcode 短代码
|
||||
- 占位符
|
||||
- URL
|
||||
- `<pre>` 和 `<code>` 元素内的代码内容(不翻译)
|
||||
- 受保护的特殊内容
|
||||
|
||||
### 多语言 SEO
|
||||
|
||||
BTranslate 为多语言页面提供必要的 SEO 支持,包括:
|
||||
HTBD 为多语言页面提供必要的 SEO 支持,包括:
|
||||
|
||||
- 多语言固定链接
|
||||
- Canonical URL
|
||||
@@ -93,42 +97,44 @@ BTranslate 为多语言页面提供必要的 SEO 支持,包括:
|
||||
|
||||
#### 方法一:上传 ZIP 压缩包
|
||||
|
||||
1. 访问 [BTranslate GitHub 仓库](https://github.com/hanyixuanten/btranslate)。
|
||||
1. 访问 [HTBD GitHub 仓库](https://github.com/hanyixuanten/HTBD)。
|
||||
2. 点击 **Releases**。
|
||||
3. 从最新版本下载 zip 文件 **btranslate-*.zip**
|
||||
3. 从最新版本下载 zip 文件 **hyx-translator-for-baidu-translate-*.zip**
|
||||
4. 登录 WordPress 管理后台。
|
||||
5. 打开“插件” → “安装插件”。
|
||||
6. 点击“上传插件”。
|
||||
7. 选择下载的 ZIP 文件并开始安装。
|
||||
8. 安装完成后启用 BTranslate。
|
||||
8. 安装完成后启用 HTBD。
|
||||
|
||||
#### 方法二:上传到服务器
|
||||
|
||||
1. 访问 [BTranslate GitHub 仓库](https://github.com/hanyixuanten/btranslate)。
|
||||
1. 访问 [HTBD GitHub 仓库](https://github.com/hanyixuanten/HTBD)。
|
||||
2. 点击 **Releases**。
|
||||
3. 从最新版本下载 zip 文件 **btranslate-*.zip**
|
||||
4. 将 `btranslate` 目录上传到 WordPress 的插件目录: `wp-content/plugins/btranslate/`
|
||||
3. 从最新版本下载 zip 文件 **hyx-translator-for-baidu-translate-*.zip**
|
||||
4. 将 `hyx-translator-for-baidu-translate` 目录上传到 WordPress 的插件目录: `wp-content/plugins/hyx-translator-for-baidu-translate/`
|
||||
5. 登录 WordPress 管理后台。
|
||||
6. 打开“插件” → “已安装插件”。
|
||||
7. 找到 BTranslate 并点击“启用”。
|
||||
7. 找到 HTBD 并点击“启用”。
|
||||
|
||||
### 三、配置插件
|
||||
|
||||
1. 登录 WordPress 管理后台。
|
||||
2. 打开 BTranslate 设置页面。
|
||||
2. 打开 HTBD 设置页面。
|
||||
3. 填写百度翻译开放平台提供的 **APP ID** 和 **密钥**。
|
||||
4. 设置网站的源语言。
|
||||
4. 设置网站的源语言,或勾选“自动识别源语言”以使用百度翻译的 `auto` 源语言模式。
|
||||
5. 添加需要启用的目标语言。
|
||||
6. 选择语言 URL 模式,例如语言子目录或域名绑定。
|
||||
7. 保存设置。
|
||||
|
||||
修改语言路由设置后,建议在 WordPress 后台打开“设置” → “固定链接”,确认固定链接配置已经正确生效。
|
||||
|
||||
自动识别状态使用独立选项保存。未勾选自动识别时在源语言输入框中主动填写 `auto`,仍属于不同的配置状态。
|
||||
|
||||
### 四、开始使用
|
||||
|
||||
1. 在 WordPress 后台打开需要翻译的文章或页面。
|
||||
2. 保存或更新内容。
|
||||
3. 使用 BTranslate 提供的翻译功能生成目标语言译文。
|
||||
3. 使用 HTBD 提供的翻译功能生成目标语言译文。
|
||||
4. 等待翻译任务完成。
|
||||
5. 通过对应语言的访问地址检查译文。
|
||||
|
||||
@@ -144,12 +150,14 @@ https://example.com/about/
|
||||
https://example.com/en/about/
|
||||
```
|
||||
|
||||
日语使用 `/ja/` 子目录时,译文地址为:
|
||||
日语使用 `/jp/` 子目录时,译文地址为:
|
||||
|
||||
```txt
|
||||
https://example.com/ja/about/
|
||||
https://example.com/jp/about/
|
||||
```
|
||||
|
||||
通过目标语言子目录或绑定域名请求 `wp-login.php`、`wp-admin` 时,插件会重定向到源站对应地址,并保留查询参数。
|
||||
|
||||
插件会优先使用已保存的译文。只有在原文发生变化、译文失效或用户主动要求刷新时,才需要重新调用翻译 API。
|
||||
|
||||
## 使用建议
|
||||
@@ -173,15 +181,15 @@ https://example.com/ja/about/
|
||||
|
||||
如果在使用过程中遇到问题,或者希望提交功能建议,可以前往 GitHub 仓库反馈:
|
||||
|
||||
[https://github.com/hanyixuanten/btranslate](https://github.com/hanyixuanten/btranslate)
|
||||
[https://github.com/hanyixuanten/HTBD](https://github.com/hanyixuanten/HTBD)
|
||||
|
||||
提交问题时,建议提供以下信息:
|
||||
|
||||
- WordPress 版本
|
||||
- PHP 版本
|
||||
- BTranslate 版本
|
||||
- HTBD 版本
|
||||
- 使用的语言和路由模式
|
||||
- 问题复现步骤
|
||||
- 已隐藏敏感信息的错误日志
|
||||
|
||||
欢迎通过 Issue 或 Pull Request 参与 BTranslate 的改进。
|
||||
欢迎通过 Issue 或 Pull Request 参与 HTBD 的改进。
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var domainMode = document.querySelector('#btranslate-routing-mode input[value="domain"]');
|
||||
var bindingsRow = document.getElementById('btranslate-domain-bindings-row');
|
||||
var domainMode = document.querySelector('#htbd-routing-mode input[value="domain"]');
|
||||
var bindingsRow = document.getElementById('htbd-domain-bindings-row');
|
||||
var autoDetectSourceLanguage = document.getElementById('htbd-auto-detect-source-language');
|
||||
var sourceLanguageRow = document.getElementById('htbd-source-language-row');
|
||||
|
||||
if (domainMode && bindingsRow) {
|
||||
domainMode.addEventListener('change', function () {
|
||||
@@ -10,16 +12,22 @@
|
||||
});
|
||||
}
|
||||
|
||||
document.querySelectorAll('form[data-btranslate-confirm]').forEach(function (form) {
|
||||
if (autoDetectSourceLanguage && sourceLanguageRow) {
|
||||
autoDetectSourceLanguage.addEventListener('change', function () {
|
||||
sourceLanguageRow.style.display = autoDetectSourceLanguage.checked ? 'none' : '';
|
||||
});
|
||||
}
|
||||
|
||||
document.querySelectorAll('form[data-htbd-confirm]').forEach(function (form) {
|
||||
form.addEventListener('submit', function (event) {
|
||||
if (!window.confirm(form.getAttribute('data-btranslate-confirm'))) {
|
||||
if (!window.confirm(form.getAttribute('data-htbd-confirm'))) {
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
var container = document.getElementById('btranslate-translation-progress');
|
||||
if (!container || typeof btranslateAdmin === 'undefined') {
|
||||
var container = document.getElementById('htbd-translation-progress');
|
||||
if (!container || typeof htbdAdmin === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -38,7 +46,7 @@
|
||||
var percent = document.createElement('strong');
|
||||
percent.textContent = progress.percent + '%';
|
||||
summary.appendChild(percent);
|
||||
appendText(summary, ' ' + format(btranslateAdmin.i18n.contentItems, [progress.completed, progress.total]));
|
||||
appendText(summary, ' ' + format(htbdAdmin.i18n.contentItems, [progress.completed, progress.total]));
|
||||
|
||||
var progressBar = document.createElement('div');
|
||||
progressBar.style.cssText = 'max-width:480px;height:12px;background:#dcdcde';
|
||||
@@ -49,12 +57,12 @@
|
||||
|
||||
var description = document.createElement('p');
|
||||
description.className = 'description';
|
||||
description.textContent = format(btranslateAdmin.i18n.latestTask, [progress.task_label, progress.posts_completed, progress.posts_total, progress.terms_completed, progress.terms_total]);
|
||||
description.textContent = format(htbdAdmin.i18n.latestTask, [progress.task_label, progress.posts_completed, progress.posts_total, progress.terms_completed, progress.terms_total]);
|
||||
|
||||
var children = [summary, progressBar, description];
|
||||
if (progress.failed_items.length) {
|
||||
var failedHeading = document.createElement('h3');
|
||||
failedHeading.textContent = btranslateAdmin.i18n.failedItems;
|
||||
failedHeading.textContent = htbdAdmin.i18n.failedItems;
|
||||
children.push(failedHeading);
|
||||
|
||||
var failedList = document.createElement('ul');
|
||||
@@ -65,7 +73,7 @@
|
||||
var retryLink = document.createElement('a');
|
||||
retryLink.className = 'button button-small';
|
||||
retryLink.href = item.retry_url;
|
||||
retryLink.textContent = btranslateAdmin.i18n.retranslate;
|
||||
retryLink.textContent = htbdAdmin.i18n.retranslate;
|
||||
failedItem.appendChild(retryLink);
|
||||
failedList.appendChild(failedItem);
|
||||
});
|
||||
@@ -77,12 +85,12 @@
|
||||
|
||||
function renderProgressError() {
|
||||
var message = document.createElement('p');
|
||||
message.textContent = btranslateAdmin.i18n.progressError;
|
||||
message.textContent = htbdAdmin.i18n.progressError;
|
||||
container.replaceChildren(message);
|
||||
}
|
||||
|
||||
function updateProgress() {
|
||||
var url = btranslateAdmin.progressUrl + '?action=btranslate_translation_progress&_ajax_nonce=' + encodeURIComponent(btranslateAdmin.progressNonce);
|
||||
var url = htbdAdmin.progressUrl + '?action=htbd_translation_progress&_ajax_nonce=' + encodeURIComponent(htbdAdmin.progressNonce);
|
||||
|
||||
window.fetch(url, { credentials: 'same-origin' })
|
||||
.then(function (response) {
|
||||
@@ -1,45 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Plugin Name: Btranslate
|
||||
* Plugin URI: https://github.com/hanyixuanten/btranslate
|
||||
* Description: Persistent multilingual WordPress translations powered by Baidu Translate.
|
||||
* Version: 0.2.2
|
||||
* Requires at least: 6.4
|
||||
* Requires PHP: 8.1
|
||||
* Author: hanyixuanten
|
||||
* License: GPL-3.0-only
|
||||
* License URI: https://www.gnu.org/licenses/gpl-3.0.html
|
||||
* Text Domain: btranslate
|
||||
* Domain Path: /languages
|
||||
*/
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
define( 'BTRANSLATE_VERSION', '0.2.2' );
|
||||
define( 'BTRANSLATE_FILE', __FILE__ );
|
||||
define( 'BTRANSLATE_PATH', plugin_dir_path( __FILE__ ) );
|
||||
|
||||
require_once BTRANSLATE_PATH . 'includes/interface-btranslate-translation-provider.php';
|
||||
require_once BTRANSLATE_PATH . 'includes/class-btranslate-translation-result.php';
|
||||
require_once BTRANSLATE_PATH . 'includes/class-btranslate-translation-identity.php';
|
||||
require_once BTRANSLATE_PATH . 'includes/class-btranslate-translation-store.php';
|
||||
require_once BTRANSLATE_PATH . 'includes/class-btranslate-baidu-provider.php';
|
||||
require_once BTRANSLATE_PATH . 'includes/class-btranslate-translation-service.php';
|
||||
require_once BTRANSLATE_PATH . 'includes/class-btranslate-content-translator.php';
|
||||
require_once BTRANSLATE_PATH . 'includes/class-btranslate-settings.php';
|
||||
require_once BTRANSLATE_PATH . 'includes/class-btranslate-language-router.php';
|
||||
require_once BTRANSLATE_PATH . 'includes/class-btranslate-sitemap-controller.php';
|
||||
require_once BTRANSLATE_PATH . 'includes/class-btranslate-content-controller.php';
|
||||
require_once BTRANSLATE_PATH . 'includes/class-btranslate-admin.php';
|
||||
require_once BTRANSLATE_PATH . 'includes/class-btranslate-plugin.php';
|
||||
require_once BTRANSLATE_PATH . 'includes/class-btranslate-uninstaller.php';
|
||||
|
||||
register_activation_hook( BTRANSLATE_FILE, array( 'BTRANSLATE_Plugin', 'activate' ) );
|
||||
register_deactivation_hook( BTRANSLATE_FILE, array( 'BTRANSLATE_Plugin', 'deactivate' ) );
|
||||
|
||||
function btranslate_load_textdomain() {
|
||||
load_plugin_textdomain( 'btranslate', false, dirname( plugin_basename( BTRANSLATE_FILE ) ) . '/languages' );
|
||||
}
|
||||
|
||||
add_action( 'plugins_loaded', 'btranslate_load_textdomain', 5 );
|
||||
add_action( 'plugins_loaded', array( 'BTRANSLATE_Plugin', 'instance' ) );
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
set -eu
|
||||
|
||||
plugin_slug="btranslate"
|
||||
plugin_slug="hyx-translator-for-baidu-translate"
|
||||
plugin_file="${plugin_slug}.php"
|
||||
script_dir="$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd)"
|
||||
build_dir="${script_dir}/build"
|
||||
plugin_dir="${build_dir}/${plugin_slug}"
|
||||
@@ -20,33 +21,52 @@ build() {
|
||||
fi
|
||||
done
|
||||
|
||||
version="$(grep -E '^ \* Version:' "${script_dir}/btranslate.php" | sed -E 's/^ \* Version: *//')"
|
||||
version="$(grep -E '^ \* Version:' "${script_dir}/${plugin_file}" | sed -E 's/^ \* Version: *//')"
|
||||
archive_name="${plugin_slug}-${version}.zip"
|
||||
archive_path="${script_dir}/${archive_name}"
|
||||
|
||||
clean
|
||||
mkdir -p "${plugin_dir}"
|
||||
cp "${script_dir}/btranslate.php" \
|
||||
cp "${script_dir}/${plugin_file}" \
|
||||
"${script_dir}/uninstall.php" \
|
||||
"${script_dir}/readme.txt" \
|
||||
"${script_dir}/readme-zh_CN.txt" \
|
||||
"${script_dir}/LICENSE" \
|
||||
"${plugin_dir}/"
|
||||
cp -R "${script_dir}/includes" "${plugin_dir}/"
|
||||
cp -R "${script_dir}/assets" "${plugin_dir}/"
|
||||
cp -R "${script_dir}/languages" "${plugin_dir}/"
|
||||
mkdir -p "${plugin_dir}/languages"
|
||||
cp "${script_dir}/languages/${plugin_slug}.pot" \
|
||||
"${script_dir}/languages/${plugin_slug}-zh_CN.po" \
|
||||
"${plugin_dir}/languages/"
|
||||
msgfmt --check --check-format \
|
||||
--output-file="${plugin_dir}/languages/btranslate-zh_CN.mo" \
|
||||
"${script_dir}/languages/btranslate-zh_CN.po"
|
||||
--output-file="${plugin_dir}/languages/${plugin_slug}-zh_CN.mo" \
|
||||
"${script_dir}/languages/${plugin_slug}-zh_CN.po"
|
||||
(
|
||||
cd "${build_dir}"
|
||||
zip -qr "${archive_path}" "${plugin_slug}"
|
||||
)
|
||||
|
||||
unzip -p "${archive_path}" "${plugin_slug}/btranslate.php" | grep -q '^ \* Plugin Name: Btranslate$'
|
||||
unzip -p "${archive_path}" "${plugin_slug}/${plugin_file}" | grep -q '^ \* Plugin Name: HTBD - hyx Translator powered by Baidu Translate$'
|
||||
unzip -p "${archive_path}" "${plugin_slug}/readme.txt" | grep -q "^Stable tag: ${version}$"
|
||||
unzip -p "${archive_path}" "${plugin_slug}/readme-zh_CN.txt" | grep -q "^Stable tag: ${version}$"
|
||||
unzip -Z1 "${archive_path}" | grep -q "^${plugin_slug}/languages/btranslate-zh_CN.mo$"
|
||||
unzip -Z1 "${archive_path}" | grep -q "^${plugin_slug}/languages/${plugin_slug}-zh_CN.mo$"
|
||||
if unzip -Z1 "${archive_path}" | grep -q '/wp-plugins-.*\.po$'; then
|
||||
printf 'Unexpected GlotPress submission file found in archive.\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
unzip -Z1 "${archive_path}" | grep -q "^${plugin_slug}/includes/interface-translation-provider.php$"
|
||||
unzip -Z1 "${archive_path}" | grep -q "^${plugin_slug}/assets/js/admin.js$"
|
||||
if unzip -Z1 "${archive_path}" | grep -Eq '/class-btranslate-[^/]+\.php$'; then
|
||||
printf 'Unexpected legacy class filename found in archive.\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
if unzip -Z1 "${archive_path}" | grep -Eq '/(interface-btranslate-translation-provider\.php|btranslate-admin\.js)$'; then
|
||||
printf 'Unexpected legacy runtime filename found in archive.\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
if grep -R -q 'BTRANSLATE' "${plugin_dir}" --include='*.php'; then
|
||||
printf 'Unexpected legacy PHP prefix found in archive.\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
if unzip -Z1 "${archive_path}" | grep -Eq '/README(\.zh-CN)?\.md$'; then
|
||||
printf 'Unexpected development README found in archive.\n' >&2
|
||||
exit 1
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
/**
|
||||
* Plugin Name: HTBD - hyx Translator powered by Baidu Translate
|
||||
* Plugin URI: https://www.vblg.top/index.php/archives/147
|
||||
* Description: Persistent multilingual WordPress translations powered by Baidu Translate.
|
||||
* Version: 0.3.1
|
||||
* Requires at least: 6.4
|
||||
* Requires PHP: 8.1
|
||||
* Author: hanyixuanten
|
||||
* Author URI: https://www.vblg.top
|
||||
* License: GPL-3.0-only
|
||||
* License URI: https://www.gnu.org/licenses/gpl-3.0.html
|
||||
* Text Domain: hyx-translator-for-baidu-translate
|
||||
* Domain Path: /languages
|
||||
*/
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
define( 'HTBD_VERSION', '0.3.1' );
|
||||
define( 'HTBD_FILE', __FILE__ );
|
||||
define( 'HTBD_PATH', plugin_dir_path( __FILE__ ) );
|
||||
|
||||
require_once HTBD_PATH . 'includes/interface-translation-provider.php';
|
||||
require_once HTBD_PATH . 'includes/class-translation-result.php';
|
||||
require_once HTBD_PATH . 'includes/class-translation-identity.php';
|
||||
require_once HTBD_PATH . 'includes/class-translation-store.php';
|
||||
require_once HTBD_PATH . 'includes/class-baidu-provider.php';
|
||||
require_once HTBD_PATH . 'includes/class-translation-service.php';
|
||||
require_once HTBD_PATH . 'includes/class-content-translator.php';
|
||||
require_once HTBD_PATH . 'includes/class-settings.php';
|
||||
require_once HTBD_PATH . 'includes/class-language-router.php';
|
||||
require_once HTBD_PATH . 'includes/class-sitemap-controller.php';
|
||||
require_once HTBD_PATH . 'includes/class-content-controller.php';
|
||||
require_once HTBD_PATH . 'includes/class-admin.php';
|
||||
require_once HTBD_PATH . 'includes/class-plugin.php';
|
||||
require_once HTBD_PATH . 'includes/class-uninstaller.php';
|
||||
|
||||
register_activation_hook( HTBD_FILE, array( 'HTBD_Plugin', 'activate' ) );
|
||||
register_deactivation_hook( HTBD_FILE, array( 'HTBD_Plugin', 'deactivate' ) );
|
||||
|
||||
function htbd_load_textdomain() {
|
||||
load_plugin_textdomain( 'hyx-translator-for-baidu-translate', false, dirname( plugin_basename( HTBD_FILE ) ) . '/languages' );
|
||||
}
|
||||
|
||||
function htbd_add_plugin_row_meta( $plugin_meta, $plugin_file ) {
|
||||
if ( plugin_basename( HTBD_FILE ) === $plugin_file ) {
|
||||
$plugin_meta[] = sprintf(
|
||||
'<a href="%1$s" target="_blank" rel="noopener noreferrer">%2$s</a>',
|
||||
esc_url( 'https://github.com/hanyixuanten/HTBD' ),
|
||||
esc_html__( 'GitHub Repository', 'hyx-translator-for-baidu-translate' )
|
||||
);
|
||||
}
|
||||
|
||||
return $plugin_meta;
|
||||
}
|
||||
|
||||
add_action( 'init', 'htbd_load_textdomain', 0 );
|
||||
add_action( 'plugins_loaded', array( 'HTBD_Plugin', 'instance' ) );
|
||||
add_filter( 'plugin_row_meta', 'htbd_add_plugin_row_meta', 10, 2 );
|
||||
@@ -0,0 +1,590 @@
|
||||
<?php
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class HTBD_Admin {
|
||||
public function register() {
|
||||
add_action( 'admin_menu', array( $this, 'add_settings_page' ) );
|
||||
add_action( 'admin_init', array( $this, 'register_settings' ) );
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_settings_assets' ) );
|
||||
add_action( 'admin_post_htbd_queue_existing_translations', array( $this, 'queue_existing_translations' ) );
|
||||
add_action( 'admin_post_htbd_clear_translation_cache', array( $this, 'clear_translation_cache' ) );
|
||||
add_action( 'admin_post_htbd_translate_post', array( $this, 'queue_post_translation' ) );
|
||||
add_action( 'admin_post_htbd_retry_failed_translation', array( $this, 'retry_failed_translation' ) );
|
||||
add_action( 'add_meta_boxes', array( $this, 'add_translation_meta_box' ) );
|
||||
add_action( 'save_post', array( $this, 'save_translation_meta_box' ), 30, 3 );
|
||||
add_action( 'wp_ajax_htbd_translation_progress', array( $this, 'translation_progress' ) );
|
||||
add_filter( 'manage_post_posts_columns', array( $this, 'add_translation_column' ) );
|
||||
add_filter( 'manage_page_posts_columns', array( $this, 'add_translation_column' ) );
|
||||
add_action( 'manage_post_posts_custom_column', array( $this, 'render_translation_column' ), 10, 2 );
|
||||
add_action( 'manage_page_posts_custom_column', array( $this, 'render_translation_column' ), 10, 2 );
|
||||
add_filter( 'plugin_action_links_' . plugin_basename( HTBD_FILE ), array( $this, 'add_plugin_settings_link' ) );
|
||||
}
|
||||
|
||||
public function add_translation_meta_box() {
|
||||
foreach ( array( 'post', 'page' ) as $post_type ) {
|
||||
add_meta_box( 'htbd-translation-editor', __( 'Manual translations', 'hyx-translator-for-baidu-translate' ), array( $this, 'render_translation_meta_box' ), $post_type, 'normal', 'default' );
|
||||
}
|
||||
}
|
||||
|
||||
public function render_translation_meta_box( $post ) {
|
||||
wp_nonce_field( 'htbd_save_translation_' . $post->ID, 'htbd_translation_nonce' );
|
||||
$fields = $this->get_post_translation_fields( $post );
|
||||
if ( empty( $fields ) ) {
|
||||
echo '<p>' . esc_html__( 'Generate a translation first, then edit it here.', 'hyx-translator-for-baidu-translate' ) . '</p>';
|
||||
return;
|
||||
}
|
||||
echo '<p class="description">' . esc_html__( 'Changes are saved as manual translations and remain until you explicitly retranslate this content.', 'hyx-translator-for-baidu-translate' ) . '</p>';
|
||||
foreach ( $fields as $language => $language_fields ) {
|
||||
echo '<h4>' . esc_html( strtoupper( $language ) ) . '</h4>';
|
||||
foreach ( $language_fields as $field ) {
|
||||
$input_id = 'htbd-translation-' . md5( $field['context'] );
|
||||
echo '<p><label for="' . esc_attr( $input_id ) . '"><strong>' . esc_html( $field['label'] ) . '</strong></label><textarea class="widefat" rows="' . esc_attr( $field['rows'] ) . '" id="' . esc_attr( $input_id ) . '" name="htbd_translation[' . esc_attr( $language ) . '][' . esc_attr( $field['context'] ) . ']">' . esc_textarea( $field['translated_value'] ) . '</textarea></p>';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function save_translation_meta_box( $post_id, $post, $update ) {
|
||||
if ( ! $update || wp_is_post_revision( $post_id ) || wp_is_post_autosave( $post_id ) || ! in_array( $post->post_type, array( 'post', 'page' ), true ) || ! current_user_can( 'edit_post', $post_id ) || empty( $_POST['htbd_translation_nonce'] ) ) {
|
||||
return;
|
||||
}
|
||||
check_admin_referer( 'htbd_save_translation_' . $post_id, 'htbd_translation_nonce' );
|
||||
$submitted = isset( $_POST['htbd_translation'] ) ? (array) wp_unslash( $_POST['htbd_translation'] ) : array();
|
||||
$allowed = $this->get_post_translation_fields( $post );
|
||||
$settings = HTBD_Settings::get();
|
||||
$provider = new HTBD_Baidu_Provider( $settings['baidu_app_id'], $settings['baidu_secret_key'] );
|
||||
$store = new HTBD_Translation_Store();
|
||||
foreach ( $allowed as $language => $language_fields ) {
|
||||
foreach ( $language_fields as $field ) {
|
||||
if ( ! isset( $submitted[ $language ][ $field['context'] ] ) || ! is_string( $submitted[ $language ][ $field['context'] ] ) ) {
|
||||
continue;
|
||||
}
|
||||
$value = 'post_content' === $field['field'] ? wp_kses_post( $submitted[ $language ][ $field['context'] ] ) : sanitize_textarea_field( $submitted[ $language ][ $field['context'] ] );
|
||||
if ( '' === trim( $value ) ) {
|
||||
continue;
|
||||
}
|
||||
$identity = HTBD_Translation_Identity::key( $field['source_value'], $settings['source_language'], $language, $field['context'], $provider->get_version() );
|
||||
$store->save_manual( $identity, $settings['source_language'], $language, $field['context'], HTBD_Translation_Identity::source_fingerprint( $field['source_value'] ), $value );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function get_post_translation_fields( $post ) {
|
||||
$settings = HTBD_Settings::get();
|
||||
$definitions = array(
|
||||
'post_title' => array( 'label' => __( 'Title', 'hyx-translator-for-baidu-translate' ), 'rows' => 2, 'source' => $post->post_title ),
|
||||
'post_excerpt' => array( 'label' => __( 'Excerpt', 'hyx-translator-for-baidu-translate' ), 'rows' => 3, 'source' => $post->post_excerpt ),
|
||||
);
|
||||
if ( '' !== $post->post_content ) {
|
||||
$definitions['post_content'] = array( 'label' => __( 'Content', 'hyx-translator-for-baidu-translate' ), 'rows' => 3, 'source' => $post->post_content );
|
||||
}
|
||||
foreach ( array( 'wpai_meta_description', '_yoast_wpseo_title', '_yoast_wpseo_metadesc', 'rank_math_title', 'rank_math_description' ) as $meta_key ) {
|
||||
$meta_value = get_post_meta( $post->ID, $meta_key, true );
|
||||
if ( is_string( $meta_value ) && '' !== $meta_value ) {
|
||||
$definitions[ 'meta:' . $meta_key ] = array( 'label' => $meta_key, 'rows' => 3, 'source' => $meta_value );
|
||||
}
|
||||
}
|
||||
$result = array();
|
||||
$store = new HTBD_Translation_Store();
|
||||
$provider = new HTBD_Baidu_Provider( $settings['baidu_app_id'], $settings['baidu_secret_key'] );
|
||||
foreach ( $settings['target_languages'] as $language ) {
|
||||
foreach ( $definitions as $field => $definition ) {
|
||||
$segments = 'post_content' === $field ? HTBD_Content_Translator::segments( $definition['source'] ) : array( $definition['source'] );
|
||||
$text_index = 0;
|
||||
foreach ( (array) $segments as $segment ) {
|
||||
if ( 'post_content' === $field ) {
|
||||
if ( HTBD_Content_Translator::is_tag( $segment ) || '' === trim( $segment ) ) { continue; }
|
||||
$source = HTBD_Content_Translator::surrounding_whitespace( $segment )['text'];
|
||||
$context = 'post:' . $post->ID . ':post_content:text:' . $text_index;
|
||||
++$text_index;
|
||||
} else {
|
||||
$source = $segment;
|
||||
$context = 'post:' . $post->ID . ':' . $field;
|
||||
}
|
||||
if ( '' === trim( $source ) ) { continue; }
|
||||
$identity = HTBD_Translation_Identity::key( $source, $settings['source_language'], $language, $context, $provider->get_version() );
|
||||
$translation = $store->find_valid( $identity );
|
||||
if ( empty( $translation['translated_value'] ) ) { continue; }
|
||||
$result[ $language ][] = array( 'field' => $field, 'context' => $context, 'source_value' => $source, 'translated_value' => $translation['translated_value'], 'label' => $definition['label'] . ( 'post_content' === $field ? ' #' . $text_index : '' ), 'rows' => $definition['rows'] );
|
||||
}
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function add_settings_page() {
|
||||
add_options_page( __( 'HTBD', 'hyx-translator-for-baidu-translate' ), __( 'HTBD', 'hyx-translator-for-baidu-translate' ), 'manage_options', 'htbd', array( $this, 'render_settings_page' ) );
|
||||
}
|
||||
|
||||
public function register_settings() {
|
||||
register_setting( 'htbd_settings', 'htbd_settings', array( $this, 'sanitize_settings' ) );
|
||||
}
|
||||
|
||||
public function enqueue_settings_assets( $hook_suffix ) {
|
||||
if ( 'settings_page_htbd' !== $hook_suffix ) {
|
||||
return;
|
||||
}
|
||||
|
||||
wp_enqueue_script(
|
||||
'htbd-admin',
|
||||
plugins_url( 'assets/js/admin.js', HTBD_FILE ),
|
||||
array(),
|
||||
HTBD_VERSION,
|
||||
true
|
||||
);
|
||||
wp_localize_script(
|
||||
'htbd-admin',
|
||||
'htbdAdmin',
|
||||
array(
|
||||
'progressUrl' => admin_url( 'admin-ajax.php' ),
|
||||
'progressNonce' => wp_create_nonce( 'htbd_translation_progress' ),
|
||||
'i18n' => array(
|
||||
/* translators: 1: translated content item count, 2: total content item count. */
|
||||
'contentItems' => __( '(%1$s / %2$s content items)', 'hyx-translator-for-baidu-translate' ),
|
||||
/* translators: 1: latest task status, 2: translated post/page count, 3: total post/page count, 4: translated category/tag count, 5: total category/tag count. */
|
||||
'latestTask' => __( 'Latest task: %1$s. Posts and pages: %2$s / %3$s; categories and tags: %4$s / %5$s. Updates automatically every 5 seconds.', 'hyx-translator-for-baidu-translate' ),
|
||||
'failedItems' => __( 'Failed translations', 'hyx-translator-for-baidu-translate' ),
|
||||
'retranslate' => __( 'Retranslate', 'hyx-translator-for-baidu-translate' ),
|
||||
'progressError' => __( 'Unable to load translation progress at this time.', 'hyx-translator-for-baidu-translate' ),
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function sanitize_settings( $settings ) {
|
||||
$settings = (array) $settings;
|
||||
$bindings = array();
|
||||
$auto_detect_source_language = ! empty( $settings['auto_detect_source_language'] );
|
||||
$routing_modes = HTBD_Settings::routing_modes( $settings['routing_mode'] ?? array() );
|
||||
$target_languages = array_values( array_filter( array_map( 'sanitize_key', explode( ',', (string) ( $settings['target_languages'] ?? 'en' ) ) ) ) );
|
||||
|
||||
foreach ( preg_split( '/\r\n|\r|\n/', (string) ( $settings['domain_bindings'] ?? '' ) ) as $line ) {
|
||||
$parts = array_map( 'trim', explode( '=', $line, 2 ) );
|
||||
$domain = 2 === count( $parts ) ? HTBD_Settings::normalize_domain( $parts[0] ) : '';
|
||||
$language = 2 === count( $parts ) ? sanitize_key( $parts[1] ) : '';
|
||||
if ( '' !== $domain && in_array( $language, $target_languages, true ) && ! in_array( $language, $bindings, true ) ) {
|
||||
$bindings[ $domain ] = $language;
|
||||
}
|
||||
}
|
||||
|
||||
return array(
|
||||
'auto_detect_source_language' => $auto_detect_source_language,
|
||||
'source_language' => $auto_detect_source_language ? 'auto' : sanitize_key( $settings['source_language'] ?? 'zh' ),
|
||||
'target_languages' => $target_languages,
|
||||
'routing_mode' => $routing_modes,
|
||||
'domain_bindings' => $bindings,
|
||||
'fallback_language' => sanitize_key( $settings['fallback_language'] ?? 'zh' ),
|
||||
'baidu_app_id' => sanitize_text_field( $settings['baidu_app_id'] ?? '' ),
|
||||
'baidu_secret_key' => sanitize_text_field( $settings['baidu_secret_key'] ?? '' ),
|
||||
'log_requests' => ! empty( $settings['log_requests'] ),
|
||||
);
|
||||
}
|
||||
|
||||
public function render_settings_page() {
|
||||
$settings = HTBD_Settings::get();
|
||||
$bindings = array();
|
||||
foreach ( (array) $settings['domain_bindings'] as $domain => $language ) {
|
||||
$bindings[] = $domain . '=' . $language;
|
||||
}
|
||||
?>
|
||||
<div class="wrap">
|
||||
<h1>HTBD</h1>
|
||||
<form action="options.php" method="post">
|
||||
<?php settings_fields( 'htbd_settings' ); ?>
|
||||
<table class="form-table" role="presentation">
|
||||
<tr><th scope="row"><?php esc_html_e( 'Source language detection', 'hyx-translator-for-baidu-translate' ); ?></th><td><label for="htbd-auto-detect-source-language"><input id="htbd-auto-detect-source-language" name="htbd_settings[auto_detect_source_language]" type="checkbox" value="1" <?php checked( $settings['auto_detect_source_language'] ); ?> /> <?php esc_html_e( 'Automatically detect the source language', 'hyx-translator-for-baidu-translate' ); ?></label></td></tr>
|
||||
<tr id="htbd-source-language-row"<?php echo $settings['auto_detect_source_language'] ? ' style="display:none"' : ''; ?>><th scope="row"><label for="htbd-source-language"><?php esc_html_e( 'Source language', 'hyx-translator-for-baidu-translate' ); ?></label></th><td><input id="htbd-source-language" name="htbd_settings[source_language]" type="text" value="<?php echo esc_attr( $settings['source_language'] ); ?>" /><p class="description"><?php esc_html_e( 'Use a Baidu Translate language code, for example zh.', 'hyx-translator-for-baidu-translate' ); ?></p></td></tr>
|
||||
<tr><th scope="row"><label for="htbd-target-languages"><?php esc_html_e( 'Target languages', 'hyx-translator-for-baidu-translate' ); ?></label></th><td><input id="htbd-target-languages" name="htbd_settings[target_languages]" type="text" value="<?php echo esc_attr( implode( ',', $settings['target_languages'] ) ); ?>" class="regular-text" /><p class="description"><?php esc_html_e( 'Separate multiple language codes with commas, for example en,jp.', 'hyx-translator-for-baidu-translate' ); ?></p></td></tr>
|
||||
<tr><th scope="row"><?php esc_html_e( 'URL mode', 'hyx-translator-for-baidu-translate' ); ?></th><td><fieldset id="htbd-routing-mode"><label><input type="checkbox" name="htbd_settings[routing_mode][]" value="subdirectory" <?php checked( in_array( 'subdirectory', $settings['routing_mode'], true ) ); ?> /> <?php esc_html_e( 'Subdirectory', 'hyx-translator-for-baidu-translate' ); ?></label><br /><label><input type="checkbox" name="htbd_settings[routing_mode][]" value="domain" <?php checked( in_array( 'domain', $settings['routing_mode'], true ) ); ?> /> <?php esc_html_e( 'Domain', 'hyx-translator-for-baidu-translate' ); ?></label></fieldset></td></tr>
|
||||
<tr id="htbd-domain-bindings-row"<?php echo in_array( 'domain', $settings['routing_mode'], true ) ? '' : ' style="display:none"'; ?>><th scope="row"><label for="htbd-domain-bindings"><?php esc_html_e( 'Domain bindings', 'hyx-translator-for-baidu-translate' ); ?></label></th><td><textarea id="htbd-domain-bindings" name="htbd_settings[domain_bindings]" rows="4" class="large-text" placeholder="en.example.com=en"><?php echo esc_textarea( implode( "\n", $bindings ) ); ?></textarea><p class="description"><?php esc_html_e( 'Enter one domain=language code pair per line, for example en.example.com=en. Do not include https://, a path, or a port.', 'hyx-translator-for-baidu-translate' ); ?></p></td></tr>
|
||||
<tr><th scope="row"><label for="htbd-baidu-app-id"><?php esc_html_e( 'Baidu application ID', 'hyx-translator-for-baidu-translate' ); ?></label></th><td><input id="htbd-baidu-app-id" name="htbd_settings[baidu_app_id]" type="text" value="<?php echo esc_attr( $settings['baidu_app_id'] ); ?>" class="regular-text" /></td></tr>
|
||||
<tr><th scope="row"><label for="htbd-baidu-secret-key"><?php esc_html_e( 'Baidu secret key', 'hyx-translator-for-baidu-translate' ); ?></label></th><td><input id="htbd-baidu-secret-key" name="htbd_settings[baidu_secret_key]" type="password" value="<?php echo esc_attr( $settings['baidu_secret_key'] ); ?>" class="regular-text" autocomplete="new-password" /></td></tr>
|
||||
<tr><th scope="row"><?php esc_html_e( 'Request logging', 'hyx-translator-for-baidu-translate' ); ?></th><td><label for="htbd-log-requests"><input id="htbd-log-requests" name="htbd_settings[log_requests]" type="checkbox" value="1" <?php checked( $settings['log_requests'] ); ?> /> <?php esc_html_e( 'Log each Baidu Translate request', 'hyx-translator-for-baidu-translate' ); ?></label><p class="description"><?php esc_html_e( 'When enabled, fires the htbd_translation_request_logged action with the language, field, text fingerprint, length, and result status. Credentials, source text, translated text, and full API responses are never included.', 'hyx-translator-for-baidu-translate' ); ?></p></td></tr>
|
||||
</table>
|
||||
<?php submit_button(); ?>
|
||||
<div class="notice notice-warning inline">
|
||||
<p><strong><?php esc_html_e( 'After saving settings for the first time, manually run "Retranslate all content" below.', 'hyx-translator-for-baidu-translate' ); ?></strong></p>
|
||||
</div>
|
||||
</form>
|
||||
<hr />
|
||||
<h2><?php esc_html_e( 'Retranslate content', 'hyx-translator-for-baidu-translate' ); ?></h2>
|
||||
<p><?php esc_html_e( 'Queue all published posts, pages, categories, and tags for translation again.', 'hyx-translator-for-baidu-translate' ); ?></p>
|
||||
<form action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" method="post" data-htbd-confirm="<?php echo esc_attr__( 'This will retranslate all content and use your API quota. Continue?', 'hyx-translator-for-baidu-translate' ); ?>">
|
||||
<input type="hidden" name="action" value="htbd_queue_existing_translations" />
|
||||
<?php wp_nonce_field( 'htbd_queue_existing_translations' ); ?>
|
||||
<?php submit_button( __( 'Retranslate all content', 'hyx-translator-for-baidu-translate' ), 'secondary', 'submit', false ); ?>
|
||||
</form>
|
||||
<p><?php esc_html_e( 'You can also retranslate a single content type.', 'hyx-translator-for-baidu-translate' ); ?></p>
|
||||
<form action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" method="post" style="display:inline-block;margin-right:8px" data-htbd-confirm="<?php echo esc_attr__( 'This will retranslate all posts and pages and use your API quota. Continue?', 'hyx-translator-for-baidu-translate' ); ?>">
|
||||
<input type="hidden" name="action" value="htbd_queue_existing_translations" />
|
||||
<input type="hidden" name="scope" value="posts" />
|
||||
<?php wp_nonce_field( 'htbd_queue_existing_translations' ); ?>
|
||||
<?php submit_button( __( 'Translate all posts', 'hyx-translator-for-baidu-translate' ), 'secondary', 'submit', false ); ?>
|
||||
</form>
|
||||
<form action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" method="post" style="display:inline-block" data-htbd-confirm="<?php echo esc_attr__( 'This will retranslate all categories and tags and use your API quota. Continue?', 'hyx-translator-for-baidu-translate' ); ?>">
|
||||
<input type="hidden" name="action" value="htbd_queue_existing_translations" />
|
||||
<input type="hidden" name="scope" value="terms" />
|
||||
<?php wp_nonce_field( 'htbd_queue_existing_translations' ); ?>
|
||||
<?php submit_button( __( 'Translate all categories and tags', 'hyx-translator-for-baidu-translate' ), 'secondary', 'submit', false ); ?>
|
||||
</form>
|
||||
<?php if ( count( $settings['target_languages'] ) > 1 ) : ?>
|
||||
<?php foreach ( $settings['target_languages'] as $target_language ) : ?>
|
||||
<hr />
|
||||
<h3><?php echo esc_html( strtoupper( $target_language ) ); ?></h3>
|
||||
<form action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" method="post" data-htbd-confirm="<?php echo esc_attr__( 'This will retranslate all content and use your API quota. Continue?', 'hyx-translator-for-baidu-translate' ); ?>">
|
||||
<input type="hidden" name="action" value="htbd_queue_existing_translations" />
|
||||
<input type="hidden" name="language" value="<?php echo esc_attr( $target_language ); ?>" />
|
||||
<?php wp_nonce_field( 'htbd_queue_existing_translations' ); ?>
|
||||
<?php submit_button( sprintf( '%s %s', $target_language, __( 'Retranslate all content', 'hyx-translator-for-baidu-translate' ) ), 'secondary', 'submit', false ); ?>
|
||||
</form>
|
||||
<form action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" method="post" style="display:inline-block;margin-right:8px" data-htbd-confirm="<?php echo esc_attr__( 'This will retranslate all posts and pages and use your API quota. Continue?', 'hyx-translator-for-baidu-translate' ); ?>">
|
||||
<input type="hidden" name="action" value="htbd_queue_existing_translations" />
|
||||
<input type="hidden" name="scope" value="posts" />
|
||||
<input type="hidden" name="language" value="<?php echo esc_attr( $target_language ); ?>" />
|
||||
<?php wp_nonce_field( 'htbd_queue_existing_translations' ); ?>
|
||||
<?php submit_button( sprintf( '%s %s', $target_language, __( 'Translate all posts', 'hyx-translator-for-baidu-translate' ) ), 'secondary', 'submit', false ); ?>
|
||||
</form>
|
||||
<form action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" method="post" style="display:inline-block" data-htbd-confirm="<?php echo esc_attr__( 'This will retranslate all categories and tags and use your API quota. Continue?', 'hyx-translator-for-baidu-translate' ); ?>">
|
||||
<input type="hidden" name="action" value="htbd_queue_existing_translations" />
|
||||
<input type="hidden" name="scope" value="terms" />
|
||||
<input type="hidden" name="language" value="<?php echo esc_attr( $target_language ); ?>" />
|
||||
<?php wp_nonce_field( 'htbd_queue_existing_translations' ); ?>
|
||||
<?php submit_button( sprintf( '%s %s', $target_language, __( 'Translate all categories and tags', 'hyx-translator-for-baidu-translate' ) ), 'secondary', 'submit', false ); ?>
|
||||
</form>
|
||||
<form action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" method="post" data-htbd-confirm="<?php echo esc_attr( sprintf( /* translators: %s: target language code. */ __( 'This will delete all saved %s translations. Continue?', 'hyx-translator-for-baidu-translate' ), $target_language ) ); ?>">
|
||||
<input type="hidden" name="action" value="htbd_clear_translation_cache" />
|
||||
<input type="hidden" name="language" value="<?php echo esc_attr( $target_language ); ?>" />
|
||||
<?php wp_nonce_field( 'htbd_clear_translation_cache_' . $target_language ); ?>
|
||||
<?php submit_button( sprintf( '%s %s', $target_language, __( 'Clear translation cache', 'hyx-translator-for-baidu-translate' ) ), 'delete', 'submit', false ); ?>
|
||||
</form>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
<hr />
|
||||
<h2><?php esc_html_e( 'Translation cache', 'hyx-translator-for-baidu-translate' ); ?></h2>
|
||||
<p><?php esc_html_e( 'After clearing the cache, the front end temporarily displays source content. Baidu Translate API calls resume when content is translated again.', 'hyx-translator-for-baidu-translate' ); ?></p>
|
||||
<form action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" method="post" data-htbd-confirm="<?php echo esc_attr__( 'This will delete all saved translations, cancel pending translation tasks, and reset progress. Continue?', 'hyx-translator-for-baidu-translate' ); ?>">
|
||||
<input type="hidden" name="action" value="htbd_clear_translation_cache" />
|
||||
<?php wp_nonce_field( 'htbd_clear_translation_cache' ); ?>
|
||||
<?php submit_button( __( 'Clear translation cache', 'hyx-translator-for-baidu-translate' ), 'delete', 'submit', false ); ?>
|
||||
</form>
|
||||
<hr />
|
||||
<h2><?php esc_html_e( 'Translation progress', 'hyx-translator-for-baidu-translate' ); ?></h2>
|
||||
<div id="htbd-translation-progress" aria-live="polite">
|
||||
<p><?php esc_html_e( 'Loading translation progress...', 'hyx-translator-for-baidu-translate' ); ?></p>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
public function queue_existing_translations() {
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
wp_die( esc_html__( 'You do not have permission to perform this action.', 'hyx-translator-for-baidu-translate' ) );
|
||||
}
|
||||
|
||||
check_admin_referer( 'htbd_queue_existing_translations' );
|
||||
$scope = isset( $_POST['scope'] ) ? sanitize_key( wp_unslash( $_POST['scope'] ) ) : 'all';
|
||||
if ( ! in_array( $scope, array( 'all', 'posts', 'terms' ), true ) ) {
|
||||
wp_die( esc_html__( 'Invalid translation scope.', 'hyx-translator-for-baidu-translate' ) );
|
||||
}
|
||||
$target_language = isset( $_POST['language'] ) ? sanitize_key( wp_unslash( $_POST['language'] ) ) : '';
|
||||
$languages = HTBD_Settings::target_languages();
|
||||
if ( '' !== $target_language ) {
|
||||
if ( ! in_array( $target_language, $languages, true ) ) {
|
||||
wp_die( esc_html__( 'Invalid translation request.', 'hyx-translator-for-baidu-translate' ) );
|
||||
}
|
||||
$languages = array( $target_language );
|
||||
}
|
||||
|
||||
$scheduled = 0;
|
||||
$next_run = time();
|
||||
$post_ids = array();
|
||||
$term_ids = array();
|
||||
|
||||
if ( 'all' === $scope || 'posts' === $scope ) {
|
||||
$post_ids = get_posts(
|
||||
array(
|
||||
'post_type' => array( 'post', 'page' ),
|
||||
'post_status' => 'publish',
|
||||
'posts_per_page' => -1,
|
||||
'fields' => 'ids',
|
||||
)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
if ( 'all' === $scope || 'terms' === $scope ) {
|
||||
$term_ids = get_terms(
|
||||
array(
|
||||
'taxonomy' => array( 'category', 'post_tag' ),
|
||||
'hide_empty' => false,
|
||||
'fields' => 'ids',
|
||||
)
|
||||
);
|
||||
if ( is_wp_error( $term_ids ) ) {
|
||||
$term_ids = array();
|
||||
}
|
||||
}
|
||||
|
||||
$this->update_translation_task( $post_ids, $term_ids, $languages );
|
||||
|
||||
foreach ( $post_ids as $post_id ) {
|
||||
foreach ( $languages as $target_language ) {
|
||||
$this->clear_scheduled_event( 'htbd_process_translation', array( $post_id, $target_language, true ) );
|
||||
wp_schedule_single_event( $next_run, 'htbd_process_translation', array( $post_id, $target_language, true ) );
|
||||
$next_run += 2;
|
||||
++$scheduled;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( $term_ids as $term_id ) {
|
||||
foreach ( $languages as $target_language ) {
|
||||
$this->clear_scheduled_event( 'htbd_process_term_translation', array( $term_id, $target_language, true ) );
|
||||
wp_schedule_single_event( $next_run, 'htbd_process_term_translation', array( $term_id, $target_language, true ) );
|
||||
$next_run += 2;
|
||||
++$scheduled;
|
||||
}
|
||||
}
|
||||
|
||||
wp_safe_redirect( add_query_arg( 'htbd_queued', $scheduled, admin_url( 'options-general.php?page=htbd' ) ) );
|
||||
exit;
|
||||
}
|
||||
|
||||
public function clear_translation_cache() {
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
wp_die( esc_html__( 'You do not have permission to perform this action.', 'hyx-translator-for-baidu-translate' ) );
|
||||
}
|
||||
|
||||
$target_language = isset( $_POST['language'] ) ? sanitize_key( wp_unslash( $_POST['language'] ) ) : '';
|
||||
if ( '' !== $target_language && ! in_array( $target_language, HTBD_Settings::target_languages(), true ) ) {
|
||||
wp_die( esc_html__( 'Invalid translation request.', 'hyx-translator-for-baidu-translate' ) );
|
||||
}
|
||||
|
||||
check_admin_referer( 'htbd_clear_translation_cache' . ( $target_language ? '_' . $target_language : '' ) );
|
||||
$deleted = ( new HTBD_Translation_Store() )->clear( $target_language );
|
||||
|
||||
if ( false === $deleted ) {
|
||||
wp_die( esc_html__( 'Failed to clear the translation cache.', 'hyx-translator-for-baidu-translate' ) );
|
||||
}
|
||||
|
||||
if ( '' === $target_language ) {
|
||||
wp_clear_scheduled_hook( 'htbd_process_translation' );
|
||||
wp_clear_scheduled_hook( 'htbd_process_term_translation' );
|
||||
wp_clear_scheduled_hook( 'htbd_process_seo_output_translation' );
|
||||
delete_option( 'htbd_translation_task' );
|
||||
delete_option( 'htbd_retranslation_batch' );
|
||||
}
|
||||
|
||||
wp_safe_redirect( add_query_arg( 'htbd_cache_cleared', $target_language ? $target_language : 1, admin_url( 'options-general.php?page=htbd' ) ) );
|
||||
exit;
|
||||
}
|
||||
|
||||
private function clear_scheduled_event( $hook, $args ) {
|
||||
while ( false !== ( $timestamp = wp_next_scheduled( $hook, $args ) ) ) {
|
||||
wp_unschedule_event( $timestamp, $hook, $args );
|
||||
}
|
||||
}
|
||||
|
||||
public function add_plugin_settings_link( $links ) {
|
||||
array_unshift( $links, '<a href="' . esc_url( admin_url( 'options-general.php?page=htbd' ) ) . '">' . esc_html__( 'Settings', 'hyx-translator-for-baidu-translate' ) . '</a>' );
|
||||
|
||||
return $links;
|
||||
}
|
||||
|
||||
public function translation_progress() {
|
||||
check_ajax_referer( 'htbd_translation_progress' );
|
||||
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
wp_send_json_error( array( 'message' => 'forbidden' ), 403 );
|
||||
}
|
||||
|
||||
$task = (array) get_option( 'htbd_translation_task', array() );
|
||||
$post_ids = array_values( array_filter( array_map( 'absint', (array) ( $task['post_ids'] ?? array() ) ) ) );
|
||||
$term_ids = array_values( array_filter( array_map( 'absint', (array) ( $task['term_ids'] ?? array() ) ) ) );
|
||||
$languages = array_values( array_filter( array_map( 'sanitize_key', (array) ( $task['target_languages'] ?? array() ) ) ) );
|
||||
$started_at = isset( $task['started_at'] ) ? sanitize_text_field( $task['started_at'] ) : '';
|
||||
$store = new HTBD_Translation_Store();
|
||||
$counts = $store->get_completed_item_counts( $languages, $post_ids, $term_ids, $started_at );
|
||||
$failures = $this->prepare_failed_items( $store->get_failed_items( $languages, $post_ids, $term_ids, $started_at ) );
|
||||
|
||||
$posts_total = count( $post_ids ) * count( $languages );
|
||||
$terms_total = count( $term_ids ) * count( $languages );
|
||||
$total = $posts_total + $terms_total;
|
||||
$posts_completed = min( $counts['posts'], $posts_total );
|
||||
$terms_completed = min( $counts['terms'], $terms_total );
|
||||
$completed = $posts_completed + $terms_completed;
|
||||
$percent = $total ? (int) floor( ( $completed / $total ) * 100 ) : 100;
|
||||
|
||||
wp_send_json_success(
|
||||
array(
|
||||
'posts_completed' => $posts_completed,
|
||||
'posts_total' => $posts_total,
|
||||
'terms_completed' => $terms_completed,
|
||||
'terms_total' => $terms_total,
|
||||
'completed' => $completed,
|
||||
'total' => $total,
|
||||
'percent' => $percent,
|
||||
'batch_started_at' => $started_at,
|
||||
'task_label' => $this->translation_task_label( $post_ids, $term_ids ),
|
||||
'failed_items' => $failures,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function add_translation_column( $columns ) {
|
||||
$columns['htbd_translation'] = __( 'Translation status', 'hyx-translator-for-baidu-translate' );
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
public function render_translation_column( $column, $post_id ) {
|
||||
if ( 'htbd_translation' !== $column ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$store = new HTBD_Translation_Store();
|
||||
|
||||
foreach ( HTBD_Settings::target_languages() as $target_language ) {
|
||||
$status = $store->get_post_language_status( $post_id, $target_language );
|
||||
$translated = ! empty( $status['translated_fields'] );
|
||||
$button_text = $translated ? __( 'Retranslate', 'hyx-translator-for-baidu-translate' ) : __( 'Translate', 'hyx-translator-for-baidu-translate' );
|
||||
$action_url = wp_nonce_url(
|
||||
add_query_arg(
|
||||
array(
|
||||
'action' => 'htbd_translate_post',
|
||||
'post_id' => $post_id,
|
||||
'language' => $target_language,
|
||||
'force' => $translated ? '1' : '0',
|
||||
),
|
||||
admin_url( 'admin-post.php' )
|
||||
),
|
||||
'htbd_translate_post_' . $post_id . '_' . $target_language
|
||||
);
|
||||
|
||||
echo '<p><strong>' . esc_html( strtoupper( $target_language ) ) . '</strong>: ';
|
||||
echo $translated ? '<span style="color:#008a20">' . esc_html__( 'Translated', 'hyx-translator-for-baidu-translate' ) . '</span>' : '<span>' . esc_html__( 'Not translated', 'hyx-translator-for-baidu-translate' ) . '</span>';
|
||||
if ( $translated && ! empty( $status['last_translated_at'] ) ) {
|
||||
echo '<br><small>' . esc_html__( 'Last translated:', 'hyx-translator-for-baidu-translate' ) . ' ' . esc_html( get_date_from_gmt( $status['last_translated_at'], 'Y-m-d H:i' ) ) . '</small>';
|
||||
}
|
||||
echo '<br><a class="button button-small" href="' . esc_url( $action_url ) . '">' . esc_html( $button_text ) . '</a></p>';
|
||||
}
|
||||
}
|
||||
|
||||
public function queue_post_translation() {
|
||||
$post_id = isset( $_GET['post_id'] ) ? absint( $_GET['post_id'] ) : 0;
|
||||
$target_language = isset( $_GET['language'] ) ? sanitize_key( wp_unslash( $_GET['language'] ) ) : '';
|
||||
$force_refresh = ! empty( $_GET['force'] );
|
||||
|
||||
if ( ! $post_id || ! current_user_can( 'edit_post', $post_id ) || ! HTBD_Settings::is_supported_language( $target_language ) || $target_language === HTBD_Settings::get()['source_language'] ) {
|
||||
wp_die( esc_html__( 'Invalid translation request.', 'hyx-translator-for-baidu-translate' ) );
|
||||
}
|
||||
|
||||
check_admin_referer( 'htbd_translate_post_' . $post_id . '_' . $target_language );
|
||||
$this->update_translation_task( array( $post_id ), array(), array( $target_language ) );
|
||||
wp_schedule_single_event( time() + 5, 'htbd_process_translation', array( $post_id, $target_language, $force_refresh ) );
|
||||
|
||||
wp_safe_redirect( wp_get_referer() ? wp_get_referer() : admin_url( 'edit.php?post_type=' . get_post_type( $post_id ) ) );
|
||||
exit;
|
||||
}
|
||||
|
||||
public function retry_failed_translation() {
|
||||
$item_type = isset( $_GET['item_type'] ) ? sanitize_key( wp_unslash( $_GET['item_type'] ) ) : '';
|
||||
$item_id = isset( $_GET['item_id'] ) ? absint( $_GET['item_id'] ) : 0;
|
||||
$target_language = isset( $_GET['language'] ) ? sanitize_key( wp_unslash( $_GET['language'] ) ) : '';
|
||||
|
||||
if ( ! current_user_can( 'manage_options' ) || ! $item_id || ! in_array( $item_type, array( 'post', 'term' ), true ) || ! in_array( $target_language, HTBD_Settings::target_languages(), true ) ) {
|
||||
wp_die( esc_html__( 'Invalid translation request.', 'hyx-translator-for-baidu-translate' ) );
|
||||
}
|
||||
|
||||
check_admin_referer( 'htbd_retry_failed_translation_' . $item_type . '_' . $item_id . '_' . $target_language );
|
||||
|
||||
if ( 'post' === $item_type ) {
|
||||
wp_schedule_single_event( time() + 5, 'htbd_process_translation', array( $item_id, $target_language, true ) );
|
||||
} else {
|
||||
wp_schedule_single_event( time() + 5, 'htbd_process_term_translation', array( $item_id, $target_language, true ) );
|
||||
}
|
||||
|
||||
wp_safe_redirect( admin_url( 'options-general.php?page=htbd' ) );
|
||||
exit;
|
||||
}
|
||||
|
||||
private function prepare_failed_items( $failures ) {
|
||||
$items = array();
|
||||
|
||||
foreach ( (array) $failures as $failure ) {
|
||||
$context_parts = explode( ':', $failure['item_context'], 2 );
|
||||
$item_type = $context_parts[0] ?? '';
|
||||
$item_id = isset( $context_parts[1] ) ? absint( $context_parts[1] ) : 0;
|
||||
$target_language = sanitize_key( $failure['target_language'] ?? '' );
|
||||
|
||||
if ( ! $item_id || ! in_array( $item_type, array( 'post', 'term' ), true ) || '' === $target_language ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( 'post' === $item_type ) {
|
||||
$name = get_the_title( $item_id );
|
||||
} else {
|
||||
$term = get_term( $item_id );
|
||||
$name = $term && ! is_wp_error( $term ) ? $term->name : '';
|
||||
}
|
||||
|
||||
if ( '' === $name ) {
|
||||
/* translators: %d: content item ID. */
|
||||
$name = sprintf( __( 'Content #%d', 'hyx-translator-for-baidu-translate' ), $item_id );
|
||||
}
|
||||
|
||||
$retry_url = wp_nonce_url(
|
||||
add_query_arg(
|
||||
array(
|
||||
'action' => 'htbd_retry_failed_translation',
|
||||
'item_type' => $item_type,
|
||||
'item_id' => $item_id,
|
||||
'language' => $target_language,
|
||||
),
|
||||
admin_url( 'admin-post.php' )
|
||||
),
|
||||
'htbd_retry_failed_translation_' . $item_type . '_' . $item_id . '_' . $target_language
|
||||
);
|
||||
|
||||
$items[] = array(
|
||||
'name' => $name,
|
||||
'language' => strtoupper( $target_language ),
|
||||
'retry_url' => $retry_url,
|
||||
);
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
private function update_translation_task( $post_ids, $term_ids, $target_languages ) {
|
||||
update_option(
|
||||
'htbd_translation_task',
|
||||
array(
|
||||
'started_at' => current_time( 'mysql', true ),
|
||||
'post_ids' => array_values( array_filter( array_map( 'absint', (array) $post_ids ) ) ),
|
||||
'term_ids' => array_values( array_filter( array_map( 'absint', (array) $term_ids ) ) ),
|
||||
'target_languages' => array_values( array_filter( array_map( 'sanitize_key', (array) $target_languages ) ) ),
|
||||
),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
private function translation_task_label( $post_ids, $term_ids ) {
|
||||
if ( ! empty( $post_ids ) && ! empty( $term_ids ) ) {
|
||||
return __( 'Full translation', 'hyx-translator-for-baidu-translate' );
|
||||
}
|
||||
|
||||
if ( 1 === count( $post_ids ) ) {
|
||||
return __( 'Single post translation', 'hyx-translator-for-baidu-translate' );
|
||||
}
|
||||
|
||||
if ( ! empty( $post_ids ) ) {
|
||||
return __( 'Posts and pages translation', 'hyx-translator-for-baidu-translate' );
|
||||
}
|
||||
|
||||
if ( ! empty( $term_ids ) ) {
|
||||
return __( 'Categories and tags translation', 'hyx-translator-for-baidu-translate' );
|
||||
}
|
||||
|
||||
return __( 'No translation task', 'hyx-translator-for-baidu-translate' );
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class BTRANSLATE_Baidu_Provider implements BTRANSLATE_Translation_Provider {
|
||||
class HTBD_Baidu_Provider implements HTBD_Translation_Provider {
|
||||
private $app_id;
|
||||
private $secret_key;
|
||||
|
||||
@@ -19,7 +19,7 @@ class BTRANSLATE_Baidu_Provider implements BTRANSLATE_Translation_Provider {
|
||||
if ( '' === $this->app_id || '' === $this->secret_key ) {
|
||||
$this->log_request( $source_value, $source_language, $target_language, $context, 'missing_credentials' );
|
||||
|
||||
return BTRANSLATE_Translation_Result::failure( 'missing_credentials', __( 'Baidu Translate credentials are not configured.', 'btranslate' ) );
|
||||
return HTBD_Translation_Result::failure( 'missing_credentials', __( 'Baidu Translate credentials are not configured.', 'hyx-translator-for-baidu-translate' ) );
|
||||
}
|
||||
|
||||
$salt = (string) wp_rand( 100000, 999999 );
|
||||
@@ -42,27 +42,27 @@ class BTRANSLATE_Baidu_Provider implements BTRANSLATE_Translation_Provider {
|
||||
if ( is_wp_error( $response ) ) {
|
||||
$this->log_request( $source_value, $source_language, $target_language, $context, 'request_failed', $response->get_error_code() );
|
||||
|
||||
return BTRANSLATE_Translation_Result::failure( 'request_failed', $response->get_error_message() );
|
||||
return HTBD_Translation_Result::failure( 'request_failed', $response->get_error_message() );
|
||||
}
|
||||
|
||||
$body = json_decode( wp_remote_retrieve_body( $response ), true );
|
||||
|
||||
if ( ! is_array( $body ) || empty( $body['trans_result'] ) || ! is_array( $body['trans_result'] ) ) {
|
||||
$error_code = isset( $body['error_code'] ) ? sanitize_key( $body['error_code'] ) : 'invalid_response';
|
||||
$error_message = isset( $body['error_msg'] ) ? sanitize_text_field( $body['error_msg'] ) : __( 'Baidu Translate returned an invalid response.', 'btranslate' );
|
||||
$error_message = isset( $body['error_msg'] ) ? sanitize_text_field( $body['error_msg'] ) : __( 'Baidu Translate returned an invalid response.', 'hyx-translator-for-baidu-translate' );
|
||||
$this->log_request( $source_value, $source_language, $target_language, $context, 'failed', $error_code );
|
||||
|
||||
return BTRANSLATE_Translation_Result::failure( $error_code, $error_message );
|
||||
return HTBD_Translation_Result::failure( $error_code, $error_message );
|
||||
}
|
||||
|
||||
$translated_parts = wp_list_pluck( $body['trans_result'], 'dst' );
|
||||
$this->log_request( $source_value, $source_language, $target_language, $context, 'complete' );
|
||||
|
||||
return BTRANSLATE_Translation_Result::success( implode( "\n", $translated_parts ) );
|
||||
return HTBD_Translation_Result::success( implode( "\n", $translated_parts ) );
|
||||
}
|
||||
|
||||
private function log_request( $source_value, $source_language, $target_language, $context, $status, $error_code = '' ) {
|
||||
$settings = BTRANSLATE_Settings::get();
|
||||
$settings = HTBD_Settings::get();
|
||||
|
||||
if ( empty( $settings['log_requests'] ) ) {
|
||||
return;
|
||||
@@ -73,12 +73,12 @@ class BTRANSLATE_Baidu_Provider implements BTRANSLATE_Translation_Provider {
|
||||
'source_language' => sanitize_key( $source_language ),
|
||||
'target_language' => sanitize_key( $target_language ),
|
||||
'context' => sanitize_key( $context ),
|
||||
'source_fingerprint' => BTRANSLATE_Translation_Identity::source_fingerprint( (string) $source_value ),
|
||||
'source_fingerprint' => HTBD_Translation_Identity::source_fingerprint( (string) $source_value ),
|
||||
'source_length' => strlen( (string) $source_value ),
|
||||
'status' => sanitize_key( $status ),
|
||||
'error_code' => sanitize_key( $error_code ),
|
||||
);
|
||||
|
||||
do_action( 'btranslate_translation_request_logged', $entry );
|
||||
do_action( 'htbd_translation_request_logged', $entry );
|
||||
}
|
||||
}
|
||||
@@ -1,445 +0,0 @@
|
||||
<?php
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class BTRANSLATE_Admin {
|
||||
public function register() {
|
||||
add_action( 'admin_menu', array( $this, 'add_settings_page' ) );
|
||||
add_action( 'admin_init', array( $this, 'register_settings' ) );
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_settings_assets' ) );
|
||||
add_action( 'admin_post_btranslate_queue_existing_translations', array( $this, 'queue_existing_translations' ) );
|
||||
add_action( 'admin_post_btranslate_clear_translation_cache', array( $this, 'clear_translation_cache' ) );
|
||||
add_action( 'admin_post_btranslate_translate_post', array( $this, 'queue_post_translation' ) );
|
||||
add_action( 'admin_post_btranslate_retry_failed_translation', array( $this, 'retry_failed_translation' ) );
|
||||
add_action( 'wp_ajax_btranslate_translation_progress', array( $this, 'translation_progress' ) );
|
||||
add_filter( 'manage_post_posts_columns', array( $this, 'add_translation_column' ) );
|
||||
add_filter( 'manage_page_posts_columns', array( $this, 'add_translation_column' ) );
|
||||
add_action( 'manage_post_posts_custom_column', array( $this, 'render_translation_column' ), 10, 2 );
|
||||
add_action( 'manage_page_posts_custom_column', array( $this, 'render_translation_column' ), 10, 2 );
|
||||
add_filter( 'plugin_action_links_' . plugin_basename( BTRANSLATE_FILE ), array( $this, 'add_plugin_settings_link' ) );
|
||||
}
|
||||
|
||||
public function add_settings_page() {
|
||||
add_options_page( __( 'Btranslate', 'btranslate' ), __( 'Btranslate', 'btranslate' ), 'manage_options', 'btranslate', array( $this, 'render_settings_page' ) );
|
||||
}
|
||||
|
||||
public function register_settings() {
|
||||
register_setting( 'btranslate_settings', 'btranslate_settings', array( $this, 'sanitize_settings' ) );
|
||||
}
|
||||
|
||||
public function enqueue_settings_assets( $hook_suffix ) {
|
||||
if ( 'settings_page_btranslate' !== $hook_suffix ) {
|
||||
return;
|
||||
}
|
||||
|
||||
wp_enqueue_script(
|
||||
'btranslate-admin',
|
||||
plugins_url( 'assets/js/btranslate-admin.js', BTRANSLATE_FILE ),
|
||||
array(),
|
||||
BTRANSLATE_VERSION,
|
||||
true
|
||||
);
|
||||
wp_localize_script(
|
||||
'btranslate-admin',
|
||||
'btranslateAdmin',
|
||||
array(
|
||||
'progressUrl' => admin_url( 'admin-ajax.php' ),
|
||||
'progressNonce' => wp_create_nonce( 'btranslate_translation_progress' ),
|
||||
'i18n' => array(
|
||||
'contentItems' => __( '(%1$s / %2$s content items)', 'btranslate' ),
|
||||
'latestTask' => __( 'Latest task: %1$s. Posts and pages: %2$s / %3$s; categories and tags: %4$s / %5$s. Updates automatically every 5 seconds.', 'btranslate' ),
|
||||
'failedItems' => __( 'Failed translations', 'btranslate' ),
|
||||
'retranslate' => __( 'Retranslate', 'btranslate' ),
|
||||
'progressError' => __( 'Unable to load translation progress at this time.', 'btranslate' ),
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function sanitize_settings( $settings ) {
|
||||
$settings = (array) $settings;
|
||||
$bindings = array();
|
||||
$routing_modes = BTRANSLATE_Settings::routing_modes( $settings['routing_mode'] ?? array() );
|
||||
$target_languages = array_values( array_filter( array_map( 'sanitize_key', explode( ',', (string) ( $settings['target_languages'] ?? 'en' ) ) ) ) );
|
||||
|
||||
foreach ( preg_split( '/\r\n|\r|\n/', (string) ( $settings['domain_bindings'] ?? '' ) ) as $line ) {
|
||||
$parts = array_map( 'trim', explode( '=', $line, 2 ) );
|
||||
$domain = 2 === count( $parts ) ? BTRANSLATE_Settings::normalize_domain( $parts[0] ) : '';
|
||||
$language = 2 === count( $parts ) ? sanitize_key( $parts[1] ) : '';
|
||||
if ( '' !== $domain && in_array( $language, $target_languages, true ) && ! in_array( $language, $bindings, true ) ) {
|
||||
$bindings[ $domain ] = $language;
|
||||
}
|
||||
}
|
||||
|
||||
return array(
|
||||
'source_language' => sanitize_key( $settings['source_language'] ?? 'zh' ),
|
||||
'target_languages' => $target_languages,
|
||||
'routing_mode' => $routing_modes,
|
||||
'domain_bindings' => $bindings,
|
||||
'fallback_language' => sanitize_key( $settings['fallback_language'] ?? 'zh' ),
|
||||
'baidu_app_id' => sanitize_text_field( $settings['baidu_app_id'] ?? '' ),
|
||||
'baidu_secret_key' => sanitize_text_field( $settings['baidu_secret_key'] ?? '' ),
|
||||
'log_requests' => ! empty( $settings['log_requests'] ),
|
||||
);
|
||||
}
|
||||
|
||||
public function render_settings_page() {
|
||||
$settings = BTRANSLATE_Settings::get();
|
||||
$bindings = array();
|
||||
foreach ( (array) $settings['domain_bindings'] as $domain => $language ) {
|
||||
$bindings[] = $domain . '=' . $language;
|
||||
}
|
||||
?>
|
||||
<div class="wrap">
|
||||
<h1>Btranslate</h1>
|
||||
<form action="options.php" method="post">
|
||||
<?php settings_fields( 'btranslate_settings' ); ?>
|
||||
<table class="form-table" role="presentation">
|
||||
<tr><th scope="row"><label for="btranslate-source-language"><?php esc_html_e( 'Source language', 'btranslate' ); ?></label></th><td><input id="btranslate-source-language" name="btranslate_settings[source_language]" type="text" value="<?php echo esc_attr( $settings['source_language'] ); ?>" /><p class="description"><?php esc_html_e( 'Use a Baidu Translate language code, for example zh.', 'btranslate' ); ?></p></td></tr>
|
||||
<tr><th scope="row"><label for="btranslate-target-languages"><?php esc_html_e( 'Target languages', 'btranslate' ); ?></label></th><td><input id="btranslate-target-languages" name="btranslate_settings[target_languages]" type="text" value="<?php echo esc_attr( implode( ',', $settings['target_languages'] ) ); ?>" class="regular-text" /><p class="description"><?php esc_html_e( 'Separate multiple language codes with commas, for example en,ja.', 'btranslate' ); ?></p></td></tr>
|
||||
<tr><th scope="row"><?php esc_html_e( 'URL mode', 'btranslate' ); ?></th><td><fieldset id="btranslate-routing-mode"><label><input type="checkbox" name="btranslate_settings[routing_mode][]" value="subdirectory" <?php checked( in_array( 'subdirectory', $settings['routing_mode'], true ) ); ?> /> <?php esc_html_e( 'Subdirectory', 'btranslate' ); ?></label><br /><label><input type="checkbox" name="btranslate_settings[routing_mode][]" value="domain" <?php checked( in_array( 'domain', $settings['routing_mode'], true ) ); ?> /> <?php esc_html_e( 'Domain', 'btranslate' ); ?></label></fieldset></td></tr>
|
||||
<tr id="btranslate-domain-bindings-row"<?php echo in_array( 'domain', $settings['routing_mode'], true ) ? '' : ' style="display:none"'; ?>><th scope="row"><label for="btranslate-domain-bindings"><?php esc_html_e( 'Domain bindings', 'btranslate' ); ?></label></th><td><textarea id="btranslate-domain-bindings" name="btranslate_settings[domain_bindings]" rows="4" class="large-text" placeholder="en.example.com=en"><?php echo esc_textarea( implode( "\n", $bindings ) ); ?></textarea><p class="description"><?php esc_html_e( 'Enter one domain=language code pair per line, for example en.example.com=en. Do not include https://, a path, or a port.', 'btranslate' ); ?></p></td></tr>
|
||||
<tr><th scope="row"><label for="btranslate-baidu-app-id"><?php esc_html_e( 'Baidu application ID', 'btranslate' ); ?></label></th><td><input id="btranslate-baidu-app-id" name="btranslate_settings[baidu_app_id]" type="text" value="<?php echo esc_attr( $settings['baidu_app_id'] ); ?>" class="regular-text" /></td></tr>
|
||||
<tr><th scope="row"><label for="btranslate-baidu-secret-key"><?php esc_html_e( 'Baidu secret key', 'btranslate' ); ?></label></th><td><input id="btranslate-baidu-secret-key" name="btranslate_settings[baidu_secret_key]" type="password" value="<?php echo esc_attr( $settings['baidu_secret_key'] ); ?>" class="regular-text" autocomplete="new-password" /></td></tr>
|
||||
<tr><th scope="row"><?php esc_html_e( 'Request logging', 'btranslate' ); ?></th><td><label for="btranslate-log-requests"><input id="btranslate-log-requests" name="btranslate_settings[log_requests]" type="checkbox" value="1" <?php checked( $settings['log_requests'] ); ?> /> <?php esc_html_e( 'Log each Baidu Translate request', 'btranslate' ); ?></label><p class="description"><?php esc_html_e( 'When enabled, fires the btranslate_translation_request_logged action with the language, field, text fingerprint, length, and result status. Credentials, source text, translated text, and full API responses are never included.', 'btranslate' ); ?></p></td></tr>
|
||||
</table>
|
||||
<?php submit_button(); ?>
|
||||
<div class="notice notice-warning inline">
|
||||
<p><strong><?php esc_html_e( 'After saving settings for the first time, manually run "Retranslate all content" below.', 'btranslate' ); ?></strong></p>
|
||||
</div>
|
||||
</form>
|
||||
<hr />
|
||||
<h2><?php esc_html_e( 'Retranslate content', 'btranslate' ); ?></h2>
|
||||
<p><?php esc_html_e( 'Queue all published posts, pages, categories, and tags for translation again.', 'btranslate' ); ?></p>
|
||||
<form action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" method="post" data-btranslate-confirm="<?php echo esc_attr__( 'This will retranslate all content and use your API quota. Continue?', 'btranslate' ); ?>">
|
||||
<input type="hidden" name="action" value="btranslate_queue_existing_translations" />
|
||||
<?php wp_nonce_field( 'btranslate_queue_existing_translations' ); ?>
|
||||
<?php submit_button( __( 'Retranslate all content', 'btranslate' ), 'secondary', 'submit', false ); ?>
|
||||
</form>
|
||||
<p><?php esc_html_e( 'You can also retranslate a single content type.', 'btranslate' ); ?></p>
|
||||
<form action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" method="post" style="display:inline-block;margin-right:8px" data-btranslate-confirm="<?php echo esc_attr__( 'This will retranslate all posts and pages and use your API quota. Continue?', 'btranslate' ); ?>">
|
||||
<input type="hidden" name="action" value="btranslate_queue_existing_translations" />
|
||||
<input type="hidden" name="scope" value="posts" />
|
||||
<?php wp_nonce_field( 'btranslate_queue_existing_translations' ); ?>
|
||||
<?php submit_button( __( 'Translate all posts', 'btranslate' ), 'secondary', 'submit', false ); ?>
|
||||
</form>
|
||||
<form action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" method="post" style="display:inline-block" data-btranslate-confirm="<?php echo esc_attr__( 'This will retranslate all categories and tags and use your API quota. Continue?', 'btranslate' ); ?>">
|
||||
<input type="hidden" name="action" value="btranslate_queue_existing_translations" />
|
||||
<input type="hidden" name="scope" value="terms" />
|
||||
<?php wp_nonce_field( 'btranslate_queue_existing_translations' ); ?>
|
||||
<?php submit_button( __( 'Translate all categories and tags', 'btranslate' ), 'secondary', 'submit', false ); ?>
|
||||
</form>
|
||||
<hr />
|
||||
<h2><?php esc_html_e( 'Translation cache', 'btranslate' ); ?></h2>
|
||||
<p><?php esc_html_e( 'After clearing the cache, the front end temporarily displays source content. Baidu Translate API calls resume when content is translated again.', 'btranslate' ); ?></p>
|
||||
<form action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" method="post" data-btranslate-confirm="<?php echo esc_attr__( 'This will delete all saved translations, cancel pending translation tasks, and reset progress. Continue?', 'btranslate' ); ?>">
|
||||
<input type="hidden" name="action" value="btranslate_clear_translation_cache" />
|
||||
<?php wp_nonce_field( 'btranslate_clear_translation_cache' ); ?>
|
||||
<?php submit_button( __( 'Clear translation cache', 'btranslate' ), 'delete', 'submit', false ); ?>
|
||||
</form>
|
||||
<hr />
|
||||
<h2><?php esc_html_e( 'Translation progress', 'btranslate' ); ?></h2>
|
||||
<div id="btranslate-translation-progress" aria-live="polite">
|
||||
<p><?php esc_html_e( 'Loading translation progress...', 'btranslate' ); ?></p>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
public function queue_existing_translations() {
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
wp_die( esc_html__( 'You do not have permission to perform this action.', 'btranslate' ) );
|
||||
}
|
||||
|
||||
check_admin_referer( 'btranslate_queue_existing_translations' );
|
||||
$scope = isset( $_POST['scope'] ) ? sanitize_key( wp_unslash( $_POST['scope'] ) ) : 'all';
|
||||
if ( ! in_array( $scope, array( 'all', 'posts', 'terms' ), true ) ) {
|
||||
wp_die( esc_html__( 'Invalid translation scope.', 'btranslate' ) );
|
||||
}
|
||||
|
||||
$scheduled = 0;
|
||||
$next_run = time();
|
||||
$post_ids = array();
|
||||
$term_ids = array();
|
||||
$languages = BTRANSLATE_Settings::target_languages();
|
||||
|
||||
if ( 'all' === $scope || 'posts' === $scope ) {
|
||||
$post_ids = get_posts(
|
||||
array(
|
||||
'post_type' => array( 'post', 'page' ),
|
||||
'post_status' => 'publish',
|
||||
'posts_per_page' => -1,
|
||||
'fields' => 'ids',
|
||||
)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
if ( 'all' === $scope || 'terms' === $scope ) {
|
||||
$term_ids = get_terms(
|
||||
array(
|
||||
'taxonomy' => array( 'category', 'post_tag' ),
|
||||
'hide_empty' => false,
|
||||
'fields' => 'ids',
|
||||
)
|
||||
);
|
||||
if ( is_wp_error( $term_ids ) ) {
|
||||
$term_ids = array();
|
||||
}
|
||||
}
|
||||
|
||||
$this->update_translation_task( $post_ids, $term_ids, $languages );
|
||||
|
||||
foreach ( $post_ids as $post_id ) {
|
||||
foreach ( $languages as $target_language ) {
|
||||
$this->clear_scheduled_event( 'btranslate_process_translation', array( $post_id, $target_language, true ) );
|
||||
wp_schedule_single_event( $next_run, 'btranslate_process_translation', array( $post_id, $target_language, true ) );
|
||||
$next_run += 2;
|
||||
++$scheduled;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( $term_ids as $term_id ) {
|
||||
foreach ( $languages as $target_language ) {
|
||||
$this->clear_scheduled_event( 'btranslate_process_term_translation', array( $term_id, $target_language, true ) );
|
||||
wp_schedule_single_event( $next_run, 'btranslate_process_term_translation', array( $term_id, $target_language, true ) );
|
||||
$next_run += 2;
|
||||
++$scheduled;
|
||||
}
|
||||
}
|
||||
|
||||
wp_safe_redirect( add_query_arg( 'btranslate_queued', $scheduled, admin_url( 'options-general.php?page=btranslate' ) ) );
|
||||
exit;
|
||||
}
|
||||
|
||||
public function clear_translation_cache() {
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
wp_die( esc_html__( 'You do not have permission to perform this action.', 'btranslate' ) );
|
||||
}
|
||||
|
||||
check_admin_referer( 'btranslate_clear_translation_cache' );
|
||||
$deleted = ( new BTRANSLATE_Translation_Store() )->clear();
|
||||
|
||||
if ( false === $deleted ) {
|
||||
wp_die( esc_html__( 'Failed to clear the translation cache.', 'btranslate' ) );
|
||||
}
|
||||
|
||||
wp_clear_scheduled_hook( 'btranslate_process_translation' );
|
||||
wp_clear_scheduled_hook( 'btranslate_process_term_translation' );
|
||||
wp_clear_scheduled_hook( 'btranslate_process_seo_output_translation' );
|
||||
delete_option( 'btranslate_translation_task' );
|
||||
delete_option( 'btranslate_retranslation_batch' );
|
||||
|
||||
wp_safe_redirect( add_query_arg( 'btranslate_cache_cleared', 1, admin_url( 'options-general.php?page=btranslate' ) ) );
|
||||
exit;
|
||||
}
|
||||
|
||||
private function clear_scheduled_event( $hook, $args ) {
|
||||
while ( false !== ( $timestamp = wp_next_scheduled( $hook, $args ) ) ) {
|
||||
wp_unschedule_event( $timestamp, $hook, $args );
|
||||
}
|
||||
}
|
||||
|
||||
public function add_plugin_settings_link( $links ) {
|
||||
array_unshift( $links, '<a href="' . esc_url( admin_url( 'options-general.php?page=btranslate' ) ) . '">' . esc_html__( 'Settings', 'btranslate' ) . '</a>' );
|
||||
|
||||
return $links;
|
||||
}
|
||||
|
||||
public function translation_progress() {
|
||||
check_ajax_referer( 'btranslate_translation_progress' );
|
||||
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
wp_send_json_error( array( 'message' => 'forbidden' ), 403 );
|
||||
}
|
||||
|
||||
$task = (array) get_option( 'btranslate_translation_task', array() );
|
||||
$post_ids = array_values( array_filter( array_map( 'absint', (array) ( $task['post_ids'] ?? array() ) ) ) );
|
||||
$term_ids = array_values( array_filter( array_map( 'absint', (array) ( $task['term_ids'] ?? array() ) ) ) );
|
||||
$languages = array_values( array_filter( array_map( 'sanitize_key', (array) ( $task['target_languages'] ?? array() ) ) ) );
|
||||
$started_at = isset( $task['started_at'] ) ? sanitize_text_field( $task['started_at'] ) : '';
|
||||
$store = new BTRANSLATE_Translation_Store();
|
||||
$counts = $store->get_completed_item_counts( $languages, $post_ids, $term_ids, $started_at );
|
||||
$failures = $this->prepare_failed_items( $store->get_failed_items( $languages, $post_ids, $term_ids, $started_at ) );
|
||||
|
||||
$posts_total = count( $post_ids ) * count( $languages );
|
||||
$terms_total = count( $term_ids ) * count( $languages );
|
||||
$total = $posts_total + $terms_total;
|
||||
$posts_completed = min( $counts['posts'], $posts_total );
|
||||
$terms_completed = min( $counts['terms'], $terms_total );
|
||||
$completed = $posts_completed + $terms_completed;
|
||||
$percent = $total ? (int) floor( ( $completed / $total ) * 100 ) : 100;
|
||||
|
||||
wp_send_json_success(
|
||||
array(
|
||||
'posts_completed' => $posts_completed,
|
||||
'posts_total' => $posts_total,
|
||||
'terms_completed' => $terms_completed,
|
||||
'terms_total' => $terms_total,
|
||||
'completed' => $completed,
|
||||
'total' => $total,
|
||||
'percent' => $percent,
|
||||
'batch_started_at' => $started_at,
|
||||
'task_label' => $this->translation_task_label( $post_ids, $term_ids ),
|
||||
'failed_items' => $failures,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function add_translation_column( $columns ) {
|
||||
$columns['btranslate_translation'] = __( 'Translation status', 'btranslate' );
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
public function render_translation_column( $column, $post_id ) {
|
||||
if ( 'btranslate_translation' !== $column ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$store = new BTRANSLATE_Translation_Store();
|
||||
|
||||
foreach ( BTRANSLATE_Settings::target_languages() as $target_language ) {
|
||||
$status = $store->get_post_language_status( $post_id, $target_language );
|
||||
$translated = ! empty( $status['translated_fields'] );
|
||||
$button_text = $translated ? __( 'Retranslate', 'btranslate' ) : __( 'Translate', 'btranslate' );
|
||||
$action_url = wp_nonce_url(
|
||||
add_query_arg(
|
||||
array(
|
||||
'action' => 'btranslate_translate_post',
|
||||
'post_id' => $post_id,
|
||||
'language' => $target_language,
|
||||
'force' => $translated ? '1' : '0',
|
||||
),
|
||||
admin_url( 'admin-post.php' )
|
||||
),
|
||||
'btranslate_translate_post_' . $post_id . '_' . $target_language
|
||||
);
|
||||
|
||||
echo '<p><strong>' . esc_html( strtoupper( $target_language ) ) . '</strong>: ';
|
||||
echo $translated ? '<span style="color:#008a20">' . esc_html__( 'Translated', 'btranslate' ) . '</span>' : '<span>' . esc_html__( 'Not translated', 'btranslate' ) . '</span>';
|
||||
if ( $translated && ! empty( $status['last_translated_at'] ) ) {
|
||||
echo '<br><small>' . esc_html__( 'Last translated:', 'btranslate' ) . ' ' . esc_html( get_date_from_gmt( $status['last_translated_at'], 'Y-m-d H:i' ) ) . '</small>';
|
||||
}
|
||||
echo '<br><a class="button button-small" href="' . esc_url( $action_url ) . '">' . esc_html( $button_text ) . '</a></p>';
|
||||
}
|
||||
}
|
||||
|
||||
public function queue_post_translation() {
|
||||
$post_id = isset( $_GET['post_id'] ) ? absint( $_GET['post_id'] ) : 0;
|
||||
$target_language = isset( $_GET['language'] ) ? sanitize_key( wp_unslash( $_GET['language'] ) ) : '';
|
||||
$force_refresh = ! empty( $_GET['force'] );
|
||||
|
||||
if ( ! $post_id || ! current_user_can( 'edit_post', $post_id ) || ! BTRANSLATE_Settings::is_supported_language( $target_language ) || $target_language === BTRANSLATE_Settings::get()['source_language'] ) {
|
||||
wp_die( esc_html__( 'Invalid translation request.', 'btranslate' ) );
|
||||
}
|
||||
|
||||
check_admin_referer( 'btranslate_translate_post_' . $post_id . '_' . $target_language );
|
||||
$this->update_translation_task( array( $post_id ), array(), array( $target_language ) );
|
||||
wp_schedule_single_event( time() + 5, 'btranslate_process_translation', array( $post_id, $target_language, $force_refresh ) );
|
||||
|
||||
wp_safe_redirect( wp_get_referer() ? wp_get_referer() : admin_url( 'edit.php?post_type=' . get_post_type( $post_id ) ) );
|
||||
exit;
|
||||
}
|
||||
|
||||
public function retry_failed_translation() {
|
||||
$item_type = isset( $_GET['item_type'] ) ? sanitize_key( wp_unslash( $_GET['item_type'] ) ) : '';
|
||||
$item_id = isset( $_GET['item_id'] ) ? absint( $_GET['item_id'] ) : 0;
|
||||
$target_language = isset( $_GET['language'] ) ? sanitize_key( wp_unslash( $_GET['language'] ) ) : '';
|
||||
|
||||
if ( ! current_user_can( 'manage_options' ) || ! $item_id || ! in_array( $item_type, array( 'post', 'term' ), true ) || ! in_array( $target_language, BTRANSLATE_Settings::target_languages(), true ) ) {
|
||||
wp_die( esc_html__( 'Invalid translation request.', 'btranslate' ) );
|
||||
}
|
||||
|
||||
check_admin_referer( 'btranslate_retry_failed_translation_' . $item_type . '_' . $item_id . '_' . $target_language );
|
||||
|
||||
if ( 'post' === $item_type ) {
|
||||
wp_schedule_single_event( time() + 5, 'btranslate_process_translation', array( $item_id, $target_language, true ) );
|
||||
} else {
|
||||
wp_schedule_single_event( time() + 5, 'btranslate_process_term_translation', array( $item_id, $target_language, true ) );
|
||||
}
|
||||
|
||||
wp_safe_redirect( admin_url( 'options-general.php?page=btranslate' ) );
|
||||
exit;
|
||||
}
|
||||
|
||||
private function prepare_failed_items( $failures ) {
|
||||
$items = array();
|
||||
|
||||
foreach ( (array) $failures as $failure ) {
|
||||
$context_parts = explode( ':', $failure['item_context'], 2 );
|
||||
$item_type = $context_parts[0] ?? '';
|
||||
$item_id = isset( $context_parts[1] ) ? absint( $context_parts[1] ) : 0;
|
||||
$target_language = sanitize_key( $failure['target_language'] ?? '' );
|
||||
|
||||
if ( ! $item_id || ! in_array( $item_type, array( 'post', 'term' ), true ) || '' === $target_language ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( 'post' === $item_type ) {
|
||||
$name = get_the_title( $item_id );
|
||||
} else {
|
||||
$term = get_term( $item_id );
|
||||
$name = $term && ! is_wp_error( $term ) ? $term->name : '';
|
||||
}
|
||||
|
||||
if ( '' === $name ) {
|
||||
$name = sprintf( __( 'Content #%d', 'btranslate' ), $item_id );
|
||||
}
|
||||
|
||||
$retry_url = wp_nonce_url(
|
||||
add_query_arg(
|
||||
array(
|
||||
'action' => 'btranslate_retry_failed_translation',
|
||||
'item_type' => $item_type,
|
||||
'item_id' => $item_id,
|
||||
'language' => $target_language,
|
||||
),
|
||||
admin_url( 'admin-post.php' )
|
||||
),
|
||||
'btranslate_retry_failed_translation_' . $item_type . '_' . $item_id . '_' . $target_language
|
||||
);
|
||||
|
||||
$items[] = array(
|
||||
'name' => $name,
|
||||
'language' => strtoupper( $target_language ),
|
||||
'retry_url' => $retry_url,
|
||||
);
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
private function update_translation_task( $post_ids, $term_ids, $target_languages ) {
|
||||
update_option(
|
||||
'btranslate_translation_task',
|
||||
array(
|
||||
'started_at' => current_time( 'mysql', true ),
|
||||
'post_ids' => array_values( array_filter( array_map( 'absint', (array) $post_ids ) ) ),
|
||||
'term_ids' => array_values( array_filter( array_map( 'absint', (array) $term_ids ) ) ),
|
||||
'target_languages' => array_values( array_filter( array_map( 'sanitize_key', (array) $target_languages ) ) ),
|
||||
),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
private function translation_task_label( $post_ids, $term_ids ) {
|
||||
if ( ! empty( $post_ids ) && ! empty( $term_ids ) ) {
|
||||
return __( 'Full translation', 'btranslate' );
|
||||
}
|
||||
|
||||
if ( 1 === count( $post_ids ) ) {
|
||||
return __( 'Single post translation', 'btranslate' );
|
||||
}
|
||||
|
||||
if ( ! empty( $post_ids ) ) {
|
||||
return __( 'Posts and pages translation', 'btranslate' );
|
||||
}
|
||||
|
||||
if ( ! empty( $term_ids ) ) {
|
||||
return __( 'Categories and tags translation', 'btranslate' );
|
||||
}
|
||||
|
||||
return __( 'No translation task', 'btranslate' );
|
||||
}
|
||||
}
|
||||
@@ -1,201 +0,0 @@
|
||||
<?php
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class BTRANSLATE_Language_Router {
|
||||
private $request_language = '';
|
||||
|
||||
public function register() {
|
||||
add_filter( 'query_vars', array( $this, 'query_vars' ) );
|
||||
add_filter( 'do_parse_request', array( $this, 'strip_language_prefix' ), 1, 3 );
|
||||
add_action( 'parse_request', array( $this, 'resolve_domain_language' ) );
|
||||
add_filter( 'home_url', array( $this, 'localize_home_url' ), 20, 4 );
|
||||
}
|
||||
|
||||
public function register_rewrite_rules() {
|
||||
// Subdirectory requests reuse WordPress's existing rewrite rules after their language prefix is removed.
|
||||
}
|
||||
|
||||
public function query_vars( $query_vars ) {
|
||||
$query_vars[] = 'btranslate_language';
|
||||
|
||||
return $query_vars;
|
||||
}
|
||||
|
||||
public function strip_language_prefix( $do_parse_request, $wp, $extra_query_vars ) {
|
||||
if ( ! $do_parse_request || ! BTRANSLATE_Settings::is_routing_mode_enabled( 'subdirectory' ) || empty( $_SERVER['REQUEST_URI'] ) ) {
|
||||
return $do_parse_request;
|
||||
}
|
||||
|
||||
$request_uri = sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) );
|
||||
$path = wp_parse_url( $request_uri, PHP_URL_PATH );
|
||||
$query = wp_parse_url( $request_uri, PHP_URL_QUERY );
|
||||
|
||||
if ( ! is_string( $path ) ) {
|
||||
return $do_parse_request;
|
||||
}
|
||||
|
||||
$home_path = $this->home_path();
|
||||
$relative = ltrim( (string) preg_replace( '#^' . preg_quote( $home_path, '#' ) . '(?=/|$)#', '', $path, 1 ), '/' );
|
||||
$segments = explode( '/', $relative, 2 );
|
||||
$language = sanitize_key( $segments[0] );
|
||||
|
||||
if ( ! in_array( $language, BTRANSLATE_Settings::subdirectory_languages(), true ) ) {
|
||||
return $do_parse_request;
|
||||
}
|
||||
|
||||
$this->request_language = $language;
|
||||
$remaining_path = isset( $segments[1] ) ? $segments[1] : '';
|
||||
$source_path = '/' . ( '' !== $home_path ? trim( $home_path, '/' ) . '/' : '' ) . ltrim( $remaining_path, '/' );
|
||||
$_SERVER['REQUEST_URI'] = $source_path . ( is_string( $query ) && '' !== $query ? '?' . $query : '' );
|
||||
|
||||
return $do_parse_request;
|
||||
}
|
||||
|
||||
public function resolve_domain_language( $wp ) {
|
||||
$settings = BTRANSLATE_Settings::get();
|
||||
$host = isset( $_SERVER['HTTP_HOST'] ) ? BTRANSLATE_Settings::normalize_domain( sanitize_text_field( wp_unslash( $_SERVER['HTTP_HOST'] ) ) ) : '';
|
||||
$bindings = (array) $settings['domain_bindings'];
|
||||
|
||||
if ( '' !== $this->request_language ) {
|
||||
$wp->query_vars['btranslate_language'] = $this->request_language;
|
||||
}
|
||||
|
||||
if ( BTRANSLATE_Settings::is_routing_mode_enabled( 'domain' ) && '' !== $host && isset( $bindings[ $host ] ) && BTRANSLATE_Settings::is_supported_language( $bindings[ $host ] ) ) {
|
||||
if ( '' !== $this->request_language ) {
|
||||
$wp->query_vars = array( 'error' => '404' );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$wp->query_vars['btranslate_language'] = sanitize_key( $bindings[ $host ] );
|
||||
}
|
||||
}
|
||||
|
||||
public function current_language() {
|
||||
$settings = BTRANSLATE_Settings::get();
|
||||
$language = get_query_var( 'btranslate_language' );
|
||||
|
||||
if ( $language && BTRANSLATE_Settings::is_supported_language( $language ) ) {
|
||||
return sanitize_key( $language );
|
||||
}
|
||||
|
||||
return sanitize_key( $settings['source_language'] );
|
||||
}
|
||||
|
||||
public function localized_url( $url, $language = '' ) {
|
||||
$settings = BTRANSLATE_Settings::get();
|
||||
$language = '' === $language ? $this->current_language() : sanitize_key( $language );
|
||||
|
||||
if ( $language === $settings['source_language'] || ! BTRANSLATE_Settings::is_supported_language( $language ) ) {
|
||||
return $url;
|
||||
}
|
||||
|
||||
if ( BTRANSLATE_Settings::is_routing_mode_enabled( 'domain' ) && $this->request_uses_domain( $language ) ) {
|
||||
$domain = array_search( $language, (array) $settings['domain_bindings'], true );
|
||||
$parts = wp_parse_url( $url );
|
||||
|
||||
if ( false !== $domain && is_array( $parts ) && ! empty( $parts['host'] ) ) {
|
||||
$scheme = isset( $parts['scheme'] ) ? $parts['scheme'] : 'https';
|
||||
$path = $this->source_path( isset( $parts['path'] ) ? $parts['path'] : '/' );
|
||||
$query = isset( $parts['query'] ) ? '?' . $parts['query'] : '';
|
||||
$fragment = isset( $parts['fragment'] ) ? '#' . $parts['fragment'] : '';
|
||||
|
||||
return $scheme . '://' . $domain . $path . $query . $fragment;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! BTRANSLATE_Settings::is_routing_mode_enabled( 'subdirectory' ) ) {
|
||||
return $url;
|
||||
}
|
||||
|
||||
$parts = wp_parse_url( $url );
|
||||
if ( ! is_array( $parts ) || empty( $parts['path'] ) ) {
|
||||
return $url;
|
||||
}
|
||||
$parts = $this->source_origin_parts( $parts );
|
||||
|
||||
$scheme = isset( $parts['scheme'] ) ? $parts['scheme'] . '://' : '//';
|
||||
$host = isset( $parts['host'] ) ? $parts['host'] : '';
|
||||
$port = isset( $parts['port'] ) ? ':' . $parts['port'] : '';
|
||||
$path = $this->localized_path( $parts['path'], $language );
|
||||
$query = isset( $parts['query'] ) ? '?' . $parts['query'] : '';
|
||||
$fragment = isset( $parts['fragment'] ) ? '#' . $parts['fragment'] : '';
|
||||
|
||||
return $scheme . $host . $port . $path . $query . $fragment;
|
||||
}
|
||||
|
||||
public function localize_home_url( $url, $path, $scheme, $blog_id ) {
|
||||
return $this->localized_url( $url );
|
||||
}
|
||||
|
||||
private function request_uses_domain( $language ) {
|
||||
$host = isset( $_SERVER['HTTP_HOST'] ) ? BTRANSLATE_Settings::normalize_domain( sanitize_text_field( wp_unslash( $_SERVER['HTTP_HOST'] ) ) ) : '';
|
||||
$bindings = (array) BTRANSLATE_Settings::get()['domain_bindings'];
|
||||
|
||||
return '' !== $host && isset( $bindings[ $host ] ) && sanitize_key( $bindings[ $host ] ) === $language;
|
||||
}
|
||||
|
||||
private function source_origin_parts( $parts ) {
|
||||
$host = isset( $_SERVER['HTTP_HOST'] ) ? BTRANSLATE_Settings::normalize_domain( sanitize_text_field( wp_unslash( $_SERVER['HTTP_HOST'] ) ) ) : '';
|
||||
$bindings = (array) BTRANSLATE_Settings::get()['domain_bindings'];
|
||||
|
||||
if ( '' === $host || ! isset( $bindings[ $host ] ) ) {
|
||||
return $parts;
|
||||
}
|
||||
|
||||
$home_parts = wp_parse_url( (string) get_option( 'home', '' ) );
|
||||
if ( ! is_array( $home_parts ) || empty( $home_parts['host'] ) ) {
|
||||
return $parts;
|
||||
}
|
||||
|
||||
$parts['scheme'] = isset( $home_parts['scheme'] ) ? $home_parts['scheme'] : 'https';
|
||||
$parts['host'] = $home_parts['host'];
|
||||
|
||||
if ( isset( $home_parts['port'] ) ) {
|
||||
$parts['port'] = $home_parts['port'];
|
||||
} else {
|
||||
unset( $parts['port'] );
|
||||
}
|
||||
|
||||
return $parts;
|
||||
}
|
||||
|
||||
private function localized_path( $path, $language ) {
|
||||
$home_path = $this->home_path();
|
||||
$relative = $this->relative_path( $path );
|
||||
$languages = array_map( 'preg_quote', BTRANSLATE_Settings::target_languages() );
|
||||
|
||||
if ( ! empty( $languages ) ) {
|
||||
$relative = preg_replace( '#^(?:' . implode( '|', $languages ) . ')(?=/|$)#', $language, $relative, 1 );
|
||||
}
|
||||
|
||||
if ( $language !== strtok( $relative, '/' ) ) {
|
||||
$relative = $language . '/' . $relative;
|
||||
}
|
||||
|
||||
return '/' . ( '' !== $home_path ? trim( $home_path, '/' ) . '/' : '' ) . ltrim( $relative, '/' );
|
||||
}
|
||||
|
||||
private function source_path( $path ) {
|
||||
$home_path = $this->home_path();
|
||||
$relative = $this->relative_path( $path );
|
||||
$languages = array_map( 'preg_quote', BTRANSLATE_Settings::target_languages() );
|
||||
|
||||
if ( ! empty( $languages ) ) {
|
||||
$relative = preg_replace( '#^(?:' . implode( '|', $languages ) . ')(?=/|$)/?#', '', $relative, 1 );
|
||||
}
|
||||
|
||||
return '/' . ( '' !== $home_path ? trim( $home_path, '/' ) . '/' : '' ) . ltrim( $relative, '/' );
|
||||
}
|
||||
|
||||
private function relative_path( $path ) {
|
||||
return ltrim( (string) preg_replace( '#^' . preg_quote( $this->home_path(), '#' ) . '(?=/|$)#', '', $path, 1 ), '/' );
|
||||
}
|
||||
|
||||
private function home_path() {
|
||||
$home_path = wp_parse_url( (string) get_option( 'home', '' ), PHP_URL_PATH );
|
||||
|
||||
return is_string( $home_path ) ? untrailingslashit( $home_path ) : '';
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
<?php
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class BTRANSLATE_Plugin {
|
||||
private static $instance;
|
||||
private $router;
|
||||
private $sitemap_controller;
|
||||
private $content_controller;
|
||||
private $admin;
|
||||
|
||||
private function __construct() {
|
||||
$this->router = new BTRANSLATE_Language_Router();
|
||||
$this->sitemap_controller = new BTRANSLATE_Sitemap_Controller( $this->router );
|
||||
$this->content_controller = new BTRANSLATE_Content_Controller( new BTRANSLATE_Translation_Store(), $this->router );
|
||||
$this->admin = new BTRANSLATE_Admin();
|
||||
|
||||
add_action( 'init', array( $this, 'register' ) );
|
||||
add_action( 'update_option_btranslate_settings', array( $this, 'refresh_rewrite_rules' ), 10, 2 );
|
||||
$this->admin->register();
|
||||
}
|
||||
|
||||
public static function instance() {
|
||||
if ( null === self::$instance ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
public static function activate() {
|
||||
BTRANSLATE_Translation_Store::install();
|
||||
add_option(
|
||||
'btranslate_settings',
|
||||
array(
|
||||
'source_language' => 'zh',
|
||||
'target_languages' => array( 'en' ),
|
||||
'routing_mode' => array( 'subdirectory' ),
|
||||
'domain_bindings' => array(),
|
||||
'fallback_language' => 'zh',
|
||||
),
|
||||
'',
|
||||
false
|
||||
);
|
||||
$router = new BTRANSLATE_Language_Router();
|
||||
$router->register_rewrite_rules();
|
||||
flush_rewrite_rules();
|
||||
}
|
||||
|
||||
public static function deactivate() {
|
||||
wp_clear_scheduled_hook( 'btranslate_process_translation' );
|
||||
wp_clear_scheduled_hook( 'btranslate_process_term_translation' );
|
||||
wp_clear_scheduled_hook( 'btranslate_process_seo_output_translation' );
|
||||
flush_rewrite_rules();
|
||||
}
|
||||
|
||||
public function register() {
|
||||
$this->router->register();
|
||||
$this->sitemap_controller->register();
|
||||
$this->content_controller->register();
|
||||
|
||||
if ( get_option( 'btranslate_flush_rewrite_rules', false ) ) {
|
||||
delete_option( 'btranslate_flush_rewrite_rules' );
|
||||
flush_rewrite_rules();
|
||||
}
|
||||
}
|
||||
|
||||
public function refresh_rewrite_rules( $old_value, $new_value ) {
|
||||
if ( (array) $old_value !== (array) $new_value ) {
|
||||
update_option( 'btranslate_flush_rewrite_rules', 1, false );
|
||||
}
|
||||
}
|
||||
}
|
||||
+69
-41
@@ -2,20 +2,20 @@
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class BTRANSLATE_Content_Controller {
|
||||
class HTBD_Content_Controller {
|
||||
private $store;
|
||||
private $router;
|
||||
|
||||
public function __construct( BTRANSLATE_Translation_Store $store, BTRANSLATE_Language_Router $router ) {
|
||||
public function __construct( HTBD_Translation_Store $store, HTBD_Language_Router $router ) {
|
||||
$this->store = $store;
|
||||
$this->router = $router;
|
||||
}
|
||||
|
||||
public function register() {
|
||||
add_action( 'save_post', array( $this, 'schedule_post_translation' ), 20, 3 );
|
||||
add_action( 'btranslate_process_translation', array( $this, 'translate_post' ), 10, 3 );
|
||||
add_action( 'btranslate_process_term_translation', array( $this, 'translate_term' ), 10, 3 );
|
||||
add_action( 'btranslate_process_seo_output_translation', array( $this, 'translate_seo_output' ), 10, 4 );
|
||||
add_action( 'htbd_process_translation', array( $this, 'translate_post' ), 10, 3 );
|
||||
add_action( 'htbd_process_term_translation', array( $this, 'translate_term' ), 10, 3 );
|
||||
add_action( 'htbd_process_seo_output_translation', array( $this, 'translate_seo_output' ), 10, 4 );
|
||||
add_action( 'created_term', array( $this, 'schedule_term_translation' ), 20, 3 );
|
||||
add_action( 'edited_term', array( $this, 'schedule_term_translation' ), 20, 3 );
|
||||
add_filter( 'the_title', array( $this, 'translate_title' ), 20, 2 );
|
||||
@@ -47,24 +47,24 @@ class BTRANSLATE_Content_Controller {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( BTRANSLATE_Settings::target_languages() as $target_language ) {
|
||||
if ( ! wp_next_scheduled( 'btranslate_process_translation', array( $post_id, $target_language ) ) ) {
|
||||
wp_schedule_single_event( time() + 10, 'btranslate_process_translation', array( $post_id, $target_language ) );
|
||||
foreach ( HTBD_Settings::target_languages() as $target_language ) {
|
||||
if ( ! wp_next_scheduled( 'htbd_process_translation', array( $post_id, $target_language ) ) ) {
|
||||
wp_schedule_single_event( time() + 10, 'htbd_process_translation', array( $post_id, $target_language ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function translate_post( $post_id, $target_language, $force_refresh = false ) {
|
||||
$post = get_post( $post_id );
|
||||
$settings = BTRANSLATE_Settings::get();
|
||||
$settings = HTBD_Settings::get();
|
||||
|
||||
if ( ! $post || 'publish' !== $post->post_status || ! BTRANSLATE_Settings::is_supported_language( $target_language ) ) {
|
||||
if ( ! $post || 'publish' !== $post->post_status || ! HTBD_Settings::is_supported_language( $target_language ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$service = new BTRANSLATE_Translation_Service(
|
||||
$service = new HTBD_Translation_Service(
|
||||
$this->store,
|
||||
new BTRANSLATE_Baidu_Provider( $settings['baidu_app_id'], $settings['baidu_secret_key'] )
|
||||
new HTBD_Baidu_Provider( $settings['baidu_app_id'], $settings['baidu_secret_key'] )
|
||||
);
|
||||
|
||||
$fields = array(
|
||||
@@ -91,7 +91,7 @@ class BTRANSLATE_Content_Controller {
|
||||
if ( '' !== $value ) {
|
||||
$context = 'post:' . $post_id . ':' . $field;
|
||||
if ( 'post_content' === $field ) {
|
||||
( new BTRANSLATE_Content_Translator( $service ) )->translate( $value, $settings['source_language'], $target_language, $context, $force_refresh );
|
||||
( new HTBD_Content_Translator( $service ) )->translate( $value, $settings['source_language'], $target_language, $context, $force_refresh );
|
||||
} else {
|
||||
$service->get_or_translate( $value, $settings['source_language'], $target_language, $context, $force_refresh );
|
||||
}
|
||||
@@ -107,24 +107,24 @@ class BTRANSLATE_Content_Controller {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( BTRANSLATE_Settings::target_languages() as $target_language ) {
|
||||
if ( ! wp_next_scheduled( 'btranslate_process_term_translation', array( $term_id, $target_language, false ) ) ) {
|
||||
wp_schedule_single_event( time() + 10, 'btranslate_process_term_translation', array( $term_id, $target_language, false ) );
|
||||
foreach ( HTBD_Settings::target_languages() as $target_language ) {
|
||||
if ( ! wp_next_scheduled( 'htbd_process_term_translation', array( $term_id, $target_language, false ) ) ) {
|
||||
wp_schedule_single_event( time() + 10, 'htbd_process_term_translation', array( $term_id, $target_language, false ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function translate_term( $term_id, $target_language, $force_refresh = false ) {
|
||||
$term = get_term( $term_id );
|
||||
$settings = BTRANSLATE_Settings::get();
|
||||
$settings = HTBD_Settings::get();
|
||||
|
||||
if ( ! $term || is_wp_error( $term ) || ! in_array( $term->taxonomy, array( 'category', 'post_tag' ), true ) || ! BTRANSLATE_Settings::is_supported_language( $target_language ) ) {
|
||||
if ( ! $term || is_wp_error( $term ) || ! in_array( $term->taxonomy, array( 'category', 'post_tag' ), true ) || ! HTBD_Settings::is_supported_language( $target_language ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$service = new BTRANSLATE_Translation_Service(
|
||||
$service = new HTBD_Translation_Service(
|
||||
$this->store,
|
||||
new BTRANSLATE_Baidu_Provider( $settings['baidu_app_id'], $settings['baidu_secret_key'] )
|
||||
new HTBD_Baidu_Provider( $settings['baidu_app_id'], $settings['baidu_secret_key'] )
|
||||
);
|
||||
|
||||
if ( '' !== $term->name ) {
|
||||
@@ -136,22 +136,38 @@ class BTRANSLATE_Content_Controller {
|
||||
}
|
||||
|
||||
public function translate_seo_output( $post_id, $source_value, $target_language, $context ) {
|
||||
$settings = BTRANSLATE_Settings::get();
|
||||
$settings = HTBD_Settings::get();
|
||||
|
||||
if ( ! get_post( $post_id ) || ! BTRANSLATE_Settings::is_supported_language( $target_language ) || '' === $source_value ) {
|
||||
if ( ! get_post( $post_id ) || ! HTBD_Settings::is_supported_language( $target_language ) || '' === $source_value ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$service = new BTRANSLATE_Translation_Service(
|
||||
$service = new HTBD_Translation_Service(
|
||||
$this->store,
|
||||
new BTRANSLATE_Baidu_Provider( $settings['baidu_app_id'], $settings['baidu_secret_key'] )
|
||||
new HTBD_Baidu_Provider( $settings['baidu_app_id'], $settings['baidu_secret_key'] )
|
||||
);
|
||||
|
||||
$service->get_or_translate( $source_value, $settings['source_language'], $target_language, $context );
|
||||
}
|
||||
|
||||
public function translate_title( $title, $post_id ) {
|
||||
return $this->escaped_translated_value( $title, 'post:' . $post_id . ':post_title' );
|
||||
$post_id = absint( $post_id );
|
||||
if ( ! $post_id && is_singular() ) {
|
||||
$post_id = get_queried_object_id();
|
||||
}
|
||||
|
||||
if ( ! $post_id ) {
|
||||
return $title;
|
||||
}
|
||||
|
||||
$source_title = get_post_field( 'post_title', $post_id, 'raw' );
|
||||
if ( ! is_string( $source_title ) || '' === $source_title ) {
|
||||
return $title;
|
||||
}
|
||||
|
||||
$translated_title = $this->translated_value( $source_title, 'post:' . $post_id . ':post_title' );
|
||||
|
||||
return $translated_title !== $source_title ? esc_html( $translated_title ) : $title;
|
||||
}
|
||||
|
||||
public function translate_content( $content ) {
|
||||
@@ -160,18 +176,24 @@ class BTRANSLATE_Content_Controller {
|
||||
return $this->escaped_translated_value( $content, 'post:' . $post_id . ':post_content' );
|
||||
}
|
||||
|
||||
$segments = BTRANSLATE_Content_Translator::segments( $content );
|
||||
$segments = HTBD_Content_Translator::segments( $content );
|
||||
if ( false === $segments ) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
$text_index = 0;
|
||||
$text_index = 0;
|
||||
$protected_depth = 0;
|
||||
foreach ( $segments as $index => $segment ) {
|
||||
if ( BTRANSLATE_Content_Translator::is_tag( $segment ) || '' === trim( $segment ) ) {
|
||||
if ( HTBD_Content_Translator::is_tag( $segment ) ) {
|
||||
$protected_depth = HTBD_Content_Translator::protected_depth_after_tag( $segment, $protected_depth );
|
||||
continue;
|
||||
}
|
||||
|
||||
$whitespace = BTRANSLATE_Content_Translator::surrounding_whitespace( $segment );
|
||||
if ( 0 < $protected_depth || '' === trim( $segment ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$whitespace = HTBD_Content_Translator::surrounding_whitespace( $segment );
|
||||
$translated = $this->escaped_translated_value( $whitespace['text'], 'post:' . $post_id . ':post_content:text:' . $text_index );
|
||||
$segments[ $index ] = $whitespace['leading'] . $translated . $whitespace['trailing'];
|
||||
++$text_index;
|
||||
@@ -181,7 +203,7 @@ class BTRANSLATE_Content_Controller {
|
||||
}
|
||||
|
||||
public function translate_excerpt( $excerpt, $post ) {
|
||||
return $this->translated_value( $excerpt, 'post:' . $post->ID . ':post_excerpt' );
|
||||
return $this->kses_translated_value( $excerpt, 'post:' . $post->ID . ':post_excerpt' );
|
||||
}
|
||||
|
||||
public function translate_image_alt( $attributes, $attachment ) {
|
||||
@@ -195,11 +217,11 @@ class BTRANSLATE_Content_Controller {
|
||||
}
|
||||
|
||||
public function translate_term_title( $term_name, $term ) {
|
||||
return $this->translated_value( $term_name, 'term:' . $term->term_id . ':name' );
|
||||
return $this->escaped_translated_value( $term_name, 'term:' . $term->term_id . ':name' );
|
||||
}
|
||||
|
||||
public function translate_term_description( $description, $term_id, $taxonomy ) {
|
||||
return $this->translated_value( $description, 'term:' . $term_id . ':description' );
|
||||
return $this->kses_translated_value( $description, 'term:' . $term_id . ':description' );
|
||||
}
|
||||
|
||||
public function translate_document_title_parts( $parts ) {
|
||||
@@ -258,8 +280,8 @@ class BTRANSLATE_Content_Controller {
|
||||
}
|
||||
|
||||
$translated_term = clone $term;
|
||||
$translated_term->name = $this->translated_value( $term->name, 'term:' . $term->term_id . ':name' );
|
||||
$translated_term->description = $this->translated_value( $term->description, 'term:' . $term->term_id . ':description' );
|
||||
$translated_term->name = sanitize_text_field( $this->translated_value( $term->name, 'term:' . $term->term_id . ':name' ) );
|
||||
$translated_term->description = $this->kses_translated_value( $term->description, 'term:' . $term->term_id . ':description' );
|
||||
|
||||
return $translated_term;
|
||||
}
|
||||
@@ -306,15 +328,15 @@ class BTRANSLATE_Content_Controller {
|
||||
}
|
||||
|
||||
private function translated_value( $source_value, $context ) {
|
||||
$settings = BTRANSLATE_Settings::get();
|
||||
$settings = HTBD_Settings::get();
|
||||
$target_language = $this->router->current_language();
|
||||
|
||||
if ( $target_language === $settings['source_language'] || '' === $source_value ) {
|
||||
return $source_value;
|
||||
}
|
||||
|
||||
$provider = new BTRANSLATE_Baidu_Provider( $settings['baidu_app_id'], $settings['baidu_secret_key'] );
|
||||
$identity_key = BTRANSLATE_Translation_Identity::key( $source_value, $settings['source_language'], $target_language, $context, $provider->get_version() );
|
||||
$provider = new HTBD_Baidu_Provider( $settings['baidu_app_id'], $settings['baidu_secret_key'] );
|
||||
$identity_key = HTBD_Translation_Identity::key( $source_value, $settings['source_language'], $target_language, $context, $provider->get_version() );
|
||||
$translation = $this->store->find_valid( $identity_key );
|
||||
|
||||
return ! empty( $translation['translated_value'] ) ? $translation['translated_value'] : $source_value;
|
||||
@@ -323,11 +345,17 @@ class BTRANSLATE_Content_Controller {
|
||||
private function escaped_translated_value( $source_value, $context ) {
|
||||
$translated_value = $this->translated_value( $source_value, $context );
|
||||
|
||||
return $translated_value === $source_value ? $source_value : esc_html( $translated_value );
|
||||
return esc_html( $translated_value );
|
||||
}
|
||||
|
||||
private function kses_translated_value( $source_value, $context ) {
|
||||
$translated_value = $this->translated_value( $source_value, $context );
|
||||
|
||||
return wp_kses_post( $translated_value );
|
||||
}
|
||||
|
||||
private function translated_or_schedule_seo_output( $source_value, $post_id, $field_context ) {
|
||||
$settings = BTRANSLATE_Settings::get();
|
||||
$settings = HTBD_Settings::get();
|
||||
$target_language = $this->router->current_language();
|
||||
$context = 'post:' . $post_id . ':' . $field_context;
|
||||
|
||||
@@ -341,8 +369,8 @@ class BTRANSLATE_Content_Controller {
|
||||
}
|
||||
|
||||
$event_args = array( $post_id, $source_value, $target_language, $context );
|
||||
if ( ! wp_next_scheduled( 'btranslate_process_seo_output_translation', $event_args ) ) {
|
||||
wp_schedule_single_event( time() + 5, 'btranslate_process_seo_output_translation', $event_args );
|
||||
if ( ! wp_next_scheduled( 'htbd_process_seo_output_translation', $event_args ) ) {
|
||||
wp_schedule_single_event( time() + 5, 'htbd_process_seo_output_translation', $event_args );
|
||||
}
|
||||
|
||||
return $source_value;
|
||||
+24
-6
@@ -2,22 +2,28 @@
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class BTRANSLATE_Content_Translator {
|
||||
class HTBD_Content_Translator {
|
||||
private $service;
|
||||
|
||||
public function __construct( BTRANSLATE_Translation_Service $service ) {
|
||||
public function __construct( HTBD_Translation_Service $service ) {
|
||||
$this->service = $service;
|
||||
}
|
||||
|
||||
public function translate( $content, $source_language, $target_language, $context, $force_refresh = false ) {
|
||||
$segments = self::segments( $content );
|
||||
if ( false === $segments ) {
|
||||
return BTRANSLATE_Translation_Result::failure( 'content_parse_failed', 'Unable to protect links before translation.' );
|
||||
return HTBD_Translation_Result::failure( 'content_parse_failed', 'Unable to protect links before translation.' );
|
||||
}
|
||||
|
||||
$text_index = 0;
|
||||
$text_index = 0;
|
||||
$protected_depth = 0;
|
||||
foreach ( $segments as $segment ) {
|
||||
if ( self::is_tag( $segment ) || '' === trim( $segment ) ) {
|
||||
if ( self::is_tag( $segment ) ) {
|
||||
$protected_depth = self::protected_depth_after_tag( $segment, $protected_depth );
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( 0 < $protected_depth || '' === trim( $segment ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -29,7 +35,7 @@ class BTRANSLATE_Content_Translator {
|
||||
++$text_index;
|
||||
}
|
||||
|
||||
return BTRANSLATE_Translation_Result::success( $content );
|
||||
return HTBD_Translation_Result::success( $content );
|
||||
}
|
||||
|
||||
public static function segments( $content ) {
|
||||
@@ -48,6 +54,18 @@ class BTRANSLATE_Content_Translator {
|
||||
return 1 === preg_match( '/^<[^>]+>$/s', $segment );
|
||||
}
|
||||
|
||||
public static function protected_depth_after_tag( $segment, $protected_depth ) {
|
||||
if ( 1 !== preg_match( '/^<\s*(\/?)\s*(?:pre|code)\b[^>]*>/i', $segment, $matches ) ) {
|
||||
return $protected_depth;
|
||||
}
|
||||
|
||||
if ( '/' === $matches[1] ) {
|
||||
return max( 0, $protected_depth - 1 );
|
||||
}
|
||||
|
||||
return str_ends_with( rtrim( $segment ), '/>' ) ? $protected_depth : $protected_depth + 1;
|
||||
}
|
||||
|
||||
public static function surrounding_whitespace( $segment ) {
|
||||
preg_match( '/^(\s*)(.*?)(\s*)$/s', $segment, $matches );
|
||||
|
||||
@@ -0,0 +1,344 @@
|
||||
<?php
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class HTBD_Language_Router {
|
||||
private $request_language = '';
|
||||
|
||||
public function register() {
|
||||
$this->redirect_admin_request_to_source();
|
||||
add_filter( 'query_vars', array( $this, 'query_vars' ) );
|
||||
add_filter( 'do_parse_request', array( $this, 'strip_language_prefix' ), 1, 3 );
|
||||
add_action( 'parse_request', array( $this, 'resolve_domain_language' ) );
|
||||
add_filter( 'home_url', array( $this, 'localize_home_url' ), 20, 4 );
|
||||
add_filter( 'site_url', array( $this, 'localize_comment_post_url' ), 20, 4 );
|
||||
add_filter( 'allowed_redirect_hosts', array( $this, 'allow_language_redirect_hosts' ) );
|
||||
add_filter( 'comment_post_redirect', array( $this, 'localize_comment_redirect' ) );
|
||||
}
|
||||
|
||||
private function redirect_admin_request_to_source() {
|
||||
if ( empty( $_SERVER['REQUEST_URI'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$request_uri = sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) );
|
||||
$path = wp_parse_url( $request_uri, PHP_URL_PATH );
|
||||
$query = wp_parse_url( $request_uri, PHP_URL_QUERY );
|
||||
|
||||
if ( ! is_string( $path ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$relative_path = $this->relative_path( $path );
|
||||
$segments = explode( '/', $relative_path, 2 );
|
||||
$language = sanitize_key( $segments[0] );
|
||||
$uses_language_path = HTBD_Settings::is_routing_mode_enabled( 'subdirectory' ) && in_array( $language, HTBD_Settings::subdirectory_languages(), true );
|
||||
$uses_language_host = HTBD_Settings::is_routing_mode_enabled( 'domain' ) && $this->request_uses_bound_domain();
|
||||
|
||||
if ( ! $uses_language_path && ! $uses_language_host ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( $uses_language_path ) {
|
||||
$relative_path = isset( $segments[1] ) ? $segments[1] : '';
|
||||
}
|
||||
|
||||
if ( ! preg_match( '#^(?:wp-login\.php|wp-admin)(?:/|$)#', $relative_path ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$source_url = untrailingslashit( (string) get_option( 'home', '' ) );
|
||||
if ( '' === $source_url ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$redirect_url = $source_url . '/' . ltrim( $relative_path, '/' );
|
||||
if ( is_string( $query ) && '' !== $query ) {
|
||||
$redirect_url .= '?' . $query;
|
||||
}
|
||||
|
||||
wp_redirect( esc_url_raw( $redirect_url ), 302, 'HTBD' );
|
||||
exit;
|
||||
}
|
||||
|
||||
public function register_rewrite_rules() {
|
||||
// Subdirectory requests reuse WordPress's existing rewrite rules after their language prefix is removed.
|
||||
}
|
||||
|
||||
public function query_vars( $query_vars ) {
|
||||
$query_vars[] = 'htbd_language';
|
||||
|
||||
return $query_vars;
|
||||
}
|
||||
|
||||
public function strip_language_prefix( $do_parse_request, $wp, $extra_query_vars ) {
|
||||
if ( ! $do_parse_request || ! HTBD_Settings::is_routing_mode_enabled( 'subdirectory' ) || empty( $_SERVER['REQUEST_URI'] ) ) {
|
||||
return $do_parse_request;
|
||||
}
|
||||
|
||||
$request_uri = sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) );
|
||||
$path = wp_parse_url( $request_uri, PHP_URL_PATH );
|
||||
$query = wp_parse_url( $request_uri, PHP_URL_QUERY );
|
||||
|
||||
if ( ! is_string( $path ) ) {
|
||||
return $do_parse_request;
|
||||
}
|
||||
|
||||
$home_path = $this->home_path();
|
||||
$relative = ltrim( (string) preg_replace( '#^' . preg_quote( $home_path, '#' ) . '(?=/|$)#', '', $path, 1 ), '/' );
|
||||
$segments = explode( '/', $relative, 2 );
|
||||
$language = sanitize_key( $segments[0] );
|
||||
|
||||
if ( ! in_array( $language, HTBD_Settings::subdirectory_languages(), true ) ) {
|
||||
return $do_parse_request;
|
||||
}
|
||||
|
||||
$this->request_language = $language;
|
||||
$remaining_path = isset( $segments[1] ) ? $segments[1] : '';
|
||||
$source_path = '/' . ( '' !== $home_path ? trim( $home_path, '/' ) . '/' : '' ) . ltrim( $remaining_path, '/' );
|
||||
$_SERVER['REQUEST_URI'] = $source_path . ( is_string( $query ) && '' !== $query ? '?' . $query : '' );
|
||||
|
||||
return $do_parse_request;
|
||||
}
|
||||
|
||||
public function resolve_domain_language( $wp ) {
|
||||
$settings = HTBD_Settings::get();
|
||||
$host = isset( $_SERVER['HTTP_HOST'] ) ? HTBD_Settings::normalize_domain( sanitize_text_field( wp_unslash( $_SERVER['HTTP_HOST'] ) ) ) : '';
|
||||
$bindings = (array) $settings['domain_bindings'];
|
||||
|
||||
if ( '' !== $this->request_language ) {
|
||||
$wp->query_vars['htbd_language'] = $this->request_language;
|
||||
}
|
||||
|
||||
if ( HTBD_Settings::is_routing_mode_enabled( 'domain' ) && '' !== $host && isset( $bindings[ $host ] ) && HTBD_Settings::is_supported_language( $bindings[ $host ] ) ) {
|
||||
if ( '' !== $this->request_language ) {
|
||||
$wp->query_vars = array( 'error' => '404' );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$wp->query_vars['htbd_language'] = sanitize_key( $bindings[ $host ] );
|
||||
}
|
||||
}
|
||||
|
||||
public function current_language() {
|
||||
$settings = HTBD_Settings::get();
|
||||
$language = get_query_var( 'htbd_language' );
|
||||
|
||||
if ( $language && HTBD_Settings::is_supported_language( $language ) ) {
|
||||
return sanitize_key( $language );
|
||||
}
|
||||
|
||||
return sanitize_key( $settings['source_language'] );
|
||||
}
|
||||
|
||||
public function localized_url( $url, $language = '' ) {
|
||||
$settings = HTBD_Settings::get();
|
||||
$language = '' === $language ? $this->current_language() : sanitize_key( $language );
|
||||
|
||||
if ( $language === $settings['source_language'] || ! HTBD_Settings::is_supported_language( $language ) ) {
|
||||
return $url;
|
||||
}
|
||||
|
||||
if ( HTBD_Settings::is_routing_mode_enabled( 'domain' ) && $this->request_uses_domain( $language ) ) {
|
||||
return $this->localized_domain_url( $url, $language );
|
||||
}
|
||||
|
||||
if ( ! HTBD_Settings::is_routing_mode_enabled( 'subdirectory' ) ) {
|
||||
return $url;
|
||||
}
|
||||
|
||||
$parts = wp_parse_url( $url );
|
||||
if ( ! is_array( $parts ) || empty( $parts['path'] ) ) {
|
||||
return $url;
|
||||
}
|
||||
$parts = $this->source_origin_parts( $parts );
|
||||
|
||||
$scheme = isset( $parts['scheme'] ) ? $parts['scheme'] . '://' : '//';
|
||||
$host = isset( $parts['host'] ) ? $parts['host'] : '';
|
||||
$port = isset( $parts['port'] ) ? ':' . $parts['port'] : '';
|
||||
$path = $this->localized_path( $parts['path'], $language );
|
||||
$query = isset( $parts['query'] ) ? '?' . $parts['query'] : '';
|
||||
$fragment = isset( $parts['fragment'] ) ? '#' . $parts['fragment'] : '';
|
||||
|
||||
return $scheme . $host . $port . $path . $query . $fragment;
|
||||
}
|
||||
|
||||
public function localize_home_url( $url, $path, $scheme, $blog_id ) {
|
||||
return $this->localized_url( $url );
|
||||
}
|
||||
|
||||
public function localize_comment_post_url( $url, $path, $scheme, $blog_id ) {
|
||||
if ( 'wp-comments-post.php' !== basename( (string) wp_parse_url( $url, PHP_URL_PATH ) ) ) {
|
||||
return $url;
|
||||
}
|
||||
|
||||
$language = $this->current_language();
|
||||
|
||||
if ( ! HTBD_Settings::is_routing_mode_enabled( 'domain' ) || ! $this->request_uses_domain( $language ) ) {
|
||||
return $url;
|
||||
}
|
||||
|
||||
return $this->localized_domain_url( $url, $language );
|
||||
}
|
||||
|
||||
public function allow_language_redirect_hosts( $hosts ) {
|
||||
if ( ! HTBD_Settings::is_routing_mode_enabled( 'domain' ) ) {
|
||||
return $hosts;
|
||||
}
|
||||
|
||||
foreach ( (array) HTBD_Settings::get()['domain_bindings'] as $domain => $language ) {
|
||||
$domain = HTBD_Settings::normalize_domain( $domain );
|
||||
|
||||
if ( '' !== $domain && HTBD_Settings::is_supported_language( $language ) ) {
|
||||
$hosts[] = $domain;
|
||||
}
|
||||
}
|
||||
|
||||
return array_values( array_unique( $hosts ) );
|
||||
}
|
||||
|
||||
public function localize_comment_redirect( $location ) {
|
||||
$referer = wp_get_raw_referer();
|
||||
|
||||
if ( ! is_string( $referer ) || '' === $referer ) {
|
||||
return $location;
|
||||
}
|
||||
|
||||
$language = $this->language_from_url( $referer );
|
||||
|
||||
if ( '' === $language ) {
|
||||
return $location;
|
||||
}
|
||||
|
||||
$referer_host = HTBD_Settings::normalize_domain( (string) wp_parse_url( $referer, PHP_URL_HOST ) );
|
||||
$bindings = (array) HTBD_Settings::get()['domain_bindings'];
|
||||
|
||||
if ( HTBD_Settings::is_routing_mode_enabled( 'domain' ) && isset( $bindings[ $referer_host ] ) ) {
|
||||
return $this->localized_domain_url( $location, $language );
|
||||
}
|
||||
|
||||
return $this->localized_url( $location, $language );
|
||||
}
|
||||
|
||||
private function localized_domain_url( $url, $language ) {
|
||||
$domain = array_search( $language, (array) HTBD_Settings::get()['domain_bindings'], true );
|
||||
$parts = wp_parse_url( $url );
|
||||
|
||||
if ( false === $domain || ! is_array( $parts ) || empty( $parts['host'] ) ) {
|
||||
return $url;
|
||||
}
|
||||
|
||||
$scheme = isset( $parts['scheme'] ) ? $parts['scheme'] : 'https';
|
||||
$path = $this->source_path( isset( $parts['path'] ) ? $parts['path'] : '/' );
|
||||
$query = isset( $parts['query'] ) ? '?' . $parts['query'] : '';
|
||||
$fragment = isset( $parts['fragment'] ) ? '#' . $parts['fragment'] : '';
|
||||
|
||||
return $scheme . '://' . $domain . $path . $query . $fragment;
|
||||
}
|
||||
|
||||
private function language_from_url( $url ) {
|
||||
$parts = wp_parse_url( $url );
|
||||
|
||||
if ( ! is_array( $parts ) || empty( $parts['host'] ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$settings = HTBD_Settings::get();
|
||||
$host = HTBD_Settings::normalize_domain( $parts['host'] . ( isset( $parts['port'] ) ? ':' . $parts['port'] : '' ) );
|
||||
$bindings = (array) $settings['domain_bindings'];
|
||||
|
||||
if ( HTBD_Settings::is_routing_mode_enabled( 'domain' ) && isset( $bindings[ $host ] ) && HTBD_Settings::is_supported_language( $bindings[ $host ] ) ) {
|
||||
return sanitize_key( $bindings[ $host ] );
|
||||
}
|
||||
|
||||
$home_host = HTBD_Settings::normalize_domain( (string) wp_parse_url( (string) get_option( 'home', '' ), PHP_URL_HOST ) );
|
||||
if ( $host !== $home_host || ! HTBD_Settings::is_routing_mode_enabled( 'subdirectory' ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$path = isset( $parts['path'] ) ? $parts['path'] : '/';
|
||||
$segments = explode( '/', $this->relative_path( $path ), 2 );
|
||||
$language = sanitize_key( $segments[0] );
|
||||
$languages = HTBD_Settings::subdirectory_languages();
|
||||
|
||||
return in_array( $language, $languages, true ) ? $language : '';
|
||||
}
|
||||
|
||||
private function request_uses_domain( $language ) {
|
||||
$host = isset( $_SERVER['HTTP_HOST'] ) ? HTBD_Settings::normalize_domain( sanitize_text_field( wp_unslash( $_SERVER['HTTP_HOST'] ) ) ) : '';
|
||||
$bindings = (array) HTBD_Settings::get()['domain_bindings'];
|
||||
|
||||
return '' !== $host && isset( $bindings[ $host ] ) && sanitize_key( $bindings[ $host ] ) === $language;
|
||||
}
|
||||
|
||||
private function request_uses_bound_domain() {
|
||||
$host = isset( $_SERVER['HTTP_HOST'] ) ? HTBD_Settings::normalize_domain( sanitize_text_field( wp_unslash( $_SERVER['HTTP_HOST'] ) ) ) : '';
|
||||
$bindings = (array) HTBD_Settings::get()['domain_bindings'];
|
||||
|
||||
return '' !== $host && isset( $bindings[ $host ] ) && HTBD_Settings::is_supported_language( $bindings[ $host ] );
|
||||
}
|
||||
|
||||
private function source_origin_parts( $parts ) {
|
||||
$host = isset( $_SERVER['HTTP_HOST'] ) ? HTBD_Settings::normalize_domain( sanitize_text_field( wp_unslash( $_SERVER['HTTP_HOST'] ) ) ) : '';
|
||||
$bindings = (array) HTBD_Settings::get()['domain_bindings'];
|
||||
|
||||
if ( '' === $host || ! isset( $bindings[ $host ] ) ) {
|
||||
return $parts;
|
||||
}
|
||||
|
||||
$home_parts = wp_parse_url( (string) get_option( 'home', '' ) );
|
||||
if ( ! is_array( $home_parts ) || empty( $home_parts['host'] ) ) {
|
||||
return $parts;
|
||||
}
|
||||
|
||||
$parts['scheme'] = isset( $home_parts['scheme'] ) ? $home_parts['scheme'] : 'https';
|
||||
$parts['host'] = $home_parts['host'];
|
||||
|
||||
if ( isset( $home_parts['port'] ) ) {
|
||||
$parts['port'] = $home_parts['port'];
|
||||
} else {
|
||||
unset( $parts['port'] );
|
||||
}
|
||||
|
||||
return $parts;
|
||||
}
|
||||
|
||||
private function localized_path( $path, $language ) {
|
||||
$home_path = $this->home_path();
|
||||
$relative = $this->relative_path( $path );
|
||||
$languages = array_map( 'preg_quote', HTBD_Settings::target_languages() );
|
||||
|
||||
if ( ! empty( $languages ) ) {
|
||||
$relative = preg_replace( '#^(?:' . implode( '|', $languages ) . ')(?=/|$)#', $language, $relative, 1 );
|
||||
}
|
||||
|
||||
if ( $language !== strtok( $relative, '/' ) ) {
|
||||
$relative = $language . '/' . $relative;
|
||||
}
|
||||
|
||||
return '/' . ( '' !== $home_path ? trim( $home_path, '/' ) . '/' : '' ) . ltrim( $relative, '/' );
|
||||
}
|
||||
|
||||
private function source_path( $path ) {
|
||||
$home_path = $this->home_path();
|
||||
$relative = $this->relative_path( $path );
|
||||
$languages = array_map( 'preg_quote', HTBD_Settings::target_languages() );
|
||||
|
||||
if ( ! empty( $languages ) ) {
|
||||
$relative = preg_replace( '#^(?:' . implode( '|', $languages ) . ')(?=/|$)/?#', '', $relative, 1 );
|
||||
}
|
||||
|
||||
return '/' . ( '' !== $home_path ? trim( $home_path, '/' ) . '/' : '' ) . ltrim( $relative, '/' );
|
||||
}
|
||||
|
||||
private function relative_path( $path ) {
|
||||
return ltrim( (string) preg_replace( '#^' . preg_quote( $this->home_path(), '#' ) . '(?=/|$)#', '', $path, 1 ), '/' );
|
||||
}
|
||||
|
||||
private function home_path() {
|
||||
$home_path = wp_parse_url( (string) get_option( 'home', '' ), PHP_URL_PATH );
|
||||
|
||||
return is_string( $home_path ) ? untrailingslashit( $home_path ) : '';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class HTBD_Plugin {
|
||||
private static $instance;
|
||||
private $router;
|
||||
private $sitemap_controller;
|
||||
private $content_controller;
|
||||
private $admin;
|
||||
|
||||
private function __construct() {
|
||||
$this->router = new HTBD_Language_Router();
|
||||
$this->sitemap_controller = new HTBD_Sitemap_Controller( $this->router );
|
||||
$this->content_controller = new HTBD_Content_Controller( new HTBD_Translation_Store(), $this->router );
|
||||
$this->admin = new HTBD_Admin();
|
||||
|
||||
add_action( 'init', array( $this, 'register' ) );
|
||||
add_action( 'update_option_htbd_settings', array( $this, 'refresh_rewrite_rules' ), 10, 2 );
|
||||
$this->admin->register();
|
||||
}
|
||||
|
||||
public static function instance() {
|
||||
if ( null === self::$instance ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
public static function activate() {
|
||||
HTBD_Translation_Store::install();
|
||||
wp_clear_scheduled_hook( 'btranslate_process_translation' );
|
||||
wp_clear_scheduled_hook( 'btranslate_process_term_translation' );
|
||||
wp_clear_scheduled_hook( 'btranslate_process_seo_output_translation' );
|
||||
$legacy_settings = get_option( 'btranslate_settings', null );
|
||||
if ( null !== $legacy_settings && false === get_option( 'htbd_settings', false ) ) {
|
||||
add_option( 'htbd_settings', $legacy_settings, '', false );
|
||||
}
|
||||
add_option(
|
||||
'htbd_settings',
|
||||
array(
|
||||
'auto_detect_source_language' => false,
|
||||
'source_language' => 'zh',
|
||||
'target_languages' => array( 'en' ),
|
||||
'routing_mode' => array( 'subdirectory' ),
|
||||
'domain_bindings' => array(),
|
||||
'fallback_language' => 'zh',
|
||||
),
|
||||
'',
|
||||
false
|
||||
);
|
||||
$router = new HTBD_Language_Router();
|
||||
$router->register_rewrite_rules();
|
||||
flush_rewrite_rules();
|
||||
}
|
||||
|
||||
public static function deactivate() {
|
||||
wp_clear_scheduled_hook( 'htbd_process_translation' );
|
||||
wp_clear_scheduled_hook( 'htbd_process_term_translation' );
|
||||
wp_clear_scheduled_hook( 'htbd_process_seo_output_translation' );
|
||||
flush_rewrite_rules();
|
||||
}
|
||||
|
||||
public function register() {
|
||||
$this->router->register();
|
||||
$this->sitemap_controller->register();
|
||||
$this->content_controller->register();
|
||||
|
||||
if ( get_option( 'htbd_flush_rewrite_rules', false ) ) {
|
||||
delete_option( 'htbd_flush_rewrite_rules' );
|
||||
flush_rewrite_rules();
|
||||
}
|
||||
}
|
||||
|
||||
public function refresh_rewrite_rules( $old_value, $new_value ) {
|
||||
if ( (array) $old_value !== (array) $new_value ) {
|
||||
update_option( 'htbd_flush_rewrite_rules', 1, false );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,20 +2,30 @@
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class BTRANSLATE_Settings {
|
||||
class HTBD_Settings {
|
||||
public static function get() {
|
||||
$defaults = array(
|
||||
'source_language' => 'zh',
|
||||
'target_languages' => array( 'en' ),
|
||||
'routing_mode' => array( 'subdirectory' ),
|
||||
'domain_bindings' => array(),
|
||||
'fallback_language' => 'zh',
|
||||
'baidu_app_id' => '',
|
||||
'baidu_secret_key' => '',
|
||||
'log_requests' => false,
|
||||
'auto_detect_source_language' => false,
|
||||
'source_language' => 'zh',
|
||||
'target_languages' => array( 'en' ),
|
||||
'routing_mode' => array( 'subdirectory' ),
|
||||
'domain_bindings' => array(),
|
||||
'fallback_language' => 'zh',
|
||||
'baidu_app_id' => '',
|
||||
'baidu_secret_key' => '',
|
||||
'log_requests' => false,
|
||||
);
|
||||
|
||||
$settings = wp_parse_args( (array) get_option( 'btranslate_settings', array() ), $defaults );
|
||||
$stored_settings = get_option( 'htbd_settings', false );
|
||||
if ( false === $stored_settings ) {
|
||||
$legacy_settings = get_option( 'btranslate_settings', false );
|
||||
if ( false !== $legacy_settings ) {
|
||||
$stored_settings = $legacy_settings;
|
||||
add_option( 'htbd_settings', $legacy_settings, '', false );
|
||||
}
|
||||
}
|
||||
|
||||
$settings = wp_parse_args( (array) $stored_settings, $defaults );
|
||||
$settings['routing_mode'] = self::routing_modes( $settings['routing_mode'] );
|
||||
|
||||
return $settings;
|
||||
+9
-9
@@ -2,11 +2,11 @@
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class BTRANSLATE_Sitemap_Controller {
|
||||
class HTBD_Sitemap_Controller {
|
||||
private $router;
|
||||
private $target_language = '';
|
||||
|
||||
public function __construct( BTRANSLATE_Language_Router $router ) {
|
||||
public function __construct( HTBD_Language_Router $router ) {
|
||||
$this->router = $router;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ class BTRANSLATE_Sitemap_Controller {
|
||||
}
|
||||
|
||||
public function intercept_request( $do_parse_request, $wp, $extra_query_vars ) {
|
||||
$settings = BTRANSLATE_Settings::get();
|
||||
$settings = HTBD_Settings::get();
|
||||
$language = $this->requested_language( $this->request_path(), $settings );
|
||||
|
||||
if ( '' === $language ) {
|
||||
@@ -52,7 +52,7 @@ class BTRANSLATE_Sitemap_Controller {
|
||||
}
|
||||
|
||||
private function source_sitemap_xml( $source_url ) {
|
||||
$cache_key = 'btranslate_sitemap_' . md5( $source_url );
|
||||
$cache_key = 'htbd_sitemap_' . md5( $source_url );
|
||||
$cached = get_transient( $cache_key );
|
||||
|
||||
if ( is_string( $cached ) && '' !== $cached ) {
|
||||
@@ -98,18 +98,18 @@ class BTRANSLATE_Sitemap_Controller {
|
||||
$path = '/' . ltrim( (string) $path, '/' );
|
||||
}
|
||||
|
||||
if ( BTRANSLATE_Settings::is_routing_mode_enabled( 'domain' ) && '/sitemap.xml' === untrailingslashit( $path ) ) {
|
||||
$host = isset( $_SERVER['HTTP_HOST'] ) ? BTRANSLATE_Settings::normalize_domain( sanitize_text_field( wp_unslash( $_SERVER['HTTP_HOST'] ) ) ) : '';
|
||||
if ( HTBD_Settings::is_routing_mode_enabled( 'domain' ) && '/sitemap.xml' === untrailingslashit( $path ) ) {
|
||||
$host = isset( $_SERVER['HTTP_HOST'] ) ? HTBD_Settings::normalize_domain( sanitize_text_field( wp_unslash( $_SERVER['HTTP_HOST'] ) ) ) : '';
|
||||
$bindings = (array) $settings['domain_bindings'];
|
||||
$language = isset( $bindings[ $host ] ) ? sanitize_key( $bindings[ $host ] ) : '';
|
||||
|
||||
if ( in_array( $language, BTRANSLATE_Settings::target_languages(), true ) ) {
|
||||
if ( in_array( $language, HTBD_Settings::target_languages(), true ) ) {
|
||||
return $language;
|
||||
}
|
||||
}
|
||||
|
||||
if ( BTRANSLATE_Settings::is_routing_mode_enabled( 'subdirectory' ) ) {
|
||||
foreach ( BTRANSLATE_Settings::subdirectory_languages() as $language ) {
|
||||
if ( HTBD_Settings::is_routing_mode_enabled( 'subdirectory' ) ) {
|
||||
foreach ( HTBD_Settings::subdirectory_languages() as $language ) {
|
||||
$language = sanitize_key( $language );
|
||||
|
||||
if ( '/' . $language . '/sitemap.xml' === untrailingslashit( $path ) ) {
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class BTRANSLATE_Translation_Identity {
|
||||
class HTBD_Translation_Identity {
|
||||
public static function source_fingerprint( $source_value ) {
|
||||
return hash( 'sha256', (string) $source_value );
|
||||
}
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class BTRANSLATE_Translation_Result {
|
||||
class HTBD_Translation_Result {
|
||||
public $success;
|
||||
public $value;
|
||||
public $error_code;
|
||||
+5
-5
@@ -2,23 +2,23 @@
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class BTRANSLATE_Translation_Service {
|
||||
class HTBD_Translation_Service {
|
||||
private $store;
|
||||
private $provider;
|
||||
|
||||
public function __construct( BTRANSLATE_Translation_Store $store, BTRANSLATE_Translation_Provider $provider ) {
|
||||
public function __construct( HTBD_Translation_Store $store, HTBD_Translation_Provider $provider ) {
|
||||
$this->store = $store;
|
||||
$this->provider = $provider;
|
||||
}
|
||||
|
||||
public function get_or_translate( $source_value, $source_language, $target_language, $context, $force_refresh = false ) {
|
||||
$source_value = (string) $source_value;
|
||||
$source_fingerprint = BTRANSLATE_Translation_Identity::source_fingerprint( $source_value );
|
||||
$identity_key = BTRANSLATE_Translation_Identity::key( $source_value, $source_language, $target_language, $context, $this->provider->get_version() );
|
||||
$source_fingerprint = HTBD_Translation_Identity::source_fingerprint( $source_value );
|
||||
$identity_key = HTBD_Translation_Identity::key( $source_value, $source_language, $target_language, $context, $this->provider->get_version() );
|
||||
$existing = $this->store->find_valid( $identity_key );
|
||||
|
||||
if ( ! $force_refresh && ! empty( $existing['translated_value'] ) ) {
|
||||
return BTRANSLATE_Translation_Result::success( $existing['translated_value'] );
|
||||
return HTBD_Translation_Result::success( $existing['translated_value'] );
|
||||
}
|
||||
|
||||
$result = $this->provider->translate( $source_value, $source_language, $target_language, $context );
|
||||
+27
-6
@@ -2,11 +2,11 @@
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class BTRANSLATE_Translation_Store {
|
||||
class HTBD_Translation_Store {
|
||||
public static function table_name() {
|
||||
global $wpdb;
|
||||
|
||||
return $wpdb->prefix . 'btranslate_translations';
|
||||
return $wpdb->prefix . 'hyx_bd_translations';
|
||||
}
|
||||
|
||||
public static function install() {
|
||||
@@ -15,6 +15,13 @@ class BTRANSLATE_Translation_Store {
|
||||
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
|
||||
|
||||
$table_name = self::table_name();
|
||||
$legacy_table = $wpdb->prefix . 'btranslate_translations';
|
||||
$table_exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $wpdb->esc_like( $table_name ) ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Checks the plugin-owned table during activation.
|
||||
$legacy_exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $wpdb->esc_like( $legacy_table ) ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Checks for the legacy plugin-owned table during activation.
|
||||
if ( $legacy_exists === $legacy_table && $table_exists !== $table_name ) {
|
||||
$wpdb->query( $wpdb->prepare( 'RENAME TABLE %i TO %i', $legacy_table, $table_name ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Migrates persisted translations to the renamed table.
|
||||
}
|
||||
|
||||
$charset_collate = $wpdb->get_charset_collate();
|
||||
$sql = "CREATE TABLE {$table_name} (
|
||||
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
@@ -41,7 +48,7 @@ class BTRANSLATE_Translation_Store {
|
||||
|
||||
return $wpdb->get_row( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- The custom translation table is the plugin's persistent cache.
|
||||
$wpdb->prepare(
|
||||
"SELECT * FROM %i WHERE identity_key = %s AND status = 'complete' LIMIT 1",
|
||||
"SELECT * FROM %i WHERE identity_key = %s AND status IN ('complete', 'manual') LIMIT 1",
|
||||
self::table_name(),
|
||||
$identity_key
|
||||
),
|
||||
@@ -71,9 +78,23 @@ class BTRANSLATE_Translation_Store {
|
||||
);
|
||||
}
|
||||
|
||||
public function clear() {
|
||||
public function save_manual( $identity_key, $source_language, $target_language, $field_context, $source_fingerprint, $translated_value ) {
|
||||
return $this->save( $identity_key, $source_language, $target_language, $field_context, $source_fingerprint, $translated_value, 'manual' );
|
||||
}
|
||||
|
||||
public function clear( $target_language = '' ) {
|
||||
global $wpdb;
|
||||
|
||||
if ( '' !== $target_language ) {
|
||||
return $wpdb->query( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- This explicitly clears one language from the plugin's persistent translation cache.
|
||||
$wpdb->prepare(
|
||||
'DELETE FROM %i WHERE target_language = %s',
|
||||
self::table_name(),
|
||||
sanitize_key( $target_language )
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return $wpdb->query( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- This explicitly clears the plugin's persistent translation cache.
|
||||
$wpdb->prepare( 'DELETE FROM %i', self::table_name() )
|
||||
);
|
||||
@@ -87,7 +108,7 @@ class BTRANSLATE_Translation_Store {
|
||||
"SELECT COUNT(*) AS translated_fields, MAX(updated_at) AS last_translated_at
|
||||
FROM %i
|
||||
WHERE target_language = %s
|
||||
AND status = 'complete'
|
||||
AND status IN ('complete', 'manual')
|
||||
AND field_context LIKE %s",
|
||||
self::table_name(),
|
||||
$target_language,
|
||||
@@ -187,7 +208,7 @@ class BTRANSLATE_Translation_Store {
|
||||
$wpdb->prepare( // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- IN clauses contain only generated %s placeholders; the optional clause is a fixed SQL fragment.
|
||||
"SELECT COUNT(DISTINCT CONCAT(target_language, '|', SUBSTRING_INDEX(field_context, ':', 2)))
|
||||
FROM %i
|
||||
WHERE status = 'complete'
|
||||
WHERE status IN ('complete', 'manual')
|
||||
AND target_language IN ({$language_placeholders})
|
||||
AND SUBSTRING_INDEX(field_context, ':', 2) IN ({$context_placeholders}){$since_clause}",
|
||||
array_merge( array( self::table_name() ), $query_args )
|
||||
@@ -2,8 +2,12 @@
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class BTRANSLATE_Uninstaller {
|
||||
class HTBD_Uninstaller {
|
||||
private const OPTION_NAMES = array(
|
||||
'htbd_settings',
|
||||
'htbd_flush_rewrite_rules',
|
||||
'htbd_translation_task',
|
||||
'htbd_retranslation_batch',
|
||||
'btranslate_settings',
|
||||
'btranslate_flush_rewrite_rules',
|
||||
'btranslate_translation_task',
|
||||
@@ -11,6 +15,9 @@ class BTRANSLATE_Uninstaller {
|
||||
);
|
||||
|
||||
private const CRON_HOOKS = array(
|
||||
'htbd_process_translation',
|
||||
'htbd_process_term_translation',
|
||||
'htbd_process_seo_output_translation',
|
||||
'btranslate_process_translation',
|
||||
'btranslate_process_term_translation',
|
||||
'btranslate_process_seo_output_translation',
|
||||
@@ -50,7 +57,12 @@ class BTRANSLATE_Uninstaller {
|
||||
delete_option( $option_name );
|
||||
}
|
||||
|
||||
$table_name = $wpdb->prefix . 'btranslate_translations';
|
||||
$wpdb->query( "DROP TABLE IF EXISTS {$table_name}" );
|
||||
$table_names = array(
|
||||
$wpdb->prefix . 'hyx_bd_translations',
|
||||
$wpdb->prefix . 'btranslate_translations',
|
||||
);
|
||||
foreach ( $table_names as $table_name ) {
|
||||
$wpdb->query( $wpdb->prepare( 'DROP TABLE IF EXISTS %i', $table_name ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
interface BTRANSLATE_Translation_Provider {
|
||||
interface HTBD_Translation_Provider {
|
||||
public function get_version();
|
||||
|
||||
public function translate( $source_value, $source_language, $target_language, $context );
|
||||
+39
-11
@@ -1,13 +1,13 @@
|
||||
# Simplified Chinese translations for Btranslate.
|
||||
# Copyright (C) 2026 Btranslate contributors
|
||||
# This file is distributed under the same license as the Btranslate package.
|
||||
# Simplified Chinese translations for HTBD.
|
||||
# Copyright (C) 2026 HTBD contributors
|
||||
# This file is distributed under the same license as the hyx-translator-for-baidu-translate package.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Btranslate 0.2.2\n"
|
||||
"Project-Id-Version: hyx-translator-for-baidu-translate 0.3.1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-07-28 14:41+0800\n"
|
||||
"PO-Revision-Date: 2026-07-28 14:45+0800\n"
|
||||
"Last-Translator: Btranslate contributors\n"
|
||||
"Last-Translator: HTBD contributors\n"
|
||||
"Language-Team: Chinese (Simplified)\n"
|
||||
"Language: zh_CN\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@@ -15,8 +15,8 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
|
||||
msgid "Btranslate"
|
||||
msgstr "Btranslate"
|
||||
msgid "HTBD"
|
||||
msgstr "HTBD"
|
||||
|
||||
#, php-format
|
||||
msgid "(%1$s / %2$s content items)"
|
||||
@@ -32,6 +32,12 @@ msgstr "暂时无法读取翻译进度。"
|
||||
msgid "Failed translations"
|
||||
msgstr "翻译失败的内容"
|
||||
|
||||
msgid "Source language detection"
|
||||
msgstr "源语言识别"
|
||||
|
||||
msgid "Automatically detect the source language"
|
||||
msgstr "自动识别源语言"
|
||||
|
||||
msgid "Source language"
|
||||
msgstr "源语言"
|
||||
|
||||
@@ -41,8 +47,8 @@ msgstr "使用百度翻译语言代码,例如 zh。"
|
||||
msgid "Target languages"
|
||||
msgstr "目标语言"
|
||||
|
||||
msgid "Separate multiple language codes with commas, for example en,ja."
|
||||
msgstr "多个语言代码用英文逗号分隔,例如 en,ja。"
|
||||
msgid "Separate multiple language codes with commas, for example en,jp."
|
||||
msgstr "多个语言代码用英文逗号分隔,例如 en,jp。"
|
||||
|
||||
msgid "URL mode"
|
||||
msgstr "网址模式"
|
||||
@@ -74,8 +80,8 @@ msgstr "请求日志"
|
||||
msgid "Log each Baidu Translate request"
|
||||
msgstr "记录每次百度翻译请求"
|
||||
|
||||
msgid "When enabled, fires the btranslate_translation_request_logged action with the language, field, text fingerprint, length, and result status. Credentials, source text, translated text, and full API responses are never included."
|
||||
msgstr "启用后触发 btranslate_translation_request_logged 操作,提供语言、字段、文本指纹、长度和结果状态;不会提供密钥、原文、译文或完整 API 响应。"
|
||||
msgid "When enabled, fires the htbd_translation_request_logged action with the language, field, text fingerprint, length, and result status. Credentials, source text, translated text, and full API responses are never included."
|
||||
msgstr "启用后触发 htbd_translation_request_logged 操作,提供语言、字段、文本指纹、长度和结果状态;不会提供密钥、原文、译文或完整 API 响应。"
|
||||
|
||||
msgid "Retranslate content"
|
||||
msgstr "重新翻译内容"
|
||||
@@ -113,6 +119,10 @@ msgstr "清除后,前台会暂时显示源文;重新翻译时将再次调用
|
||||
msgid "This will delete all saved translations, cancel pending translation tasks, and reset progress. Continue?"
|
||||
msgstr "这将删除所有已保存的翻译、取消待执行翻译任务并重置进度。确认继续吗?"
|
||||
|
||||
#, php-format
|
||||
msgid "This will delete all saved %s translations. Continue?"
|
||||
msgstr "这将删除所有已保存的 %s 翻译。确认继续吗?"
|
||||
|
||||
msgid "Clear translation cache"
|
||||
msgstr "清除已翻译的缓存"
|
||||
|
||||
@@ -179,3 +189,21 @@ msgstr "尚未配置百度翻译凭据。"
|
||||
|
||||
msgid "Baidu Translate returned an invalid response."
|
||||
msgstr "百度翻译返回了无效响应。"
|
||||
|
||||
msgid "GitHub Repository"
|
||||
msgstr "GitHub 代码仓库"
|
||||
|
||||
msgid "https://www.vblg.top"
|
||||
msgstr "https://www.vblg.top"
|
||||
|
||||
msgid "hanyixuanten"
|
||||
msgstr "hanyixuanten"
|
||||
|
||||
msgid "Persistent multilingual WordPress translations powered by Baidu Translate."
|
||||
msgstr "由百度翻译提供支持,为 WordPress 生成并持久化保存多语言翻译。"
|
||||
|
||||
msgid "https://www.vblg.top/index.php/archives/147"
|
||||
msgstr "https://www.vblg.top/index.php/archives/147"
|
||||
|
||||
msgid "HTBD - hyx Translator powered by Baidu Translate"
|
||||
msgstr "HTBD - hyx 百度翻译多语言插件"
|
||||
@@ -1,12 +1,12 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR Btranslate contributors
|
||||
# This file is distributed under the same license as the Btranslate package.
|
||||
# Copyright (C) YEAR HTBD contributors
|
||||
# This file is distributed under the same license as the hyx-translator-for-baidu-translate package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Btranslate 0.2.2\n"
|
||||
"Project-Id-Version: hyx-translator-for-baidu-translate 0.3.1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-07-28 14:41+0800\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
@@ -17,204 +17,215 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=CHARSET\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#: includes/class-btranslate-admin.php:22
|
||||
msgid "Btranslate"
|
||||
#: includes/class-admin.php:22
|
||||
msgid "HTBD"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-btranslate-admin.php:48
|
||||
#: includes/class-admin.php:48
|
||||
#, php-format
|
||||
msgid "(%1$s / %2$s content items)"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-btranslate-admin.php:49
|
||||
#: includes/class-admin.php:49
|
||||
#, php-format
|
||||
msgid ""
|
||||
"Latest task: %1$s. Posts and pages: %2$s / %3$s; categories and tags: %4$s / "
|
||||
"%5$s. Updates automatically every 5 seconds."
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-btranslate-admin.php:50
|
||||
#: includes/class-admin.php:50
|
||||
msgid "Unable to load translation progress at this time."
|
||||
msgstr ""
|
||||
|
||||
msgid "Failed translations"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-btranslate-admin.php:95
|
||||
msgid "Source language detection"
|
||||
msgstr ""
|
||||
|
||||
msgid "Automatically detect the source language"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-admin.php:95
|
||||
msgid "Source language"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-btranslate-admin.php:95
|
||||
#: includes/class-admin.php:95
|
||||
msgid "Use a Baidu Translate language code, for example zh."
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-btranslate-admin.php:96
|
||||
#: includes/class-admin.php:96
|
||||
msgid "Target languages"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-btranslate-admin.php:96
|
||||
msgid "Separate multiple language codes with commas, for example en,ja."
|
||||
#: includes/class-admin.php:96
|
||||
msgid "Separate multiple language codes with commas, for example en,jp."
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-btranslate-admin.php:97
|
||||
#: includes/class-admin.php:97
|
||||
msgid "URL mode"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-btranslate-admin.php:97
|
||||
#: includes/class-admin.php:97
|
||||
msgid "Subdirectory"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-btranslate-admin.php:97
|
||||
#: includes/class-admin.php:97
|
||||
msgid "Domain"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-btranslate-admin.php:98
|
||||
#: includes/class-admin.php:98
|
||||
msgid "Domain bindings"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-btranslate-admin.php:98
|
||||
#: includes/class-admin.php:98
|
||||
msgid ""
|
||||
"Enter one domain=language code pair per line, for example en.example.com=en. "
|
||||
"Do not include https://, a path, or a port."
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-btranslate-admin.php:99
|
||||
#: includes/class-admin.php:99
|
||||
msgid "Baidu application ID"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-btranslate-admin.php:100
|
||||
#: includes/class-admin.php:100
|
||||
msgid "Baidu secret key"
|
||||
msgstr ""
|
||||
|
||||
msgid "After saving settings for the first time, manually run \"Retranslate all content\" below."
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-btranslate-admin.php:101
|
||||
#: includes/class-admin.php:101
|
||||
msgid "Request logging"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-btranslate-admin.php:101
|
||||
#: includes/class-admin.php:101
|
||||
msgid "Log each Baidu Translate request"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-btranslate-admin.php:101
|
||||
#: includes/class-admin.php:101
|
||||
msgid ""
|
||||
"When enabled, fires the btranslate_translation_request_logged action with "
|
||||
"When enabled, fires the htbd_translation_request_logged action with "
|
||||
"the language, field, text fingerprint, length, and result status. "
|
||||
"Credentials, source text, translated text, and full API responses are never "
|
||||
"included."
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-btranslate-admin.php:106
|
||||
#: includes/class-admin.php:106
|
||||
msgid "Retranslate content"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-btranslate-admin.php:107
|
||||
#: includes/class-admin.php:107
|
||||
msgid ""
|
||||
"Queue all published posts, pages, categories, and tags for translation again."
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-btranslate-admin.php:108
|
||||
#: includes/class-admin.php:108
|
||||
msgid "This will retranslate all content and use your API quota. Continue?"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-btranslate-admin.php:111
|
||||
#: includes/class-admin.php:111
|
||||
msgid "Retranslate all content"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-btranslate-admin.php:113
|
||||
#: includes/class-admin.php:113
|
||||
msgid "You can also retranslate a single content type."
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-btranslate-admin.php:114
|
||||
#: includes/class-admin.php:114
|
||||
msgid ""
|
||||
"This will retranslate all posts and pages and use your API quota. Continue?"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-btranslate-admin.php:118
|
||||
#: includes/class-admin.php:118
|
||||
msgid "Translate all posts"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-btranslate-admin.php:120
|
||||
#: includes/class-admin.php:120
|
||||
msgid ""
|
||||
"This will retranslate all categories and tags and use your API quota. "
|
||||
"Continue?"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-btranslate-admin.php:124
|
||||
#: includes/class-admin.php:124
|
||||
msgid "Translate all categories and tags"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-btranslate-admin.php:127
|
||||
#: includes/class-admin.php:127
|
||||
msgid "Translation cache"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-btranslate-admin.php:128
|
||||
#: includes/class-admin.php:128
|
||||
msgid ""
|
||||
"After clearing the cache, the front end temporarily displays source content. "
|
||||
"Baidu Translate API calls resume when content is translated again."
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-btranslate-admin.php:129
|
||||
#: includes/class-admin.php:129
|
||||
msgid ""
|
||||
"This will delete all saved translations, cancel pending translation tasks, "
|
||||
"and reset progress. Continue?"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-btranslate-admin.php:132
|
||||
#. translators: %s: target language code.
|
||||
#, php-format
|
||||
msgid "This will delete all saved %s translations. Continue?"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-admin.php:132
|
||||
msgid "Clear translation cache"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-btranslate-admin.php:135
|
||||
#: includes/class-admin.php:135
|
||||
msgid "Translation progress"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-btranslate-admin.php:137
|
||||
#: includes/class-admin.php:137
|
||||
msgid "Loading translation progress..."
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-btranslate-admin.php:145
|
||||
#: includes/class-btranslate-admin.php:211
|
||||
#: includes/class-admin.php:145
|
||||
#: includes/class-admin.php:211
|
||||
msgid "You do not have permission to perform this action."
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-btranslate-admin.php:151
|
||||
#: includes/class-admin.php:151
|
||||
msgid "Invalid translation scope."
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-btranslate-admin.php:218
|
||||
#: includes/class-admin.php:218
|
||||
msgid "Failed to clear the translation cache."
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-btranslate-admin.php:238
|
||||
#: includes/class-admin.php:238
|
||||
msgid "Settings"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-btranslate-admin.php:281
|
||||
#: includes/class-admin.php:281
|
||||
msgid "Translation status"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-btranslate-admin.php:296
|
||||
#: includes/class-admin.php:296
|
||||
msgid "Retranslate"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-btranslate-admin.php:296
|
||||
#: includes/class-admin.php:296
|
||||
msgid "Translate"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-btranslate-admin.php:311
|
||||
#: includes/class-admin.php:311
|
||||
msgid "Translated"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-btranslate-admin.php:311
|
||||
#: includes/class-admin.php:311
|
||||
msgid "Not translated"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-btranslate-admin.php:313
|
||||
#: includes/class-admin.php:313
|
||||
msgid "Last translated:"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-btranslate-admin.php:325
|
||||
#: includes/class-admin.php:325
|
||||
msgid "Invalid translation request."
|
||||
msgstr ""
|
||||
|
||||
@@ -222,30 +233,30 @@ msgstr ""
|
||||
msgid "Content #%d"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-btranslate-admin.php:351
|
||||
#: includes/class-admin.php:351
|
||||
msgid "Full translation"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-btranslate-admin.php:355
|
||||
#: includes/class-admin.php:355
|
||||
msgid "Single post translation"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-btranslate-admin.php:359
|
||||
#: includes/class-admin.php:359
|
||||
msgid "Posts and pages translation"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-btranslate-admin.php:363
|
||||
#: includes/class-admin.php:363
|
||||
msgid "Categories and tags translation"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-btranslate-admin.php:366
|
||||
#: includes/class-admin.php:366
|
||||
msgid "No translation task"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-btranslate-baidu-provider.php:22
|
||||
#: includes/class-baidu-provider.php:22
|
||||
msgid "Baidu Translate credentials are not configured."
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-btranslate-baidu-provider.php:52
|
||||
#: includes/class-baidu-provider.php:52
|
||||
msgid "Baidu Translate returned an invalid response."
|
||||
msgstr ""
|
||||
@@ -0,0 +1,199 @@
|
||||
# Translation of Plugins - HTBD – hyx Translator powered by Baidu Translate - Stable Readme (latest release) in Chinese (China)
|
||||
# This file is distributed under the same license as the Plugins - HTBD – hyx Translator powered by Baidu Translate - Stable Readme (latest release) package.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"PO-Revision-Date: 2026-08-06 00:00+0000\n"
|
||||
"Last-Translator: HTBD contributors\n"
|
||||
"Language-Team: Chinese (China)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
"X-Generator: GlotPress/4.0.3\n"
|
||||
"Language: zh_CN\n"
|
||||
"Project-Id-Version: Plugins - HTBD – hyx Translator powered by Baidu Translate - Stable Readme (latest release)\n"
|
||||
|
||||
#. Short description.
|
||||
#, gp-priority: high
|
||||
msgid "Persistent multilingual WordPress translations powered by the Baidu Translate API."
|
||||
msgstr "由百度翻译 API 提供支持,为 WordPress 生成并持久化保存多语言翻译。"
|
||||
|
||||
#. Plugin name.
|
||||
#, gp-priority: high
|
||||
msgid "HTBD - hyx Translator powered by Baidu Translate"
|
||||
msgstr "HTBD - hyx 百度翻译多语言插件"
|
||||
|
||||
#. Found in faq paragraph.
|
||||
msgid "Uninstalling removes plugin settings, scheduled tasks, and the custom translation table, including all persisted translations."
|
||||
msgstr "卸载会删除插件设置、计划任务和自建翻译表,包括所有已持久化的翻译。"
|
||||
|
||||
#. Found in faq paragraph.
|
||||
msgid "Failed posts, pages, categories, or tags appear below the progress bar on the Settings page. Use the Retranslate button beside an item to retry it."
|
||||
msgstr "翻译失败的文章、页面、分类或标签会显示在设置页面的进度条下方。点击对应项目旁的“重新翻译”按钮即可重试。"
|
||||
|
||||
#. Found in faq paragraph.
|
||||
msgid "No. Content inside HTML <code><pre></code> and <code><code></code> elements is preserved in the source language and is not sent for translation."
|
||||
msgstr "不会。HTML <code><pre></code> 和 <code><code></code> 元素内的内容会保留源语言,不会发送进行翻译。"
|
||||
|
||||
#. Found in faq paragraph.
|
||||
msgid "No. Each value is translated by a scheduled task and persisted for reuse. Front-end requests read stored translations only."
|
||||
msgstr "不会。每个值都由计划任务翻译并持久化保存以供重复使用。前端请求只读取已保存的翻译。"
|
||||
|
||||
#. Found in faq header.
|
||||
msgid "What data is removed during uninstall?"
|
||||
msgstr "卸载时会删除哪些数据?"
|
||||
|
||||
#. Found in faq header.
|
||||
msgid "How do I retry a failed translation?"
|
||||
msgstr "如何重试失败的翻译?"
|
||||
|
||||
#. Found in faq header.
|
||||
msgid "Does the plugin translate code in posts or pages?"
|
||||
msgstr "插件会翻译文章或页面中的代码吗?"
|
||||
|
||||
#. Found in faq header.
|
||||
msgid "Does the plugin translate content on every page request?"
|
||||
msgstr "插件会在每次页面请求时翻译内容吗?"
|
||||
|
||||
#. Found in installation paragraph.
|
||||
msgid "Automatic detection is stored independently from the source language value. Manually entering <code>auto</code> without enabling automatic detection remains a distinct configuration state."
|
||||
msgstr "自动识别状态与源语言值分开保存。在未启用自动识别时手动输入 <code>auto</code>,仍属于不同的配置状态。"
|
||||
|
||||
#. Found in installation list item.
|
||||
msgid "After saving settings for the first time, manually run Retranslate all content to queue existing content."
|
||||
msgstr "第一次保存设置后,手动执行“重新翻译所有内容”,将现有内容加入队列。"
|
||||
|
||||
#. Found in installation list item.
|
||||
msgid "Open Settings > HTBD and configure the Baidu credentials, languages, and routing mode. Enable automatic source-language detection to use Baidu's <code>auto</code> source language mode."
|
||||
msgstr "打开“设置 > HTBD”,配置百度翻译凭据、语言和路由模式。启用自动识别源语言可使用百度翻译的 <code>auto</code> 源语言模式。"
|
||||
|
||||
#. Found in installation list item.
|
||||
msgid "Activate HTBD through the Plugins screen in WordPress."
|
||||
msgstr "在 WordPress 的“插件”页面启用 HTBD。"
|
||||
|
||||
#. Found in installation list item.
|
||||
msgid "Upload the <code>hyx-translator-for-baidu-translate</code> directory to <code>/wp-content/plugins/</code>."
|
||||
msgstr "将 <code>hyx-translator-for-baidu-translate</code> 目录上传到 <code>/wp-content/plugins/</code>。"
|
||||
|
||||
#. Found in description paragraph.
|
||||
msgid "HTBD stores the returned translated text in the WordPress database but does not store complete Baidu response payloads. Data sent to Baidu is subject to Baidu's own terms and privacy practices:"
|
||||
msgstr "HTBD 将百度返回的译文保存在 WordPress 数据库中,但不保存完整的百度响应内容。发送给百度的数据受百度自身条款和隐私规则约束:"
|
||||
|
||||
#. Found in description paragraph.
|
||||
msgid "For each translation request, the plugin sends the text being translated, the configured source and target language codes, the Baidu application ID, a random salt, and a request signature. Depending on the content being translated, the text may contain published post or page titles, body text, excerpts, selected SEO title and description values, attached image alternative text, and category or tag names and descriptions. Standard network information, including the originating server IP address, is also visible to Baidu as part of the HTTPS request. The secret key is used locally to create the signature and is not sent as a separate request field."
|
||||
msgstr "每次翻译请求会发送待翻译文本、配置的源语言和目标语言代码、百度应用 ID、随机盐值和请求签名。根据待翻译内容,文本可能包含已发布文章或页面的标题、正文、摘要、指定的 SEO 标题和描述、附件图片替代文本,以及分类或标签的名称和描述。作为 HTTPS 请求的一部分,百度也能看到来源服务器 IP 地址等标准网络信息。密钥只在本地用于生成签名,不会作为独立的请求字段发送。"
|
||||
|
||||
#. Found in description paragraph.
|
||||
msgid "The API is contacted by scheduled tasks after a published post or page is saved, after a supported category or tag is created or edited, or after an administrator explicitly queues or refreshes translations. Front-end page views reuse stored translations and do not contact Baidu. The plugin also makes no Baidu request when valid persisted translations are already available, unless an administrator explicitly requests a refresh."
|
||||
msgstr "已发布的文章或页面保存后、支持的分类或标签创建或编辑后,或者管理员明确将翻译加入队列或刷新翻译时,计划任务会请求该 API。前端页面访问只读取已保存的翻译,不会请求百度。已有有效的持久化翻译时,插件也不会请求百度,除非管理员明确要求刷新。"
|
||||
|
||||
#. Found in description paragraph.
|
||||
msgid "HTBD connects to the Baidu Translate Open Platform API, a service provided by Baidu, to translate WordPress content. The plugin cannot generate new translations without this service. A Baidu Translate account, application ID, and secret key are required. Baidu may apply request quotas or usage charges according to the service plan selected by the site administrator."
|
||||
msgstr "HTBD 连接由百度提供的百度翻译开放平台 API,以翻译 WordPress 内容。没有该外部服务,插件无法生成新的翻译。站点管理员需要百度翻译账户、应用 ID 和密钥。百度可能根据站点管理员选择的服务方案实施请求配额或收取使用费用。"
|
||||
|
||||
#. Found in description paragraph.
|
||||
msgid "Subdirectory routing supports paths such as <code>/en/example-post/</code>. Domain routing maps configured hostnames to target languages. Requests for <code>wp-login.php</code> or <code>wp-admin</code> through a target-language route are redirected to the corresponding source-site URL while preserving query parameters. Translation failures are non-fatal and fall back to source-language content."
|
||||
msgstr "子目录路由支持 <code>/en/example-post/</code> 等路径。域名路由可将配置的主机名映射到目标语言。通过目标语言路由请求 <code>wp-login.php</code> 或 <code>wp-admin</code> 时,插件会重定向到源站对应地址并保留查询参数。翻译失败不会中断页面请求,而会回退显示源语言内容。"
|
||||
|
||||
#. Found in description paragraph.
|
||||
msgid "Supported values include published post and page titles, content, excerpts, image alternative text, categories, tags, and selected SEO fields. Automatic translation is only scheduled for posts and pages with the <code>publish</code> status. Completed translations are stored in the <code>{$wpdb->prefix}hyx_bd_translations</code> custom database table. Existing <code>{$wpdb->prefix}btranslate_translations</code> data is migrated when the renamed plugin is activated. Front-end rendering reuses stored values and never calls the translation provider dynamically."
|
||||
msgstr "支持的值包括已发布文章和页面的标题、正文、摘要、图片替代文本、分类、标签及部分 SEO 字段。插件只为 <code>publish</code> 状态的文章和页面自动安排翻译。完成的翻译保存在 <code>{$wpdb->prefix}hyx_bd_translations</code> 自建数据表中;启用改名后的插件时,已有 <code>{$wpdb->prefix}btranslate_translations</code> 数据会自动迁移。前端渲染只读取已保存的值,不会动态请求翻译服务。"
|
||||
|
||||
#. Found in description paragraph.
|
||||
msgid "The WordPress admin interface follows the current WordPress locale. English is the default. Approved WordPress.org language packs take priority, with the bundled Simplified Chinese translation used as a fallback when WordPress is set to <code>zh_CN</code>."
|
||||
msgstr "WordPress 后台界面会跟随当前 WordPress 语言。默认显示英文;WordPress.org 已批准的语言包优先,WordPress 设为 <code>zh_CN</code> 时,插件内置的简体中文翻译用作后备。"
|
||||
|
||||
#. Found in description paragraph.
|
||||
msgid "HTBD translates supported WordPress content with the Baidu Translate API and persists each translation for reuse. It supports language-specific subdirectory URLs and domain bindings."
|
||||
msgstr "HTBD 使用百度翻译 API 翻译支持的 WordPress 内容,并将每个翻译结果持久化保存以供重复使用。插件支持语言子目录 URL 和语言域名绑定。"
|
||||
|
||||
#. Found in description list item.
|
||||
msgid "Baidu privacy policy: https://privacy.baidu.com/policy"
|
||||
msgstr "百度隐私政策:https://privacy.baidu.com/policy"
|
||||
|
||||
#. Found in description list item.
|
||||
msgid "Baidu Translate Open Platform service agreement: https://fanyi-api.baidu.com/doc/6"
|
||||
msgstr "百度翻译开放平台服务协议:https://fanyi-api.baidu.com/doc/6"
|
||||
|
||||
#. Found in description list item.
|
||||
msgid "Baidu Translate API documentation: https://fanyi-api.baidu.com/doc/23"
|
||||
msgstr "百度翻译 API 文档:https://fanyi-api.baidu.com/doc/23"
|
||||
|
||||
#. Found in description header.
|
||||
msgid "External Services"
|
||||
msgstr "外部服务"
|
||||
|
||||
#. Found in changelog list item.
|
||||
#, gp-priority: low
|
||||
msgid "Improve WordPress.org localization packaging and release deployment workflows."
|
||||
msgstr "改进 WordPress.org 本地化资源打包和发布部署工作流。"
|
||||
|
||||
#. Found in changelog list item.
|
||||
#, gp-priority: low
|
||||
msgid "Preserve target-language routing when submitting comments and following comment redirects."
|
||||
msgstr "提交评论及处理评论重定向时保留目标语言路由。"
|
||||
|
||||
#. Found in changelog list item.
|
||||
#, gp-priority: low
|
||||
msgid "Fix Baidu Japanese routing to use the supported <code>jp</code> language code."
|
||||
msgstr "修复百度日语路由,改用受支持的 <code>jp</code> 语言代码。"
|
||||
|
||||
#. Found in changelog list item.
|
||||
#, gp-priority: low
|
||||
msgid "Add per-language controls for translating posts, terms, and clearing persisted translations."
|
||||
msgstr "新增按语言翻译文章和分类项以及清除持久化翻译的控制项。"
|
||||
|
||||
#. Found in changelog list item.
|
||||
#, gp-priority: low
|
||||
msgid "Add automated GitHub Release packaging and WordPress.org SVN deployment workflows."
|
||||
msgstr "新增 GitHub Release 自动打包及 WordPress.org SVN 自动部署工作流。"
|
||||
|
||||
#. Found in changelog list item.
|
||||
#, gp-priority: low
|
||||
msgid "Redirect target-language <code>wp-admin</code> and <code>wp-login.php</code> requests to the source site while preserving query parameters."
|
||||
msgstr "将目标语言路由中的 <code>wp-admin</code> 和 <code>wp-login.php</code> 请求重定向到源站,并保留查询参数。"
|
||||
|
||||
#. Found in changelog list item.
|
||||
#, gp-priority: low
|
||||
msgid "Add automatic source-language detection using Baidu's <code>auto</code> mode."
|
||||
msgstr "新增使用百度翻译 <code>auto</code> 模式自动识别源语言的功能。"
|
||||
|
||||
#. Found in changelog list item.
|
||||
#, gp-priority: low
|
||||
msgid "Fix the release archive build workflow."
|
||||
msgstr "修复发布归档构建流程。"
|
||||
|
||||
#. Found in changelog list item.
|
||||
#, gp-priority: low
|
||||
msgid "Update packaging, localization, documentation, and issue templates for the new plugin identity."
|
||||
msgstr "更新打包流程、本地化资源、文档和问题模板以适配新的插件标识。"
|
||||
|
||||
#. Found in changelog list item.
|
||||
#, gp-priority: low
|
||||
msgid "Migrate existing translation data from the previous custom database table."
|
||||
msgstr "迁移旧版自建数据表中已有的翻译数据。"
|
||||
|
||||
#. Found in changelog list item.
|
||||
#, gp-priority: low
|
||||
msgid "Rename the plugin and codebase to HTBD / hyx-translator-for-baidu-translate."
|
||||
msgstr "将插件及代码库重命名为 HTBD / hyx-translator-for-baidu-translate。"
|
||||
|
||||
#. Found in changelog list item.
|
||||
#, gp-priority: low
|
||||
msgid "Update the plugin homepage URL."
|
||||
msgstr "更新插件主页 URL。"
|
||||
|
||||
#. Found in changelog list item.
|
||||
#, gp-priority: low
|
||||
msgid "Improve plugin security metadata and translator attribution."
|
||||
msgstr "完善插件安全元数据和译者署名。"
|
||||
|
||||
#. Found in changelog list item.
|
||||
#, gp-priority: low
|
||||
msgid "Preserve content inside HTML <code><pre></code> and <code><code></code> elements instead of sending it for translation."
|
||||
msgstr "保留 HTML <code><pre></code> 和 <code><code></code> 元素内的内容,不再将其发送进行翻译。"
|
||||
|
||||
#. Found in changelog list item.
|
||||
#, gp-priority: low
|
||||
msgid "Fix translated titles so singular views use the current post context."
|
||||
msgstr "修复翻译标题,使单篇内容视图使用当前文章上下文。"
|
||||
@@ -1,67 +0,0 @@
|
||||
=== Btranslate ===
|
||||
Contributors: hanyixuanten
|
||||
Homepage: https://github.com/hanyixuanten/btranslate
|
||||
Requires at least: 6.4
|
||||
Tested up to: 7.0
|
||||
Requires PHP: 8.1
|
||||
Stable tag: 0.2.2
|
||||
License: GPL-3.0-only
|
||||
License URI: https://www.gnu.org/licenses/gpl-3.0.html
|
||||
|
||||
使用百度翻译 API 生成并持久化保存 WordPress 多语言内容。
|
||||
|
||||
== Description ==
|
||||
|
||||
Btranslate 使用百度翻译 API 翻译 WordPress 内容,并将每个翻译结果持久化保存以供重复使用。插件支持语言子目录 URL 和语言域名绑定。
|
||||
|
||||
WordPress 后台界面会跟随当前 WordPress 语言:默认显示英文,WordPress 设为 `zh_CN` 时使用插件内置的简体中文翻译。
|
||||
|
||||
支持的内容包括已发布文章和页面的标题、正文、摘要、图片替代文本、分类、标签及部分 SEO 字段。插件只为 `publish` 状态的文章和页面自动安排翻译。完成的翻译保存在插件自建数据表中;前端渲染只读取已保存的翻译,不会动态请求翻译服务。
|
||||
|
||||
子目录路由支持 `/en/example-post/` 等路径。域名路由可将配置的主机名映射到目标语言。翻译失败不会中断页面请求;没有有效译文时会回退显示源语言内容。
|
||||
|
||||
== Installation ==
|
||||
|
||||
1. 将 `btranslate` 目录上传到 `/wp-content/plugins/`。
|
||||
2. 在 WordPress 的“插件”页面启用 Btranslate。
|
||||
3. 打开“设置 > Btranslate”,配置百度翻译凭据、源语言、目标语言和路由模式。
|
||||
4. 第一次保存设置后,手动执行“重新翻译所有内容”,为已有内容安排翻译任务。
|
||||
5. 此后保存文章或页面时,插件会自动安排翻译任务。
|
||||
|
||||
== External Services ==
|
||||
|
||||
Btranslate 连接由百度提供的百度翻译开放平台 API,以翻译 WordPress 内容。没有该外部服务,插件无法生成新的翻译。站点管理员需要百度翻译账户、应用 ID 和密钥;百度可能根据管理员选择的服务方案实施请求配额或收取使用费用。
|
||||
|
||||
已发布文章或页面保存后、支持的分类或标签创建或编辑后,或者管理员明确安排或刷新翻译时,计划任务会请求该 API。前端页面访问只读取已保存的翻译,不会请求百度。已有有效持久化翻译时也不会请求百度,除非管理员明确执行重新翻译。
|
||||
|
||||
每次翻译请求会发送待翻译文本、配置的源语言和目标语言代码、百度应用 ID、随机盐值和请求签名。根据待翻译内容,文本可能包含已发布文章或页面的标题、正文、摘要、指定 SEO 标题和描述、附件图片替代文本,以及分类或标签的名称和描述。作为 HTTPS 请求的一部分,百度也能看到来源服务器 IP 等标准网络信息。密钥只在本地用于生成签名,不会作为独立请求字段发送。
|
||||
|
||||
Btranslate 将百度返回的译文保存在 WordPress 数据库中,但不保存完整百度响应。发送给百度的数据受百度自身条款和隐私规则约束:
|
||||
|
||||
* 百度翻译 API 文档:https://fanyi-api.baidu.com/doc/23
|
||||
* 百度翻译开放平台服务协议:https://fanyi-api.baidu.com/doc/6
|
||||
* 百度隐私政策:https://privacy.baidu.com/policy
|
||||
|
||||
== Frequently Asked Questions ==
|
||||
|
||||
= 插件会在每次页面请求时翻译内容吗? =
|
||||
|
||||
不会。翻译由计划任务生成并持久化保存。前端请求只读取已保存的翻译结果。
|
||||
|
||||
= 翻译失败后如何重试? =
|
||||
|
||||
失败的文章、页面、分类或标签会显示在设置页进度条下方。点击对应的“重新翻译”按钮可逐项重试。
|
||||
|
||||
= 支持哪些 URL 路由模式? =
|
||||
|
||||
支持语言子目录、域名绑定,或同时启用这两种模式。域名和 DNS 必须预先指向同一个 WordPress 站点。
|
||||
|
||||
= 卸载插件会删除哪些数据? =
|
||||
|
||||
卸载会删除插件设置、计划任务和自建翻译表,包括所有已持久化的翻译。停用插件不会删除这些数据。
|
||||
|
||||
== Changelog ==
|
||||
|
||||
= 0.2.2 =
|
||||
|
||||
* 声明 GPLv3 许可证并补充 WordPress.org 插件元数据。
|
||||
+43
-14
@@ -1,10 +1,10 @@
|
||||
=== Btranslate ===
|
||||
=== HTBD - hyx Translator powered by Baidu Translate ===
|
||||
Contributors: hanyixuanten
|
||||
Homepage: https://github.com/hanyixuanten/btranslate
|
||||
Homepage: https://www.vblg.top/index.php/archives/147
|
||||
Requires at least: 6.4
|
||||
Tested up to: 7.0
|
||||
Requires PHP: 8.1
|
||||
Stable tag: 0.2.2
|
||||
Stable tag: 0.3.1
|
||||
License: GPL-3.0-only
|
||||
License URI: https://www.gnu.org/licenses/gpl-3.0.html
|
||||
|
||||
@@ -12,30 +12,32 @@ Persistent multilingual WordPress translations powered by the Baidu Translate AP
|
||||
|
||||
== Description ==
|
||||
|
||||
Btranslate translates supported WordPress content with the Baidu Translate API and persists each translation for reuse. It supports language-specific subdirectory URLs and domain bindings.
|
||||
HTBD translates supported WordPress content with the Baidu Translate API and persists each translation for reuse. It supports language-specific subdirectory URLs and domain bindings.
|
||||
|
||||
The WordPress admin interface follows the current WordPress locale. English is the default, and a bundled Simplified Chinese translation is used when WordPress is set to `zh_CN`.
|
||||
The WordPress admin interface follows the current WordPress locale. English is the default. Approved WordPress.org language packs take priority, with the bundled Simplified Chinese translation used as a fallback when WordPress is set to `zh_CN`.
|
||||
|
||||
Supported values include published post and page titles, content, excerpts, image alternative text, categories, tags, and selected SEO fields. Automatic translation is only scheduled for posts and pages with the `publish` status. Completed translations are stored in the plugin's custom database table. Front-end rendering reuses stored values and never calls the translation provider dynamically.
|
||||
Supported values include published post and page titles, content, excerpts, image alternative text, categories, tags, and selected SEO fields. Automatic translation is only scheduled for posts and pages with the `publish` status. Completed translations are stored in the `{$wpdb->prefix}hyx_bd_translations` custom database table. Existing `{$wpdb->prefix}btranslate_translations` data is migrated when the renamed plugin is activated. Front-end rendering reuses stored values and never calls the translation provider dynamically.
|
||||
|
||||
Subdirectory routing supports paths such as `/en/example-post/`. Domain routing maps configured hostnames to target languages. Translation failures are non-fatal and fall back to source-language content.
|
||||
Subdirectory routing supports paths such as `/en/example-post/`. Domain routing maps configured hostnames to target languages. Requests for `wp-login.php` or `wp-admin` through a target-language route are redirected to the corresponding source-site URL while preserving query parameters. Translation failures are non-fatal and fall back to source-language content.
|
||||
|
||||
== Installation ==
|
||||
|
||||
1. Upload the `btranslate` directory to `/wp-content/plugins/`.
|
||||
2. Activate Btranslate through the Plugins screen in WordPress.
|
||||
3. Open Settings > Btranslate and configure the Baidu credentials, languages, and routing mode.
|
||||
1. Upload the `hyx-translator-for-baidu-translate` directory to `/wp-content/plugins/`.
|
||||
2. Activate HTBD through the Plugins screen in WordPress.
|
||||
3. Open Settings > HTBD and configure the Baidu credentials, languages, and routing mode. Enable automatic source-language detection to use Baidu's `auto` source language mode.
|
||||
4. After saving settings for the first time, manually run Retranslate all content to queue existing content.
|
||||
|
||||
Automatic detection is stored independently from the source language value. Manually entering `auto` without enabling automatic detection remains a distinct configuration state.
|
||||
|
||||
== External Services ==
|
||||
|
||||
Btranslate connects to the Baidu Translate Open Platform API, a service provided by Baidu, to translate WordPress content. The plugin cannot generate new translations without this service. A Baidu Translate account, application ID, and secret key are required. Baidu may apply request quotas or usage charges according to the service plan selected by the site administrator.
|
||||
HTBD connects to the Baidu Translate Open Platform API, a service provided by Baidu, to translate WordPress content. The plugin cannot generate new translations without this service. A Baidu Translate account, application ID, and secret key are required. Baidu may apply request quotas or usage charges according to the service plan selected by the site administrator.
|
||||
|
||||
The API is contacted by scheduled tasks after a published post or page is saved, after a supported category or tag is created or edited, or after an administrator explicitly queues or refreshes translations. Front-end page views reuse stored translations and do not contact Baidu. The plugin also makes no Baidu request when valid persisted translations are already available, unless an administrator explicitly requests a refresh.
|
||||
|
||||
For each translation request, the plugin sends the text being translated, the configured source and target language codes, the Baidu application ID, a random salt, and a request signature. Depending on the content being translated, the text may contain published post or page titles, body text, excerpts, selected SEO title and description values, attached image alternative text, and category or tag names and descriptions. Standard network information, including the originating server IP address, is also visible to Baidu as part of the HTTPS request. The secret key is used locally to create the signature and is not sent as a separate request field.
|
||||
|
||||
Btranslate stores the returned translated text in the WordPress database but does not store complete Baidu response payloads. Data sent to Baidu is subject to Baidu's own terms and privacy practices:
|
||||
HTBD stores the returned translated text in the WordPress database but does not store complete Baidu response payloads. Data sent to Baidu is subject to Baidu's own terms and privacy practices:
|
||||
|
||||
* Baidu Translate API documentation: https://fanyi-api.baidu.com/doc/23
|
||||
* Baidu Translate Open Platform service agreement: https://fanyi-api.baidu.com/doc/6
|
||||
@@ -47,6 +49,10 @@ Btranslate stores the returned translated text in the WordPress database but doe
|
||||
|
||||
No. Each value is translated by a scheduled task and persisted for reuse. Front-end requests read stored translations only.
|
||||
|
||||
= Does the plugin translate code in posts or pages? =
|
||||
|
||||
No. Content inside HTML `<pre>` and `<code>` elements is preserved in the source language and is not sent for translation.
|
||||
|
||||
= How do I retry a failed translation? =
|
||||
|
||||
Failed posts, pages, categories, or tags appear below the progress bar on the Settings page. Use the Retranslate button beside an item to retry it.
|
||||
@@ -57,6 +63,29 @@ Uninstalling removes plugin settings, scheduled tasks, and the custom translatio
|
||||
|
||||
== Changelog ==
|
||||
|
||||
= 0.2.2 =
|
||||
= 0.3.1 =
|
||||
|
||||
* Declare GPLv3 licensing and WordPress.org plugin metadata.
|
||||
* Add per-language controls for translating posts, terms, and clearing persisted translations.
|
||||
* Fix Baidu Japanese routing to use the supported `jp` language code.
|
||||
* Preserve target-language routing when submitting comments and following comment redirects.
|
||||
* Improve WordPress.org localization packaging and release deployment workflows.
|
||||
|
||||
= 0.3.0 =
|
||||
|
||||
* Add automatic source-language detection using Baidu's `auto` mode.
|
||||
* Redirect target-language `wp-admin` and `wp-login.php` requests to the source site while preserving query parameters.
|
||||
* Add automated GitHub Release packaging and WordPress.org SVN deployment workflows.
|
||||
|
||||
= 0.2.4 =
|
||||
|
||||
* Fix translated titles so singular views use the current post context.
|
||||
* Preserve content inside HTML `<pre>` and `<code>` elements instead of sending it for translation.
|
||||
* Improve plugin security metadata and translator attribution.
|
||||
* Update the plugin homepage URL.
|
||||
|
||||
= 0.2.3 =
|
||||
|
||||
* Rename the plugin and codebase to HTBD / hyx-translator-for-baidu-translate.
|
||||
* Migrate existing translation data from the previous custom database table.
|
||||
* Update packaging, localization, documentation, and issue templates for the new plugin identity.
|
||||
* Fix the release archive build workflow.
|
||||
+4
-4
@@ -1,14 +1,14 @@
|
||||
<?php
|
||||
/**
|
||||
* Removes all data created by Btranslate.
|
||||
* Removes all data created by HTBD.
|
||||
*
|
||||
* @package BTRANSLATE
|
||||
* @package HTBD
|
||||
*/
|
||||
|
||||
if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/includes/class-btranslate-uninstaller.php';
|
||||
require_once __DIR__ . '/includes/class-uninstaller.php';
|
||||
|
||||
BTRANSLATE_Uninstaller::uninstall();
|
||||
HTBD_Uninstaller::uninstall();
|
||||
Reference in New Issue
Block a user