PluginProbe
Translate and Go multilingual – Automatic AI translation – wpLingua / 2.16.6
Translate and Go multilingual – Automatic AI translation – wpLingua v2.16.6
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 1.1.1 All 111 releases
wplingua / inc / dom / load-progress.php

load-progress.php in Translate and Go multilingual – Automatic AI translation – wpLingua 2.16.6, at inc/dom/load-progress.php

455 lines 11.5 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 * Modify dom for the "in progress" load mode
11 *
12 * @param object $dom
13 * @param array $args
14 * @return object
15 */
16 function wplng_dom_load_progress( $dom, $args ) {
17
18 wplng_args_setup( $args );
19
20 if ( $args['load'] !== 'progress'
21 || ! current_user_can( 'edit_posts' )
22 ) {
23 return $dom;
24 }
25
26 /**
27 * Add effect on unknow texts and translate know texts
28 */
29
30 $edit_link_excluded = wplng_data_excluded_editor_link();
31 $node_text_excluded = wplng_data_excluded_node_text();
32
33 foreach ( $dom->find( 'body text' ) as $element ) {
34
35 if ( in_array( $element->parent->tag, $edit_link_excluded )
36 || in_array( $element->parent->tag, $node_text_excluded )
37 ) {
38 continue;
39 }
40
41 $text = $element->innertext;
42
43 if ( empty( trim( $text ) ) ) {
44 continue;
45 }
46
47 // Manage non breaking space
48 $text = str_replace(
49 array( '&nbsp;', html_entity_decode( '&nbsp;' ) ),
50 array( ' ', ' ' ),
51 $text
52 );
53
54 /**
55 * Get spaces before and after text
56 */
57
58 $temp = array();
59 $spaces_before = '';
60 $spaces_after = '';
61
62 preg_match( '/^(\s*).*/', $text, $temp );
63 if ( ! empty( $temp[1] ) ) {
64 $spaces_before = $temp[1];
65 }
66
67 preg_match( '/.*(\s*)$/U', $text, $temp );
68 if ( ! empty( $temp[1] ) ) {
69 $spaces_after = $temp[1];
70 }
71
72 $text = wplng_text_esc( $text );
73 $translated = '';
74
75 if ( wplng_text_is_translatable( $text ) ) {
76
77 foreach ( $args['translations'] as $translation ) {
78 if ( $text === $translation['source'] ) {
79 $translated = $translation['translation'];
80 break;
81 }
82 }
83
84 if ( '' === $translated ) {
85
86 $innertext = '<span';
87 $innertext .= ' class="wplng-in-progress-text"';
88 $innertext .= ' title="' . esc_attr__( 'Translation in progress', 'wplingua' ) . '"';
89 $innertext .= '>';
90 $innertext .= esc_html( $spaces_before . $text . $spaces_after );
91 $innertext .= '</span>';
92
93 $element->innertext = $innertext;
94
95 } else {
96 $element->innertext = esc_html( $spaces_before . $translated . $spaces_after );
97 }
98 }
99 }
100
101 /**
102 * Create the html of message bar
103 */
104
105 $number_of_texts = (int) $args['count_texts'];
106 $numer_of_translated_texts = count( $args['translations'] );
107
108 // Calculate percentage
109
110 $percentage = 0;
111 if ( $number_of_texts > 0 ) {
112 $percentage = (int) ( ( $numer_of_translated_texts / $number_of_texts ) * 100 );
113 }
114
115 if ( $percentage < 1 ) {
116 $percentage = 1;
117 }
118
119 // Make the HTML
120
121 $html = '<div id="wplng-in-progress-container">';
122
123 $html .= '<div id="wplng-in-progress-message">';
124 $html .= '<span class="dashicons dashicons-update wplng-spin"></span> ';
125
126 $html .= '<span class="wplng-in-progress-text-mobile">';
127 $html .= esc_html__( 'Translation in progress', 'wplingua' );
128 $html .= '</span>';
129
130 $html .= '<span class="wplng-in-progress-text-desktop">';
131 $html .= esc_html__( 'In progress: Translation and saving of new texts', 'wplingua' );
132 $html .= '</span>';
133
134 $html .= ' - ';
135 $html .= '<span id="wplng-in-progress-percent">';
136 $html .= esc_html( $percentage );
137 $html .= '</span>';
138 $html .= ' %';
139 $html .= '</div>'; // End #wplng-translation-in-progress
140
141 $html .= '<div id="wplng-progress-bar">';
142 $html .= '<div';
143 $html .= ' id="wplng-progress-bar-value"';
144 $html .= ' style="width: ' . esc_attr( $percentage ) . '%"';
145 $html .= '>';
146 $html .= '</div>'; // End #wplng-progress-bar-value
147 $html .= '</div>'; // End #wplng-progress-bar
148
149 $html .= '<div id="wplng-in-progress-error" style="display: none;">';
150 $html .= '<span class="dashicons dashicons-info-outline"></span> ';
151
152 $html .= '<span class="wplng-in-progress-text-mobile">';
153 $html .= esc_html__( 'Error during translation.', 'wplingua' );
154 $html .= '</span>';
155
156 $html .= '<span class="wplng-in-progress-text-desktop">';
157 $html .= esc_html__( 'Error during translation. Check the browser console for more information.', 'wplingua' );
158 $html .= '</span>';
159
160 $html .= '<span';
161 $html .= ' id="wplng-in-progress-error-close"';
162 $html .= ' class="dashicons dashicons-no-alt"';
163 $html .= ' title="' . esc_attr__( 'Close', 'wplingua' ) . '"';
164 $html .= '></span>';
165
166 $html .= '</div>'; // End wplng-in-progress-error
167
168 $html .= '</div>'; // End #wplng-in-progress-container
169
170 /**
171 * Prepare the texts chunks
172 */
173
174 $texts_unknow_by_chunk = array();
175 $current_chunk = array();
176 $current_chars = 0;
177 $max_items_per_chunk = (int) ( WPLNG_MAX_TRANSLATIONS_STR / 4 );
178 $max_chars_per_chunk = (int) ( WPLNG_MAX_TRANSLATIONS_CHAR / 4 );
179
180 foreach ( $args['texts_unknow'] as $text_unknow ) {
181
182 $text = (string) $text_unknow;
183 $len = function_exists( 'mb_strlen' ) ? mb_strlen( $text, 'UTF-8' ) : strlen( $text );
184
185 // If a single item is longer than allowed, truncate it to fit the limit.
186 if ( $len > $max_chars_per_chunk ) {
187 $text = function_exists( 'mb_substr' ) ? mb_substr( $text, 0, $max_chars_per_chunk, 'UTF-8' ) : substr( $text, 0, $max_chars_per_chunk );
188 $len = $max_chars_per_chunk;
189 }
190
191 // If adding this text would exceed either limit, flush current chunk.
192 if ( count( $current_chunk ) >= $max_items_per_chunk || ( $current_chars + $len ) > $max_chars_per_chunk ) {
193 if ( ! empty( $current_chunk ) ) {
194 $texts_unknow_by_chunk[] = array(
195 'percentage' => (int) ( ( $numer_of_translated_texts / $number_of_texts ) * 100 ),
196 'texts' => $current_chunk,
197 );
198 $current_chunk = array();
199 $current_chars = 0;
200 }
201 }
202
203 $current_chunk[] = wplng_encryption_encrypt( $text );
204 $current_chars += $len;
205 ++$numer_of_translated_texts;
206
207 $max_items_per_chunk = (int) ( WPLNG_MAX_TRANSLATIONS_STR / 2 );
208 $max_chars_per_chunk = (int) ( WPLNG_MAX_TRANSLATIONS_CHAR / 2 );
209 }
210
211 // Push last chunk if any
212 if ( ! empty( $current_chunk ) ) {
213 $texts_unknow_by_chunk[] = array(
214 'percentage' => 99,
215 'texts' => $current_chunk,
216 );
217 }
218
219 /**
220 * Make the reload URL
221 */
222
223 $url_reload_query_arg = array(
224 'wplng-load' => 'translated',
225 'nocache' => (string) time() . (string) rand( 100, 999 ),
226 );
227
228 if ( $args['mode'] !== 'vanilla' ) {
229 $url_reload_query_arg['wplng-mode'] = $args['mode'];
230 }
231
232 $url_reload = add_query_arg(
233 $url_reload_query_arg,
234 $args['url_current']
235 );
236
237 // Ensure the reload URL is safe for JS: decode entities then use esc_url_raw (programmatic URL)
238 $url_reload_for_js = html_entity_decode( $url_reload, ENT_QUOTES | ENT_HTML5, 'UTF-8' );
239 $url_reload_for_js = esc_url_raw( $url_reload_for_js );
240
241 /**
242 * JS Script - Load in progress - Data
243 */
244
245 $load_in_progress_data_js = array(
246 'urlAjax' => esc_url_raw( admin_url( 'admin-ajax.php' ) ),
247 'urlReload' => $url_reload_for_js,
248 'language' => esc_attr( $args['language_target'] ),
249 'chunks' => $texts_unknow_by_chunk,
250 'nonce' => wp_create_nonce( 'wplng_load_in_progress' ),
251 );
252
253 $html .= '<script id="wplingua-js-load-in-progress-data">';
254 $html .= 'let wplngLoadInProgressData = ' . wp_json_encode( $load_in_progress_data_js ) . ';';
255 $html .= '</script>';
256
257 /**
258 * JS Script - Load in progress - File
259 */
260
261 $script_url = plugins_url() . '/wplingua/assets/js/load-in-progress.js';
262 $script_url .= '?ver=' . WPLNG_PLUGIN_VERSION;
263
264 $html .= '<script';
265 $html .= ' type="text/javascript"';
266 $html .= ' src="' . esc_url( $script_url ) . '"';
267 $html .= ' id="wplingua-js-load-in-progress-script"';
268 $html .= '>';
269 $html .= '</script>';
270
271 /**
272 * Place the HTML in the end of body
273 */
274
275 foreach ( $dom->find( 'body' ) as $body ) {
276 $body->innertext = $body->innertext . $html;
277 }
278
279 return $dom;
280 }
281
282
283 /**
284 * AJAX handler for "load in progress" translations.
285 *
286 * Validates the current user capability and incoming POST data, requests
287 * translations for the provided texts and returns a JSON response. On error
288 * a JSON error response is sent and execution ends.
289 *
290 * Expected POST parameters:
291 * - wplng_language (string) : target language id
292 * - wplng_texts (array) : list of texts to translate
293 *
294 * Permissions: current user must have 'edit_posts'.
295 *
296 * @return void Sends JSON success or error and exits.
297 */
298 function wplng_ajax_load_in_progress() {
299
300 /**
301 * Check authorizations
302 */
303
304 if ( ! current_user_can( 'edit_posts' ) ) {
305 wp_send_json_error(
306 array(
307 'error_load_in_progress' => true,
308 'error_message' => 'Unauthorized user',
309 )
310 );
311 return;
312 }
313
314 // Verify nonce sent from client
315 if ( empty( $_POST['wplng_nonce'] )
316 || ! wp_verify_nonce( wp_unslash( $_POST['wplng_nonce'] ), 'wplng_load_in_progress' )
317 ) {
318 wp_send_json_error(
319 array(
320 'error_load_in_progress' => true,
321 'error_message' => 'Invalid nonce',
322 )
323 );
324 return;
325 }
326
327 if ( ! wplng_api_feature_is_allow( 'detection' ) ) {
328 wp_send_json_error(
329 array(
330 'error_load_in_progress' => true,
331 'error_message' => 'Error detecting text',
332 )
333 );
334 return;
335 }
336
337 /**
338 * Check target language
339 */
340
341 // Check if language is defined
342
343 if ( empty( $_POST['wplng_language'] ) ) {
344 wp_send_json_error(
345 array(
346 'error_load_in_progress' => true,
347 'error_message' => 'Empty language',
348 )
349 );
350 return;
351 }
352
353 $language_target = sanitize_text_field( wp_unslash( $_POST['wplng_language'] ) );
354
355 // Check if is a valid language ID
356
357 if ( ! wplng_is_valid_language_id( $language_target ) ) {
358 wp_send_json_error(
359 array(
360 'error_load_in_progress' => true,
361 'error_message' => 'Invalid language',
362 )
363 );
364 return;
365 }
366
367 // Check if languagd is allow
368
369 if ( ! in_array( $language_target, wplng_get_languages_target_ids() ) ) {
370 wp_send_json_error(
371 array(
372 'error_load_in_progress' => true,
373 'error_message' => 'Unauthorized website language - ' . esc_attr( $language_target ),
374 )
375 );
376 return;
377 }
378
379 /**
380 * Check texts to translate
381 */
382
383 if ( empty( $_POST['wplng_texts'] )
384 || ! is_array( $_POST['wplng_texts'] )
385 ) {
386 wp_send_json_error(
387 array(
388 'error_load_in_progress' => true,
389 'error_message' => 'Invalid texts data',
390 )
391 );
392 return;
393 }
394
395 $texts_original_encrypted = wp_unslash( $_POST['wplng_texts'] );
396 $texts_original = array();
397
398 foreach ( $texts_original_encrypted as $text_encrypted ) {
399 $text_decrypted = wplng_encryption_decrypt( $text_encrypted );
400
401 if ( $text_decrypted === '' ) {
402 wp_send_json_error(
403 array(
404 'error_load_in_progress' => true,
405 'error_message' => 'Texts decryption failed',
406 )
407 );
408 return;
409 }
410
411 $texts_original[] = $text_decrypted;
412 }
413
414 /**
415 * Setup $args and get translations
416 */
417
418 $args = array( 'language_target' => $language_target );
419 wplng_args_setup( $args );
420 wplng_args_update_from_texts( $args, $texts_original );
421
422 /**
423 * Basic translation check
424 */
425
426 if ( empty( $args['translations'] ) ) {
427 wp_send_json_error(
428 array(
429 'error_load_in_progress' => true,
430 'error_message' => 'No translation returned',
431 )
432 );
433 return;
434 }
435
436 /**
437 * Setup translation array for AJAX response
438 */
439
440 $translation_response = array();
441
442 foreach ( $args['translations'] as $translation ) {
443 if ( ! isset( $translation['source'] ) || ! isset( $translation['translation'] ) ) {
444 continue;
445 }
446
447 $translation_response[] = array(
448 'source' => $translation['source'],
449 'translation' => $translation['translation'],
450 );
451 }
452
453 wp_send_json_success( $translation_response );
454 }
455