File: /var/www/quadcodewordpressapi/public/wp-content/plugins/transtations/cron.php
<?php
ignore_user_abort(true);
$_SERVER['HTTP_HOST'] = '';
if (!headers_sent()) {
header('Expires: Wed, 11 Jan 1984 05:00:00 GMT');
header('Cache-Control: no-cache, must-revalidate, max-age=0');
}
define('DOING_CRON', true);
if (!defined('ABSPATH')) {
/** Set up WordPress environment */
require_once __DIR__ . '/../../../wp-load.php';
}
use Dotenv\Dotenv;
function translateWbPosts()
{
global $table_prefix,
$wpdb;
$table_name = $table_prefix . 'translations';
$args = [
'post_type' => [POST_TYPE, GLOSSARY_TYPE, EVENT_TYPE],
'post_status' => 'publish',
'posts_per_page' => -1,
'lang' => DEFAULT_LANG,
];
// get param from cli command
$cliArgs = $_SERVER['argv'];
if (!empty($cliArgs[1])) {
$postIds = [$cliArgs[1]];
if (!empty($postIds)) {
$args['post__in'] = $postIds;
}
}
$langs = get_available_languages();
if (!empty($cliArgs[2])) {
$langs = [$cliArgs[2]];
}
$posts = get_posts($args);
foreach ($posts as $post) {
$translationSkipped = false;
/** @var WP_Post $post */
$title = $post->post_title;
$excerpt = $post->post_excerpt;
$content = $post->post_content;
$meta = get_post_meta($post->ID);
if (!empty($meta)) {
if (!empty($meta['_yoast_wpseo_title'])) {
$metaTitle = $meta['_yoast_wpseo_title'][0];
}
if (!empty($meta['_yoast_wpseo_metadesc'])) {
$metaDescription = $meta['_yoast_wpseo_metadesc'][0];
}
}
$attributes = get_fields($post->ID);
foreach ($langs as $lang) {
$translatedAttributes = [];
$translatedTitle = $translatedExcerpt = $translatedContent = $translatedMetaTitle = $translatedMetaDescription = '';
$langParts = explode('_', $lang);
$lang = $langParts[0];
$postTranslation = getPostTranslation($post->ID, $lang, 1, true);
$partialPostTranslation = getPostTranslation($post->ID, $lang, 0, true);
if (!empty($partialPostTranslation[0])) {
$translatedTitle = $partialPostTranslation[0]->post_title;
$translatedExcerpt = $partialPostTranslation[0]->post_excerpt;
$translatedContent = $partialPostTranslation[0]->post_content;
if ($partialPostTranslation[0]->post_attributes) {
if (is_base64($partialPostTranslation[0]->post_attributes)) {
$partialPostTranslation[0]->post_attributes = base64_decode($partialPostTranslation[0]->post_attributes);
}
// Bad serialized strings will be rebuild on translation cron run
$translatedAttributes = @unserialize($partialPostTranslation[0]->post_attributes);
if (!is_array($translatedAttributes)) {
$message = 'Unserialize issue. Post #'.$partialPostTranslation[0]->post_id.' failed to unserialize.';
saveLog($message);
$translatedAttributes = [];
}
}
$translatedMetaTitle = $partialPostTranslation[0]->yoast_title;
$translatedMetaDescription = $partialPostTranslation[0]->yoast_description;
} elseif (!empty($postTranslation[0])) {
$postTranslation = $postTranslation[0];
if (strtotime($post->post_modified) < strtotime($postTranslation->date_modified)) {
// Skip post translation post was not modified
$translationSkipped = true;
$message = "Skip post #{$post->ID} to " . strtoupper($lang) . " as it was not modified since last translation.";
saveLog($message);
continue;
}
}
$translatingAttributes = [];
if (empty($translatedTitle) && !empty($title)) {
$translatingAttributes[] = 'title';
}
if (empty($translatedExcerpt) && !empty($excerpt)) {
$translatingAttributes[] = 'excerpt';
}
if (empty($translatedContent) && !empty($content)) {
$translatingAttributes[] = 'content';
}
if (empty($translatedAttributes) && !empty($attributes)) {
$translatingAttributes[] = 'attributes';
}
if (empty($translatedMetaTitle) && !empty($metaTitle) && !substr_count($metaTitle, '%')) {
$translatingAttributes[] = 'meta title';
}
if (empty($translatedMetaDescription) && !empty($metaDescription)) {
$translatingAttributes[] = 'meta description';
}
$additionalMessage = '';
if (count($translatingAttributes) < 6) {
$additionalMessage = "Translating only: " . implode(', ', $translatingAttributes);
}
saveLog("Start translating post #{$post->ID} to " . strtoupper($lang) . ". {$additionalMessage}");
if (!empty($title) && empty($translatedTitle)) {
$titleResponse = translateWithOpenAi($title, $lang, false);
if (!empty($titleResponse['text'])) {
$translatedTitle = $titleResponse['text'];
}
}
if (!empty($metaTitle) && !substr_count($metaTitle, '%') && empty($translatedMetaTitle)) {
$metaTitleResponse = translateWithOpenAi($metaTitle, $lang, false);
if (!empty($metaTitleResponse['text'])) {
$translatedMetaTitle = $metaTitleResponse['text'];
}
}
if (!empty($metaDescription) && empty($translatedMetaDescription)) {
$metaDescriptionResponse = translateWithOpenAi($metaDescription, $lang, false);
if (!empty($metaDescriptionResponse['text'])) {
$translatedMetaDescription = $metaDescriptionResponse['text'];
}
}
if ($excerpt && empty($translatedExcerpt)) {
$excerptResponse = translateWithOpenAi($excerpt, $lang);
if (!empty($excerptResponse['text'])) {
$translatedExcerpt = $excerptResponse['text'];
}
}
if ($content && empty($translatedContent)) {
$parts = preg_split('/\n\s*\n/', $content);
foreach ($parts as $key => $part) {
if (preg_match('/(.*)\[video(.*)\](.*)/', trim($part))
|| preg_match('/(.*)\[postLink(.*)\](.*)/', trim($part))) {
continue;
}
$part = do_shortcode($part);
$contentResponse = translateWithOpenAi($part, $lang);
if (!empty($contentResponse['text'])) {
$parts[$key] = $contentResponse['text'];
}
if ($key % 20 == 0) { // Delay to avoid rate limits
sleep(1);
}
}
$translatedContent = implode("\n\n", $parts);
}
if (!empty($attributes) && empty($translatedAttributes)) {
foreach ($attributes as $akey => $value) {
if ($akey == 'word') {
// Not translating Glossary term
continue;
}
if (is_string($value) && !is_numeric($value) && !empty($value)) {
$attributeResponse = translateWithOpenAi($value, $lang);
if (!empty($attributeResponse['text'])) {
$translatedAttributes[$akey] = addslashes($attributeResponse['text']);
} else {
$message = "Error translating attribute: " . json_encode($value);
saveLog($message);
}
}
}
$translatedAttributes = $attributes;
if (!empty($translatedAttributes)) {
$translatedAttributes = array_merge($attributes, $translatedAttributes);
}
if (!empty($attributes['faq'])) {
foreach ($attributes['faq'] as $key => $faq) {
$faqResponse = translateWithOpenAi($faq['question'], $lang);
if (!empty($faqResponse['text'])) {
$translatedAttributes['faq'][$key]['question'] = addslashes($faqResponse['text']);
} else {
saveLog("Error translating attribute: " . json_encode($value));
}
$faqResponse = translateWithOpenAi($faq['answer'], $lang);
if (!empty($faqResponse['text'])) {
$translatedAttributes['faq'][$key]['answer'] = addslashes($faqResponse['text']);
} else {
saveLog("Error translating attribute: " . json_encode($value));
}
}
}
if (!empty($attributes['youtube_video'])) {
foreach ($attributes['youtube_video'] as $key => $youtube_video) {
$youtubeVideoResponse = translateWithOpenAi($youtube_video['title'], $lang);
if (!empty($youtubeVideoResponse['text'])) {
$translatedAttributes['youtube_video'][$key]['title'] = addslashes($youtubeVideoResponse['text']);
} else {
saveLog("Error translating attribute: Video title ");
}
$youtubeVideoResponse = translateWithOpenAi($youtube_video['description'], $lang);
if (!empty($youtubeVideoResponse['text'])) {
$translatedAttributes['youtube_video'][$key]['description'] = addslashes($youtubeVideoResponse['text']);
} else {
saveLog("Error translating attribute: video description");
}
$translatedAttributes['youtube_video'][$key]['video-embed'] = $youtube_video['video-embed'];
}
}
}
if (empty($translatedTitle)) {
$translatedTitle = '';
}
if (empty($translatedExcerpt)) {
$translatedExcerpt = '';
}
if (empty($translatedContent)) {
$translatedContent = '';
}
if (empty($translatedAttributes)) {
$translatedAttributes = '';
}
if (empty($translatedMetaTitle)) {
$translatedMetaTitle = '';
}
if (empty($translatedMetaDescription)) {
$translatedMetaDescription = '';
}
$translatedAttributes = base64_encode(serialize($translatedAttributes));
$sql = "INSERT INTO $table_name
(post_id, lang, post_title, post_excerpt, post_content, post_attributes, yoast_title, yoast_description)
VALUES ($post->ID, '$lang', '" . addslashes($translatedTitle) . "', '" . addslashes($translatedExcerpt) . "',
'" . addslashes($translatedContent) . "', '" . $translatedAttributes . "',
'" . addslashes($translatedMetaTitle) . "', '" . addslashes($translatedMetaDescription) . "')
ON DUPLICATE KEY UPDATE
post_title = VALUES(post_title), post_excerpt = VALUES(post_excerpt),
post_content = VALUES(post_content), post_attributes = VALUES(post_attributes),
yoast_title = VALUES(yoast_title),
yoast_description = VALUES(yoast_description),
is_active = 1,
date_modified = CURRENT_TIMESTAMP";
$wpdb->query($sql);
$message = "Post #{$post->ID} translated to " . strtoupper($lang);
saveLog($message);
sleep(5);
}
}
}
function translateWithOpenAi($text, $lang, $keepHtmlInResponse = true)
{
if (preg_match('/^\[.*\]$/', $text)) {
return $text;
}
if (trim(strip_tags($text)) == '') {
return $text; // Return the text as is if it's html without text
}
$dotenv = Dotenv::createImmutable(ROOT);
try {
$dotenv->load();
} catch (Exception $exception) {
echo $exception->getMessage();
}
$settings = get_option('translation_settings');
$token = $settings['gpt_token'] ?? '';
$langs = [
'es' => 'Spanish',
'pt' => 'Portuguese',
'ru' => 'Russian',
'vi' => 'Vietnamese',
'th' => 'Thai',
'ko' => 'Korean',
];
if (in_array($lang, array_keys($langs))) {
$lang = $langs[$lang];
} else {
$lang = strtoupper($lang);
}
$prompt = 'Translate this text into ' . $lang . ' language. Keep HTML formating and special chars. If text contain HTML - return html. Else return just text. Dont add <html> tag. do not translate texts in [] (ex. [video id=""]. Dont use MARKDOWN. Text to translate: ' . $text;
$headers = [
'Authorization: Bearer ' . $token,
];
$messages = [
['role' => 'user', 'content' => $prompt],
];
$url = 'https://api.openai.com/v1/chat/completions';
$data = [
'model' => 'gpt-4o-mini-2024-07-18',
'messages' => $messages,
'temperature' => 0.7,
];
$openAiResult = sendCurl($url, $data, $headers);
if (!empty($openAiResult)) {
$openAiResult = json_decode($openAiResult, true);
if (!empty($openAiResult['choices'])) {
$openAiResult = $openAiResult['choices'][0]['message']['content'];
if (!$keepHtmlInResponse && function_exists('cleanHtmlTags')) {
$openAiResult = cleanHtmlTags($openAiResult);
}
}
}
$response = [
'text' => $openAiResult,
];
return $response;
}
function translateWpUsers()
{
global $table_prefix,
$wpdb;
$translation_table_name = $table_prefix . 'user_translations';
$users = get_users();
$langs = get_available_languages();
foreach ($users as $user) {
$userData = get_fields('user_' . $user->ID);//get_user_meta($user->ID);
foreach ($langs as $lang) {
try {
$langParts = explode('_', $lang);
$lang = strtolower($langParts[0]);
$userTranslation = $wpdb->get_row(
$wpdb->prepare(
"SELECT * FROM $translation_table_name WHERE user_id = %d AND lang = %s",
$user->ID,
$lang
)
);
if (!empty($userTranslation) && $user->date_modified
&& strtotime($userTranslation->date_modified) > strtotime($user->date_modified)) {
$message = "Skip user #{$user->ID} to " . strtoupper($lang) . " as it was not modified since last translation.";
saveLog($message);
continue; // Skip already translated users
}
$message = "Start translating user #{$user->ID} to " . strtoupper($lang);
saveLog($message);
$translatedDescription = '';
if (!empty($user->description)) {
$descriptionResponse = translateWithOpenAi($user->description, $lang);
if (!empty($descriptionResponse['text'])) {
$translatedDescription = strip_tags($descriptionResponse['text']);
}
}
$translatedPosition = '';
if (!empty($userData['position']) && !empty($userData['position'])) {
$positionResponse = translateWithOpenAi($userData['position'], $lang);
if (!empty($positionResponse['text'])) {
$translatedPosition = strip_tags($positionResponse['text']);
}
}
$translatedEducation = [];
if (!empty($userData['education']) && !empty($userData['education'][0])) {
$translatedEducation = $userData['education'];
foreach ($translatedEducation as &$education) {
$educationResponse = translateWithOpenAi($education['title'], $lang);
if (!empty($educationResponse['text'])) {
$education['title'] = strip_tags($educationResponse['text']);
}
$educationResponse = translateWithOpenAi($education['subtitle'], $lang);
if (!empty($educationResponse['text'])) {
$education['subtitle'] = strip_tags($educationResponse['text']);
}
}
}
$translatedEducation = json_encode($translatedEducation, JSON_UNESCAPED_UNICODE);
$translatedExperience = [];
if (!empty($userData['experience']) && !empty($userData['experience'][0])) {
$translatedExperience = $userData['experience'];
foreach ($translatedExperience as &$experience) {
$experienceResponse = translateWithOpenAi($experience['position'], $lang);
if (!empty($experienceResponse['text'])) {
$experience['position'] = strip_tags($experienceResponse['text']);
}
$experienceResponse = translateWithOpenAi($experience['years'], $lang);
if (!empty($experienceResponse['text'])) {
$experience['years'] = strip_tags($experienceResponse['text']);
}
$experienceResponse = translateWithOpenAi($experience['description'], $lang);
if (!empty($experienceResponse['text'])) {
$experience['description'] = $experienceResponse['text'];
}
}
}
$translatedExperience = json_encode($translatedExperience, JSON_UNESCAPED_UNICODE);
$now = date("Y-m-d H:i:s");
$sql = "INSERT INTO $translation_table_name
(user_id, lang, description, position, education, experience, date_modified)
VALUES ($user->ID, '$lang', '" . addslashes($translatedDescription) . "', '" . addslashes($translatedPosition) . "',
'" . $translatedEducation . "', '" . $translatedExperience . "', '{$now}')
ON DUPLICATE KEY UPDATE
description = VALUES(description),
position = VALUES(position),
education = VALUES(education),
experience = VALUES(experience),
date_modified = '{$now}'";
$wpdb->query($sql);
// For first translation
if (!$user->date_modified) {
$translationTime = date('Y-m-d H:i:s');
$sql = "UPDATE $table_prefix" . "users SET date_modified = '$translationTime' WHERE ID = $user->ID";
$wpdb->query($sql);
}
$message = "User #{$user->ID} translated to " . strtoupper($lang);
saveLog($message);
sleep(1);
} catch (\Exception $e) {
$message = "Error translating user #{$user->ID} to " . strtoupper($lang) . ": " . $e->getMessage();
saveLog($message);
}
}
}
}
function translateWithApi($texts, $lang)
{
if (!is_array($texts)) {
$texts = [$texts];
}
if (count($texts) == 1 && preg_match('/^\[.*\]$/', $texts[0])) {
return $texts; // Return the text as is if it's a single post link
}
$dotenv = Dotenv::createImmutable(ROOT);
try {
$dotenv->load();
} catch (Exception $exception) {
echo $exception->getMessage();
}
$initialUrl = "https://ftapi.pythonanywhere.com/translate?sl=en&dl={$lang}&text=";
$response = [
'text' => [],
];
foreach ($texts as $text) {
$text = str_replace([' ', '\u00a0'], ' ', $text);
if (empty(trim($text)) || in_array($text, [';', '0'])) {
$response['text'][] = $text; // Keep texts as is
continue;
}
$url = $initialUrl . urlencode($text);
$translationResponse = sendCurl($url);
$retryCount = 1;
$translationResponse = json_decode($translationResponse, true);
if (!empty($translationResponse['destination-text'])) {
$response['text'][] = $translationResponse['destination-text'];
} else {
saveLog(json_encode($texts) . ' - ' . json_encode($translationResponse));
while (is_null($translationResponse) || !isset($translationResponse['destination-text'])) {
saveLog("Retrying " . $retryCount);
sleep($retryCount * 10);
$translationResponse = sendCurl($url);
$translationResponse = json_decode($translationResponse, true);
if (!empty($translationResponse['destination-text'])) {
$response['text'][] = $translationResponse['destination-text'];
}
if ($retryCount > 2) {
saveLog("Failed to translate after 3 retries: " . json_encode($texts));
$response['text'] = $texts; // Return original text if translation fails
return $response;
}
$retryCount++;
}
}
sleep(1);
}
return $response;
}
function translateExpertise()
{
global $wpdb, $table_prefix;
$expertiseOptions = "SELECT term_id, name from {$table_prefix}terms WHERE term_id IN (SELECT term_id FROM {$table_prefix}term_taxonomy WHERE taxonomy = 'expertise')";
$expertiseOptions = $wpdb->get_results($expertiseOptions);
$langs = get_available_languages();
foreach ($expertiseOptions as $expertiseOption) {
foreach ($langs as $lang) {
$lang = explode('_', $lang)[0];
$query = $wpdb->prepare(
"SELECT * FROM {$table_prefix}expertise_translations WHERE expertise_id = %d AND lang = %s",
$expertiseOption->term_id,
$lang
);
$translatedExpertise = $wpdb->get_row(
$query
);
if (!empty($translatedExpertise)) {
$message = "Skip {$expertiseOption->name} to " . strtoupper($lang) . " as it was already translated.";
saveLog($message);
continue; // Skip already translated expertise
}
$message = "Translating {$expertiseOption->name} to $lang";
saveLog($message);
$translatedExpertise = translateWithOpenAi($expertiseOption->name, $lang);
if (!empty($translatedExpertise['text'])) {
$translatedExpertise = strip_tags($translatedExpertise['text']);
$wpdb->query(
$wpdb->prepare(
"INSERT INTO {$table_prefix}expertise_translations (expertise_id, lang, name) VALUES (%d, %s, %s)",
$expertiseOption->term_id,
$lang,
$translatedExpertise
)
);
}
}
}
}
function translateTags()
{
global $wpdb, $table_prefix;
$args = [
'hide_empty' => false,
'orderby' => 'name',
'order' => 'ASC',
'number' => 0,
'format' => 'objects',
];
$tags = get_terms([
'taxonomy' => 'post_tag',
'hide_empty' => (bool) $args['hide_empty'],
'orderby' => $args['orderby'],
'order' => $args['order'],
'number' => (int) $args['number'],
]);
if (is_wp_error($tags) || empty($tags)) {
$tags = [];
}
$langs = get_available_languages();
foreach ($tags as $tag) {
foreach ($langs as $lang) {
$lang = explode('_', $lang)[0];
$query = $wpdb->prepare(
"SELECT * FROM {$table_prefix}tag_translations WHERE tag_id = %d AND lang = %s",
$tag->term_id,
$lang
);
$translatedTag = $wpdb->get_row(
$query
);
if (!empty($translatedTag)) {
$message = "Skip tag {$tag->name} to " . strtoupper($lang) . " as it was already translated.";
saveLog($message);
continue; // Skip already translated tag
}
$message = "Translating tag {$tag->name} to $lang";
saveLog($message);
$translatedTag = translateWithOpenAi($tag->name, $lang);
if (!empty($translatedTag['text']) && !is_array($translatedTag['text'])) {
$translatedTag = strip_tags($translatedTag['text']);
$wpdb->query(
$wpdb->prepare(
"INSERT INTO {$table_prefix}tag_translations (tag_id, lang, name) VALUES (%d, %s, %s)",
$tag->term_id,
$lang,
$translatedTag
)
);
}
}
sleep(1);
}
}
$cliArgs = $_SERVER['argv'] ?? [];
if (empty($cliArgs[1])) {
translateTags();
translateExpertise();
translateWpUsers();
}
translateWbPosts();
global $table_prefix,
$wpdb;
$stats = $wpdb->get_row(
"SELECT count(*) as non_translated FROM {$table_prefix}translations WHERE is_active = 0 AND post_id IN (SELECT id FROM {$table_prefix}posts)"
);
saveLog('Non active translations: ' . $stats->non_translated);
delete_transient('doing_cron');
die();