feat: domain mode and dir mode can be used together

This commit is contained in:
hanyixuanten
2026-07-27 19:43:50 +08:00
parent 177fc13282
commit b34aae160a
7 changed files with 75 additions and 33 deletions
+1 -1
View File
@@ -43,7 +43,7 @@ Category and tag names are translated when they are created, edited, or included
## Routing
The **Subdirectory** mode resolves configured language paths such as `/en/example-post/`. The **Domain binding** mode maps a request host to a language using one `domain=language` entry per line in Settings. Use one canonical strategy for each language to avoid duplicate URLs. In domain mode, the plugin does not register front-end rewrite rules, so the primary site URL is not affected.
The **Subdirectory** mode resolves configured language paths such as `/en/example-post/`. The **Domain binding** mode maps a request host to a language using one `domain=language` entry per line in Settings. Both modes can be enabled together: a language with a domain binding uses that domain, while an unbound target language uses its subdirectory. This keeps one URL strategy per language and avoids duplicate URLs. When only subdirectory mode is enabled, the domain-binding field is hidden.
Post, page, term, and home links are generated for the active language. For example, a theme's "back to home" link that uses `home_url()` resolves to the bound language domain or language subdirectory.
+1 -1
View File
@@ -42,7 +42,7 @@ Btranslate 是一个 WordPress 插件骨架,使用百度翻译 API 持久化
## 路由
**子目录** 模式会解析配置语言对应的路径,例如 `/en/example-post/`。**域名绑定** 模式通过主机名映射语言:在设置中每行填写一条 `domain=language`每种语言应只使用一种规范 URL 策略,以避免重复内容。在域名模式下,插件不会注册前端重写规则,因此不会影响主站 URL
**子目录** 模式会解析配置语言对应的路径,例如 `/en/example-post/`。**域名** 模式通过主机名映射语言:在设置中每行填写一条 `domain=language`两种模式可以同时启用:已绑定域名的语言使用该域名,未绑定域名的目标语言使用子目录,从而确保每种语言只有一种 URL 策略避免重复内容。只启用子目录模式时,域名绑定字段不会显示
文章、页面、术语和首页链接会按当前语言生成。例如,主题中使用 `home_url()` 的“返回首页”链接会解析为已绑定语言的域名或语言子目录。
+16 -3
View File
@@ -28,6 +28,7 @@ class BTRANSLATE_Admin {
public function sanitize_settings( $settings ) {
$settings = (array) $settings;
$bindings = array();
$routing_modes = BTRANSLATE_Settings::routing_modes( $settings['routing_mode'] ?? array() );
foreach ( preg_split( '/\r\n|\r|\n/', (string) ( $settings['domain_bindings'] ?? '' ) ) as $line ) {
$parts = array_map( 'trim', explode( '=', $line, 2 ) );
@@ -41,7 +42,7 @@ class BTRANSLATE_Admin {
return array(
'source_language' => sanitize_key( $settings['source_language'] ?? 'zh' ),
'target_languages' => array_values( array_filter( array_map( 'sanitize_key', explode( ',', (string) ( $settings['target_languages'] ?? 'en' ) ) ) ) ),
'routing_mode' => in_array( $settings['routing_mode'] ?? '', array( 'subdirectory', 'domain' ), true ) ? $settings['routing_mode'] : 'subdirectory',
'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'] ?? '' ),
@@ -64,14 +65,26 @@ class BTRANSLATE_Admin {
<table class="form-table" role="presentation">
<tr><th scope="row"><label for="btranslate-source-language">源语言</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">使用百度翻译语言代码,例如 <code>zh</code>。</p></td></tr>
<tr><th scope="row"><label for="btranslate-target-languages">目标语言</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">多个语言代码用英文逗号分隔,例如 <code>en,ja</code>。</p></td></tr>
<tr><th scope="row"><label for="btranslate-routing-mode">网址模式</label></th><td><select id="btranslate-routing-mode" name="btranslate_settings[routing_mode]"><option value="subdirectory" <?php selected( $settings['routing_mode'], 'subdirectory' ); ?>>子目录</option><option value="domain" <?php selected( $settings['routing_mode'], 'domain' ); ?>>绑定域名</option></select></td></tr>
<tr><th scope="row"><label for="btranslate-domain-bindings">域名绑定</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">每行一个 <code>域名=语言代码</code>,例如 <code>en.example.com=en</code>。请勿填写 <code>https://</code>、路径或端口。</p></td></tr>
<tr><th scope="row">网址模式</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 ) ); ?> /> 子目录</label><br /><label><input type="checkbox" name="btranslate_settings[routing_mode][]" value="domain" <?php checked( in_array( 'domain', $settings['routing_mode'], true ) ); ?> /> 子域名</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">域名绑定</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">每行一个 <code>域名=语言代码</code>,例如 <code>en.example.com=en</code>。请勿填写 <code>https://</code>、路径或端口。</p></td></tr>
<tr><th scope="row"><label for="btranslate-baidu-app-id">百度应用 ID</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">百度密钥</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">请求日志</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'] ); ?> /> 记录每次百度翻译请求</label><p class="description">日志写入 PHP 错误日志,包含语言、字段、文本指纹、长度和结果状态;不会记录密钥、原文、译文或完整 API 响应。</p></td></tr>
</table>
<?php submit_button(); ?>
</form>
<script>
(function () {
var domainMode = document.querySelector('#btranslate-routing-mode input[value="domain"]');
var bindingsRow = document.getElementById('btranslate-domain-bindings-row');
function toggleDomainBindings() {
bindingsRow.style.display = domainMode.checked ? '' : 'none';
}
domainMode.addEventListener('change', toggleDomainBindings);
}());
</script>
<hr />
<h2>重新翻译所有内容</h2>
<p>此操作会将所有已发布文章、页面、分类和标签重新加入翻译队列。</p>
+14 -12
View File
@@ -11,11 +11,11 @@ class BTRANSLATE_Language_Router {
}
public function register_rewrite_rules() {
if ( 'subdirectory' !== BTRANSLATE_Settings::get()['routing_mode'] ) {
if ( ! BTRANSLATE_Settings::is_routing_mode_enabled( 'subdirectory' ) ) {
return;
}
$language_pattern = implode( '|', array_map( 'preg_quote', array_map( 'strtolower', BTRANSLATE_Settings::target_languages() ) ) );
$language_pattern = implode( '|', array_map( 'preg_quote', array_map( 'strtolower', BTRANSLATE_Settings::subdirectory_languages() ) ) );
if ( '' === $language_pattern ) {
return;
@@ -38,7 +38,7 @@ class BTRANSLATE_Language_Router {
$host = isset( $_SERVER['HTTP_HOST'] ) ? BTRANSLATE_Settings::normalize_domain( wp_unslash( $_SERVER['HTTP_HOST'] ) ) : '';
$bindings = (array) $settings['domain_bindings'];
if ( 'domain' === $settings['routing_mode'] && '' !== $host && isset( $bindings[ $host ] ) && BTRANSLATE_Settings::is_supported_language( $bindings[ $host ] ) ) {
if ( BTRANSLATE_Settings::is_routing_mode_enabled( 'domain' ) && '' !== $host && isset( $bindings[ $host ] ) && BTRANSLATE_Settings::is_supported_language( $bindings[ $host ] ) ) {
$wp->query_vars['btranslate_language'] = sanitize_key( $bindings[ $host ] );
}
}
@@ -62,20 +62,22 @@ class BTRANSLATE_Language_Router {
return $url;
}
if ( 'domain' === $settings['routing_mode'] ) {
if ( BTRANSLATE_Settings::is_routing_mode_enabled( 'domain' ) ) {
$domain = array_search( $language, (array) $settings['domain_bindings'], true );
$parts = wp_parse_url( $url );
if ( false === $domain || ! is_array( $parts ) || empty( $parts['host'] ) ) {
return $url;
if ( false !== $domain && is_array( $parts ) && ! empty( $parts['host'] ) ) {
$scheme = isset( $parts['scheme'] ) ? $parts['scheme'] : 'https';
$path = isset( $parts['path'] ) ? $parts['path'] : '/';
$query = isset( $parts['query'] ) ? '?' . $parts['query'] : '';
$fragment = isset( $parts['fragment'] ) ? '#' . $parts['fragment'] : '';
return $scheme . '://' . $domain . $path . $query . $fragment;
}
}
$scheme = isset( $parts['scheme'] ) ? $parts['scheme'] : 'https';
$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 );
+1 -1
View File
@@ -35,7 +35,7 @@ class BTRANSLATE_Plugin {
array(
'source_language' => 'zh',
'target_languages' => array( 'en' ),
'routing_mode' => 'subdirectory',
'routing_mode' => array( 'subdirectory' ),
'domain_bindings' => array(),
'fallback_language' => 'zh',
),
+29 -2
View File
@@ -7,7 +7,7 @@ class BTRANSLATE_Settings {
$defaults = array(
'source_language' => 'zh',
'target_languages' => array( 'en' ),
'routing_mode' => 'subdirectory',
'routing_mode' => array( 'subdirectory' ),
'domain_bindings' => array(),
'fallback_language' => 'zh',
'baidu_app_id' => '',
@@ -15,13 +15,40 @@ class BTRANSLATE_Settings {
'log_requests' => false,
);
return wp_parse_args( (array) get_option( 'btranslate_settings', array() ), $defaults );
$settings = wp_parse_args( (array) get_option( 'btranslate_settings', array() ), $defaults );
$settings['routing_mode'] = self::routing_modes( $settings['routing_mode'] );
return $settings;
}
public static function routing_modes( $routing_mode = null ) {
if ( null === $routing_mode ) {
$routing_mode = self::get()['routing_mode'];
}
$modes = array_values( array_intersect( (array) $routing_mode, array( 'subdirectory', 'domain' ) ) );
return empty( $modes ) ? array( 'subdirectory' ) : $modes;
}
public static function is_routing_mode_enabled( $mode ) {
return in_array( $mode, self::routing_modes(), true );
}
public static function target_languages() {
return array_values( array_filter( array_map( 'sanitize_key', (array) self::get()['target_languages'] ) ) );
}
public static function subdirectory_languages() {
$languages = self::target_languages();
if ( self::is_routing_mode_enabled( 'domain' ) ) {
$languages = array_diff( $languages, array_values( (array) self::get()['domain_bindings'] ) );
}
return array_values( $languages );
}
public static function is_supported_language( $language ) {
$settings = self::get();
$languages = array_merge( array( sanitize_key( $settings['source_language'] ) ), self::target_languages() );
@@ -79,27 +79,27 @@ class BTRANSLATE_Sitemap_Controller {
$path = '/' . ltrim( (string) $path, '/' );
}
if ( 'subdirectory' === $settings['routing_mode'] ) {
foreach ( BTRANSLATE_Settings::target_languages() as $language ) {
if ( BTRANSLATE_Settings::is_routing_mode_enabled( 'domain' ) && '/sitemap.xml' === untrailingslashit( $path ) ) {
$host = isset( $_SERVER['HTTP_HOST'] ) ? BTRANSLATE_Settings::normalize_domain( 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 ) ) {
return $language;
}
}
if ( BTRANSLATE_Settings::is_routing_mode_enabled( 'subdirectory' ) ) {
foreach ( BTRANSLATE_Settings::subdirectory_languages() as $language ) {
$language = sanitize_key( $language );
if ( '/' . $language . '/sitemap.xml' === untrailingslashit( $path ) ) {
return $language;
}
}
return '';
}
if ( '/sitemap.xml' !== untrailingslashit( $path ) ) {
return '';
}
$host = isset( $_SERVER['HTTP_HOST'] ) ? BTRANSLATE_Settings::normalize_domain( wp_unslash( $_SERVER['HTTP_HOST'] ) ) : '';
$bindings = (array) $settings['domain_bindings'];
$language = isset( $bindings[ $host ] ) ? sanitize_key( $bindings[ $host ] ) : '';
return in_array( $language, BTRANSLATE_Settings::target_languages(), true ) ? $language : '';
return '';
}
private function source_sitemap_url() {