PluginProbe
Translate and Go multilingual – Automatic AI translation – wpLingua / 2.12.2
Translate and Go multilingual – Automatic AI translation – wpLingua v2.12.2
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
wplingua / inc / admin / translation-meta.php

translation-meta.php in Translate and Go multilingual – Automatic AI translation – wpLingua 2.12.2, at inc/admin/translation-meta.php

530 lines 14.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 // If this file is called directly, abort.
4 if ( ! defined( 'WPINC' ) ) {
5 die;
6 }
7
8
9 /**
10 * Add meta box on wpLingua translations
11 *
12 * This function adds a meta box to the wpLingua Translations post type in the
13 * WordPress admin interface. The meta box is used to display and edit the
14 * translation of a post.
15 *
16 * @param object $post The post object.
17 * @return void
18 */
19 function wplng_translation_add_meta_box( $post ) {
20
21 add_meta_box(
22 'wplng_meta_box_translation', // Unique ID for the meta box
23 __( 'Translation', 'wplingua' ), // Title of the meta box
24 'wplng_translation_meta_box_html_output', // Callback function to render the meta box HTML
25 'wplng_translation', // Screen or post type where the meta box appears
26 'normal', // Context where the meta box should appear
27 'low' // Priority within the context
28 );
29 }
30
31
32 /**
33 * Print HTML of translations editor meta box in back office
34 *
35 * This function prints the HTML of the translations editor meta box in the
36 * WordPress admin interface. The meta box is used to display and edit the
37 * translation of a post.
38 *
39 * @param object $post The post object.
40 * @return string HTML The HTML of the meta box.
41 */
42 function wplng_translation_meta_box_html_output( $post ) {
43
44 echo '<div id="wplng-translation-editor">';
45 echo wplng_translation_editor_get_html( $post );
46 echo '</div>';
47 }
48
49
50 /**
51 * Return HTML of translations editor in modal
52 *
53 * @param WP_Post $post The post object.
54 * @return string The HTML of the translations editor in the modal.
55 */
56 function wplng_translation_editor_get_html( $post ) {
57
58 // used later for security
59 $html = wp_nonce_field(
60 basename( __FILE__ ),
61 'wplng_translation_meta_box_nonce',
62 true,
63 false
64 );
65
66 $meta = get_post_meta( $post->ID );
67
68 // Display original text
69 if ( ! empty( $meta['wplng_translation_original'][0] )
70 && is_string( $meta['wplng_translation_original'][0] )
71 && ! empty( $meta['wplng_translation_original_language_id'][0] )
72 && wplng_is_valid_language_id( $meta['wplng_translation_original_language_id'][0] )
73 ) {
74
75 $language_id = $meta['wplng_translation_original_language_id'][0];
76 $language = wplng_get_language_by_id( $language_id );
77 $alt = __( 'Flag for language: ', 'wplingua' ) . $language['name'];
78
79 $html .= '<div id="wplng-original-language" wplng-lang="' . esc_attr( $language_id ) . '">';
80 $html .= '<div id="wplng-source-title">';
81 $html .= '<img';
82 $html .= ' src="' . esc_url( $language['flag'] ) . '"';
83 $html .= ' alt="' . esc_attr( $alt ) . '"';
84 $html .= ' class="wplng-flag"';
85 $html .= '>';
86 $html .= esc_html( $language['name'] );
87 $html .= esc_html__( ' - Original text: ', 'wplingua' );
88 $html .= '</div>'; // End #wplng-source-title
89 $html .= '<div id="wplng-source">';
90 $html .= esc_html( wplng_text_esc_displayed( $meta['wplng_translation_original'][0] ) );
91 $html .= '</div>'; // End #wplng-source
92 $html .= '</div>'; // End #wplng-original-language
93
94 }
95
96 // Foreach translation, display form textarea to edit
97 if ( ! empty( $meta['wplng_translation_translations'][0] )
98 && is_string( $meta['wplng_translation_translations'][0] )
99 ) {
100
101 $translations_data = json_decode( $meta['wplng_translation_translations'][0], true );
102 $languages_target = wplng_get_languages_target_ids();
103 $translations = array();
104
105 if ( empty( $translations_data ) ) {
106 $translations_data = array();
107 }
108
109 foreach ( $languages_target as $language_target ) {
110
111 $is_in = false;
112
113 foreach ( $translations_data as $translation_data ) {
114
115 if ( empty( $translation_data['language_id'] )
116 || ! wplng_is_valid_language_id( $translation_data['language_id'] )
117 || empty( $translation_data['translation'] )
118 || ! is_string( $translation_data['translation'] )
119 || ( $translation_data['language_id'] !== $language_target )
120 ) {
121 continue;
122 }
123
124 $is_in = true;
125 $translations[] = $translation_data;
126
127 }
128
129 if ( ! $is_in ) {
130 $translations[] = array(
131 'language_id' => $language_target,
132 'translation' => '[WPLNG_EMPTY]',
133 'status' => 'ungenerated',
134 );
135 }
136 }
137
138 foreach ( $translations as $translation ) {
139
140 $language_id = $translation['language_id'];
141 $language = wplng_get_language_by_id( $language_id );
142 $textarea = $translation['translation'];
143 $name = 'wplng_translation_' . $language_id;
144 $container_id = 'wplng-translation-' . $language_id;
145 $generate_link = __( 'Regenerate translation', 'wplingua' );
146 $alt = __( 'Flag for language: ', 'wplingua' ) . $language['name'];
147 $class = 'wplng-edit-language';
148 $reviewed_title = __( 'Mark as review', 'wplingua' );
149 $is_reviewed = false;
150
151 if ( '[WPLNG_EMPTY]' === $textarea ) {
152 $textarea = '';
153 }
154
155 switch ( $translation['status'] ) {
156 case 'ungenerated':
157 $generate_link = __( 'Generate translation', 'wplingua' );
158 $class .= ' wplng-status-ungenerated';
159 break;
160
161 case 'generated':
162 $class .= ' wplng-status-generated';
163 break;
164
165 default:
166 if ( is_int( $translation['status'] ) ) {
167
168 $class .= ' wplng-status-reviewed';
169 $is_reviewed = true;
170
171 // Get and check date format
172 $date_format = get_option( 'date_format' );
173 if ( ! is_string( $date_format ) || empty( $date_format ) ) {
174 $date_format = 'F j, Y';
175 }
176
177 // Get and check time format
178 $time_format = get_option( 'time_format' );
179 if ( ! is_string( $time_format ) || empty( $time_format ) ) {
180 $time_format = 'g:i a';
181 }
182
183 $reviewed_title = __( 'Reviewed on ', 'wplingua' );
184 $reviewed_title .= esc_html(
185 gmdate(
186 $date_format,
187 $translation['status']
188 )
189 );
190 $reviewed_title .= ', ' . esc_html(
191 gmdate(
192 $time_format,
193 $translation['status']
194 )
195 );
196 }
197 break;
198 }
199
200 $html .= '<div';
201 $html .= ' id="' . esc_attr( $container_id ) . '"';
202 $html .= ' class="' . esc_attr( $class ) . '"';
203 $html .= ' wplng-lang="' . esc_attr( $language_id ) . '"';
204 $html .= '>';
205 $html .= '<label for="' . esc_attr( $name ) . '" class="wplng-target-title">';
206 $html .= '<img';
207 $html .= ' src="' . esc_url( $language['flag'] ) . '"';
208 $html .= ' alt="' . esc_attr( $alt ) . '"';
209 $html .= ' class="wplng-flag"';
210 $html .= '>';
211 $html .= esc_html( $language['name'] );
212 $html .= esc_html__( ' - Translation: ', 'wplingua' );
213 $html .= '</label>';
214 $html .= '<textarea';
215 $html .= ' name="' . esc_attr( $name ) . '"';
216 $html .= ' id="' . esc_attr( $name ) . '"';
217 $html .= ' class="wplng-translation-textarea"';
218 $html .= ' lang="' . esc_attr( $language_id ) . '"';
219 $html .= ' spellcheck="false"';
220 $html .= '>';
221 $html .= esc_html( html_entity_decode( $textarea ) );
222 $html .= '</textarea>';
223
224 if ( empty( $translation['status'] ) ) {
225 $html .= '</div>';
226 continue;
227 }
228
229 $html .= '<div class="wplng-translation-footer">';
230 $html .= '<div class="wplng-translation-footer-left">';
231
232 $html .= '<fieldset class="wplng-mark-as-reviewed">';
233
234 $html .= '<input';
235 $html .= ' type="checkbox"';
236 $html .= ' id="wplng_mark_as_reviewed_' . esc_attr( $language_id ) . '"';
237 $html .= ' name="wplng_mark_as_reviewed_' . esc_attr( $language_id ) . '"';
238 $html .= ' wplng-lang="' . esc_attr( $language_id ) . '"';
239 $html .= checked( $is_reviewed, true, false );
240 $html .= '>';
241
242 $html .= '<label';
243 $html .= ' for="wplng_mark_as_reviewed_' . esc_attr( $language_id ) . '"';
244 $html .= ' wplng-lang="' . esc_attr( $language_id ) . '"';
245 $html .= ' title="' . esc_attr( $reviewed_title ) . '"';
246 $html .= '>';
247 $html .= esc_html__( 'Is reviewed', 'wplingua' );
248 $html .= '</label>';
249
250 $html .= '</fieldset>';
251
252 $html .= '</div>'; // End .wplng-translation-footer-right
253 $html .= '<div class="wplng-translation-footer-right">';
254
255 $html .= '<span';
256 $html .= ' class="dashicons dashicons-update wplng-spin wplng-generate-spin"';
257 $html .= ' style="display: none;"';
258 $html .= '></span> ';
259
260 $html .= '<a';
261 $html .= ' href="javascript:void(0);"';
262 $html .= ' class="wplng-generate"';
263 $html .= ' wplng-lang="' . esc_attr( $language_id ) . '"';
264 $html .= '>';
265 $html .= esc_html( $generate_link );
266 $html .= '</a>';
267
268 $html .= '</div>'; // End .wplng-translation-footer-right
269 $html .= '</div>'; // End .wplng-translation-footer
270 $html .= '</div>'; // End .wplng-edit-language
271
272 }
273 }
274
275 if ( ! empty( $meta['wplng_translation_discovery_url'][0] )
276 && is_string( $meta['wplng_translation_discovery_url'][0] )
277 ) {
278 $url = home_url( $meta['wplng_translation_discovery_url'][0] );
279
280 $html .= '<div id="wplng-discovery-url">';
281 $html .= '<strong>';
282 $html .= esc_html__( 'Discovery URL: ' ) . ' ';
283 $html .= '</strong>';
284 $html .= '<a';
285 $html .= ' href="' . esc_url( $url ) . '"';
286 $html .= '>';
287 $html .= esc_html( $url );
288 $html .= '</a>';
289 $html .= '</div>'; // End #wplng-discovery-url
290 }
291
292 return $html;
293 }
294
295
296 /**
297 * Save meta box data of wpLingua translations
298 *
299 * @param int $post_id
300 * @return void
301 */
302 function wplng_translation_save_meta_boxes_data( $post_id ) {
303
304 // Check if nonce is set
305 if ( ! isset( $_POST['wplng_translation_meta_box_nonce'] ) ) {
306 return false;
307 }
308
309 // Sanitize the nonce
310 $nonce = $_POST['wplng_translation_meta_box_nonce'];
311 $nonce = sanitize_text_field( wp_unslash( $nonce ) );
312
313 // Check for nonce to top xss
314 if ( ! wp_verify_nonce( $nonce, basename( __FILE__ ) ) ) {
315 return false;
316 }
317
318 // check for correct user capabilities - stop internal xss from customers
319 if ( ! current_user_can( 'edit_post', $post_id ) ) {
320 return false;
321 }
322
323 wplng_clear_translations_cache();
324
325 $meta = get_post_meta( $post_id );
326
327 if ( empty( $meta['wplng_translation_translations'][0] ) ) {
328 return false;
329 }
330
331 $languages_target = wplng_get_languages_target_ids();
332 $translations = json_decode(
333 $meta['wplng_translation_translations'][0],
334 true
335 );
336
337 if ( empty( $translations ) ) {
338 $translations = array();
339 }
340
341 foreach ( $languages_target as $language_target ) {
342
343 $is_in = false;
344
345 foreach ( $translations as $translation ) {
346
347 if ( empty( $translation['language_id'] )
348 || ! wplng_is_valid_language_id( $translation['language_id'] )
349 || ! isset( $translation['translation'] )
350 || ! is_string( $translation['translation'] )
351 || ( $translation['language_id'] !== $language_target )
352 ) {
353 continue;
354 }
355
356 $is_in = true;
357 break;
358 }
359
360 if ( ! $is_in ) {
361 $translations[] = array(
362 'language_id' => $language_target,
363 'translation' => '[WPLNG_EMPTY]',
364 'status' => 'ungenerated',
365 );
366 }
367 }
368
369 foreach ( $translations as $key => $translation ) {
370
371 if ( empty( $translation['language_id'] )
372 || ! wplng_is_valid_language_id( $translation['language_id'] )
373 ) {
374 continue;
375 }
376
377 $name = 'wplng_translation_' . $translation['language_id'];
378 $reviewed = 'wplng_mark_as_reviewed_' . $translation['language_id'];
379
380 if ( ! isset( $_POST[ $name ] ) ) {
381 continue;
382 }
383
384 $temp = stripslashes( wplng_text_esc( $_POST[ $name ] ) );
385
386 if ( empty( $temp ) ) {
387 $temp = '[WPLNG_EMPTY]';
388 $translations[ $key ]['status'] = 'ungenerated';
389 } else {
390
391 if ( $temp !== $translation['translation'] ) {
392 $temp = str_replace( '\\', '', $temp );
393 }
394
395 if ( ! empty( $_POST[ $reviewed ] ) ) {
396 if ( 'false' !== $_POST[ $reviewed ] ) {
397 $translations[ $key ]['status'] = time();
398 } else {
399 $translations[ $key ]['status'] = 'generated';
400 }
401 } else {
402 $translations[ $key ]['status'] = 'generated';
403 }
404 }
405
406 $translations[ $key ]['translation'] = esc_html( $temp );
407 }
408
409 /**
410 * Save meta: Translation array as JSON
411 */
412
413 $meta_return = update_post_meta(
414 $post_id,
415 'wplng_translation_translations',
416 wp_json_encode(
417 $translations,
418 JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
419 )
420 );
421
422 if ( false === $meta_return ) {
423
424 // Try to save it with encoded emoji
425 foreach ( $translations as $key => $translation ) {
426
427 if ( empty( $translation['translation'] )
428 || '[WPLNG_EMPTY]' === $translation['translation']
429 ) {
430 continue;
431 }
432
433 $translations[ $key ]['translation'] = wp_encode_emoji( $translation['translation'] );
434 }
435
436 $meta_return = update_post_meta(
437 $post_id,
438 'wplng_translation_translations',
439 wp_json_encode(
440 $translations,
441 JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
442 )
443 );
444
445 }
446
447 return $meta_return;
448 }
449
450
451 /**
452 * wpLingua AJAX function to get translations on CPT edit page
453 *
454 * @return void
455 */
456 function wplng_ajax_generate_translation() {
457
458 /**
459 * Check and sanitize data
460 */
461
462 // Check data
463
464 if ( empty( $_POST['language_source'] )
465 || ! is_string( $_POST['language_source'] )
466 || empty( $_POST['language_target'] )
467 || ! is_string( $_POST['language_target'] )
468 || ! isset( $_POST['text'] )
469 || ! is_string( $_POST['text'] )
470 ) {
471 wp_send_json_error( __( 'Invalid parameters', 'wplingua' ) );
472 return;
473 }
474
475 // Check and sanitize sources and target languages
476
477 $language_source = sanitize_key( $_POST['language_source'] );
478 $language_target = sanitize_key( $_POST['language_target'] );
479
480 if ( ! wplng_is_valid_language_id( $language_source )
481 || ! wplng_is_valid_language_id( $language_target )
482 || $language_source === $language_target
483 ) {
484 wp_send_json_error( __( 'Invalid languages parameters', 'wplingua' ) );
485 return;
486 }
487
488 // Check and sanitize text to translate
489 // (And convert img emoji to emoji)
490
491 $text = wp_kses(
492 $_POST['text'],
493 array(
494 'img' => array(
495 'alt' => array(),
496 ),
497 )
498 );
499
500 $text = preg_replace(
501 '/<img alt=\\"(.*)\\">/U',
502 '$1',
503 $text
504 );
505
506 $text = wplng_text_esc( $text );
507
508 if ( ! wplng_text_is_translatable( $text ) ) {
509 wp_send_json_success( $text );
510 return;
511 }
512
513 /**
514 * Call API and get translation
515 */
516
517 $response = wplng_api_call_translate(
518 array( $text ),
519 $language_source,
520 $language_target
521 );
522
523 if ( ! isset( $response[0] ) ) {
524 wp_send_json_error( __( 'Invalid API response', 'wplingua' ) );
525 return;
526 }
527
528 wp_send_json_success( $response[0] );
529 }
530