fix: links may encounter translate error
This commit is contained in:
@@ -33,6 +33,8 @@ Changing source text produces a new fingerprint and therefore schedules a new tr
|
||||
|
||||
Only the listed SEO meta fields are sent for translation. Arbitrary metadata, serialized data, credentials, and code are excluded.
|
||||
|
||||
When post content contains HTML links, the plugin preserves each link's `href` and attributes, translates its visible text as a separate value, and inserts that translated text back into the original link. Re-run a post translation after upgrading to regenerate previously translated content that contains links.
|
||||
|
||||
Category and tag names are translated when they are created, edited, or included in a full retranslation. On a translated site, term objects used by archives, navigation, and post metadata use the persisted translated name and description.
|
||||
|
||||
## Routing
|
||||
|
||||
@@ -81,7 +81,12 @@ class WPT_Content_Controller {
|
||||
|
||||
foreach ( $fields as $field => $value ) {
|
||||
if ( '' !== $value ) {
|
||||
$service->get_or_translate( $value, $settings['source_language'], $target_language, 'post:' . $post_id . ':' . $field, $force_refresh );
|
||||
$context = 'post:' . $post_id . ':' . $field;
|
||||
if ( 'post_content' === $field ) {
|
||||
( new WPT_Content_Translator( $service ) )->translate( $value, $settings['source_language'], $target_language, $context, $force_refresh );
|
||||
} else {
|
||||
$service->get_or_translate( $value, $settings['source_language'], $target_language, $context, $force_refresh );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,7 +147,37 @@ class WPT_Content_Controller {
|
||||
}
|
||||
|
||||
public function translate_content( $content ) {
|
||||
return $this->translated_value( $content, 'post:' . get_the_ID() . ':post_content' );
|
||||
$post_id = get_the_ID();
|
||||
if ( ! $post_id || false === stripos( $content, '<a' ) ) {
|
||||
return $this->translated_value( $content, 'post:' . $post_id . ':post_content' );
|
||||
}
|
||||
|
||||
$links = array();
|
||||
$template = preg_replace_callback(
|
||||
'#<a\b([^>]*)>(.*?)</a>#is',
|
||||
function ( $matches ) use ( &$links ) {
|
||||
$index = count( $links );
|
||||
$links[] = $matches;
|
||||
|
||||
return '[[WPT_LINK_' . $index . ']]';
|
||||
},
|
||||
$content
|
||||
);
|
||||
if ( null === $template ) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
$translated_template = $this->translated_value( $template, 'post:' . $post_id . ':post_content:template' );
|
||||
if ( $translated_template === $template ) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
foreach ( $links as $index => $link ) {
|
||||
$translated_text = $this->translated_value( $link[2], 'post:' . $post_id . ':post_content:link:' . $index );
|
||||
$translated_template = str_replace( '[[WPT_LINK_' . $index . ']]', '<a' . $link[1] . '>' . $translated_text . '</a>', $translated_template );
|
||||
}
|
||||
|
||||
return $translated_template;
|
||||
}
|
||||
|
||||
public function translate_excerpt( $excerpt, $post ) {
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class WPT_Content_Translator {
|
||||
private $service;
|
||||
|
||||
public function __construct( WPT_Translation_Service $service ) {
|
||||
$this->service = $service;
|
||||
}
|
||||
|
||||
public function translate( $content, $source_language, $target_language, $context, $force_refresh = false ) {
|
||||
$links = array();
|
||||
$template = preg_replace_callback(
|
||||
'#<a\b([^>]*)>(.*?)</a>#is',
|
||||
function ( $matches ) use ( &$links ) {
|
||||
$index = count( $links );
|
||||
$links[ $index ] = array(
|
||||
'attributes' => $matches[1],
|
||||
'text' => $matches[2],
|
||||
);
|
||||
|
||||
return '[[WPT_LINK_' . $index . ']]';
|
||||
},
|
||||
$content
|
||||
);
|
||||
|
||||
if ( null === $template ) {
|
||||
return WPT_Translation_Result::failure( 'content_parse_failed', 'Unable to protect links before translation.' );
|
||||
}
|
||||
|
||||
$template_context = empty( $links ) ? $context : $context . ':template';
|
||||
$template_result = $this->service->get_or_translate( $template, $source_language, $target_language, $template_context, $force_refresh );
|
||||
|
||||
if ( ! $template_result->success || empty( $links ) ) {
|
||||
return $template_result;
|
||||
}
|
||||
|
||||
$translated_content = $template_result->value;
|
||||
|
||||
foreach ( $links as $index => $link ) {
|
||||
$link_result = $this->service->get_or_translate(
|
||||
$link['text'],
|
||||
$source_language,
|
||||
$target_language,
|
||||
$context . ':link:' . $index,
|
||||
$force_refresh
|
||||
);
|
||||
|
||||
if ( ! $link_result->success ) {
|
||||
return $link_result;
|
||||
}
|
||||
|
||||
$anchor = '<a' . $link['attributes'] . '>' . $link_result->value . '</a>';
|
||||
$translated_content = str_replace( '[[WPT_LINK_' . $index . ']]', $anchor, $translated_content );
|
||||
}
|
||||
|
||||
return WPT_Translation_Result::success( $translated_content );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class ContentTranslatorTest extends TestCase {
|
||||
public function test_translates_link_text_separately_and_preserves_link_attributes() {
|
||||
$store = new WPT_Test_Translation_Store();
|
||||
$provider = new WPT_Test_Translation_Provider();
|
||||
$service = new WPT_Translation_Service( $store, $provider );
|
||||
$translator = new WPT_Content_Translator( $service );
|
||||
|
||||
$result = $translator->translate( 'Read <a href="https://example.com/docs" target="_blank">our guide</a>.', 'en', 'zh', 'post:1:post_content' );
|
||||
|
||||
$this->assertTrue( $result->success );
|
||||
$this->assertSame( '[zh] Read <a href="https://example.com/docs" target="_blank">[zh] our guide</a>.', $result->value );
|
||||
$this->assertSame( 2, $provider->calls );
|
||||
$this->assertSame( 'post:1:post_content:template', $store->save_calls[0]['field_context'] );
|
||||
$this->assertSame( 'post:1:post_content:link:0', $store->save_calls[1]['field_context'] );
|
||||
}
|
||||
}
|
||||
@@ -7,4 +7,5 @@ require_once dirname( __DIR__ ) . '/includes/class-wpt-translation-result.php';
|
||||
require_once dirname( __DIR__ ) . '/includes/class-wpt-translation-identity.php';
|
||||
require_once dirname( __DIR__ ) . '/includes/class-wpt-translation-store.php';
|
||||
require_once dirname( __DIR__ ) . '/includes/class-wpt-translation-service.php';
|
||||
require_once dirname( __DIR__ ) . '/includes/class-wpt-content-translator.php';
|
||||
require_once dirname( __DIR__ ) . '/includes/class-wpt-settings.php';
|
||||
@@ -21,6 +21,7 @@ require_once WPT_PATH . 'includes/class-wpt-translation-identity.php';
|
||||
require_once WPT_PATH . 'includes/class-wpt-translation-store.php';
|
||||
require_once WPT_PATH . 'includes/class-wpt-baidu-provider.php';
|
||||
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-content-controller.php';
|
||||
|
||||
Reference in New Issue
Block a user