simplifize the dir
This commit is contained in:
@@ -1,10 +1,7 @@
|
||||
/vendor/
|
||||
/node_modules/
|
||||
/.phpunit.result.cache
|
||||
/coverage/
|
||||
/build/
|
||||
/dist/
|
||||
/phpunit.xml
|
||||
/.intelephense/
|
||||
.env
|
||||
.env.*
|
||||
|
||||
@@ -45,5 +45,5 @@ Unless a user explicitly requests a refresh, each translatable value must be tra
|
||||
- Mock Baidu API calls in every test. Tests must not require live credentials or outbound network access.
|
||||
- Keep changes narrow and avoid unrelated refactors or formatting churn.
|
||||
- Plugin slug and PHP prefix: `wp-translate` and `wpt_`/`WPT_`. The current minimum supported PHP version is 8.1 and the current minimum WordPress version is 6.4.
|
||||
- Composer manages the development dependencies. Run `composer test` for isolated unit tests and `composer lint` for PHP syntax checks. WordPress API calls must be mocked in tests; tests must not require live credentials, external network access, or a WordPress database.
|
||||
- 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.
|
||||
|
||||
@@ -63,21 +63,13 @@ Uninstalling Btranslate permanently deletes its settings and credentials, retran
|
||||
|
||||
## Development
|
||||
|
||||
Install development dependencies with `composer install`. The project includes WordPress API stubs for Intelephense and PHPUnit tests that use fake providers and storage, so they do not need WordPress, a database, credentials, or network access.
|
||||
|
||||
Run tests with:
|
||||
|
||||
```sh
|
||||
composer test
|
||||
```
|
||||
|
||||
Validate PHP syntax with:
|
||||
The repository has no Composer or PHPUnit dependency. Validate PHP syntax with the PHP executable available on your system:
|
||||
|
||||
```sh
|
||||
find . -path './.git' -prune -o -type f -name '*.php' -print0 | xargs -0 -n1 php -l
|
||||
```
|
||||
|
||||
Before production use, add WordPress PHPUnit integration tests with mocked `wp_remote_post` responses. Required coverage includes language resolution, rewrite behavior, persistence reuse and invalidation, Baidu request signing and errors, content filtering, and fallback rendering.
|
||||
Before production use, test the plugin in a WordPress staging environment. Cover language resolution, rewrite behavior, persistence reuse and invalidation, Baidu request signing and errors, content filtering, and fallback rendering. Mock `wp_remote_post` responses for any automated tests so they do not need live credentials, a database, or outbound network access.
|
||||
|
||||
## Retranslation
|
||||
|
||||
|
||||
+2
-10
@@ -62,21 +62,13 @@ Btranslate 是一个 WordPress 插件骨架,使用百度翻译 API 持久化
|
||||
|
||||
## 开发
|
||||
|
||||
使用 `composer install` 安装开发依赖。项目包含用于 Intelephense 的 WordPress API 存根,以及使用伪造提供程序和存储的 PHPUnit 测试;因此测试不需要 WordPress、数据库、凭据或网络访问。
|
||||
|
||||
运行测试:
|
||||
|
||||
```sh
|
||||
composer test
|
||||
```
|
||||
|
||||
验证 PHP 语法:
|
||||
仓库不依赖 Composer 或 PHPUnit。使用系统中可用的 PHP 可执行文件验证 PHP 语法:
|
||||
|
||||
```sh
|
||||
find . -path './.git' -prune -o -type f -name '*.php' -print0 | xargs -0 -n1 php -l
|
||||
```
|
||||
|
||||
投入生产前,应添加 WordPress PHPUnit 集成测试,并模拟 `wp_remote_post` 响应。所需覆盖范围包括语言解析、重写行为、持久化复用与失效、百度请求签名和错误处理、内容过滤和回退渲染。
|
||||
投入生产前,应在 WordPress 预发布环境中测试插件,覆盖语言解析、重写行为、持久化复用与失效、百度请求签名和错误处理、内容过滤和回退渲染。任何自动化测试都应模拟 `wp_remote_post` 响应,避免依赖真实凭据、数据库或外部网络访问。
|
||||
|
||||
## 重新翻译
|
||||
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"name": "wp-btranslate/wp-btranslate",
|
||||
"description": "Persistent multilingual WordPress translations powered by Baidu Translate.",
|
||||
"type": "wordpress-plugin",
|
||||
"license": "proprietary",
|
||||
"require": {
|
||||
"php": "^8.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"php-stubs/wordpress-stubs": "^6.8",
|
||||
"phpunit/phpunit": "^12"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "find . -path './.git' -prune -o -path './vendor' -prune -o -type f -name '*.php' -print0 | xargs -0 -n1 php -l",
|
||||
"test": "phpunit"
|
||||
}
|
||||
}
|
||||
Generated
-1754
File diff suppressed because it is too large
Load Diff
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit bootstrap="tests/bootstrap.php" colors="true">
|
||||
<testsuites>
|
||||
<testsuite name="WP Translate">
|
||||
<directory suffix="Test.php">tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
</phpunit>
|
||||
@@ -1,30 +0,0 @@
|
||||
<?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( 'Read <a href="https://example.com/docs" target="_blank">our guide</a>.', $result->value );
|
||||
$this->assertSame( 3, $provider->calls );
|
||||
$this->assertSame( 'post:1:post_content:text:0', $store->save_calls[0]['field_context'] );
|
||||
$this->assertSame( 'post:1:post_content:text:1', $store->save_calls[1]['field_context'] );
|
||||
$this->assertSame( 'post:1:post_content:text:2', $store->save_calls[2]['field_context'] );
|
||||
}
|
||||
|
||||
public function test_splits_text_while_preserving_links_images_and_other_tags() {
|
||||
$segments = WPT_Content_Translator::segments( 'See <a href="/docs">docs</a><img src="/hero.jpg" alt="Hero"><strong>now</strong>.' );
|
||||
|
||||
$this->assertSame(
|
||||
array( 'See ', '<a href="/docs">', 'docs', '</a>', '<img src="/hero.jpg" alt="Hero">', '<strong>', 'now', '</strong>', '.' ),
|
||||
$segments
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class WPT_Test_Language_Router extends WPT_Language_Router {
|
||||
public function current_language() {
|
||||
return 'en';
|
||||
}
|
||||
}
|
||||
|
||||
class DocumentTitleTest extends TestCase {
|
||||
public function test_document_title_callback_leaves_non_singular_title_parts_unchanged() {
|
||||
$controller = new WPT_Content_Controller( new WPT_Test_Translation_Store(), new WPT_Test_Language_Router() );
|
||||
$parts = array( 'title' => '中文主页', 'site' => '示例站点' );
|
||||
|
||||
$this->assertSame( $parts, $controller->translate_document_title_parts( $parts ) );
|
||||
}
|
||||
|
||||
public function test_wordpress_ai_meta_description_metadata_uses_persisted_translation() {
|
||||
$store = new WPT_Test_Translation_Store();
|
||||
$description = 'Original description';
|
||||
$identity_key = WPT_Translation_Identity::key( $description, 'zh', 'en', 'post:42:meta:wpai_meta_description', 'baidu-vip-v1' );
|
||||
$store->values[ $identity_key ] = array( 'translated_value' => 'Translated description' );
|
||||
$controller = new WPT_Content_Controller( $store, new WPT_Test_Language_Router() );
|
||||
global $wpt_test_post_meta;
|
||||
$wpt_test_post_meta[42]['wpai_meta_description'] = $description;
|
||||
|
||||
$this->assertSame(
|
||||
'Translated description',
|
||||
$controller->translate_wordpress_ai_meta_description_metadata( null, 42, 'wpai_meta_description', true )
|
||||
);
|
||||
$this->assertNull( $controller->translate_wordpress_ai_meta_description_metadata( null, 42, 'wpai_meta_description', false ) );
|
||||
$this->assertSame( $description, $controller->translate_wordpress_ai_meta_description_metadata( $description, 42, '_unrelated_meta', true ) );
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class SettingsTest extends TestCase {
|
||||
public function test_normalize_domain_accepts_a_subdomain_and_removes_protocol_port_and_path() {
|
||||
$this->assertSame( 'en.example.com', WPT_Settings::normalize_domain( 'https://EN.Example.com:8443/news' ) );
|
||||
}
|
||||
|
||||
public function test_normalize_domain_rejects_a_domain_without_a_public_suffix() {
|
||||
$this->assertSame( '', WPT_Settings::normalize_domain( 'invalid-domain' ) );
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class TranslationIdentityTest extends TestCase {
|
||||
public function test_key_is_stable_for_identical_translation_input() {
|
||||
$first = WPT_Translation_Identity::key( 'Hello', 'en', 'zh', 'post:15:post_title', 'provider-v1' );
|
||||
$second = WPT_Translation_Identity::key( 'Hello', 'en', 'zh', 'post:15:post_title', 'provider-v1' );
|
||||
|
||||
$this->assertSame( $first, $second );
|
||||
}
|
||||
|
||||
public function test_key_changes_when_source_value_changes() {
|
||||
$first = WPT_Translation_Identity::key( 'Hello', 'en', 'zh', 'post:15:post_title', 'provider-v1' );
|
||||
$second = WPT_Translation_Identity::key( 'Hello again', 'en', 'zh', 'post:15:post_title', 'provider-v1' );
|
||||
|
||||
$this->assertNotSame( $first, $second );
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class WPT_Test_Translation_Store extends WPT_Translation_Store {
|
||||
public $values = array();
|
||||
public $save_calls = array();
|
||||
|
||||
public function find_valid( $identity_key ) {
|
||||
return $this->values[ $identity_key ] ?? null;
|
||||
}
|
||||
|
||||
public function save( $identity_key, $source_language, $target_language, $field_context, $source_fingerprint, $translated_value, $status ) {
|
||||
$this->save_calls[] = compact( 'identity_key', 'source_language', 'target_language', 'field_context', 'source_fingerprint', 'translated_value', 'status' );
|
||||
if ( 'complete' === $status ) {
|
||||
$this->values[ $identity_key ] = array( 'translated_value' => $translated_value );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class WPT_Test_Translation_Provider implements WPT_Translation_Provider {
|
||||
public $calls = 0;
|
||||
|
||||
public function get_version() {
|
||||
return 'test-provider-v1';
|
||||
}
|
||||
|
||||
public function translate( $source_value, $source_language, $target_language, $context ) {
|
||||
++$this->calls;
|
||||
|
||||
return WPT_Translation_Result::success( '[' . $target_language . '] ' . $source_value );
|
||||
}
|
||||
}
|
||||
|
||||
class TranslationServiceTest extends TestCase {
|
||||
public function test_second_request_reuses_persisted_translation_without_provider_call() {
|
||||
$store = new WPT_Test_Translation_Store();
|
||||
$provider = new WPT_Test_Translation_Provider();
|
||||
$service = new WPT_Translation_Service( $store, $provider );
|
||||
|
||||
$first = $service->get_or_translate( 'Welcome', 'en', 'zh', 'post:8:post_title' );
|
||||
$second = $service->get_or_translate( 'Welcome', 'en', 'zh', 'post:8:post_title' );
|
||||
|
||||
$this->assertTrue( $first->success );
|
||||
$this->assertSame( '[zh] Welcome', $second->value );
|
||||
$this->assertSame( 1, $provider->calls );
|
||||
$this->assertCount( 1, $store->save_calls );
|
||||
}
|
||||
|
||||
public function test_failed_request_is_persisted_with_failed_status() {
|
||||
$store = new WPT_Test_Translation_Store();
|
||||
$provider = new class implements WPT_Translation_Provider {
|
||||
public function get_version() {
|
||||
return 'test-provider-v1';
|
||||
}
|
||||
|
||||
public function translate( $source_value, $source_language, $target_language, $context ) {
|
||||
return WPT_Translation_Result::failure( 'request_failed', 'Network error' );
|
||||
}
|
||||
};
|
||||
$service = new WPT_Translation_Service( $store, $provider );
|
||||
|
||||
$result = $service->get_or_translate( 'Welcome', 'en', 'zh', 'post:8:post_title' );
|
||||
|
||||
$this->assertFalse( $result->success );
|
||||
$this->assertSame( 'failed', $store->save_calls[0]['status'] );
|
||||
}
|
||||
|
||||
public function test_forced_refresh_calls_the_provider_when_a_translation_is_already_persisted() {
|
||||
$store = new WPT_Test_Translation_Store();
|
||||
$provider = new WPT_Test_Translation_Provider();
|
||||
$service = new WPT_Translation_Service( $store, $provider );
|
||||
|
||||
$service->get_or_translate( 'Welcome', 'en', 'zh', 'post:8:post_title' );
|
||||
$service->get_or_translate( 'Welcome', 'en', 'zh', 'post:8:post_title', true );
|
||||
|
||||
$this->assertSame( 2, $provider->calls );
|
||||
$this->assertCount( 2, $store->save_calls );
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
<?php
|
||||
|
||||
define( 'ABSPATH', __DIR__ . '/' );
|
||||
|
||||
function is_singular() {
|
||||
return false;
|
||||
}
|
||||
|
||||
function get_option( $option, $default = false ) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
function wp_parse_args( $args, $defaults = array() ) {
|
||||
return array_merge( $defaults, $args );
|
||||
}
|
||||
|
||||
function get_metadata_raw( $meta_type, $object_id, $meta_key = '', $single = false ) {
|
||||
global $wpt_test_post_meta;
|
||||
|
||||
$value = $wpt_test_post_meta[ $object_id ][ $meta_key ] ?? null;
|
||||
|
||||
return $single ? $value : array( $value );
|
||||
}
|
||||
|
||||
require_once dirname( __DIR__ ) . '/includes/interface-wpt-translation-provider.php';
|
||||
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-baidu-provider.php';
|
||||
require_once dirname( __DIR__ ) . '/includes/class-wpt-content-translator.php';
|
||||
require_once dirname( __DIR__ ) . '/includes/class-wpt-settings.php';
|
||||
require_once dirname( __DIR__ ) . '/includes/class-wpt-language-router.php';
|
||||
require_once dirname( __DIR__ ) . '/includes/class-wpt-content-controller.php';
|
||||
Reference in New Issue
Block a user