add sitemap rewrite
This commit is contained in:
@@ -49,7 +49,9 @@ Post, page, term, and home links are generated for the active language. For exam
|
||||
|
||||
For a bound subdomain, enter a value such as `en.example.com=en`. The server and DNS must route that hostname to the same WordPress installation before WordPress can resolve the language.
|
||||
|
||||
After changing routing settings, rewrite rules are refreshed. The initial implementation does not yet generate translated permalinks, canonical URLs, `hreflang` alternates, redirects, or sitemap entries.
|
||||
Sitemap requests made through a configured target-language subdirectory or bound domain are proxied to the same sitemap path on the source site. The plugin rewrites standard `<loc>` entries in both sitemap indexes and URL sets to the active language URL, so sitemap index links continue through the same language route. Source sitemap failures remain non-fatal and fall back to normal WordPress request handling. The source sitemap must be available from the WordPress `home` origin; upstream redirects are not followed.
|
||||
|
||||
After changing routing settings, rewrite rules are refreshed. The initial implementation does not yet generate translated permalinks, canonical URLs, `hreflang` alternates, or redirects.
|
||||
|
||||
## Security And Data Retention
|
||||
|
||||
|
||||
+3
-1
@@ -48,7 +48,9 @@ Btranslate 是一个 WordPress 插件骨架,使用百度翻译 API 持久化
|
||||
|
||||
对于已绑定的子域名,请填写类似 `en.example.com=en` 的值。服务器和 DNS 必须先将该主机名指向同一 WordPress 安装,WordPress 才能解析该语言。
|
||||
|
||||
路由设置变更后会刷新重写规则。初始实现尚不生成翻译固定链接、规范 URL、`hreflang` 替代链接、重定向或站点地图条目。
|
||||
通过已配置目标语言子目录或绑定域名发起的 sitemap 请求,会代理到源站相同路径的 sitemap。插件会改写 sitemap 索引和 URL 集中的标准 `<loc>` 条目,使索引内的子 sitemap 链接继续使用同一语言路由。源站 sitemap 获取失败时不会造成致命错误,而是交回 WordPress 按普通请求处理。源 sitemap 必须可从 WordPress `home` 配置的源站访问,且插件不会跟随上游重定向。
|
||||
|
||||
路由设置变更后会刷新重写规则。初始实现尚不生成翻译固定链接、规范 URL、`hreflang` 替代链接或重定向。
|
||||
|
||||
## 安全和数据保留
|
||||
|
||||
|
||||
@@ -5,11 +5,13 @@ defined( 'ABSPATH' ) || exit;
|
||||
class WPT_Plugin {
|
||||
private static $instance;
|
||||
private $router;
|
||||
private $sitemap_controller;
|
||||
private $content_controller;
|
||||
private $admin;
|
||||
|
||||
private function __construct() {
|
||||
$this->router = new WPT_Language_Router();
|
||||
$this->sitemap_controller = new WPT_Sitemap_Controller( $this->router );
|
||||
$this->content_controller = new WPT_Content_Controller( new WPT_Translation_Store(), $this->router );
|
||||
$this->admin = new WPT_Admin();
|
||||
|
||||
@@ -52,6 +54,7 @@ class WPT_Plugin {
|
||||
|
||||
public function register() {
|
||||
$this->router->register();
|
||||
$this->sitemap_controller->register();
|
||||
$this->content_controller->register();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class WPT_Sitemap_Controller {
|
||||
private $router;
|
||||
|
||||
public function __construct( WPT_Language_Router $router ) {
|
||||
$this->router = $router;
|
||||
}
|
||||
|
||||
public function register() {
|
||||
add_action( 'template_redirect', array( $this, 'maybe_serve_localized_sitemap' ), 0 );
|
||||
}
|
||||
|
||||
public function maybe_serve_localized_sitemap() {
|
||||
$settings = WPT_Settings::get();
|
||||
$language = $this->router->current_language();
|
||||
|
||||
if ( $language === sanitize_key( $settings['source_language'] ) || ! in_array( $language, WPT_Settings::target_languages(), true ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$request_path = $this->request_path();
|
||||
if ( ! $this->is_sitemap_path( $request_path ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$source_url = $this->source_sitemap_url( $request_path, $language, $settings['routing_mode'] );
|
||||
if ( '' === $source_url ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$response = wp_remote_get(
|
||||
$source_url,
|
||||
array(
|
||||
'timeout' => 10,
|
||||
'redirection' => 0,
|
||||
'headers' => array(
|
||||
'Accept' => 'application/xml, text/xml;q=0.9',
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$xml = $this->rewrite_sitemap( wp_remote_retrieve_body( $response ) );
|
||||
if ( '' === $xml ) {
|
||||
return;
|
||||
}
|
||||
|
||||
status_header( 200 );
|
||||
header( 'Content-Type: application/xml; charset=UTF-8' );
|
||||
header( 'X-Robots-Tag: noindex, follow', true );
|
||||
echo $xml; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Serialized XML from DOMDocument.
|
||||
exit;
|
||||
}
|
||||
|
||||
private function request_path() {
|
||||
$request_uri = isset( $_SERVER['REQUEST_URI'] ) ? wp_unslash( $_SERVER['REQUEST_URI'] ) : '';
|
||||
$path = wp_parse_url( $request_uri, PHP_URL_PATH );
|
||||
|
||||
return is_string( $path ) ? '/' . ltrim( $path, '/' ) : '';
|
||||
}
|
||||
|
||||
private function is_sitemap_path( $path ) {
|
||||
$filename = basename( $path );
|
||||
|
||||
return 1 === preg_match( '/^[a-z0-9_-]*sitemap[a-z0-9_.-]*\.xml$/i', $filename );
|
||||
}
|
||||
|
||||
private function source_sitemap_url( $request_path, $language, $routing_mode ) {
|
||||
$home_url = (string) get_option( 'home', '' );
|
||||
$parts = wp_parse_url( $home_url );
|
||||
|
||||
if ( ! is_array( $parts ) || empty( $parts['scheme'] ) || empty( $parts['host'] ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if ( 'subdirectory' === $routing_mode ) {
|
||||
$request_path = preg_replace( '#^/' . preg_quote( $language, '#' ) . '(?=/|$)#i', '', $request_path, 1 );
|
||||
$request_path = '/' . ltrim( (string) $request_path, '/' );
|
||||
}
|
||||
|
||||
$port = isset( $parts['port'] ) ? ':' . $parts['port'] : '';
|
||||
|
||||
return $parts['scheme'] . '://' . $parts['host'] . $port . $request_path;
|
||||
}
|
||||
|
||||
private function rewrite_sitemap( $xml ) {
|
||||
if ( '' === trim( $xml ) || ! class_exists( 'DOMDocument' ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$document = new DOMDocument();
|
||||
$document->preserveWhiteSpace = true;
|
||||
$previous_errors = libxml_use_internal_errors( true );
|
||||
$loaded = $document->loadXML( $xml, LIBXML_NONET );
|
||||
libxml_clear_errors();
|
||||
libxml_use_internal_errors( $previous_errors );
|
||||
|
||||
if ( ! $loaded ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$xpath = new DOMXPath( $document );
|
||||
$nodes = $xpath->query( '/*[local-name()="urlset" or local-name()="sitemapindex"]/*[local-name()="url" or local-name()="sitemap"]/*[local-name()="loc"]' );
|
||||
|
||||
if ( false === $nodes ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
foreach ( $nodes as $node ) {
|
||||
$node->nodeValue = $this->router->localized_url( trim( $node->textContent ) );
|
||||
}
|
||||
|
||||
$result = $document->saveXML();
|
||||
|
||||
return false === $result ? '' : $result;
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@ require_once WPT_PATH . 'includes/class-wpt-translation-service.php';
|
||||
require_once WPT_PATH . 'includes/class-wpt-content-translator.php';
|
||||
require_once WPT_PATH . 'includes/class-wpt-settings.php';
|
||||
require_once WPT_PATH . 'includes/class-wpt-language-router.php';
|
||||
require_once WPT_PATH . 'includes/class-wpt-sitemap-controller.php';
|
||||
require_once WPT_PATH . 'includes/class-wpt-content-controller.php';
|
||||
require_once WPT_PATH . 'includes/class-wpt-admin.php';
|
||||
require_once WPT_PATH . 'includes/class-wpt-plugin.php';
|
||||
|
||||
Reference in New Issue
Block a user