PluginProbe
Translate and boost SEO – Automatic AI translation – wpLingua / 2.16.8
Translate and boost SEO – Automatic AI translation – wpLingua v2.16.8
2.16.8 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 All 113 releases
← All changes | inc/translation.php +665 -665 2.16.12.16.8 View file →
@@ -1,665 +1,665 @@
1 -<?php
2 -
3 -// If this file is called directly, abort.
4 -if ( ! defined( 'WPINC' ) ) {
5 - die;
6 -}
7 -
8 -
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( '&nbsp;', html_entity_decode( '&nbsp;' ) ),
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 -/**
84 - * Get translation data from original text
85 - *
86 - * @param string $original
87 - * @return array Translation data
88 - */
89 -function wplng_get_translation_saved_from_original( $original ) {
90 -
91 - if ( empty( $original ) ) {
92 - return false;
93 - }
94 -
95 - $args = array(
96 - 'post_type' => 'wplng_translation',
97 - 'posts_per_page' => -1,
98 - 'fields' => 'ids',
99 - 'meta_query' => array(
100 - 'relation' => 'AND',
101 - array(
102 - 'key' => 'wplng_translation_md5',
103 - 'value' => md5( $original ),
104 - 'compare' => '=',
105 - ),
106 - array(
107 - 'key' => 'wplng_translation_original_language_id',
108 - 'value' => wplng_get_language_website_id(),
109 - 'compare' => '=',
110 - ),
111 - ),
112 - );
113 -
114 - $posts = get_posts( $args );
115 -
116 - if ( empty( $posts ) ) {
117 - return false;
118 - }
119 -
120 - foreach ( $posts as $post_id ) {
121 -
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
127 - ) {
128 - return get_post( $post_id );
129 - }
130 - }
131 -
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();
149 - }
150 -
151 - return $translations;
152 -}
153 -
154 -
155 -/**
156 - * Get all saved translations from a wp_query for all languages
157 - *
158 - * @return array
159 - */
160 -function wplng_get_translations_from_query() {
161 -
162 - $translations = array();
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 - ),
179 - );
180 -
181 - $post_ids = get_posts( $args );
182 -
183 - if ( empty( $post_ids ) ) {
184 - return array();
185 - }
186 -
187 - foreach ( $post_ids as $id ) {
188 -
189 - $meta = get_post_meta( $id );
190 -
191 - // Ensure 'wplng_translation_original' exists and is a valid string
192 - if ( empty( $meta['wplng_translation_original'][0] )
193 - || ! is_string( $meta['wplng_translation_original'][0] )
194 - || trim( $meta['wplng_translation_original'][0] ) === ''
195 - ) {
196 - continue;
197 - }
198 -
199 - $source = wplng_text_esc( trim( $meta['wplng_translation_original'][0] ) );
200 -
201 - $translation = array(
202 - 'source' => $source,
203 - 'post_id' => $id,
204 - 'review' => array(),
205 - 'translations' => array(),
206 - );
207 -
208 - // Ensure 'wplng_translation_translations' exists
209 - if ( empty( $meta['wplng_translation_translations'][0] ) ) {
210 - continue;
211 - }
212 -
213 - $translations_meta = json_decode( $meta['wplng_translation_translations'][0], true );
214 -
215 - // Ensure JSON decoding was successful
216 - if ( ! is_array( $translations_meta ) ) {
217 - continue;
218 - }
219 -
220 - foreach ( $translations_meta as $translation_meta ) {
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'] )
235 - && is_string( $translation_meta['translation'] )
236 - ) {
237 - $translated_text = wplng_text_esc( $translation_meta['translation'] );
238 -
239 - $translation['translations'][ $language_id ] = $translated_text;
240 - }
241 -
242 - if ( isset( $translation_meta['status'] )
243 - && is_int( $translation_meta['status'] )
244 - ) {
245 - $translation['review'][] = $language_id;
246 - }
247 - }
248 -
249 - // Skip entries with no valid translations
250 - if ( empty( $translation['translations'] ) ) {
251 - continue;
252 - }
253 -
254 - $array_index = (string) mb_substr( $source, 0, 1 ) . (string) strlen( $source );
255 -
256 - $translations[ $array_index ][] = $translation;
257 - }
258 -
259 - // Cache translations as a persistent option
260 - update_option( 'wplng_cached_translations', $translations, false );
261 -
262 - return $translations;
263 -}
264 -
265 -
266 -/**
267 - * Save a translation
268 - *
269 - * @param string $language_id
270 - * @param string $original
271 - * @param array $translation
272 - * @return int|false Post ID or false on failure
273 - */
274 -function wplng_save_translation_new( $language_id, $original, $translation ) {
275 -
276 - $translation = str_replace( '\\', '', $translation );
277 -
278 - /**
279 - * Make the title
280 - */
281 -
282 - $tite_max_length = 100;
283 - $title = mb_substr( $original, 0, $tite_max_length );
284 - $title = wplng_text_esc_displayed( $title );
285 - $title = wp_encode_emoji( $title );
286 - if ( strlen( $original ) > $tite_max_length ) {
287 - $title .= __( '...', 'wplingua' );
288 - }
289 -
290 - /**
291 - * Create the post and get this ID
292 - */
293 -
294 - $new_post_id = wp_insert_post(
295 - array(
296 - 'post_title' => esc_html( $title ),
297 - 'post_type' => 'wplng_translation',
298 - 'post_status' => 'publish',
299 - 'post_name' => sanitize_title(
300 - 'wplingua-translation-' . md5( $title ) . '-' . $title
301 - ),
302 - )
303 - );
304 -
305 - if ( is_wp_error( $new_post_id ) || empty( $new_post_id ) ) {
306 - return false;
307 - }
308 -
309 - /**
310 - * Make $translation_meta
311 - */
312 -
313 - $languages_target = wplng_get_languages_target_ids();
314 - $translation_meta = array();
315 -
316 - foreach ( $languages_target as $target_language ) {
317 -
318 - if ( $target_language === $language_id ) {
319 - $translation_meta[] = array(
320 - 'language_id' => $target_language,
321 - 'translation' => esc_html( $translation ),
322 - 'status' => 'generated',
323 - );
324 - } else {
325 - $translation_meta[] = array(
326 - 'language_id' => $target_language,
327 - 'translation' => '[WPLNG_EMPTY]',
328 - 'status' => 'ungenerated',
329 - );
330 - }
331 - }
332 -
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(
349 - $new_post_id,
350 - 'wplng_translation_discovery_url',
351 - $discovery_url
352 - );
353 -
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(
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,
380 - 'wplng_translation_md5',
381 - md5( $original )
382 - );
383 -
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(
394 - $new_post_id,
395 - 'wplng_translation_original',
396 - $original
397 - );
398 -
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(
421 - $new_post_id,
422 - 'wplng_translation_translations',
423 - wp_json_encode(
424 - $translation_meta,
425 - JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
426 - )
427 - );
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 -
458 - return $new_post_id;
459 -}
460 -
461 -
462 -/**
463 - * Update a translation
464 - *
465 - * @param object $post
466 - * @param string $language_id
467 - * @param array $translation
468 - * @return int|false Post ID, false on failure
469 - */
470 -function wplng_update_translation( $post, $language_id, $translation ) {
471 -
472 - $translation = str_replace( '\\', '', $translation );
473 - $meta = get_post_meta( $post->ID );
474 - $languages_target = wplng_get_languages_target_ids();
475 - $website_language = wplng_get_language_website_id();
476 -
477 - $translation_meta = ( empty( $meta['wplng_translation_translations'][0] ) )
478 - ? array() :
479 - json_decode( $meta['wplng_translation_translations'][0], true );
480 -
481 - $original_language_id_meta = ( empty( $meta['wplng_translation_original_language_id'][0] ) )
482 - ? wplng_get_language_website_id()
483 - : $meta['wplng_translation_original_language_id'][0];
484 -
485 - // If translation found is not valid
486 - if ( empty( $translation_meta )
487 - || $website_language !== $original_language_id_meta
488 - ) {
489 -
490 - /**
491 - * $original_language_id_meta must be the same as in option page
492 - */
493 -
494 - $meta_return = update_post_meta(
495 - $post->ID,
496 - 'wplng_translation_original_language_id',
497 - $website_language
498 - );
499 -
500 - if ( false === $meta_return ) {
501 - return false;
502 - }
503 -
504 - /**
505 - * Make $translation_meta
506 - */
507 -
508 - $translation_meta = array();
509 - foreach ( $languages_target as $key => $target_language ) {
510 -
511 - if ( $target_language === $language_id && $translation !== '[WPLNG_EMPTY]' ) {
512 - $translation_meta[] = array(
513 - 'language_id' => $target_language,
514 - 'translation' => esc_html( $translation ),
515 - 'status' => 'generated',
516 - );
517 - } else {
518 - $translation_meta[] = array(
519 - 'language_id' => $target_language,
520 - 'translation' => '[WPLNG_EMPTY]',
521 - 'status' => 'ungenerated',
522 - );
523 - }
524 - }
525 - } else { // Translation is valid
526 -
527 - // Set or update new translation
528 - $translation_already_in = false;
529 -
530 - foreach ( $translation_meta as $key => $translation_e ) {
531 - if ( $translation_e['language_id'] === $language_id ) {
532 - $translation_already_in = true;
533 - $translation_meta[ $key ] = array(
534 - 'language_id' => $language_id,
535 - 'translation' => esc_html( $translation ),
536 - 'status' => 'generated',
537 - );
538 - break;
539 - }
540 - }
541 -
542 - if ( ! $translation_already_in ) {
543 - $translation_meta[] = array(
544 - 'language_id' => $language_id,
545 - 'translation' => esc_html( $translation ),
546 - 'status' => 'generated',
547 - );
548 - }
549 - }
550 -
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(
579 - $post->ID,
580 - 'wplng_translation_translations',
581 - wp_json_encode(
582 - $translation_meta,
583 - JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
584 - )
585 - );
586 -
587 - if ( false === $meta_return ) {
588 - return false;
589 - }
590 - }
591 -
592 - return $post->ID;
593 -}
594 -
595 -
596 -/**
597 - * Save a list of translations
598 - *
599 - * @param array $translations
600 - * @param string $language_target_id
601 - * @return array $translations with post IDs
602 - */
603 -function wplng_save_translations( $translations, $language_target_id ) {
604 -
605 - if ( empty( $translations ) || ! is_array( $translations ) ) {
606 - return array();
607 - }
608 -
609 - foreach ( $translations as $key => $translation ) {
610 -
611 - if (
612 - ! isset( $translation['source'] ) // Original text
613 - || ! isset( $translation['translation'] ) // Translater text
614 - ) {
615 - continue;
616 - }
617 -
618 - $translations[ $key ]['post_id'] = wplng_save_translation(
619 - $language_target_id,
620 - $translation['source'],
621 - $translation['translation'],
622 - false
623 - );
624 - }
625 -
626 - wplng_clear_translations_cache();
627 -
628 - return $translations;
629 -}
630 -
631 -
632 -/**
633 - * Save translation
634 - *
635 - * @param string $target_language_id
636 - * @param string $original
637 - * @param array $translation
638 - * @return array $translation with post ID
639 - */
640 -function wplng_save_translation( $target_language_id, $original, $translation, $clear_cache = true ) {
641 -
642 - $saved_translation = wplng_get_translation_saved_from_original( $original );
643 -
644 - if ( empty( $saved_translation ) ) {
645 - // Create new translation post
646 - $result = wplng_save_translation_new(
647 - $target_language_id,
648 - $original,
649 - $translation
650 - );
651 - } else {
652 - // Update the translation post
653 - $result = wplng_update_translation(
654 - $saved_translation,
655 - $target_language_id,
656 - $translation
657 - );
658 - }
659 -
660 - if ( $clear_cache ) {
661 - wplng_clear_translations_cache();
662 - }
663 -
664 - return $result;
665 -}
1 +<?php
2 +
3 +// If this file is called directly, abort.
4 +if ( ! defined( 'WPINC' ) ) {
5 + die;
6 +}
7 +
8 +
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( '&nbsp;', html_entity_decode( '&nbsp;' ) ),
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 +/**
84 + * Get translation data from original text
85 + *
86 + * @param string $original
87 + * @return array Translation data
88 + */
89 +function wplng_get_translation_saved_from_original( $original ) {
90 +
91 + if ( empty( $original ) ) {
92 + return false;
93 + }
94 +
95 + $args = array(
96 + 'post_type' => 'wplng_translation',
97 + 'posts_per_page' => -1,
98 + 'fields' => 'ids',
99 + 'meta_query' => array(
100 + 'relation' => 'AND',
101 + array(
102 + 'key' => 'wplng_translation_md5',
103 + 'value' => md5( $original ),
104 + 'compare' => '=',
105 + ),
106 + array(
107 + 'key' => 'wplng_translation_original_language_id',
108 + 'value' => wplng_get_language_website_id(),
109 + 'compare' => '=',
110 + ),
111 + ),
112 + );
113 +
114 + $posts = get_posts( $args );
115 +
116 + if ( empty( $posts ) ) {
117 + return false;
118 + }
119 +
120 + foreach ( $posts as $post_id ) {
121 +
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
127 + ) {
128 + return get_post( $post_id );
129 + }
130 + }
131 +
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();
149 + }
150 +
151 + return $translations;
152 +}
153 +
154 +
155 +/**
156 + * Get all saved translations from a wp_query for all languages
157 + *
158 + * @return array
159 + */
160 +function wplng_get_translations_from_query() {
161 +
162 + $translations = array();
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 + ),
179 + );
180 +
181 + $post_ids = get_posts( $args );
182 +
183 + if ( empty( $post_ids ) ) {
184 + return array();
185 + }
186 +
187 + foreach ( $post_ids as $id ) {
188 +
189 + $meta = get_post_meta( $id );
190 +
191 + // Ensure 'wplng_translation_original' exists and is a valid string
192 + if ( empty( $meta['wplng_translation_original'][0] )
193 + || ! is_string( $meta['wplng_translation_original'][0] )
194 + || trim( $meta['wplng_translation_original'][0] ) === ''
195 + ) {
196 + continue;
197 + }
198 +
199 + $source = wplng_text_esc( trim( $meta['wplng_translation_original'][0] ) );
200 +
201 + $translation = array(
202 + 'source' => $source,
203 + 'post_id' => $id,
204 + 'review' => array(),
205 + 'translations' => array(),
206 + );
207 +
208 + // Ensure 'wplng_translation_translations' exists
209 + if ( empty( $meta['wplng_translation_translations'][0] ) ) {
210 + continue;
211 + }
212 +
213 + $translations_meta = json_decode( $meta['wplng_translation_translations'][0], true );
214 +
215 + // Ensure JSON decoding was successful
216 + if ( ! is_array( $translations_meta ) ) {
217 + continue;
218 + }
219 +
220 + foreach ( $translations_meta as $translation_meta ) {
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'] )
235 + && is_string( $translation_meta['translation'] )
236 + ) {
237 + $translated_text = wplng_text_esc( $translation_meta['translation'] );
238 +
239 + $translation['translations'][ $language_id ] = $translated_text;
240 + }
241 +
242 + if ( isset( $translation_meta['status'] )
243 + && is_int( $translation_meta['status'] )
244 + ) {
245 + $translation['review'][] = $language_id;
246 + }
247 + }
248 +
249 + // Skip entries with no valid translations
250 + if ( empty( $translation['translations'] ) ) {
251 + continue;
252 + }
253 +
254 + $array_index = (string) mb_substr( $source, 0, 1 ) . (string) strlen( $source );
255 +
256 + $translations[ $array_index ][] = $translation;
257 + }
258 +
259 + // Cache translations as a persistent option
260 + update_option( 'wplng_cached_translations', $translations, false );
261 +
262 + return $translations;
263 +}
264 +
265 +
266 +/**
267 + * Save a translation
268 + *
269 + * @param string $language_id
270 + * @param string $original
271 + * @param array $translation
272 + * @return int|false Post ID or false on failure
273 + */
274 +function wplng_save_translation_new( $language_id, $original, $translation ) {
275 +
276 + $translation = str_replace( '\\', '', $translation );
277 +
278 + /**
279 + * Make the title
280 + */
281 +
282 + $tite_max_length = 100;
283 + $title = mb_substr( $original, 0, $tite_max_length );
284 + $title = wplng_text_esc_displayed( $title );
285 + $title = wp_encode_emoji( $title );
286 + if ( strlen( $original ) > $tite_max_length ) {
287 + $title .= __( '...', 'wplingua' );
288 + }
289 +
290 + /**
291 + * Create the post and get this ID
292 + */
293 +
294 + $new_post_id = wp_insert_post(
295 + array(
296 + 'post_title' => esc_html( $title ),
297 + 'post_type' => 'wplng_translation',
298 + 'post_status' => 'publish',
299 + 'post_name' => sanitize_title(
300 + 'wplingua-translation-' . md5( $title ) . '-' . $title
301 + ),
302 + )
303 + );
304 +
305 + if ( is_wp_error( $new_post_id ) || empty( $new_post_id ) ) {
306 + return false;
307 + }
308 +
309 + /**
310 + * Make $translation_meta
311 + */
312 +
313 + $languages_target = wplng_get_languages_target_ids();
314 + $translation_meta = array();
315 +
316 + foreach ( $languages_target as $target_language ) {
317 +
318 + if ( $target_language === $language_id ) {
319 + $translation_meta[] = array(
320 + 'language_id' => $target_language,
321 + 'translation' => esc_html( $translation ),
322 + 'status' => 'generated',
323 + );
324 + } else {
325 + $translation_meta[] = array(
326 + 'language_id' => $target_language,
327 + 'translation' => '[WPLNG_EMPTY]',
328 + 'status' => 'ungenerated',
329 + );
330 + }
331 + }
332 +
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(
349 + $new_post_id,
350 + 'wplng_translation_discovery_url',
351 + $discovery_url
352 + );
353 +
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(
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,
380 + 'wplng_translation_md5',
381 + md5( $original )
382 + );
383 +
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(
394 + $new_post_id,
395 + 'wplng_translation_original',
396 + $original
397 + );
398 +
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(
421 + $new_post_id,
422 + 'wplng_translation_translations',
423 + wp_json_encode(
424 + $translation_meta,
425 + JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
426 + )
427 + );
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 +
458 + return $new_post_id;
459 +}
460 +
461 +
462 +/**
463 + * Update a translation
464 + *
465 + * @param object $post
466 + * @param string $language_id
467 + * @param array $translation
468 + * @return int|false Post ID, false on failure
469 + */
470 +function wplng_update_translation( $post, $language_id, $translation ) {
471 +
472 + $translation = str_replace( '\\', '', $translation );
473 + $meta = get_post_meta( $post->ID );
474 + $languages_target = wplng_get_languages_target_ids();
475 + $website_language = wplng_get_language_website_id();
476 +
477 + $translation_meta = ( empty( $meta['wplng_translation_translations'][0] ) )
478 + ? array() :
479 + json_decode( $meta['wplng_translation_translations'][0], true );
480 +
481 + $original_language_id_meta = ( empty( $meta['wplng_translation_original_language_id'][0] ) )
482 + ? wplng_get_language_website_id()
483 + : $meta['wplng_translation_original_language_id'][0];
484 +
485 + // If translation found is not valid
486 + if ( empty( $translation_meta )
487 + || $website_language !== $original_language_id_meta
488 + ) {
489 +
490 + /**
491 + * $original_language_id_meta must be the same as in option page
492 + */
493 +
494 + $meta_return = update_post_meta(
495 + $post->ID,
496 + 'wplng_translation_original_language_id',
497 + $website_language
498 + );
499 +
500 + if ( false === $meta_return ) {
501 + return false;
502 + }
503 +
504 + /**
505 + * Make $translation_meta
506 + */
507 +
508 + $translation_meta = array();
509 + foreach ( $languages_target as $key => $target_language ) {
510 +
511 + if ( $target_language === $language_id && $translation !== '[WPLNG_EMPTY]' ) {
512 + $translation_meta[] = array(
513 + 'language_id' => $target_language,
514 + 'translation' => esc_html( $translation ),
515 + 'status' => 'generated',
516 + );
517 + } else {
518 + $translation_meta[] = array(
519 + 'language_id' => $target_language,
520 + 'translation' => '[WPLNG_EMPTY]',
521 + 'status' => 'ungenerated',
522 + );
523 + }
524 + }
525 + } else { // Translation is valid
526 +
527 + // Set or update new translation
528 + $translation_already_in = false;
529 +
530 + foreach ( $translation_meta as $key => $translation_e ) {
531 + if ( $translation_e['language_id'] === $language_id ) {
532 + $translation_already_in = true;
533 + $translation_meta[ $key ] = array(
534 + 'language_id' => $language_id,
535 + 'translation' => esc_html( $translation ),
536 + 'status' => 'generated',
537 + );
538 + break;
539 + }
540 + }
541 +
542 + if ( ! $translation_already_in ) {
543 + $translation_meta[] = array(
544 + 'language_id' => $language_id,
545 + 'translation' => esc_html( $translation ),
546 + 'status' => 'generated',
547 + );
548 + }
549 + }
550 +
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(
579 + $post->ID,
580 + 'wplng_translation_translations',
581 + wp_json_encode(
582 + $translation_meta,
583 + JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
584 + )
585 + );
586 +
587 + if ( false === $meta_return ) {
588 + return false;
589 + }
590 + }
591 +
592 + return $post->ID;
593 +}
594 +
595 +
596 +/**
597 + * Save a list of translations
598 + *
599 + * @param array $translations
600 + * @param string $language_target_id
601 + * @return array $translations with post IDs
602 + */
603 +function wplng_save_translations( $translations, $language_target_id ) {
604 +
605 + if ( empty( $translations ) || ! is_array( $translations ) ) {
606 + return array();
607 + }
608 +
609 + foreach ( $translations as $key => $translation ) {
610 +
611 + if (
612 + ! isset( $translation['source'] ) // Original text
613 + || ! isset( $translation['translation'] ) // Translater text
614 + ) {
615 + continue;
616 + }
617 +
618 + $translations[ $key ]['post_id'] = wplng_save_translation(
619 + $language_target_id,
620 + $translation['source'],
621 + $translation['translation'],
622 + false
623 + );
624 + }
625 +
626 + wplng_clear_translations_cache();
627 +
628 + return $translations;
629 +}
630 +
631 +
632 +/**
633 + * Save translation
634 + *
635 + * @param string $target_language_id
636 + * @param string $original
637 + * @param array $translation
638 + * @return array $translation with post ID
639 + */
640 +function wplng_save_translation( $target_language_id, $original, $translation, $clear_cache = true ) {
641 +
642 + $saved_translation = wplng_get_translation_saved_from_original( $original );
643 +
644 + if ( empty( $saved_translation ) ) {
645 + // Create new translation post
646 + $result = wplng_save_translation_new(
647 + $target_language_id,
648 + $original,
649 + $translation
650 + );
651 + } else {
652 + // Update the translation post
653 + $result = wplng_update_translation(
654 + $saved_translation,
655 + $target_language_id,
656 + $translation
657 + );
658 + }
659 +
660 + if ( $clear_cache ) {
661 + wplng_clear_translations_cache();
662 + }
663 +
664 + return $result;
665 +}