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

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

618 lines 12.2 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 the count of published 'wplng_slug' posts.
368 *
369 * This function retrieves the number of posts of type 'wplng_slug'
370 * that are currently published on the site.
371 *
372 * @return int The number of published 'wplng_slug' posts.
373 */
374 function wplng_get_slug_count() {
375
376 global $wplng_slug_count;
377
378 if ( null !== $wplng_slug_count ) {
379 return $wplng_slug_count;
380 }
381
382 $count_posts = wp_count_posts( 'wplng_slug' );
383 $wplng_slug_count = (int) ( $count_posts->publish ?? 0 );
384
385 return $wplng_slug_count;
386 }
387
388
389 /**
390 * Get all saved slugs from a wp_query
391 *
392 * @return array
393 */
394 function wplng_get_slugs_from_query() {
395
396 $slug_to_delete = array();
397 $slugs = array();
398 $args = array(
399 'post_type' => 'wplng_slug',
400 'posts_per_page' => -1,
401 'no_found_rows' => true,
402 'update_post_term_cache' => false,
403 'update_post_meta_cache' => false,
404 'cache_results' => false,
405 'fields' => 'ids', // Only retrieve post IDs
406 'meta_query' => array(
407 array(
408 'key' => 'wplng_slug_original_language_id',
409 'value' => wplng_get_language_website_id(),
410 'compare' => '=',
411 ),
412 ),
413 );
414
415 // Get all slug IDs
416 $post_ids = get_posts( $args );
417
418 if ( empty( $post_ids ) ) {
419 return $slugs;
420 }
421
422 foreach ( $post_ids as $slug_id ) {
423 $meta = get_post_meta( $slug_id );
424
425 // Check for valid source slug
426 if ( ! isset( $meta['wplng_slug_original'][0] )
427 || ! is_string( $meta['wplng_slug_original'][0] )
428 ) {
429 continue;
430 }
431
432 $source = sanitize_title( $meta['wplng_slug_original'][0] );
433
434 // Skip unwanted slugs and mark them for deletion
435 if ( in_array(
436 $source,
437 array(
438 'index-php',
439 'wp-includes',
440 'wp-json',
441 'go',
442 'refer',
443 'recommend',
444 'recommends',
445 ),
446 true
447 )
448 ) {
449 $slug_to_delete[] = $slug_id;
450 continue;
451 }
452
453 // Check for translations and build translation array
454 if ( empty( $meta['wplng_slug_translations'][0] ) ) {
455 continue;
456 }
457
458 $translations = array();
459 $translations_meta = json_decode( $meta['wplng_slug_translations'][0], true );
460
461 if ( ! is_array( $translations_meta ) ) {
462 continue;
463 }
464
465 foreach ( $translations_meta as $translation_meta ) {
466
467 // Validate language ID
468 if ( empty( $translation_meta['language_id'] )
469 || ! wplng_is_valid_language_id( $translation_meta['language_id'] )
470 ) {
471 continue;
472 }
473
474 $language_id = sanitize_key( $translation_meta['language_id'] );
475
476 // Check and validate slug translation
477 if ( empty( $translation_meta['translation'] )
478 || ! is_string( $translation_meta['translation'] )
479 || $translation_meta['translation'] === '[WPLNG_EMPTY]'
480 || $translation_meta['translation'] === $source
481 ) {
482 continue;
483 }
484
485 // Sanitize and add valid translation
486 $translations[ $language_id ] = sanitize_title( $translation_meta['translation'] );
487 }
488
489 // Add source and translations to slugs array
490 $slugs[] = array(
491 'source' => $source,
492 'id' => $slug_id,
493 'translations' => $translations,
494 );
495 }
496
497 // Delete invalid slugs, limit to 32 deletions
498 if ( ! empty( $slug_to_delete ) ) {
499
500 foreach ( array_slice( $slug_to_delete, 0, 32 ) as $id ) {
501 wp_delete_post( $id, true );
502 }
503
504 // Cache slugs for 30 seconds after deletion
505 set_transient( 'wplng_cached_slugs', $slugs, 30 );
506
507 } else {
508 // Cache slugs for a month if no deletions occurred
509 set_transient( 'wplng_cached_slugs', $slugs, MONTH_IN_SECONDS );
510 }
511
512 return $slugs;
513 }
514
515
516 /**
517 * Get the slug translations
518 *
519 * @return array Slug translations
520 */
521 function wplng_get_slugs() {
522
523 $slugs_from_cache = get_transient( 'wplng_cached_slugs' );
524
525 if ( ! is_array( $slugs_from_cache ) ) {
526 return wplng_get_slugs_from_query();
527 }
528
529 $slugs = array();
530
531 foreach ( $slugs_from_cache as $slug ) {
532
533 if ( empty( $slug['source'] )
534 || ! is_string( $slug['source'] )
535 || ! is_array( $slug['translations'] )
536 ) {
537 continue;
538 }
539
540 $translations = array();
541 $language_target_ids = wplng_get_languages_target_ids();
542
543 foreach ( $slug['translations'] as $language_id => $translation ) {
544 if ( ! in_array( $language_id, $language_target_ids )
545 || ! is_string( $translation )
546 ) {
547 continue;
548 }
549
550 $translations[ $language_id ] = $translation;
551 }
552
553 $slugs[] = array(
554 'source' => $slug['source'],
555 'translations' => $translations,
556 );
557
558 }
559
560 return $slugs;
561 }
562
563
564 /**
565 * Get slug data from original slug
566 *
567 * @param string $original
568 * @return array|false Translation data
569 */
570 function wplng_get_slug_saved_from_original( $original ) {
571
572 $original = trim( strtolower( $original ) );
573
574 if ( empty( $original ) ) {
575 return false;
576 }
577
578 $args = array(
579 'post_type' => 'wplng_slug',
580 'posts_per_page' => -1,
581 'meta_query' => array(
582 array(
583 'key' => 'wplng_slug_md5',
584 'value' => md5( $original ),
585 'compare' => '=',
586 ),
587 ),
588 'fields' => 'ids',
589 'meta_query' => array(
590 array(
591 'key' => 'wplng_slug_original_language_id',
592 'value' => wplng_get_language_website_id(),
593 'compare' => '=',
594 ),
595 ),
596 );
597
598 $posts = get_posts( $args );
599
600 if ( empty( $posts ) ) {
601 return false;
602 }
603
604 foreach ( $posts as $post_id ) {
605
606 $meta = get_post_meta( $post_id );
607
608 if ( isset( $meta['wplng_slug_original'][0] )
609 && is_string( $meta['wplng_slug_original'][0] )
610 && $meta['wplng_slug_original'][0] === $original
611 ) {
612 return get_post( $post_id );
613 }
614 }
615
616 return false;
617 }
618