Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a1f7933e90 | ||
|
|
becb4ae3d6 | ||
|
|
7aead0fb97 | ||
|
|
06591a0b24 | ||
|
|
f6f6a4cbb6 | ||
|
|
ae9d1cad4c | ||
|
|
3fa50cb126 | ||
|
|
67c897256f | ||
|
|
b51c35b9da | ||
|
|
424c6eba32 | ||
|
|
ad7f7d498d | ||
|
|
2f2046a809 | ||
|
|
7ad51fb281 | ||
|
|
9a865912c0 | ||
|
|
460a70fc3c | ||
|
|
ea65c7b93b |
@@ -33,7 +33,7 @@ body:
|
||||
id: plugin-version
|
||||
attributes:
|
||||
label: HTBD version
|
||||
placeholder: "For example: 0.2.4"
|
||||
placeholder: "For example: 0.3.1"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ body:
|
||||
id: plugin-version
|
||||
attributes:
|
||||
label: HTBD 版本
|
||||
placeholder: 例如:0.2.4
|
||||
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:
|
||||
@@ -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/
|
||||
@@ -48,6 +48,7 @@ Unless a user explicitly requests a refresh, each translatable value must be tra
|
||||
- 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
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ The plugin supports translating the main content of WordPress posts and pages, i
|
||||
|
||||
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
|
||||
|
||||
@@ -121,13 +121,15 @@ Because the plugin has not yet been approved for the WordPress.org Plugin Direct
|
||||
1. Sign in to WordPress Admin.
|
||||
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.
|
||||
@@ -148,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
|
||||
|
||||
+8
-4
@@ -37,7 +37,7 @@ HTBD 使用百度翻译开放平台提供的翻译 API。用户可以在 WordPre
|
||||
|
||||
HTBD 可以为不同语言生成独立的访问地址,支持:
|
||||
|
||||
- 语言子目录,例如 `/en/`、`/ja/`
|
||||
- 语言子目录,例如 `/en/`、`/jp/`
|
||||
- 不同语言绑定不同域名
|
||||
- 根据配置生成对应语言的固定链接
|
||||
|
||||
@@ -121,13 +121,15 @@ HTBD 为多语言页面提供必要的 SEO 支持,包括:
|
||||
1. 登录 WordPress 管理后台。
|
||||
2. 打开 HTBD 设置页面。
|
||||
3. 填写百度翻译开放平台提供的 **APP ID** 和 **密钥**。
|
||||
4. 设置网站的源语言。
|
||||
4. 设置网站的源语言,或勾选“自动识别源语言”以使用百度翻译的 `auto` 源语言模式。
|
||||
5. 添加需要启用的目标语言。
|
||||
6. 选择语言 URL 模式,例如语言子目录或域名绑定。
|
||||
7. 保存设置。
|
||||
|
||||
修改语言路由设置后,建议在 WordPress 后台打开“设置” → “固定链接”,确认固定链接配置已经正确生效。
|
||||
|
||||
自动识别状态使用独立选项保存。未勾选自动识别时在源语言输入框中主动填写 `auto`,仍属于不同的配置状态。
|
||||
|
||||
### 四、开始使用
|
||||
|
||||
1. 在 WordPress 后台打开需要翻译的文章或页面。
|
||||
@@ -148,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。
|
||||
|
||||
## 使用建议
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
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,6 +12,12 @@
|
||||
});
|
||||
}
|
||||
|
||||
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-htbd-confirm'))) {
|
||||
|
||||
@@ -30,12 +30,14 @@ build() {
|
||||
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/${plugin_slug}-zh_CN.mo" \
|
||||
"${script_dir}/languages/${plugin_slug}-zh_CN.po"
|
||||
@@ -46,8 +48,11 @@ build() {
|
||||
|
||||
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/${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
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* 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.2.4
|
||||
* Version: 0.3.1
|
||||
* Requires at least: 6.4
|
||||
* Requires PHP: 8.1
|
||||
* Author: hanyixuanten
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
define( 'HTBD_VERSION', '0.2.4' );
|
||||
define( 'HTBD_VERSION', '0.3.1' );
|
||||
define( 'HTBD_FILE', __FILE__ );
|
||||
define( 'HTBD_PATH', plugin_dir_path( __FILE__ ) );
|
||||
|
||||
@@ -54,6 +54,6 @@ function htbd_add_plugin_row_meta( $plugin_meta, $plugin_file ) {
|
||||
return $plugin_meta;
|
||||
}
|
||||
|
||||
add_action( 'plugins_loaded', 'htbd_load_textdomain', 5 );
|
||||
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 );
|
||||
+165
-23
@@ -11,6 +11,8 @@ class HTBD_Admin {
|
||||
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' ) );
|
||||
@@ -19,6 +21,97 @@ class HTBD_Admin {
|
||||
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' ) );
|
||||
}
|
||||
@@ -59,10 +152,11 @@ class HTBD_Admin {
|
||||
}
|
||||
|
||||
public function sanitize_settings( $settings ) {
|
||||
$settings = (array) $settings;
|
||||
$bindings = array();
|
||||
$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' ) ) ) ) );
|
||||
$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 ) );
|
||||
@@ -74,14 +168,15 @@ class HTBD_Admin {
|
||||
}
|
||||
|
||||
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'] ),
|
||||
'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'] ),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -97,8 +192,9 @@ class HTBD_Admin {
|
||||
<form action="options.php" method="post">
|
||||
<?php settings_fields( 'htbd_settings' ); ?>
|
||||
<table class="form-table" role="presentation">
|
||||
<tr><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,ja.', 'hyx-translator-for-baidu-translate' ); ?></p></td></tr>
|
||||
<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>
|
||||
@@ -131,6 +227,38 @@ class HTBD_Admin {
|
||||
<?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>
|
||||
@@ -158,12 +286,19 @@ class HTBD_Admin {
|
||||
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();
|
||||
$languages = HTBD_Settings::target_languages();
|
||||
|
||||
if ( 'all' === $scope || 'posts' === $scope ) {
|
||||
$post_ids = get_posts(
|
||||
@@ -219,20 +354,27 @@ class HTBD_Admin {
|
||||
wp_die( esc_html__( 'You do not have permission to perform this action.', 'hyx-translator-for-baidu-translate' ) );
|
||||
}
|
||||
|
||||
check_admin_referer( 'htbd_clear_translation_cache' );
|
||||
$deleted = ( new HTBD_Translation_Store() )->clear();
|
||||
$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' ) );
|
||||
}
|
||||
|
||||
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' );
|
||||
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', 1, admin_url( 'options-general.php?page=htbd' ) ) );
|
||||
wp_safe_redirect( add_query_arg( 'htbd_cache_cleared', $target_language ? $target_language : 1, admin_url( 'options-general.php?page=htbd' ) ) );
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,10 +6,59 @@ 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() {
|
||||
@@ -92,17 +141,7 @@ class HTBD_Language_Router {
|
||||
}
|
||||
|
||||
if ( HTBD_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;
|
||||
}
|
||||
return $this->localized_domain_url( $url, $language );
|
||||
}
|
||||
|
||||
if ( ! HTBD_Settings::is_routing_mode_enabled( 'subdirectory' ) ) {
|
||||
@@ -129,6 +168,103 @@ class HTBD_Language_Router {
|
||||
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'];
|
||||
@@ -136,6 +272,13 @@ class HTBD_Language_Router {
|
||||
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'];
|
||||
|
||||
@@ -40,11 +40,12 @@ class HTBD_Plugin {
|
||||
add_option(
|
||||
'htbd_settings',
|
||||
array(
|
||||
'source_language' => 'zh',
|
||||
'target_languages' => array( 'en' ),
|
||||
'routing_mode' => array( 'subdirectory' ),
|
||||
'domain_bindings' => array(),
|
||||
'fallback_language' => 'zh',
|
||||
'auto_detect_source_language' => false,
|
||||
'source_language' => 'zh',
|
||||
'target_languages' => array( 'en' ),
|
||||
'routing_mode' => array( 'subdirectory' ),
|
||||
'domain_bindings' => array(),
|
||||
'fallback_language' => 'zh',
|
||||
),
|
||||
'',
|
||||
false
|
||||
|
||||
@@ -5,14 +5,15 @@ defined( 'ABSPATH' ) || exit;
|
||||
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,
|
||||
);
|
||||
|
||||
$stored_settings = get_option( 'htbd_settings', false );
|
||||
|
||||
@@ -48,7 +48,7 @@ class HTBD_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
|
||||
),
|
||||
@@ -78,9 +78,23 @@ class HTBD_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() )
|
||||
);
|
||||
@@ -94,7 +108,7 @@ class HTBD_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,
|
||||
@@ -194,7 +208,7 @@ class HTBD_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 )
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# This file is distributed under the same license as the hyx-translator-for-baidu-translate package.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: hyx-translator-for-baidu-translate 0.2.4\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"
|
||||
@@ -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 "网址模式"
|
||||
@@ -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 百度翻译多语言插件"
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: hyx-translator-for-baidu-translate 0.2.4\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"
|
||||
@@ -40,6 +40,12 @@ msgstr ""
|
||||
msgid "Failed translations"
|
||||
msgstr ""
|
||||
|
||||
msgid "Source language detection"
|
||||
msgstr ""
|
||||
|
||||
msgid "Automatically detect the source language"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-admin.php:95
|
||||
msgid "Source language"
|
||||
msgstr ""
|
||||
@@ -53,7 +59,7 @@ msgid "Target languages"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-admin.php:96
|
||||
msgid "Separate multiple language codes with commas, for example en,ja."
|
||||
msgid "Separate multiple language codes with commas, for example en,jp."
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-admin.php:97
|
||||
@@ -161,6 +167,11 @@ msgid ""
|
||||
"and reset progress. Continue?"
|
||||
msgstr ""
|
||||
|
||||
#. 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 ""
|
||||
|
||||
@@ -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,81 +0,0 @@
|
||||
=== HTBD - hyx Translator powered by Baidu Translate ===
|
||||
Contributors: hanyixuanten
|
||||
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.4
|
||||
License: GPL-3.0-only
|
||||
License URI: https://www.gnu.org/licenses/gpl-3.0.html
|
||||
|
||||
使用百度翻译 API 生成并持久化保存 WordPress 多语言内容。
|
||||
|
||||
== Description ==
|
||||
|
||||
HTBD 使用百度翻译 API 翻译 WordPress 内容,并将每个翻译结果持久化保存以供重复使用。插件支持语言子目录 URL 和语言域名绑定。
|
||||
|
||||
WordPress 后台界面会跟随当前 WordPress 语言:默认显示英文,WordPress 设为 `zh_CN` 时使用插件内置的简体中文翻译。
|
||||
|
||||
支持的内容包括已发布文章和页面的标题、正文、摘要、图片替代文本、分类、标签及部分 SEO 字段。插件只为 `publish` 状态的文章和页面自动安排翻译。完成的翻译保存在 `{$wpdb->prefix}hyx_bd_translations` 自建数据表中;启用改名后的插件时,已有 `{$wpdb->prefix}btranslate_translations` 数据会自动迁移。前端渲染只读取已保存的翻译,不会动态请求翻译服务。
|
||||
|
||||
子目录路由支持 `/en/example-post/` 等路径。域名路由可将配置的主机名映射到目标语言。翻译失败不会中断页面请求;没有有效译文时会回退显示源语言内容。
|
||||
|
||||
== Installation ==
|
||||
|
||||
1. 将 `hyx-translator-for-baidu-translate` 目录上传到 `/wp-content/plugins/`。
|
||||
2. 在 WordPress 的“插件”页面启用 HTBD。
|
||||
3. 打开“设置 > HTBD”,配置百度翻译凭据、源语言、目标语言和路由模式。
|
||||
4. 第一次保存设置后,手动执行“重新翻译所有内容”,为已有内容安排翻译任务。
|
||||
5. 此后保存文章或页面时,插件会自动安排翻译任务。
|
||||
|
||||
== External Services ==
|
||||
|
||||
HTBD 连接由百度提供的百度翻译开放平台 API,以翻译 WordPress 内容。没有该外部服务,插件无法生成新的翻译。站点管理员需要百度翻译账户、应用 ID 和密钥;百度可能根据管理员选择的服务方案实施请求配额或收取使用费用。
|
||||
|
||||
已发布文章或页面保存后、支持的分类或标签创建或编辑后,或者管理员明确安排或刷新翻译时,计划任务会请求该 API。前端页面访问只读取已保存的翻译,不会请求百度。已有有效持久化翻译时也不会请求百度,除非管理员明确执行重新翻译。
|
||||
|
||||
每次翻译请求会发送待翻译文本、配置的源语言和目标语言代码、百度应用 ID、随机盐值和请求签名。根据待翻译内容,文本可能包含已发布文章或页面的标题、正文、摘要、指定 SEO 标题和描述、附件图片替代文本,以及分类或标签的名称和描述。作为 HTTPS 请求的一部分,百度也能看到来源服务器 IP 等标准网络信息。密钥只在本地用于生成签名,不会作为独立请求字段发送。
|
||||
|
||||
HTBD 将百度返回的译文保存在 WordPress 数据库中,但不保存完整百度响应。发送给百度的数据受百度自身条款和隐私规则约束:
|
||||
|
||||
* 百度翻译 API 文档:https://fanyi-api.baidu.com/doc/23
|
||||
* 百度翻译开放平台服务协议:https://fanyi-api.baidu.com/doc/6
|
||||
* 百度隐私政策:https://privacy.baidu.com/policy
|
||||
|
||||
== Frequently Asked Questions ==
|
||||
|
||||
= 插件会在每次页面请求时翻译内容吗? =
|
||||
|
||||
不会。翻译由计划任务生成并持久化保存。前端请求只读取已保存的翻译结果。
|
||||
|
||||
= 插件会翻译文章或页面中的代码吗? =
|
||||
|
||||
不会。HTML `<pre>` 和 `<code>` 元素内的内容会保留源语言,不会发送翻译请求。
|
||||
|
||||
= 翻译失败后如何重试? =
|
||||
|
||||
失败的文章、页面、分类或标签会显示在设置页进度条下方。点击对应的“重新翻译”按钮可逐项重试。
|
||||
|
||||
= 支持哪些 URL 路由模式? =
|
||||
|
||||
支持语言子目录、域名绑定,或同时启用这两种模式。域名和 DNS 必须预先指向同一个 WordPress 站点。
|
||||
|
||||
= 卸载插件会删除哪些数据? =
|
||||
|
||||
卸载会删除插件设置、计划任务和自建翻译表,包括所有已持久化的翻译。停用插件不会删除这些数据。
|
||||
|
||||
== Changelog ==
|
||||
|
||||
= 0.2.4 =
|
||||
|
||||
* 修复单篇内容页面未按当前文章上下文显示翻译标题的问题。
|
||||
* 保留 HTML `<pre>` 和 `<code>` 元素内的内容,不再将其发送翻译。
|
||||
* 完善插件安全元数据和译者署名。
|
||||
* 更新插件主页 URL。
|
||||
|
||||
= 0.2.3 =
|
||||
|
||||
* 将插件及代码库重命名为 HTBD / hyx-translator-for-baidu-translate。
|
||||
* 自动迁移旧版自建数据表中的翻译数据。
|
||||
* 更新打包流程、本地化资源、文档和问题模板以适配新的插件标识。
|
||||
* 修复发布归档构建流程。
|
||||
+19
-4
@@ -4,7 +4,7 @@ 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.4
|
||||
Stable tag: 0.3.1
|
||||
License: GPL-3.0-only
|
||||
License URI: https://www.gnu.org/licenses/gpl-3.0.html
|
||||
|
||||
@@ -14,19 +14,21 @@ Persistent multilingual WordPress translations powered by the Baidu Translate AP
|
||||
|
||||
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 `{$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 `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.
|
||||
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 ==
|
||||
|
||||
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.
|
||||
@@ -61,6 +63,19 @@ Uninstalling removes plugin settings, scheduled tasks, and the custom translatio
|
||||
|
||||
== Changelog ==
|
||||
|
||||
= 0.3.1 =
|
||||
|
||||
* 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.
|
||||
|
||||
Reference in New Issue
Block a user