wenpai.org/wp-content/plugins/wporg-gp-translation-suggestions/inc/class-translation-memory-client.php
2024-06-21 23:57:39 +08:00

225 lines
5.7 KiB
PHP

<?php
namespace WordPressdotorg\GlotPress\TranslationSuggestions;
use GP;
use Platform\Chinese_Format\Chinese_Format;
use Platform\Logger\Logger;
use Text_Diff;
use WP_Error;
use WP_Http;
use WP_Text_Diff_Renderer_inline;
use function Platform\Helper\is_chinese;
require_once ABSPATH . '/wp-includes/wp-diff.php';
class Translation_Memory_Client {
const API_ENDPOINT = 'http://127.0.0.1:9200/translate_memory/_search';
const API_BULK_ENDPOINT = 'http://127.0.0.1:9200/translate_memory/_bulk';
/**
* 更新翻译记忆库内容
*
* 这里将记忆库后端由WordPress.com更改为本地服务器上的ES
*
* @param array $translations List of translation IDs, keyed by original ID.
*
* @return bool|WP_Error True on success, WP_Error on failure.
*/
public static function update( array $translations ): bool|WP_Error {
$requests = [];
foreach ( $translations as $original_id => $translation_id ) {
$translation = GP::$translation->get( $translation_id );
// Check again in case the translation was changed.
if ( 'current' !== $translation->status ) {
continue;
}
$original = GP::$original->get( $original_id );
$source = $original->fields()['singular'] ?? '';
if ( empty( $source ) || empty( $translation->translation_0 ) || ! is_chinese( $translation->translation_0 ) ) {
continue;
}
$source = strtolower( trim( $source ) );
$format = Chinese_Format::get_instance();
$target = trim( $translation->translation_0 );
$target = $format->convert( $target );
$id = md5(
$source
. '|'
. $target
);
$requests[] = wp_json_encode( array(
'index' => array(
'_id' => $id,
),
) );
$requests[] = wp_json_encode( array(
'id' => $id,
'source' => $source,
'target' => $target,
) );
}
if ( empty( $requests ) ) {
return new WP_Error( '尝试导入空翻译' );
}
$body = join( PHP_EOL, $requests );
$body .= PHP_EOL;
$request = wp_remote_post(
self::API_BULK_ENDPOINT,
[
'timeout' => 10,
'headers' => array(
'Content-Type' => 'application/x-ndjson',
),
'body' => $body,
]
);
if ( is_wp_error( $request ) ) {
return $request;
}
if ( WP_Http::OK !== wp_remote_retrieve_response_code( $request ) ) {
Logger::error( Logger::TRANSLATE, '导入记忆库未返回正确的状态码', [ 'body' => wp_remote_retrieve_body( $request ) ] );
return new WP_Error( '导入记忆库未返回正确的状态码' );
}
$body = wp_remote_retrieve_body( $request );
$result = json_decode( $body, true );
if ( JSON_ERROR_NONE !== json_last_error() ) {
Logger::error( Logger::TRANSLATE, '导入解析记忆库返回失败', [ 'body' => wp_remote_retrieve_body( $request ) ] );
return new WP_Error( '导入解析记忆库返回失败' );
}
if ( isset( $result['errors'] ) && $result['errors'] ) {
Logger::error( Logger::TRANSLATE, '导入记忆库失败', [ 'body' => wp_remote_retrieve_body( $request ) ] );
return new WP_Error( '导入记忆库失败' );
}
return true;
}
/**
* Queries translation memory for a string.
*
* @param string $text Text to search translations for.
* @param string $target_locale Locale to search in.
*
* @return array|WP_Error List of suggestions on success, WP_Error on failure.
*/
public static function query( string $text, string $target_locale = 'zh_CN' ): WP_Error|array {
$body = array(
'query' => array(
'bool' => array(
'should' => array(
array(
'match' => array(
'source' => array(
'query' => $text,
'boost' => 2,
),
),
),
),
),
),
'sort' => array(
array(
'_score' => 'desc',
),
),
'size' => 10,
);
$body = wp_json_encode( $body );
$request = wp_remote_post(
self::API_ENDPOINT,
[
'timeout' => 10,
'headers' => array(
'Content-Type' => 'application/json',
),
'body' => $body,
]
);
if ( is_wp_error( $request ) ) {
return $request;
}
if ( WP_Http::OK !== wp_remote_retrieve_response_code( $request ) ) {
Logger::error( Logger::TRANSLATE, '记忆库未返回正确的状态码', [ 'body' => wp_remote_retrieve_body( $request ) ] );
return new WP_Error( '记忆库未返回正确的状态码' );
}
$body = wp_remote_retrieve_body( $request );
$result = json_decode( $body, true );
if ( JSON_ERROR_NONE !== json_last_error() ) {
Logger::error( Logger::TRANSLATE, '解析记忆库返回失败', [ 'body' => wp_remote_retrieve_body( $request ) ] );
return new WP_Error( '解析记忆库返回失败' );
}
if ( empty( $result ) ) {
return [];
}
$result = $result['hits']['hits'] ?? array();
$suggestions = [];
foreach ( $result as $match ) {
$source = $match['_source']['source'] ?? '';
$translation = $match['_source']['target'] ?? '';
$text = strtolower( trim( $text ) );
$source = strtolower( trim( $source ) );
similar_text( $source, $text, $similarity_score );
if ( $similarity_score < 70 ) {
continue;
}
$suggestions[] = [
'similarity_score' => $similarity_score,
'source' => $source,
'translation' => $translation,
'diff' => ( 100 == $similarity_score ) ? null : self::diff( $text, $source ),
];
}
return $suggestions;
}
/**
* Generates the differences between two sequences of strings.
*
* @param string $previous_text Previous text.
* @param string $text New text.
*
* @return string HTML markup for the differences between the two texts.
*/
protected static function diff( string $previous_text, string $text ): string {
$diff = new Text_Diff( 'auto', [ [ $previous_text ], [ $text ] ] );
$renderer = new WP_Text_Diff_Renderer_inline();
return $renderer->render( $diff );
}
}