Changeset 3451583
- Timestamp:
- 02/01/2026 08:20:52 PM (3 weeks ago)
- Location:
- ai-translate
- Files:
-
- 7 edited
- 42 copied
-
tags/2.2.4 (copied) (copied from ai-translate/trunk)
-
tags/2.2.4/README.md (copied) (copied from ai-translate/trunk/README.md) (2 diffs)
-
tags/2.2.4/ai-translate.php (copied) (copied from ai-translate/trunk/ai-translate.php) (1 diff)
-
tags/2.2.4/assets/admin-page.js (copied) (copied from ai-translate/trunk/assets/admin-page.js)
-
tags/2.2.4/docs/readme-ar.po (copied) (copied from ai-translate/trunk/docs/readme-ar.po)
-
tags/2.2.4/docs/readme-de_DE.po (copied) (copied from ai-translate/trunk/docs/readme-de_DE.po)
-
tags/2.2.4/docs/readme-es_ES.po (copied) (copied from ai-translate/trunk/docs/readme-es_ES.po)
-
tags/2.2.4/docs/readme-fr_FR.po (copied) (copied from ai-translate/trunk/docs/readme-fr_FR.po)
-
tags/2.2.4/docs/readme-hi_IN.po (copied) (copied from ai-translate/trunk/docs/readme-hi_IN.po)
-
tags/2.2.4/docs/readme-it_IT.po (copied) (copied from ai-translate/trunk/docs/readme-it_IT.po)
-
tags/2.2.4/docs/readme-ja_JP.po (copied) (copied from ai-translate/trunk/docs/readme-ja_JP.po) (1 diff)
-
tags/2.2.4/docs/readme-nl_NL.po (copied) (copied from ai-translate/trunk/docs/readme-nl_NL.po)
-
tags/2.2.4/docs/readme-pt_PT.po (copied) (copied from ai-translate/trunk/docs/readme-pt_PT.po) (1 diff)
-
tags/2.2.4/docs/readme-zh_CN.po (copied) (copied from ai-translate/trunk/docs/readme-zh_CN.po)
-
tags/2.2.4/includes/admin-page.php (copied) (copied from ai-translate/trunk/includes/admin-page.php) (2 diffs)
-
tags/2.2.4/includes/class-ai-batch.php (copied) (copied from ai-translate/trunk/includes/class-ai-batch.php) (9 diffs)
-
tags/2.2.4/includes/class-ai-ob.php (copied) (copied from ai-translate/trunk/includes/class-ai-ob.php)
-
tags/2.2.4/includes/class-ai-slugs.php (copied) (copied from ai-translate/trunk/includes/class-ai-slugs.php)
-
tags/2.2.4/includes/class-ai-translate-core.php (copied) (copied from ai-translate/trunk/includes/class-ai-translate-core.php) (12 diffs)
-
tags/2.2.4/languages/ai-translate-ar.mo (copied) (copied from ai-translate/trunk/languages/ai-translate-ar.mo)
-
tags/2.2.4/languages/ai-translate-ar.po (copied) (copied from ai-translate/trunk/languages/ai-translate-ar.po)
-
tags/2.2.4/languages/ai-translate-de_DE.mo (copied) (copied from ai-translate/trunk/languages/ai-translate-de_DE.mo)
-
tags/2.2.4/languages/ai-translate-de_DE.po (copied) (copied from ai-translate/trunk/languages/ai-translate-de_DE.po)
-
tags/2.2.4/languages/ai-translate-en_US.mo (copied) (copied from ai-translate/trunk/languages/ai-translate-en_US.mo)
-
tags/2.2.4/languages/ai-translate-en_US.po (copied) (copied from ai-translate/trunk/languages/ai-translate-en_US.po)
-
tags/2.2.4/languages/ai-translate-es_ES.mo (copied) (copied from ai-translate/trunk/languages/ai-translate-es_ES.mo)
-
tags/2.2.4/languages/ai-translate-es_ES.po (copied) (copied from ai-translate/trunk/languages/ai-translate-es_ES.po)
-
tags/2.2.4/languages/ai-translate-fr_FR.mo (copied) (copied from ai-translate/trunk/languages/ai-translate-fr_FR.mo)
-
tags/2.2.4/languages/ai-translate-fr_FR.po (copied) (copied from ai-translate/trunk/languages/ai-translate-fr_FR.po)
-
tags/2.2.4/languages/ai-translate-hi_IN.mo (copied) (copied from ai-translate/trunk/languages/ai-translate-hi_IN.mo)
-
tags/2.2.4/languages/ai-translate-hi_IN.po (copied) (copied from ai-translate/trunk/languages/ai-translate-hi_IN.po)
-
tags/2.2.4/languages/ai-translate-it_IT.mo (copied) (copied from ai-translate/trunk/languages/ai-translate-it_IT.mo)
-
tags/2.2.4/languages/ai-translate-it_IT.po (copied) (copied from ai-translate/trunk/languages/ai-translate-it_IT.po)
-
tags/2.2.4/languages/ai-translate-ja.mo (copied) (copied from ai-translate/trunk/languages/ai-translate-ja.mo)
-
tags/2.2.4/languages/ai-translate-ja.po (copied) (copied from ai-translate/trunk/languages/ai-translate-ja.po)
-
tags/2.2.4/languages/ai-translate-nl_NL.mo (copied) (copied from ai-translate/trunk/languages/ai-translate-nl_NL.mo)
-
tags/2.2.4/languages/ai-translate-nl_NL.po (copied) (copied from ai-translate/trunk/languages/ai-translate-nl_NL.po)
-
tags/2.2.4/languages/ai-translate-pt_PT.mo (copied) (copied from ai-translate/trunk/languages/ai-translate-pt_PT.mo)
-
tags/2.2.4/languages/ai-translate-pt_PT.po (copied) (copied from ai-translate/trunk/languages/ai-translate-pt_PT.po)
-
tags/2.2.4/languages/ai-translate-zh_CN.mo (copied) (copied from ai-translate/trunk/languages/ai-translate-zh_CN.mo)
-
tags/2.2.4/languages/ai-translate-zh_CN.po (copied) (copied from ai-translate/trunk/languages/ai-translate-zh_CN.po)
-
tags/2.2.4/languages/ai-translate.pot (copied) (copied from ai-translate/trunk/languages/ai-translate.pot)
-
trunk/README.md (modified) (2 diffs)
-
trunk/ai-translate.php (modified) (1 diff)
-
trunk/docs/readme-ja_JP.po (modified) (1 diff)
-
trunk/docs/readme-pt_PT.po (modified) (1 diff)
-
trunk/includes/admin-page.php (modified) (2 diffs)
-
trunk/includes/class-ai-batch.php (modified) (9 diffs)
-
trunk/includes/class-ai-translate-core.php (modified) (12 diffs)
Legend:
- Unmodified
- Added
- Removed
-
ai-translate/tags/2.2.4/README.md
r3451503 r3451583 1 1 === AI Translate === 2 2 Contributors: gkanters 3 Tags: translation, multilingual, woocommerce, seo, artificial intelligence , ai3 Tags: translation, multilingual, woocommerce, seo, artificial intelligence 4 4 Requires at least: 5.0 5 5 Tested up to: 6.9 6 Stable tag: 2.2. 36 Stable tag: 2.2.4 7 7 Requires PHP: 8.0 8 8 License: GPLv2 or later … … 184 184 ## Changelog 185 185 186 ### 2.2. 3186 ### 2.2.4 187 187 188 188 - Added cache warming for pages to admin. -
ai-translate/tags/2.2.4/ai-translate.php
r3451123 r3451583 6 6 * Author: NetCare 7 7 * Author URI: https://netcare.nl/ 8 * Version: 2.2. 38 * Version: 2.2.4 9 9 * Requires at least: 5.0 10 10 * Tested up to: 6.9 -
ai-translate/tags/2.2.4/docs/readme-ja_JP.po
r3451499 r3451583 26 26 msgstr "35言語で自動ウェブサイト翻訳を可能にするAI搭載プラグイン。トラフィックを増やし、SEOを改善します。" 27 27 28 #. Short Description29 msgid "AI-powered translation for WordPress & WooCommerce. Automatically translate your site in 35+ languages with your brand's unique tone of voice. Fast caching, SEO-friendly, and cost-effective."30 msgstr "WordPress & WooCommerce向けのAI翻訳。35以上の言語でサイトを自動翻訳し、ブランドの独自のトーンで表現。高速キャッシュ、SEO対応、コスト効率に優れています。"31 32 28 #. Description 33 29 msgid "AI Translate automatically makes your WordPress website available in 35+ languages. Increase your reach and improve your SEO without manual work." -
ai-translate/tags/2.2.4/docs/readme-pt_PT.po
r3451499 r3451583 26 26 msgstr "Plugin alimentado por IA para tradução automática de sites em 35 idiomas. Aumenta o tráfego e melhora o seu SEO." 27 27 28 #. Short Description29 msgid "AI-powered translation for WordPress & WooCommerce. Automatically translate your site in 35+ languages with your brand's unique tone of voice. Fast caching, SEO-friendly, and cost-effective."30 msgstr "Tradução com IA para WordPress e WooCommerce. Traduza automaticamente o seu site em mais de 35 idiomas com o tom de voz único da sua marca. Cache rápido, otimizado para SEO e económico."31 32 28 #. Description 33 29 msgid "AI Translate automatically makes your WordPress website available in 35+ languages. Increase your reach and improve your SEO without manual work." -
ai-translate/tags/2.2.4/includes/admin-page.php
r3451503 r3451583 2389 2389 // OpenRouter requires Referer header 2390 2390 if ($provider_key === 'openrouter' || ($provider_key === 'custom' && strpos($api_url, 'openrouter.ai') !== false)) { 2391 $headers[' Referer'] = home_url();2392 $headers['X-Title'] = get_bloginfo('name');2391 $headers['HTTP-Referer'] = 'https://github.com/gerard-kanters/ai-translate'; 2392 $headers['X-Title'] = 'AI Translate'; 2393 2393 } 2394 2394 $response = wp_remote_get($endpoint, [ … … 2420 2420 }, $data['data']); 2421 2421 $models = array_filter($models); 2422 // Block only o1/o3 (no reasoning_effort support); gpt-5 models allowed2423 $models = array_filter($models, function ($model) {2422 // o1/o3 reasoning models don't support temperature; block from selection to avoid API errors 2423 $models = array_filter($models, function ($model) { 2424 2424 return !preg_match('/^(o1-|o3-)/i', $model); 2425 2425 }); -
ai-translate/tags/2.2.4/includes/class-ai-batch.php
r3451123 r3451583 10 10 /** 11 11 * Translate a plan's segments using configured provider. 12 * GPT-5 models use reasoning_effort=minimal for fast translations.13 * Blocked: o1, o3 (no reasoning_effort parameter support).14 12 * 15 13 * @param array $plan … … 25 23 26 24 $timeLimit = (int) ini_get('max_execution_time'); 27 $elapsed = microtime(true) - ($_SERVER['REQUEST_TIME_FLOAT'] ?? microtime(true)); 25 $requestTime = isset($_SERVER['REQUEST_TIME_FLOAT']) ? $_SERVER['REQUEST_TIME_FLOAT'] : microtime(true); 26 $elapsed = microtime(true) - $requestTime; 28 27 $remaining = $timeLimit > 0 ? ($timeLimit - $elapsed) : 60; 29 28 if ($remaining < 15) { … … 37 36 $apiKeys = isset($settings['api_keys']) && is_array($settings['api_keys']) ? $settings['api_keys'] : []; 38 37 $apiKey = $provider !== '' ? ($apiKeys[$provider] ?? '') : ''; 39 40 // Block o1/o3 reasoning models: they don't support reasoning_effort parameter41 if ($model !== '' && preg_match('/^(o1-|o3-)/i', $model)) {42 return ['segments' => [], 'map' => []];43 }44 38 45 39 if (empty($segments)) { … … 204 198 ], 205 199 ]; 206 // Newer models (gpt-5.x, o1-series, o3-series) and DeepSeek v3.2 use max_completion_tokens instead of max_tokens 207 // These models also don't support temperature != 1, so we omit it 208 if (str_starts_with($model, 'gpt-5') || str_starts_with($model, 'o1-') || str_starts_with($model, 'o3-') || str_starts_with($model, 'deepseek/deepseek-v3')) { 209 $body['max_completion_tokens'] = 4096; 210 // GPT-5 models: use minimal reasoning for translations (fast, no extended thinking) 211 if (str_starts_with($model, 'gpt-5')) { 212 $body['reasoning_effort'] = 'minimal'; 213 } 214 } else { 215 $body['max_tokens'] = 4096; 216 $body['temperature'] = 0; 217 } 200 $body['temperature'] = 0; 218 201 $headers = [ 'Authorization' => 'Bearer ' . $apiKey, 'Content-Type' => 'application/json' ]; 219 202 // OpenRouter requires Referer header (WordPress adds HTTP- prefix automatically) … … 245 228 foreach ($batchesSingle as $i => $batchSegs2) { 246 229 $userPayload = self::buildUserPayload($batchSegs2); 247 $body = [ 'model' => $model, 'messages' => [ ['role' => 'system', 'content' => $system], ['role' => 'user', 'content' => $userPayload] ] ]; 248 if (str_starts_with($model, 'gpt-5') || str_starts_with($model, 'o1-') || str_starts_with($model, 'o3-') || str_starts_with($model, 'deepseek/deepseek-v3')) { 249 $body['max_completion_tokens'] = 4096; 250 // GPT-5 models: use minimal reasoning for translations (fast, no extended thinking) 251 if (str_starts_with($model, 'gpt-5')) { 252 $body['reasoning_effort'] = 'minimal'; 253 } 254 } else { 255 $body['max_tokens'] = 4096; 256 $body['temperature'] = 0; 257 } 230 $body = [ 'model' => $model, 'messages' => [ ['role' => 'system', 'content' => $system], ['role' => 'user', 'content' => $userPayload] ] ]; $body['temperature'] = 0; 258 231 $fallbackHeaders = [ 'Authorization' => 'Bearer ' . $apiKey, 'Content-Type' => 'application/json' ]; 259 232 if ($provider === 'custom' && isset($settings['custom_api_url']) && strpos($settings['custom_api_url'], 'openrouter.ai') !== false) { … … 460 433 foreach ($retryChunks as $rc) { 461 434 $userPayload = self::buildUserPayload($rc); 462 $body = [ 'model' => $model, 'messages' => [ ['role' => 'system', 'content' => $strictSystem], ['role' => 'user', 'content' => $userPayload] ] ]; 463 if (str_starts_with($model, 'gpt-5') || str_starts_with($model, 'o1-') || str_starts_with($model, 'o3-') || str_starts_with($model, 'deepseek/deepseek-v3')) { 464 $body['max_completion_tokens'] = 4096; 465 // GPT-5 models: use minimal reasoning for translations (fast, no extended thinking) 466 if (str_starts_with($model, 'gpt-5')) { 467 $body['reasoning_effort'] = 'minimal'; 468 } 469 } else { 470 $body['max_tokens'] = 4096; 471 $body['temperature'] = 0; 472 } 435 $body = [ 'model' => $model, 'messages' => [ ['role' => 'system', 'content' => $strictSystem], ['role' => 'user', 'content' => $userPayload] ] ]; 436 $body['temperature'] = 0; 473 437 $retryHeaders = [ 'Authorization' => 'Bearer ' . $apiKey, 'Content-Type' => 'application/json' ]; 474 438 if ($provider === 'custom' && isset($settings['custom_api_url']) && strpos($settings['custom_api_url'], 'openrouter.ai') !== false) { … … 556 520 ], 557 521 ]; 558 // Newer models (gpt-5.x, o1-series, o3-series) use max_completion_tokens instead of max_tokens 559 // DeepSeek v3.2 also uses max_completion_tokens 560 // These models also don't support temperature != 1, so we omit it 561 if (str_starts_with($model, 'gpt-5') || str_starts_with($model, 'o1-') || str_starts_with($model, 'o3-') || str_starts_with($model, 'deepseek/deepseek-v3')) { 562 $body['max_completion_tokens'] = 4096; 563 // GPT-5 models: use minimal reasoning for translations (fast, no extended thinking) 564 if (str_starts_with($model, 'gpt-5')) { 565 $body['reasoning_effort'] = 'minimal'; 566 } 567 } else { 568 $body['max_tokens'] = 4096; 522 if (!preg_match('/^(o1-|o3-)/i', $model)) { 569 523 $body['temperature'] = 0; 570 524 } … … 576 530 do { 577 531 $attempts++; 578 $timeRemaining = $timeLimit > 0 ? ($timeLimit - (microtime(true) - ($_SERVER['REQUEST_TIME_FLOAT'] ?? microtime(true)))) : 60; 532 $requestTime = isset($_SERVER['REQUEST_TIME_FLOAT']) ? $_SERVER['REQUEST_TIME_FLOAT'] : microtime(true); 533 $timeRemaining = $timeLimit > 0 ? ($timeLimit - (microtime(true) - $requestTime)) : 60; 579 534 $safeTimeout = min($timeoutSeconds, max(10, (int)($timeRemaining - 10))); 580 535 … … 723 678 $userPayload = self::buildUserPayload($rc); 724 679 $body = [ 'model' => $model, 'messages' => [ ['role' => 'system', 'content' => $strictSystem], ['role' => 'user', 'content' => $userPayload] ] ]; 725 if (str_starts_with($model, 'gpt-5') || str_starts_with($model, 'o1-') || str_starts_with($model, 'o3-') || str_starts_with($model, 'deepseek/deepseek-v3')) { 726 $body['max_completion_tokens'] = 4096; 727 // GPT-5 models: use minimal reasoning for translations (fast, no extended thinking) 728 if (str_starts_with($model, 'gpt-5')) { 729 $body['reasoning_effort'] = 'minimal'; 730 } 731 } else { 732 $body['max_tokens'] = 4096; 680 if (!preg_match('/^(o1-|o3-)/i', $model)) { 733 681 $body['temperature'] = 0; 734 682 } -
ai-translate/tags/2.2.4/includes/class-ai-translate-core.php
r3451499 r3451583 104 104 // OpenRouter requires Referer header 105 105 if ($provider_key === 'custom' && strpos($custom_api_url, 'openrouter.ai') !== false) { 106 $headers[' Referer'] = home_url();107 $headers['X-Title'] = get_bloginfo('name');106 $headers['HTTP-Referer'] = 'https://github.com/gerard-kanters/ai-translate'; 107 $headers['X-Title'] = 'AI Translate'; 108 108 } 109 109 $resp = wp_remote_get($endpoint, [ … … 153 153 // OpenRouter requires Referer header 154 154 if ($provider_key === 'custom' && strpos($custom_api_url, 'openrouter.ai') !== false) { 155 $chatHeaders[' Referer'] = home_url();156 $chatHeaders['X-Title'] = get_bloginfo('name');155 $chatHeaders['HTTP-Referer'] = 'https://github.com/gerard-kanters/ai-translate'; 156 $chatHeaders['X-Title'] = 'AI Translate'; 157 157 } 158 158 $chatBody = [ … … 162 162 ], 163 163 ]; 164 // Chat test: ruime limiet voor alle models inclusief reasoning models165 $chatBody['max_completion_tokens'] = 10000;166 164 $chatResp = wp_remote_post($chatEndpoint, [ 167 165 'headers' => $chatHeaders, … … 856 854 857 855 if (!empty($content)) { 858 if (defined('WP_DEBUG') && WP_DEBUG) {859 error_log('AI Translate: Fetched content from domain ' . $domain . ' (length: ' . mb_strlen($content) . ')');860 }861 856 return $content; 862 857 } … … 877 872 $content = ''; 878 873 $home_id = (int) get_option('page_on_front'); 879 $site_name = (string) get_bloginfo('name');874 $site_name = (string) 'AI Translate'; 880 875 881 876 if ($home_id > 0) { … … 989 984 } 990 985 } else { 991 $site_name = (string) get_bloginfo('name');986 $site_name = (string) 'AI Translate'; 992 987 } 993 988 … … 1013 1008 ], 1014 1009 ]; 1015 1016 // Token limits - ruime limiet voor alle models (je betaalt alleen wat je gebruikt)1017 $body['max_completion_tokens'] = 16000;1010 if ($provider === 'openrouter' || ($provider === 'custom' && strpos($baseUrl, 'openrouter.ai') !== false)) { 1011 $body['user'] = !empty($domain) ? $domain : parse_url(home_url(), PHP_URL_HOST); 1012 } 1018 1013 1019 1014 $headers = [ … … 1029 1024 $protocol = sanitize_text_field(wp_unslash($_SERVER['REQUEST_SCHEME'])); 1030 1025 } 1031 $headers[' Referer'] = $protocol . '://' . $domain . '/';1026 $headers['HTTP-Referer'] = 'https://github.com/gerard-kanters/ai-translate'; 1032 1027 } else { 1033 $headers[' Referer'] = home_url();1028 $headers['HTTP-Referer'] = 'https://github.com/gerard-kanters/ai-translate'; 1034 1029 } 1035 $headers['X-Title'] = $site_name;1030 $headers['X-Title'] = 'AI Translate'; 1036 1031 } 1037 1032 … … 1125 1120 $site_name = strtok($domain, ':'); 1126 1121 } 1127 1128 if (defined('WP_DEBUG') && WP_DEBUG) {1129 error_log('AI Translate: Using domain-specific site name for ' . $domain . ': ' . $site_name);1130 }1131 1122 } else { 1132 $site_name = (string) get_bloginfo('name');1123 $site_name = (string) 'AI Translate'; 1133 1124 } 1134 1125 … … 1159 1150 "Website Content:\n" . $content . $context_section; 1160 1151 1152 $metaModel = (stripos($model, 'gpt-5') !== false) ? 'gpt-4.1-mini' : $model; 1161 1153 $body = [ 1162 'model' => $m odel,1154 'model' => $metaModel, 1163 1155 'messages' => [ 1164 1156 ['role' => 'system', 'content' => 'You are an expert SEO copywriter specializing in writing compelling, keyword-rich meta descriptions that drive click-through rates. You understand that generic descriptions hurt SEO performance.'], … … 1166 1158 ], 1167 1159 ]; 1168 1169 // Token limits - ruime limiet voor alle models (je betaalt alleen wat je gebruikt) 1170 $body['max_completion_tokens'] = 16000; 1160 $m = strtolower((string) $model); 1161 if ($m !== '' && strpos($m, 'gpt-5') === false) { 1162 if (strpos($m, 'o1-') === 0 || strpos($m, 'o3-') === 0) { 1163 $body['thinking'] = false; 1164 } elseif (strpos($m, 'deepseek-r1') !== false || strpos($m, 'deepseek-reasoner') !== false) { 1165 $body['thinking'] = array('type' => 'disabled'); 1166 } 1167 } 1168 if ($provider === 'openrouter' || ($provider === 'custom' && strpos($baseUrl, 'openrouter.ai') !== false)) { 1169 $body['user'] = !empty($domain) ? $domain : parse_url(home_url(), PHP_URL_HOST); 1170 } 1171 1171 1172 1172 $headers = [ … … 1182 1182 $protocol = sanitize_text_field(wp_unslash($_SERVER['REQUEST_SCHEME'])); 1183 1183 } 1184 $headers[' Referer'] = $protocol . '://' . $domain . '/';1184 $headers['HTTP-Referer'] = 'https://github.com/gerard-kanters/ai-translate'; 1185 1185 } else { 1186 $headers[' Referer'] = home_url();1186 $headers['HTTP-Referer'] = 'https://github.com/gerard-kanters/ai-translate'; 1187 1187 } 1188 $headers['X-Title'] = $site_name; 1189 } 1190 1188 $headers['X-Title'] = 'AI Translate'; 1189 } 1190 1191 // Shorter timeout for meta descriptions (simple task; reasoning minimized above). 1191 1192 $response = wp_remote_post($endpoint, [ 1192 1193 'headers' => $headers, 1193 'timeout' => 45,1194 'timeout' => 60, 1194 1195 'sslverify' => true, 1195 1196 'body' => wp_json_encode($body), -
ai-translate/trunk/README.md
r3451503 r3451583 1 1 === AI Translate === 2 2 Contributors: gkanters 3 Tags: translation, multilingual, woocommerce, seo, artificial intelligence , ai3 Tags: translation, multilingual, woocommerce, seo, artificial intelligence 4 4 Requires at least: 5.0 5 5 Tested up to: 6.9 6 Stable tag: 2.2. 36 Stable tag: 2.2.4 7 7 Requires PHP: 8.0 8 8 License: GPLv2 or later … … 184 184 ## Changelog 185 185 186 ### 2.2. 3186 ### 2.2.4 187 187 188 188 - Added cache warming for pages to admin. -
ai-translate/trunk/ai-translate.php
r3451123 r3451583 6 6 * Author: NetCare 7 7 * Author URI: https://netcare.nl/ 8 * Version: 2.2. 38 * Version: 2.2.4 9 9 * Requires at least: 5.0 10 10 * Tested up to: 6.9 -
ai-translate/trunk/docs/readme-ja_JP.po
r3451499 r3451583 26 26 msgstr "35言語で自動ウェブサイト翻訳を可能にするAI搭載プラグイン。トラフィックを増やし、SEOを改善します。" 27 27 28 #. Short Description29 msgid "AI-powered translation for WordPress & WooCommerce. Automatically translate your site in 35+ languages with your brand's unique tone of voice. Fast caching, SEO-friendly, and cost-effective."30 msgstr "WordPress & WooCommerce向けのAI翻訳。35以上の言語でサイトを自動翻訳し、ブランドの独自のトーンで表現。高速キャッシュ、SEO対応、コスト効率に優れています。"31 32 28 #. Description 33 29 msgid "AI Translate automatically makes your WordPress website available in 35+ languages. Increase your reach and improve your SEO without manual work." -
ai-translate/trunk/docs/readme-pt_PT.po
r3451499 r3451583 26 26 msgstr "Plugin alimentado por IA para tradução automática de sites em 35 idiomas. Aumenta o tráfego e melhora o seu SEO." 27 27 28 #. Short Description29 msgid "AI-powered translation for WordPress & WooCommerce. Automatically translate your site in 35+ languages with your brand's unique tone of voice. Fast caching, SEO-friendly, and cost-effective."30 msgstr "Tradução com IA para WordPress e WooCommerce. Traduza automaticamente o seu site em mais de 35 idiomas com o tom de voz único da sua marca. Cache rápido, otimizado para SEO e económico."31 32 28 #. Description 33 29 msgid "AI Translate automatically makes your WordPress website available in 35+ languages. Increase your reach and improve your SEO without manual work." -
ai-translate/trunk/includes/admin-page.php
r3451503 r3451583 2389 2389 // OpenRouter requires Referer header 2390 2390 if ($provider_key === 'openrouter' || ($provider_key === 'custom' && strpos($api_url, 'openrouter.ai') !== false)) { 2391 $headers[' Referer'] = home_url();2392 $headers['X-Title'] = get_bloginfo('name');2391 $headers['HTTP-Referer'] = 'https://github.com/gerard-kanters/ai-translate'; 2392 $headers['X-Title'] = 'AI Translate'; 2393 2393 } 2394 2394 $response = wp_remote_get($endpoint, [ … … 2420 2420 }, $data['data']); 2421 2421 $models = array_filter($models); 2422 // Block only o1/o3 (no reasoning_effort support); gpt-5 models allowed2423 $models = array_filter($models, function ($model) {2422 // o1/o3 reasoning models don't support temperature; block from selection to avoid API errors 2423 $models = array_filter($models, function ($model) { 2424 2424 return !preg_match('/^(o1-|o3-)/i', $model); 2425 2425 }); -
ai-translate/trunk/includes/class-ai-batch.php
r3451123 r3451583 10 10 /** 11 11 * Translate a plan's segments using configured provider. 12 * GPT-5 models use reasoning_effort=minimal for fast translations.13 * Blocked: o1, o3 (no reasoning_effort parameter support).14 12 * 15 13 * @param array $plan … … 25 23 26 24 $timeLimit = (int) ini_get('max_execution_time'); 27 $elapsed = microtime(true) - ($_SERVER['REQUEST_TIME_FLOAT'] ?? microtime(true)); 25 $requestTime = isset($_SERVER['REQUEST_TIME_FLOAT']) ? $_SERVER['REQUEST_TIME_FLOAT'] : microtime(true); 26 $elapsed = microtime(true) - $requestTime; 28 27 $remaining = $timeLimit > 0 ? ($timeLimit - $elapsed) : 60; 29 28 if ($remaining < 15) { … … 37 36 $apiKeys = isset($settings['api_keys']) && is_array($settings['api_keys']) ? $settings['api_keys'] : []; 38 37 $apiKey = $provider !== '' ? ($apiKeys[$provider] ?? '') : ''; 39 40 // Block o1/o3 reasoning models: they don't support reasoning_effort parameter41 if ($model !== '' && preg_match('/^(o1-|o3-)/i', $model)) {42 return ['segments' => [], 'map' => []];43 }44 38 45 39 if (empty($segments)) { … … 204 198 ], 205 199 ]; 206 // Newer models (gpt-5.x, o1-series, o3-series) and DeepSeek v3.2 use max_completion_tokens instead of max_tokens 207 // These models also don't support temperature != 1, so we omit it 208 if (str_starts_with($model, 'gpt-5') || str_starts_with($model, 'o1-') || str_starts_with($model, 'o3-') || str_starts_with($model, 'deepseek/deepseek-v3')) { 209 $body['max_completion_tokens'] = 4096; 210 // GPT-5 models: use minimal reasoning for translations (fast, no extended thinking) 211 if (str_starts_with($model, 'gpt-5')) { 212 $body['reasoning_effort'] = 'minimal'; 213 } 214 } else { 215 $body['max_tokens'] = 4096; 216 $body['temperature'] = 0; 217 } 200 $body['temperature'] = 0; 218 201 $headers = [ 'Authorization' => 'Bearer ' . $apiKey, 'Content-Type' => 'application/json' ]; 219 202 // OpenRouter requires Referer header (WordPress adds HTTP- prefix automatically) … … 245 228 foreach ($batchesSingle as $i => $batchSegs2) { 246 229 $userPayload = self::buildUserPayload($batchSegs2); 247 $body = [ 'model' => $model, 'messages' => [ ['role' => 'system', 'content' => $system], ['role' => 'user', 'content' => $userPayload] ] ]; 248 if (str_starts_with($model, 'gpt-5') || str_starts_with($model, 'o1-') || str_starts_with($model, 'o3-') || str_starts_with($model, 'deepseek/deepseek-v3')) { 249 $body['max_completion_tokens'] = 4096; 250 // GPT-5 models: use minimal reasoning for translations (fast, no extended thinking) 251 if (str_starts_with($model, 'gpt-5')) { 252 $body['reasoning_effort'] = 'minimal'; 253 } 254 } else { 255 $body['max_tokens'] = 4096; 256 $body['temperature'] = 0; 257 } 230 $body = [ 'model' => $model, 'messages' => [ ['role' => 'system', 'content' => $system], ['role' => 'user', 'content' => $userPayload] ] ]; $body['temperature'] = 0; 258 231 $fallbackHeaders = [ 'Authorization' => 'Bearer ' . $apiKey, 'Content-Type' => 'application/json' ]; 259 232 if ($provider === 'custom' && isset($settings['custom_api_url']) && strpos($settings['custom_api_url'], 'openrouter.ai') !== false) { … … 460 433 foreach ($retryChunks as $rc) { 461 434 $userPayload = self::buildUserPayload($rc); 462 $body = [ 'model' => $model, 'messages' => [ ['role' => 'system', 'content' => $strictSystem], ['role' => 'user', 'content' => $userPayload] ] ]; 463 if (str_starts_with($model, 'gpt-5') || str_starts_with($model, 'o1-') || str_starts_with($model, 'o3-') || str_starts_with($model, 'deepseek/deepseek-v3')) { 464 $body['max_completion_tokens'] = 4096; 465 // GPT-5 models: use minimal reasoning for translations (fast, no extended thinking) 466 if (str_starts_with($model, 'gpt-5')) { 467 $body['reasoning_effort'] = 'minimal'; 468 } 469 } else { 470 $body['max_tokens'] = 4096; 471 $body['temperature'] = 0; 472 } 435 $body = [ 'model' => $model, 'messages' => [ ['role' => 'system', 'content' => $strictSystem], ['role' => 'user', 'content' => $userPayload] ] ]; 436 $body['temperature'] = 0; 473 437 $retryHeaders = [ 'Authorization' => 'Bearer ' . $apiKey, 'Content-Type' => 'application/json' ]; 474 438 if ($provider === 'custom' && isset($settings['custom_api_url']) && strpos($settings['custom_api_url'], 'openrouter.ai') !== false) { … … 556 520 ], 557 521 ]; 558 // Newer models (gpt-5.x, o1-series, o3-series) use max_completion_tokens instead of max_tokens 559 // DeepSeek v3.2 also uses max_completion_tokens 560 // These models also don't support temperature != 1, so we omit it 561 if (str_starts_with($model, 'gpt-5') || str_starts_with($model, 'o1-') || str_starts_with($model, 'o3-') || str_starts_with($model, 'deepseek/deepseek-v3')) { 562 $body['max_completion_tokens'] = 4096; 563 // GPT-5 models: use minimal reasoning for translations (fast, no extended thinking) 564 if (str_starts_with($model, 'gpt-5')) { 565 $body['reasoning_effort'] = 'minimal'; 566 } 567 } else { 568 $body['max_tokens'] = 4096; 522 if (!preg_match('/^(o1-|o3-)/i', $model)) { 569 523 $body['temperature'] = 0; 570 524 } … … 576 530 do { 577 531 $attempts++; 578 $timeRemaining = $timeLimit > 0 ? ($timeLimit - (microtime(true) - ($_SERVER['REQUEST_TIME_FLOAT'] ?? microtime(true)))) : 60; 532 $requestTime = isset($_SERVER['REQUEST_TIME_FLOAT']) ? $_SERVER['REQUEST_TIME_FLOAT'] : microtime(true); 533 $timeRemaining = $timeLimit > 0 ? ($timeLimit - (microtime(true) - $requestTime)) : 60; 579 534 $safeTimeout = min($timeoutSeconds, max(10, (int)($timeRemaining - 10))); 580 535 … … 723 678 $userPayload = self::buildUserPayload($rc); 724 679 $body = [ 'model' => $model, 'messages' => [ ['role' => 'system', 'content' => $strictSystem], ['role' => 'user', 'content' => $userPayload] ] ]; 725 if (str_starts_with($model, 'gpt-5') || str_starts_with($model, 'o1-') || str_starts_with($model, 'o3-') || str_starts_with($model, 'deepseek/deepseek-v3')) { 726 $body['max_completion_tokens'] = 4096; 727 // GPT-5 models: use minimal reasoning for translations (fast, no extended thinking) 728 if (str_starts_with($model, 'gpt-5')) { 729 $body['reasoning_effort'] = 'minimal'; 730 } 731 } else { 732 $body['max_tokens'] = 4096; 680 if (!preg_match('/^(o1-|o3-)/i', $model)) { 733 681 $body['temperature'] = 0; 734 682 } -
ai-translate/trunk/includes/class-ai-translate-core.php
r3451499 r3451583 104 104 // OpenRouter requires Referer header 105 105 if ($provider_key === 'custom' && strpos($custom_api_url, 'openrouter.ai') !== false) { 106 $headers[' Referer'] = home_url();107 $headers['X-Title'] = get_bloginfo('name');106 $headers['HTTP-Referer'] = 'https://github.com/gerard-kanters/ai-translate'; 107 $headers['X-Title'] = 'AI Translate'; 108 108 } 109 109 $resp = wp_remote_get($endpoint, [ … … 153 153 // OpenRouter requires Referer header 154 154 if ($provider_key === 'custom' && strpos($custom_api_url, 'openrouter.ai') !== false) { 155 $chatHeaders[' Referer'] = home_url();156 $chatHeaders['X-Title'] = get_bloginfo('name');155 $chatHeaders['HTTP-Referer'] = 'https://github.com/gerard-kanters/ai-translate'; 156 $chatHeaders['X-Title'] = 'AI Translate'; 157 157 } 158 158 $chatBody = [ … … 162 162 ], 163 163 ]; 164 // Chat test: ruime limiet voor alle models inclusief reasoning models165 $chatBody['max_completion_tokens'] = 10000;166 164 $chatResp = wp_remote_post($chatEndpoint, [ 167 165 'headers' => $chatHeaders, … … 856 854 857 855 if (!empty($content)) { 858 if (defined('WP_DEBUG') && WP_DEBUG) {859 error_log('AI Translate: Fetched content from domain ' . $domain . ' (length: ' . mb_strlen($content) . ')');860 }861 856 return $content; 862 857 } … … 877 872 $content = ''; 878 873 $home_id = (int) get_option('page_on_front'); 879 $site_name = (string) get_bloginfo('name');874 $site_name = (string) 'AI Translate'; 880 875 881 876 if ($home_id > 0) { … … 989 984 } 990 985 } else { 991 $site_name = (string) get_bloginfo('name');986 $site_name = (string) 'AI Translate'; 992 987 } 993 988 … … 1013 1008 ], 1014 1009 ]; 1015 1016 // Token limits - ruime limiet voor alle models (je betaalt alleen wat je gebruikt)1017 $body['max_completion_tokens'] = 16000;1010 if ($provider === 'openrouter' || ($provider === 'custom' && strpos($baseUrl, 'openrouter.ai') !== false)) { 1011 $body['user'] = !empty($domain) ? $domain : parse_url(home_url(), PHP_URL_HOST); 1012 } 1018 1013 1019 1014 $headers = [ … … 1029 1024 $protocol = sanitize_text_field(wp_unslash($_SERVER['REQUEST_SCHEME'])); 1030 1025 } 1031 $headers[' Referer'] = $protocol . '://' . $domain . '/';1026 $headers['HTTP-Referer'] = 'https://github.com/gerard-kanters/ai-translate'; 1032 1027 } else { 1033 $headers[' Referer'] = home_url();1028 $headers['HTTP-Referer'] = 'https://github.com/gerard-kanters/ai-translate'; 1034 1029 } 1035 $headers['X-Title'] = $site_name;1030 $headers['X-Title'] = 'AI Translate'; 1036 1031 } 1037 1032 … … 1125 1120 $site_name = strtok($domain, ':'); 1126 1121 } 1127 1128 if (defined('WP_DEBUG') && WP_DEBUG) {1129 error_log('AI Translate: Using domain-specific site name for ' . $domain . ': ' . $site_name);1130 }1131 1122 } else { 1132 $site_name = (string) get_bloginfo('name');1123 $site_name = (string) 'AI Translate'; 1133 1124 } 1134 1125 … … 1159 1150 "Website Content:\n" . $content . $context_section; 1160 1151 1152 $metaModel = (stripos($model, 'gpt-5') !== false) ? 'gpt-4.1-mini' : $model; 1161 1153 $body = [ 1162 'model' => $m odel,1154 'model' => $metaModel, 1163 1155 'messages' => [ 1164 1156 ['role' => 'system', 'content' => 'You are an expert SEO copywriter specializing in writing compelling, keyword-rich meta descriptions that drive click-through rates. You understand that generic descriptions hurt SEO performance.'], … … 1166 1158 ], 1167 1159 ]; 1168 1169 // Token limits - ruime limiet voor alle models (je betaalt alleen wat je gebruikt) 1170 $body['max_completion_tokens'] = 16000; 1160 $m = strtolower((string) $model); 1161 if ($m !== '' && strpos($m, 'gpt-5') === false) { 1162 if (strpos($m, 'o1-') === 0 || strpos($m, 'o3-') === 0) { 1163 $body['thinking'] = false; 1164 } elseif (strpos($m, 'deepseek-r1') !== false || strpos($m, 'deepseek-reasoner') !== false) { 1165 $body['thinking'] = array('type' => 'disabled'); 1166 } 1167 } 1168 if ($provider === 'openrouter' || ($provider === 'custom' && strpos($baseUrl, 'openrouter.ai') !== false)) { 1169 $body['user'] = !empty($domain) ? $domain : parse_url(home_url(), PHP_URL_HOST); 1170 } 1171 1171 1172 1172 $headers = [ … … 1182 1182 $protocol = sanitize_text_field(wp_unslash($_SERVER['REQUEST_SCHEME'])); 1183 1183 } 1184 $headers[' Referer'] = $protocol . '://' . $domain . '/';1184 $headers['HTTP-Referer'] = 'https://github.com/gerard-kanters/ai-translate'; 1185 1185 } else { 1186 $headers[' Referer'] = home_url();1186 $headers['HTTP-Referer'] = 'https://github.com/gerard-kanters/ai-translate'; 1187 1187 } 1188 $headers['X-Title'] = $site_name; 1189 } 1190 1188 $headers['X-Title'] = 'AI Translate'; 1189 } 1190 1191 // Shorter timeout for meta descriptions (simple task; reasoning minimized above). 1191 1192 $response = wp_remote_post($endpoint, [ 1192 1193 'headers' => $headers, 1193 'timeout' => 45,1194 'timeout' => 60, 1194 1195 'sslverify' => true, 1195 1196 'body' => wp_json_encode($body),
Note: See TracChangeset
for help on using the changeset viewer.