PluginProbe
Translate and Go multilingual – Automatic AI translation – wpLingua / 2.16.7
Translate and Go multilingual – Automatic AI translation – wpLingua v2.16.7
2.16.7 2.16.6 2.16.5 2.16.4 2.16.3 2.16.2 2.16.1 2.16.0 2.15.2 2.15.1 2.15.0 2.14.3 2.14.2 2.14.1 2.14.0 2.13.1 2.13.0 2.12.3 2.12.2 2.12.1 trunk 1.0.3 1.0.4 1.0.5 1.1.0 All 112 releases
← All changes | inc/translation.php +348 -152 1.0.32.16.7 View file →
@@ -6,8 +6,82 @@
6 6 }
7 7
8 8
9 9 /**
10 + * Get the translated text from translations array
11 + *
12 + * @param string $text
13 + * @param array $translations
14 + * @return string
15 + */
16 +function wplng_get_translated_text_from_translations( $text, $translations ) {
17 +
18 + if ( empty( trim( $text ) ) ) {
19 + return $text;
20 + }
21 +
22 + // Manage non breaking space
23 + $text = str_replace(
24 + array( ' ', html_entity_decode( ' ' ) ),
25 + array( ' ', ' ' ),
26 + $text
27 + );
28 +
29 + /**
30 + * Get spaces before and after text
31 + */
32 + $temp = array();
33 + $spaces_before = '';
34 + $spaces_after = '';
35 +
36 + preg_match( '/^(\s*).*/', $text, $temp );
37 + if ( ! empty( $temp[1] ) ) {
38 + $spaces_before = $temp[1];
39 + }
40 +
41 + preg_match( '/(\s+)$/', $text, $temp );
42 + if ( ! empty( $temp[1] ) ) {
43 + $spaces_after = $temp[1];
44 + }
45 +
46 + $text = wplng_text_esc( $text );
47 + $translated = $text;
48 +
49 + foreach ( $translations as $translation ) {
50 + if ( $text === $translation['source'] ) {
51 + $translated = $translation['translation'];
52 + break;
53 + }
54 + }
55 +
56 + return $spaces_before . $translated . $spaces_after;
57 +}
58 +
59 +
60 +/**
61 + * Get the count of published 'wplng_translation' posts.
62 + *
63 + * This function retrieves the number of posts of type 'wplng_translation'
64 + * that are currently published on the site.
65 + *
66 + * @return int The number of published 'wplng_translation' posts.
67 + */
68 +function wplng_get_translation_count() {
69 +
70 + global $wplng_translation_count;
71 +
72 + if ( null !== $wplng_translation_count ) {
73 + return $wplng_translation_count;
74 + }
75 +
76 + $count_posts = wp_count_posts( 'wplng_translation' );
77 + $wplng_translation_count = (int) ( $count_posts->publish ?? 0 );
78 +
79 + return $wplng_translation_count;
80 +}
81 +
82 +
83 +/**
10 84 * Get translation data from original text
11 85 *
12 86 * @param string $original
13 87 * @return array Translation data
@@ -13,44 +87,69 @@
13 87 * @return array Translation data
14 88 */
15 89 function wplng_get_translation_saved_from_original( $original ) {
16 90
17 - $translation = false;
91 + if ( empty( $original ) ) {
92 + return false;
93 + }
18 94
19 - $args = array(
20 - 'post_type' => 'wplng_translation',
21 - 'meta_query' => array(
95 + $args = array(
96 + 'post_type' => 'wplng_translation',
97 + 'posts_per_page' => -1,
98 + 'fields' => 'ids',
99 + 'meta_query' => array(
100 + 'relation' => 'AND',
22 101 array(
23 102 'key' => 'wplng_translation_md5',
24 103 'value' => md5( $original ),
25 104 'compare' => '=',
26 105 ),
106 + array(
107 + 'key' => 'wplng_translation_original_language_id',
108 + 'value' => wplng_get_language_website_id(),
109 + 'compare' => '=',
110 + ),
27 111 ),
28 112 );
29 - $the_query = new WP_Query( $args );
30 113
31 - // The Loop
32 - while ( $the_query->have_posts() ) {
114 + $posts = get_posts( $args );
33 115
34 - $the_query->the_post();
116 + if ( empty( $posts ) ) {
117 + return false;
118 + }
35 119
36 - $meta = get_post_meta( get_the_ID() );
120 + foreach ( $posts as $post_id ) {
37 121
38 - if ( ! isset( $meta['wplng_translation_original'][0] )
39 - || ! is_string( $meta['wplng_translation_original'][0] )
122 + $meta = get_post_meta( $post_id );
123 +
124 + if ( isset( $meta['wplng_translation_original'][0] )
125 + && is_string( $meta['wplng_translation_original'][0] )
126 + && $meta['wplng_translation_original'][0] === $original
40 127 ) {
41 - continue;
128 + return get_post( $post_id );
42 129 }
130 + }
43 131
44 - if ( $meta['wplng_translation_original'][0] === $original ) {
45 - $translation = get_post();
46 - break;
47 - }
132 + return false;
133 +}
134 +
135 +
136 +/**
137 + * Get all saved translations for all languages from cache or wp_query
138 + *
139 + * @return array
140 + */
141 +function wplng_get_translations() {
142 +
143 + $translations = get_option( 'wplng_cached_translations' );
144 +
145 + if ( empty( $translations )
146 + || ! is_array( $translations )
147 + ) {
148 + $translations = wplng_get_translations_from_query();
48 149 }
49 150
50 - wp_reset_postdata();
51 -
52 - return $translation;
151 + return $translations;
53 152 }
54 153
55 154
56 155 /**
@@ -60,126 +159,118 @@
60 159 */
61 160 function wplng_get_translations_from_query() {
62 161
63 162 $translations = array();
64 - $args = array(
65 - 'post_type' => 'wplng_translation',
66 - 'posts_per_page' => -1,
67 - 'order' => 'ASC',
163 +
164 + $args = array(
165 + 'post_type' => 'wplng_translation',
166 + 'posts_per_page' => -1,
167 + 'no_found_rows' => true,
168 + 'update_post_term_cache' => false,
169 + 'update_post_meta_cache' => false,
170 + 'cache_results' => false,
171 + 'fields' => 'ids', // Retrieve only post IDs for better performance
172 + 'meta_query' => array(
173 + array(
174 + 'key' => 'wplng_translation_original_language_id',
175 + 'value' => wplng_get_language_website_id(),
176 + 'compare' => '=',
177 + ),
178 + ),
68 179 );
69 180
70 - $the_query = new WP_Query( $args );
181 + $post_ids = get_posts( $args );
71 182
72 - while ( $the_query->have_posts() ) {
183 + if ( empty( $post_ids ) ) {
184 + return array();
185 + }
73 186
74 - $the_query->the_post();
187 + foreach ( $post_ids as $id ) {
75 188
76 - $meta = get_post_meta( get_the_ID() );
189 + $meta = get_post_meta( $id );
77 190
78 - if ( ! isset( $meta['wplng_translation_original'][0] )
191 + // Ensure 'wplng_translation_original' exists and is a valid string
192 + if ( empty( $meta['wplng_translation_original'][0] )
79 193 || ! is_string( $meta['wplng_translation_original'][0] )
194 + || trim( $meta['wplng_translation_original'][0] ) === ''
80 195 ) {
81 196 continue;
82 197 }
83 198
199 + $source = wplng_text_esc( trim( $meta['wplng_translation_original'][0] ) );
200 +
84 201 $translation = array(
85 - 'source' => $meta['wplng_translation_original'][0],
86 - 'post_id' => get_the_ID(),
202 + 'source' => $source,
203 + 'post_id' => $id,
204 + 'review' => array(),
87 205 'translations' => array(),
88 206 );
89 207
90 - // Get translation for current language target
208 + // Ensure 'wplng_translation_translations' exists
91 209 if ( empty( $meta['wplng_translation_translations'][0] ) ) {
92 210 continue;
93 211 }
94 212
95 - $translations_meta = json_decode(
96 - $meta['wplng_translation_translations'][0],
97 - true
98 - );
213 + $translations_meta = json_decode( $meta['wplng_translation_translations'][0], true );
99 214
215 + // Ensure JSON decoding was successful
216 + if ( ! is_array( $translations_meta ) ) {
217 + continue;
218 + }
219 +
100 220 foreach ( $translations_meta as $translation_meta ) {
101 - if ( ! empty( $translation_meta['language_id'] )
102 - && wplng_is_valid_language_id( $translation_meta['language_id'] )
103 - && isset( $translation_meta['translation'] )
221 + // Validate each translation entry
222 + if ( empty( $translation_meta['language_id'] )
223 + || ! wplng_is_valid_language_id( $translation_meta['language_id'] )
224 + || (
225 + isset( $translation_meta['translation'] )
226 + && $translation_meta['translation'] === '[WPLNG_EMPTY]'
227 + )
228 + ) {
229 + continue;
230 + }
231 +
232 + $language_id = sanitize_key( $translation_meta['language_id'] );
233 +
234 + if ( isset( $translation_meta['translation'] )
104 235 && is_string( $translation_meta['translation'] )
105 - && $translation_meta['translation'] !== '[WPLNG_EMPTY]'
106 236 ) {
107 - $language_id = sanitize_key( $translation_meta['language_id'] );
108 - $translated_text = sanitize_text_field( $translation_meta['translation'] );
237 + $translated_text = wplng_text_esc( $translation_meta['translation'] );
109 238
110 239 $translation['translations'][ $language_id ] = $translated_text;
111 240 }
241 +
242 + if ( isset( $translation_meta['status'] )
243 + && is_int( $translation_meta['status'] )
244 + ) {
245 + $translation['review'][] = $language_id;
246 + }
112 247 }
113 248
249 + // Skip entries with no valid translations
114 250 if ( empty( $translation['translations'] ) ) {
115 251 continue;
116 252 }
117 253
118 - $translations[] = $translation;
254 + $array_index = (string) mb_substr( $source, 0, 1 ) . (string) strlen( $source );
255 +
256 + $translations[ $array_index ][] = $translation;
119 257 }
120 258
121 - wp_reset_postdata();
259 + // Cache translations as a persistent option
260 + update_option( 'wplng_cached_translations', $translations, false );
122 261
123 - set_transient(
124 - 'wplng_cached_translations',
125 - $translations
126 - );
127 -
128 262 return $translations;
129 263 }
130 264
131 265
132 266 /**
133 - * Get all saved translations for a target language
134 - *
135 - * @param string $target_language_id
136 - * @return array
137 - */
138 -function wplng_get_translations_target( $target_language_id ) {
139 -
140 - $translations_all_languages = get_transient( 'wplng_cached_translations' );
141 -
142 - if ( empty( $translations_all_languages )
143 - || ! is_array( $translations_all_languages )
144 - ) {
145 - $translations_all_languages = wplng_get_translations_from_query();
146 - }
147 -
148 - $translations_target = array();
149 -
150 - foreach ( $translations_all_languages as $translation ) {
151 - if ( empty( $translation['source'] )
152 - || ! is_string( $translation['source'] )
153 - || empty( $translation['post_id'] )
154 - || empty( $translation['translations'][ $target_language_id ] )
155 - || ! is_string( $translation['translations'][ $target_language_id ] )
156 - ) {
157 - continue;
158 - }
159 -
160 - $translation_text = sanitize_text_field(
161 - $translation['translations'][ $target_language_id ]
162 - );
163 -
164 - $translations_target[] = array(
165 - 'source' => sanitize_text_field( $translation['source'] ),
166 - 'post_id' => $translation['post_id'],
167 - 'translation' => $translation_text,
168 - );
169 - }
170 -
171 - return $translations_target;
172 -}
173 -
174 -
175 -/**
176 267 * Save a translation
177 268 *
178 269 * @param string $language_id
179 270 * @param string $original
180 - * @param array $translation
181 - * @return mixed Post ID or false
271 + * @param array $translation
272 + * @return int|false Post ID or false on failure
182 273 */
183 274 function wplng_save_translation_new( $language_id, $original, $translation ) {
184 275
185 276 $translation = str_replace( '\\', '', $translation );
@@ -186,10 +277,13 @@
186 277
187 278 /**
188 279 * Make the title
189 280 */
281 +
190 282 $tite_max_length = 100;
191 - $title = substr( $original, 0, $tite_max_length );
283 + $title = mb_substr( $original, 0, $tite_max_length );
284 + $title = wplng_text_esc_displayed( $title );
285 + $title = wp_encode_emoji( $title );
192 286 if ( strlen( $original ) > $tite_max_length ) {
193 287 $title .= __( '...', 'wplingua' );
194 288 }
195 289
@@ -195,8 +289,9 @@
195 289
196 290 /**
197 291 * Create the post and get this ID
198 292 */
293 +
199 294 $new_post_id = wp_insert_post(
200 295 array(
201 296 'post_title' => esc_html( $title ),
202 297 'post_type' => 'wplng_translation',
@@ -206,9 +301,9 @@
206 301 ),
207 302 )
208 303 );
209 304
210 - if ( is_wp_error( $new_post_id ) ) {
305 + if ( is_wp_error( $new_post_id ) || empty( $new_post_id ) ) {
211 306 return false;
212 307 }
213 308
214 309 /**
@@ -213,8 +308,9 @@
213 308
214 309 /**
215 310 * Make $translation_meta
216 311 */
312 +
217 313 $languages_target = wplng_get_languages_target_ids();
218 314 $translation_meta = array();
219 315
220 316 foreach ( $languages_target as $target_language ) {
@@ -219,9 +315,8 @@
219 315
220 316 foreach ( $languages_target as $target_language ) {
221 317
222 318 if ( $target_language === $language_id ) {
223 -
224 319 $translation_meta[] = array(
225 320 'language_id' => $target_language,
226 321 'translation' => esc_html( $translation ),
227 322 'status' => 'generated',
@@ -234,27 +329,96 @@
234 329 );
235 330 }
236 331 }
237 332
238 - add_post_meta(
333 + /**
334 + * Get the URL where the translation was discovered
335 + */
336 +
337 + $discovery_url = wplng_get_url_original();
338 + $discovery_url = wp_make_link_relative( $discovery_url );
339 + $discovery_url = remove_query_arg(
340 + array( 'wplng-mode', 'wplng-load', 'nocache' ),
341 + $discovery_url
342 + );
343 +
344 + /**
345 + * Save meta: Discovered URL
346 + */
347 +
348 + $meta_return = add_post_meta(
239 349 $new_post_id,
240 - 'wplng_translation_original',
241 - $original
350 + 'wplng_translation_discovery_url',
351 + $discovery_url
242 352 );
243 353
244 - add_post_meta(
354 + if ( false === $meta_return ) {
355 + wp_delete_post( $new_post_id, true );
356 + return false;
357 + }
358 +
359 + /**
360 + * Save meta: original language ID
361 + */
362 +
363 + $meta_return = add_post_meta(
245 364 $new_post_id,
365 + 'wplng_translation_original_language_id',
366 + wplng_get_language_website_id()
367 + );
368 +
369 + if ( false === $meta_return ) {
370 + wp_delete_post( $new_post_id, true );
371 + return false;
372 + }
373 +
374 + /**
375 + * Save meta: original text MD5
376 + */
377 +
378 + $meta_return = add_post_meta(
379 + $new_post_id,
246 380 'wplng_translation_md5',
247 381 md5( $original )
248 382 );
249 383
250 - add_post_meta(
384 + if ( false === $meta_return ) {
385 + wp_delete_post( $new_post_id, true );
386 + return false;
387 + }
388 +
389 + /**
390 + * Save meta: original text
391 + */
392 +
393 + $meta_return = add_post_meta(
251 394 $new_post_id,
252 - 'wplng_translation_original_language_id',
253 - wplng_get_language_website_id()
395 + 'wplng_translation_original',
396 + $original
254 397 );
255 398
256 - add_post_meta(
399 + if ( false === $meta_return ) {
400 +
401 + // Try to save it with encoded emoji
402 + $original = wp_encode_emoji( $original );
403 +
404 + $meta_return = add_post_meta(
405 + $new_post_id,
406 + 'wplng_translation_original',
407 + $original
408 + );
409 +
410 + if ( false === $meta_return ) {
411 + wp_delete_post( $new_post_id, true );
412 + return false;
413 + }
414 + }
415 +
416 + /**
417 + * Save meta: Translation array as JSON
418 + */
419 +
420 + $meta_return = add_post_meta(
257 421 $new_post_id,
258 422 'wplng_translation_translations',
259 423 wp_json_encode(
260 424 $translation_meta,
@@ -261,8 +425,37 @@
261 425 JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
262 426 )
263 427 );
264 428
429 + if ( false === $meta_return ) {
430 +
431 + // Try to save it with encoded emoji
432 + foreach ( $translation_meta as $key => $translation ) {
433 +
434 + if ( empty( $translation['translation'] )
435 + || '[WPLNG_EMPTY]' === $translation['translation']
436 + ) {
437 + continue;
438 + }
439 +
440 + $translation_meta[ $key ]['translation'] = wp_encode_emoji( $translation['translation'] );
441 + }
442 +
443 + $meta_return = add_post_meta(
444 + $new_post_id,
445 + 'wplng_translation_translations',
446 + wp_json_encode(
447 + $translation_meta,
448 + JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
449 + )
450 + );
451 +
452 + if ( false === $meta_return ) {
453 + wp_delete_post( $new_post_id, true );
454 + return false;
455 + }
456 + }
457 +
265 458 return $new_post_id;
266 459 }
267 460
268 461
@@ -270,10 +463,10 @@
270 463 * Update a translation
271 464 *
272 465 * @param object $post
273 466 * @param string $language_id
274 - * @param array $translation
275 - * @return int Post ID
467 + * @param array $translation
468 + * @return int|false Post ID, false on failure
276 469 */
277 470 function wplng_update_translation( $post, $language_id, $translation ) {
278 471
279 472 $translation = str_replace( '\\', '', $translation );
@@ -296,17 +489,23 @@
296 489
297 490 /**
298 491 * $original_language_id_meta must be the same as in option page
299 492 */
300 - update_post_meta(
493 +
494 + $meta_return = update_post_meta(
301 495 $post->ID,
302 496 'wplng_translation_original_language_id',
303 497 $website_language
304 498 );
305 499
500 + if ( false === $meta_return ) {
501 + return false;
502 + }
503 +
306 504 /**
307 505 * Make $translation_meta
308 506 */
507 +
309 508 $translation_meta = array();
310 509 foreach ( $languages_target as $key => $target_language ) {
311 510
312 511 if ( $target_language === $language_id && $translation !== '[WPLNG_EMPTY]' ) {
@@ -311,29 +510,19 @@
311 510
312 511 if ( $target_language === $language_id && $translation !== '[WPLNG_EMPTY]' ) {
313 512 $translation_meta[] = array(
314 513 'language_id' => $target_language,
315 - 'translation' => '[WPLNG_EMPTY]',
316 - 'status' => 'ungenerated',
514 + 'translation' => esc_html( $translation ),
515 + 'status' => 'generated',
317 516 );
318 517 } else {
319 518 $translation_meta[] = array(
320 519 'language_id' => $target_language,
321 - 'translation' => esc_html( $translation ),
322 - 'status' => 'generated',
520 + 'translation' => '[WPLNG_EMPTY]',
521 + 'status' => 'ungenerated',
323 522 );
324 523 }
325 524 }
326 -
327 - update_post_meta(
328 - $post->ID,
329 - 'wplng_translation_translations',
330 - wp_json_encode(
331 - $translation_meta,
332 - JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
333 - )
334 - );
335 -
336 525 } else { // Translation is valid
337 526
338 527 // Set or update new translation
339 528 $translation_already_in = false;
@@ -338,9 +527,9 @@
338 527 // Set or update new translation
339 528 $translation_already_in = false;
340 529
341 530 foreach ( $translation_meta as $key => $translation_e ) {
342 - if ( $translation_e['language_id'] == $language_id ) {
531 + if ( $translation_e['language_id'] === $language_id ) {
343 532 $translation_already_in = true;
344 533 $translation_meta[ $key ] = array(
345 534 'language_id' => $language_id,
346 535 'translation' => esc_html( $translation ),
@@ -356,10 +545,38 @@
356 545 'translation' => esc_html( $translation ),
357 546 'status' => 'generated',
358 547 );
359 548 }
549 + }
360 550
361 - update_post_meta(
551 + /**
552 + * Update the translations post meta
553 + */
554 +
555 + $meta_return = update_post_meta(
556 + $post->ID,
557 + 'wplng_translation_translations',
558 + wp_json_encode(
559 + $translation_meta,
560 + JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
561 + )
562 + );
563 +
564 + if ( false === $meta_return ) {
565 +
566 + // Try to save it with encoded emoji
567 + foreach ( $translation_meta as $key => $translation ) {
568 +
569 + if ( empty( $translation['translation'] )
570 + || '[WPLNG_EMPTY]' === $translation['translation']
571 + ) {
572 + continue;
573 + }
574 +
575 + $translation_meta[ $key ]['translation'] = wp_encode_emoji( $translation['translation'] );
576 + }
577 +
578 + $meta_return = update_post_meta(
362 579 $post->ID,
363 580 'wplng_translation_translations',
364 581 wp_json_encode(
365 582 $translation_meta,
@@ -366,8 +583,11 @@
366 583 JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
367 584 )
368 585 );
369 586
587 + if ( false === $meta_return ) {
588 + return false;
589 + }
370 590 }
371 591
372 592 return $post->ID;
373 593 }
@@ -375,9 +595,9 @@
375 595
376 596 /**
377 597 * Save a list of translations
378 598 *
379 - * @param array $translations
599 + * @param array $translations
380 600 * @param string $language_target_id
381 601 * @return array $translations with post IDs
382 602 */
383 603 function wplng_save_translations( $translations, $language_target_id ) {
@@ -413,9 +633,9 @@
413 633 * Save translation
414 634 *
415 635 * @param string $target_language_id
416 636 * @param string $original
417 - * @param array $translation
637 + * @param array $translation
418 638 * @return array $translation with post ID
419 639 */
420 640 function wplng_save_translation( $target_language_id, $original, $translation, $clear_cache = true ) {
421 641
@@ -422,9 +642,9 @@
422 642 $saved_translation = wplng_get_translation_saved_from_original( $original );
423 643
424 644 if ( empty( $saved_translation ) ) {
425 645 // Create new translation post
426 - return wplng_save_translation_new(
646 + $result = wplng_save_translation_new(
427 647 $target_language_id,
428 648 $original,
429 649 $translation
430 650 );
@@ -429,9 +649,9 @@
429 649 $translation
430 650 );
431 651 } else {
432 652 // Update the translation post
433 - return wplng_update_translation(
653 + $result = wplng_update_translation(
434 654 $saved_translation,
435 655 $target_language_id,
436 656 $translation
437 657 );
@@ -440,30 +660,6 @@
440 660 if ( $clear_cache ) {
441 661 wplng_clear_translations_cache();
442 662 }
443 663
444 -}
445 -
446 -
447 -/**
448 - * Clear cached translations
449 - *
450 - * @return void
451 - */
452 -function wplng_clear_translations_cache() {
453 - delete_transient( 'wplng_cached_translations' );
454 -}
455 -
456 -
457 -/**
458 - * Clear cached translations if $post_id parametter is a translation
459 - *
460 - * @return void
461 - */
462 -function wplng_clear_translations_cache_trash_untrash( $post_id ) {
463 -
464 - if ( 'wplng_translation' !== get_post_type( $post_id ) ) {
465 - return;
466 - }
467 -
468 - wplng_clear_translations_cache();
664 + return $result;
469 665 }