get_active_languages();
$default_lang = $sitepress->get_default_language();
$string_domains = [];
$string_domains_grouped = [];
if ($wpdb->get_var("SHOW TABLES LIKE '{$wpdb->prefix}icl_strings'")) {
$string_domains = $wpdb->get_col("SELECT DISTINCT context FROM {$wpdb->prefix}icl_strings ORDER BY context ASC");
$string_domains_grouped = [
'Theme' => [],
'Plugins' => [],
'Blocks' => [],
'Settings / Admin' => [],
'Other' => []
];
$theme_slugs = [get_stylesheet(), get_template()];
$plugin_slugs = [];
foreach (get_option('active_plugins', []) as $plugin) {
$plugin_slugs[] = dirname($plugin);
}
foreach ($string_domains as $domain) {
if (strpos($domain, 'admin_texts_') === 0) {
$string_domains_grouped['Settings / Admin'][] = $domain;
} elseif (strpos($domain, 'gutenberg_') === 0 || strpos($domain, 'block') !== false) {
$string_domains_grouped['Blocks'][] = $domain;
} elseif (in_array($domain, $theme_slugs)) {
$string_domains_grouped['Theme'][] = $domain;
} elseif (in_array($domain, $plugin_slugs) || strpos($domain, 'plugin-') === 0) {
$string_domains_grouped['Plugins'][] = $domain;
} else {
$string_domains_grouped['Other'][] = $domain;
}
}
}
?>
get_param('domain');
$target_lang = $request->get_param('target_lang');
if (empty($domain) || empty($target_lang)) {
return new \WP_REST_Response(['success' => false, 'message' => 'Missing domain or target_lang parameters'], 400);
}
// phpcs:ignore WordPress.DB.DirectDatabaseQuery
$results = $wpdb->get_results($wpdb->prepare("
SELECT s.id, s.name, s.value
FROM {$wpdb->prefix}icl_strings s
LEFT JOIN {$wpdb->prefix}icl_string_translations st
ON s.id = st.string_id AND st.language = %s
WHERE s.context = %s
AND s.language != %s
AND (st.id IS NULL OR st.status != 10)
ORDER BY s.id DESC LIMIT 500
", $target_lang, $domain, $target_lang));
$items = [];
foreach ($results as $r) {
$items[] = [
'id' => $r->id,
'name' => esc_html(mb_strimwidth($r->name, 0, 80, '...')),
'value' => esc_html(mb_strimwidth($r->value, 0, 150, '...'))
];
}
return new \WP_REST_Response(['success' => true, 'items' => $items], 200);
}
public function handle_wpml_strings_translate(\WP_REST_Request $request) {
global $wpdb;
$string_id = (int)$request->get_param('string_id');
$target_lang = $request->get_param('target_lang');
if (!$string_id || empty($target_lang) || !function_exists('icl_add_string_translation')) {
return new \WP_REST_Response(['success' => false, 'message' => 'Invalid request or ST not installed'], 400);
}
$string_value = $wpdb->get_var($wpdb->prepare("SELECT value FROM {$wpdb->prefix}icl_strings WHERE id = %d", $string_id));
if (empty($string_value)) {
return new \WP_REST_Response(['success' => false, 'message' => 'String empty or not found'], 400);
}
$prompt = "You are an expert translator. Translate the following UI/text string into the language represented by the ISO code '{$target_lang}'.\n";
$prompt .= "IMPORTANT RULES:\n";
$prompt .= "1. Respond ONLY with the translated text. Do NOT include any explanations, quotes, or JSON.\n";
$prompt .= "2. Preserve any placeholders like %s, %d, {name}, HTML tags exactly.\n\n";
$prompt .= "TEXT TO TRANSLATE:\n" . $string_value;
$model = get_option('aiutoma_wpml_model', '');
if ($model === 'google_free' && method_exists($this, 'google_translate_free')) {
$source_lang = $wpdb->get_var($wpdb->prepare("SELECT language FROM {$wpdb->prefix}icl_strings WHERE id = %d", $string_id));
if (!$source_lang) $source_lang = 'en'; // default fallback
$result = $this->google_translate_free($source_lang, $target_lang, $string_value);
} else {
$result = $this->call_ai_for_text($prompt);
}
if (is_wp_error($result)) {
return new \WP_REST_Response(['success' => false, 'message' => $result->get_error_message()], 500);
}
if (trim($result) === '') {
return new \WP_REST_Response(['success' => false, 'message' => 'Empty translation received'], 500);
}
icl_add_string_translation($string_id, $target_lang, trim($result), 10);
return new \WP_REST_Response(['success' => true], 200);
}
private function call_ai_for_text($prompt) {
if (!class_exists('\WordPress\AiClient\AiClient')) {
return new \WP_Error('ai_missing', 'AI Client not found.');
}
$preferred_model = get_option('aiutoma_wpml_model', '');
$auto_fallback = get_option('aiutoma_wpml_auto_fallback', 0);
$fallback_models = get_option('aiutoma_wpml_fallback_models', []);
$models_to_try = [];
if (!empty($preferred_model)) {
$models_to_try[] = $preferred_model;
if ($auto_fallback && !empty($fallback_models)) {
$models_to_try = array_merge($models_to_try, $fallback_models);
}
}
if (empty($models_to_try)) {
$registry = \WordPress\AiClient\AiClient::defaultRegistry();
$requirements = new \WordPress\AiClient\Providers\Models\DTO\ModelRequirements(
[\WordPress\AiClient\Providers\Models\Enums\CapabilityEnum::textGeneration()], []
);
$providerModels = $registry->findModelsMetadataForSupport($requirements);
foreach ($providerModels as $pm) {
$pid = $pm->getProvider()->getId();
foreach ($pm->getModels() as $m) {
$models_to_try[] = $pid . '|' . $m->getId();
break 2;
}
}
}
$last_exception = null;
foreach ($models_to_try as $model_str) {
$model_parts = explode('|', $model_str);
if (count($model_parts) !== 2) continue;
$max_retries = 2;
for ($attempt = 0; $attempt <= $max_retries; $attempt++) {
try {
$ai_query = \WordPress\AiClient\AiClient::prompt([
new \WordPress\AiClient\Messages\DTO\UserMessage([
new \WordPress\AiClient\Messages\DTO\MessagePart($prompt)
])
]);
$ai_query->usingModelPreference($model_parts);
$res = $ai_query->generateResult();
$text = $res->toText();
if (!empty($text)) {
return $text;
} else {
throw new \Exception("Empty text returned from model.");
}
} catch (\Exception $e) {
$last_exception = $e;
$msg = strtolower($e->getMessage());
if (strpos($msg, 'token') !== false || strpos($msg, 'quota') !== false || strpos($msg, 'credit') !== false || strpos($msg, 'rate') !== false || strpos($msg, 'limit') !== false || strpos($msg, '429') !== false || strpos($msg, '408') !== false || strpos($msg, '500') !== false || strpos($msg, '502') !== false || strpos($msg, '503') !== false || strpos($msg, 'timeout') !== false) {
if ($attempt < $max_retries) {
sleep(2);
continue;
}
}
break;
}
}
}
if ($last_exception) {
return new \WP_Error('ai_exception', 'All AI models failed. Last error: ' . $last_exception->getMessage());
}
return new \WP_Error('ai_unknown_error', 'Failed to generate text.');
}
public function aiutoma_wpml_has_strings() {
global $wpdb;
return (bool) $wpdb->get_var("SHOW TABLES LIKE '{$wpdb->prefix}icl_strings'");
}
}