PluginProbe
Translate and Go multilingual – Automatic AI translation – wpLingua / 2.10.0
Translate and Go multilingual – Automatic AI translation – wpLingua v2.10.0
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 / slug.php

slug.php in Translate and Go multilingual – Automatic AI translation – wpLingua 2.10.0, at inc/slug.php

620 lines 12.7 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 * Get the original slug from a translation (without "/")
11 *
12 * @param string $slug
13 * @param string $language_id
14 * @param array|false $slugs_translations
15 * @return array Original slug
16 */
17 function wplng_slug_original( $slug, $language_id, $slugs_translations = false ) {
18
19 if ( ! wplng_text_is_translatable( $slug )
20 || wplng_str_contains( $slug, '.' )
21 ) {
22 return $slug;
23 }
24
25 if ( false === $slugs_translations ) {
26 $slugs_translations = wplng_get_slugs();
27 }
28
29 foreach ( $slugs_translations as $slug_translations ) {
30
31 if ( ! isset( $slug_translations['translations'][ $language_id ] )
32 || $slug !== $slug_translations['translations'][ $language_id ]
33 ) {
34 continue;
35 }
36
37 $slug = $slug_translations['source'];
38
39 break;
40 }
41
42 return $slug;
43 }
44
45
46 /**
47 * Get the original path from a translation
48 *
49 * @param string $path
50 * @param string $language_id
51 * @return string Original path
52 */
53 function wplng_slug_original_path( $path, $language_id ) {
54
55 /**
56 * Remove language ID
57 */
58
59 if ( wplng_str_starts_with( $path, '/' . $language_id . '/' ) ) {
60 $path = str_replace( '/' . $language_id . '/', '/', $path );
61 } elseif ( wplng_str_starts_with( $path, $language_id . '/' ) ) {
62 $path = str_replace( $language_id . '/', '/', $path );
63 }
64
65 /**
66 * Return parth if no contains slug
67 */
68
69 if ( '/' === $path || '' === $path ) {
70 return $path;
71 }
72
73 /**
74 * Transform the multi part slug to an slug array
75 */
76
77 $slugs = explode( '/', $path );
78 $slugs = array_filter( $slugs );
79
80 /**
81 * Get the slug translation list and translate $path
82 */
83
84 $slugs_translations = wplng_get_slugs();
85
86 $path_original = '/';
87
88 foreach ( $slugs as $slug ) {
89 $path_original .= wplng_slug_original(
90 $slug,
91 $language_id,
92 $slugs_translations
93 ) . '/';
94
95 }
96
97 /**
98 * Set the first and last '/' to $path_original like $path
99 */
100
101 if ( ! wplng_str_starts_with( $path, '/' ) ) {
102 $path_original = substr( $path_original, 1 );
103 }
104
105 if ( ! wplng_str_ends_with( $path, '/' ) ) {
106 $path_original = substr( $path_original, 0, -1 );
107 }
108
109 return $path_original;
110 }
111
112
113 /**
114 * Get a translated slug
115 *
116 * @param string $slug
117 * @param string $language_id
118 * @param array|false $slugs_translations
119 * @return string Translated slug
120 */
121 function wplng_slug_translate( $slug, $language_id, $slugs_translations = false ) {
122
123 if ( ! wplng_text_is_translatable( $slug )
124 || wplng_str_contains( $slug, '.' )
125 ) {
126 return $slug;
127 }
128
129 if ( false === $slugs_translations ) {
130 $slugs_translations = wplng_get_slugs();
131 }
132
133 $slug_translation_exist = false;
134
135 foreach ( $slugs_translations as $slug_translations ) {
136
137 if ( $slug !== $slug_translations['source']
138 || ! isset( $slug_translations['translations'][ $language_id ] )
139 ) {
140 continue;
141 }
142
143 $slug = $slug_translations['translations'][ $language_id ];
144
145 $slug_translation_exist = true;
146 break;
147 }
148
149 /**
150 * Slug translation is not in slug cache
151 * Check if exist in DB
152 *
153 * If not exist, create it
154 */
155
156 if ( false === $slug_translation_exist
157 && false === wplng_get_slug_saved_from_original( $slug )
158 ) {
159 wplng_create_slug( $slug );
160 }
161
162 return $slug;
163 }
164
165
166 /**
167 * Get a translated path
168 *
169 * @param string $path
170 * @param string $language_id
171 * @return string Translated path
172 */
173 function wplng_slug_translate_path( $path, $language_id ) {
174
175 /**
176 * Remove language ID
177 */
178
179 $target = wplng_get_languages_target_ids();
180
181 foreach ( $target as $target_id ) {
182 if ( wplng_str_starts_with( $path, '/' . $target_id . '/' ) ) {
183 $path = str_replace( '/' . $target_id . '/', '/', $path );
184 } elseif ( wplng_str_starts_with( $path, $target_id . '/' ) ) {
185 $path = str_replace( $target_id . '/', '/', $path );
186 }
187 }
188
189 /**
190 * Manage anchor ang $_GET parameters
191 */
192
193 $path = str_replace(
194 array( '#', '?' ),
195 array( '/#', '/?' ),
196 $path
197 );
198
199 /**
200 * Return parth if no contains slug
201 */
202
203 if ( '/' === $path || '' === $path ) {
204 return $path;
205 }
206
207 /**
208 * Transform the multi part slug to an slug array
209 */
210
211 $slugs = explode( '/', $path );
212 $slugs = array_filter( $slugs );
213
214 /**
215 * Get the slug translation list and translate $path
216 */
217
218 $slugs_translations = wplng_get_slugs();
219
220 $path_translated = '/';
221
222 foreach ( $slugs as $slug ) {
223 if ( wplng_text_is_translatable( $slug )
224 && ! wplng_str_starts_with( $slug, '#' )
225 && ! wplng_str_starts_with( $slug, '?' )
226 && ! wplng_str_contains( $slug, '.' )
227 ) {
228 $path_translated .= wplng_slug_translate(
229 $slug,
230 $language_id,
231 $slugs_translations
232 ) . '/';
233 } else {
234 $path_translated .= $slug . '/';
235 }
236 }
237
238 /**
239 * Set the first and last '/' to $path_translated like $path
240 */
241
242 if ( ! wplng_str_starts_with( $path, '/' ) ) {
243 $path_translated = substr( $path_translated, 1 );
244 }
245
246 if ( ! wplng_str_ends_with( $path, '/' ) ) {
247 $path_translated = substr( $path_translated, 0, -1 );
248 }
249
250 return $path_translated;
251 }
252
253
254 /**
255 * Create a wplng_slug
256 *
257 * @param string $slug
258 * @return int|false The post ID or false
259 */
260 function wplng_create_slug( $slug ) {
261
262 if ( is_404()
263 || ! current_user_can( 'edit_posts' )
264 || wplng_str_contains( $slug, '.' )
265 ) {
266 return false;
267 }
268
269 $slug = sanitize_title( $slug );
270
271 if ( '' === $slug
272 || ! wplng_text_is_translatable( $slug )
273 || wplng_is_valid_language_id( $slug )
274 || 'go' === $slug
275 || 'refer' === $slug
276 || 'recommend' === $slug
277 || 'recommends' === $slug
278 || 'wp-includes' === $slug
279 || 'wp-json' === $slug
280 ) {
281 return false;
282 }
283
284 /**
285 * Make the title
286 */
287
288 $tite_max_length = 100;
289 $title = mb_substr( $slug, 0, $tite_max_length );
290 $title = sanitize_title( $slug );
291 $title = urldecode( $slug );
292
293 if ( strlen( $slug ) > $tite_max_length ) {
294 $title = '/' . $title . __( '...', 'wplingua' );
295 } else {
296 $title = '/' . $title . '/';
297 }
298
299 /**
300 * Create the post and get this ID
301 */
302
303 $new_post_id = wp_insert_post(
304 array(
305 'post_title' => esc_html( $title ),
306 'post_type' => 'wplng_slug',
307 'post_status' => 'publish',
308 'post_name' => sanitize_title(
309 'wplingua-slug-' . md5( $title ) . '-' . $title
310 ),
311 )
312 );
313
314 if ( is_wp_error( $new_post_id ) || empty( $new_post_id ) ) {
315 return false;
316 }
317
318 /**
319 * Make $translation_meta
320 */
321
322 $languages_target = wplng_get_languages_target_ids();
323 $slug_meta = array();
324
325 foreach ( $languages_target as $target_language ) {
326 $slug_meta[] = array(
327 'language_id' => $target_language,
328 'translation' => $slug,
329 'status' => 'ungenerated',
330 );
331 }
332
333 add_post_meta(
334 $new_post_id,
335 'wplng_slug_original',
336 $slug
337 );
338
339 add_post_meta(
340 $new_post_id,
341 'wplng_slug_md5',
342 md5( $slug )
343 );
344
345 add_post_meta(
346 $new_post_id,
347 'wplng_slug_original_language_id',
348 wplng_get_language_website_id()
349 );
350
351 add_post_meta(
352 $new_post_id,
353 'wplng_slug_translations',
354 wp_json_encode(
355 $slug_meta,
356 JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
357 )
358 );
359
360 wplng_clear_slugs_cache();
361
362 return $new_post_id;
363 }
364
365
366 /**
367 * Get all saved slugs from a wp_query
368 *
369 * @return array
370 */
371 function wplng_get_slugs_from_query() {
372
373 $slug_to_delete = array();
374 $slugs = array();
375 $args = array(
376 'post_type' => 'wplng_slug',
377 'posts_per_page' => -1,
378 'no_found_rows' => true,
379 'update_post_term_cache' => false,
380 'update_post_meta_cache' => false,
381 'cache_results' => false,
382 'fields' => 'ids', // Only retrieve post IDs
383 'meta_query' => array(
384 array(
385 'key' => 'wplng_slug_original_language_id',
386 'value' => wplng_get_language_website_id(),
387 'compare' => '='
388 ),
389 ),
390 );
391
392 // Get all slug IDs
393 $post_ids = get_posts( $args );
394
395 if ( empty( $post_ids ) ) {
396 return $slugs;
397 }
398
399 foreach ( $post_ids as $slug_id ) {
400 $meta = get_post_meta( $slug_id );
401
402 // Check for valid source slug
403 if ( ! isset( $meta['wplng_slug_original'][0] )
404 || ! is_string( $meta['wplng_slug_original'][0] )
405 ) {
406 continue;
407 }
408
409 $source = sanitize_title( $meta['wplng_slug_original'][0] );
410
411 // Skip unwanted slugs and mark them for deletion
412 if ( in_array(
413 $source,
414 array(
415 'index-php',
416 'wp-includes',
417 'wp-json',
418 'go',
419 'refer',
420 'recommend',
421 'recommends',
422 ),
423 true
424 )
425 ) {
426 $slug_to_delete[] = $slug_id;
427 continue;
428 }
429
430 // Check for translations and build translation array
431 if ( empty( $meta['wplng_slug_translations'][0] ) ) {
432 continue;
433 }
434
435 $translations = array();
436 $translations_meta = json_decode( $meta['wplng_slug_translations'][0], true );
437
438 if ( ! is_array( $translations_meta ) ) {
439 continue;
440 }
441
442 foreach ( $translations_meta as $translation_meta ) {
443
444 // Validate language ID
445 if ( empty( $translation_meta['language_id'] )
446 || ! wplng_is_valid_language_id( $translation_meta['language_id'] )
447 ) {
448 continue;
449 }
450
451 $language_id = sanitize_key( $translation_meta['language_id'] );
452
453 // Check and validate slug translation
454 if ( empty( $translation_meta['translation'] )
455 || ! is_string( $translation_meta['translation'] )
456 || $translation_meta['translation'] === '[WPLNG_EMPTY]'
457 || $translation_meta['translation'] === $source
458 ) {
459 continue;
460 }
461
462 // Sanitize and add valid translation
463 $translations[ $language_id ] = sanitize_title( $translation_meta['translation'] );
464 }
465
466 // Add source and translations to slugs array
467 $slugs[] = array(
468 'source' => $source,
469 'translations' => $translations,
470 );
471 }
472
473 // Delete invalid slugs, limit to 32 deletions
474 if ( ! empty( $slug_to_delete ) ) {
475
476 foreach ( array_slice( $slug_to_delete, 0, 32 ) as $id ) {
477 wp_delete_post( $id, true );
478 }
479
480 // Cache slugs for 30 seconds after deletion
481 set_transient( 'wplng_cached_slugs', $slugs, 30 );
482
483 } else {
484 // Cache slugs for a month if no deletions occurred
485 set_transient( 'wplng_cached_slugs', $slugs, MONTH_IN_SECONDS );
486 }
487
488 return $slugs;
489 }
490
491
492 /**
493 * Get the slug translations
494 *
495 * @return array Slug translations
496 */
497 function wplng_get_slugs() {
498
499 $slugs_from_cache = get_transient( 'wplng_cached_slugs' );
500
501 if ( ! is_array( $slugs_from_cache ) ) {
502 return wplng_get_slugs_from_query();
503 }
504
505 $slugs = array();
506
507 foreach ( $slugs_from_cache as $slug ) {
508
509 if ( empty( $slug['source'] )
510 || ! is_string( $slug['source'] )
511 || ! is_array( $slug['translations'] )
512 ) {
513 continue;
514 }
515
516 $translations = array();
517 $language_target_ids = wplng_get_languages_target_ids();
518
519 foreach ( $slug['translations'] as $language_id => $translation ) {
520 if ( ! in_array( $language_id, $language_target_ids )
521 || ! is_string( $translation )
522 ) {
523 continue;
524 }
525
526 $translations[ $language_id ] = $translation;
527 }
528
529 $slugs[] = array(
530 'source' => $slug['source'],
531 'translations' => $translations,
532 );
533
534 }
535
536 return $slugs;
537 }
538
539
540 /**
541 * Get slug data from original slug
542 *
543 * @param string $original
544 * @return array|false Translation data
545 */
546 function wplng_get_slug_saved_from_original( $original ) {
547
548 $original = trim( strtolower( $original ) );
549
550 if ( empty( $original ) ) {
551 return false;
552 }
553
554 $args = array(
555 'post_type' => 'wplng_slug',
556 'posts_per_page' => -1,
557 'meta_query' => array(
558 array(
559 'key' => 'wplng_slug_md5',
560 'value' => md5( $original ),
561 'compare' => '=',
562 ),
563 ),
564 'fields' => 'ids',
565 'meta_query' => array(
566 array(
567 'key' => 'wplng_slug_original_language_id',
568 'value' => wplng_get_language_website_id(),
569 'compare' => '='
570 ),
571 ),
572 );
573
574 $posts = get_posts( $args );
575
576 if ( empty( $posts ) ) {
577 return false;
578 }
579
580 foreach ( $posts as $post_id ) {
581
582 $meta = get_post_meta( $post_id );
583
584 if ( isset( $meta['wplng_slug_original'][0] )
585 && is_string( $meta['wplng_slug_original'][0] )
586 && $meta['wplng_slug_original'][0] === $original
587 ) {
588 return get_post( $post_id );
589 }
590 }
591
592 return false;
593 }
594
595
596 /**
597 * Clear cached slugs
598 *
599 * @return void
600 */
601 function wplng_clear_slugs_cache() {
602 delete_transient( 'wplng_cached_slugs' );
603 wp_cache_flush();
604 }
605
606
607 /**
608 * Clear cached translations if $post_id parametter is a slug
609 *
610 * @return void
611 */
612 function wplng_clear_slugs_cache_trash_untrash( $post_id ) {
613
614 if ( 'wplng_slug' !== get_post_type( $post_id ) ) {
615 return;
616 }
617
618 wplng_clear_slugs_cache();
619 }
620