PluginProbe
Translate and Go multilingual – Automatic AI translation – wpLingua / 2.14.0
Translate and Go multilingual – Automatic AI translation – wpLingua v2.14.0
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 / url.php

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

523 lines 12.1 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 language ID in path while preserving WordPress home base path.
11 *
12 * @param string $path
13 * @param string $language_target_id
14 * @return string
15 */
16 function wplng_add_language_to_path( $path, $language_target_id ) {
17
18 if ( ! is_string( $path ) || '' === $path ) {
19 return '/' . $language_target_id . '/';
20 }
21
22 $path_only = $path;
23 $suffix = '';
24
25 $query_pos = strpos( $path_only, '?' );
26 $fragment_pos = strpos( $path_only, '#' );
27 $cut_pos = false;
28
29 if ( false !== $query_pos && false !== $fragment_pos ) {
30 $cut_pos = min( $query_pos, $fragment_pos );
31 } elseif ( false !== $query_pos ) {
32 $cut_pos = $query_pos;
33 } elseif ( false !== $fragment_pos ) {
34 $cut_pos = $fragment_pos;
35 }
36
37 if ( false !== $cut_pos ) {
38 $path_only = substr( $path_only, 0, $cut_pos );
39 $suffix = substr( $path, $cut_pos );
40 }
41
42 $home_base_path = wplng_get_home_base_path();
43 $home_base_with_slash = trailingslashit( $home_base_path );
44
45 if ( '' !== $home_base_path
46 && ( $path_only === $home_base_path
47 || wplng_str_starts_with( $path_only, $home_base_with_slash ) )
48 ) {
49 $path_after_base = substr( $path_only, strlen( $home_base_with_slash ) );
50 $path_only = $home_base_with_slash . $language_target_id . '/';
51
52 if ( '' !== $path_after_base ) {
53 $path_only .= ltrim( $path_after_base, '/' );
54 }
55 } elseif ( wplng_str_starts_with( $path_only, '/' ) ) {
56 $path_only = '/' . $language_target_id . '/' . ltrim( $path_only, '/' );
57 } else {
58 $path_only = $language_target_id . '/' . $path_only;
59 }
60
61 $path_only = preg_replace( '#//+#', '/', $path_only );
62
63 return $path_only . $suffix;
64 }
65
66
67 /**
68 * Get the translated URL
69 *
70 * @param string $url
71 * @param string $language_target_id
72 * @return string Translated URL
73 */
74 function wplng_url_translate( $url, $language_target_id = '' ) {
75 return apply_filters(
76 'wplng_url_translate',
77 wplng_url_translate_no_filter(
78 $url,
79 $language_target_id
80 )
81 );
82 }
83
84
85 /**
86 * Get the translated URL - No apply 'wplng_url_translate' filter
87 *
88 * @param string $url
89 * @param string $language_target_id
90 * @return string Translated URL
91 */
92 function wplng_url_translate_no_filter( $url, $language_target_id = '' ) {
93
94 // Check if URL is an empty string
95 if ( '' === $url ) {
96 return '';
97 }
98
99 // Get current target ID if not set
100 if ( '' === $language_target_id ) {
101 $language_target_id = wplng_get_language_current_id();
102 }
103
104 // Apply links / Medias rules
105 $url_link_media_applied = wplng_link_media_apply_rules(
106 $url,
107 $language_target_id
108 );
109
110 // Check if a links / Medias rules was applied
111 if ( $url_link_media_applied !== $url ) {
112 return $url_link_media_applied;
113 }
114
115 // Check if URL is not translatable (exclude /admin/, ...)
116 if ( ! wplng_url_is_translatable( $url ) ) {
117 return $url;
118 }
119
120 $languages_target = wplng_get_languages_target();
121 $home_base_path = wplng_get_home_base_path();
122
123 static $preg_domain = null;
124 static $parsed_url_home = null;
125
126 if ( null === $parsed_url_home ) {
127 $parsed_url_home = wp_parse_url( home_url() );
128 $preg_domain = '';
129 if ( isset( $parsed_url_home['host'] )
130 && is_string( $parsed_url_home['host'] )
131 ) {
132 $preg_domain = preg_quote( $parsed_url_home['host'] );
133 }
134 }
135
136 if ( ! empty( $preg_domain )
137 && preg_match( '#^(http:\/\/|https:\/\/)?' . $preg_domain . '(.*)$#', $url )
138 ) {
139
140 /**
141 * It's an URL starting y a domain name
142 */
143
144 // Check if URL is already translated
145 foreach ( $languages_target as $language_target ) {
146 if ( wplng_str_contains( $url, '/' . $language_target['id'] . '/' ) ) {
147 return $url;
148 }
149 }
150
151 $parsed_url = wp_parse_url( $url );
152
153 if ( '' === $home_base_path ) {
154
155 if ( ! empty( $parsed_url['path'] ) ) {
156 $url = str_replace(
157 $parsed_url['path'],
158 wplng_slug_translate_path(
159 $parsed_url['path'],
160 $language_target_id
161 ),
162 $url
163 );
164 }
165
166 $url = preg_replace(
167 '#^(http:\/\/|https:\/\/)?' . $preg_domain . '(.*)$#',
168 '${1}' . $parsed_url_home['host'] . '/' . $language_target_id . '${2}',
169 $url
170 );
171
172 } else {
173
174 $path = '/';
175
176 if ( ! empty( $parsed_url['path'] ) ) {
177 $path = $parsed_url['path'];
178 }
179
180 $path = wplng_slug_translate_path(
181 $path,
182 $language_target_id
183 );
184
185 $path = wplng_add_language_to_path(
186 $path,
187 $language_target_id
188 );
189
190 if ( ! empty( $parsed_url['path'] ) ) {
191 $url = str_replace( $parsed_url['path'], $path, $url );
192 } else {
193 $url = untrailingslashit( $url ) . $path;
194 }
195 }
196
197 if ( empty( $parsed_url['fragment'] )
198 && empty( $parsed_url['query'] )
199 ) {
200 // Add slash at the end if is not an anchor link
201 $url = trailingslashit( $url );
202 }
203 } elseif ( preg_match( '#^[^\/]+\/[^\/].*$|^\/[^\/].*$#', $url ) ) {
204
205 /**
206 * It's a path
207 */
208
209 // Check if URL is already translated
210 foreach ( $languages_target as $language_target ) {
211 if ( wplng_str_starts_with( $url, '/' . $language_target['id'] . '/' )
212 || wplng_str_starts_with( $url, $language_target['id'] . '/' )
213 ) {
214 return $url;
215 }
216 }
217
218 $url = wplng_slug_translate_path(
219 $url,
220 $language_target_id
221 );
222
223 if ( '' === $home_base_path ) {
224 if ( wplng_str_starts_with( $url, '/' ) ) {
225 $url = '/' . $language_target_id . $url;
226 } else {
227 $url = $language_target_id . '/' . $url;
228 }
229 } else {
230 $url = wplng_add_language_to_path(
231 $url,
232 $language_target_id
233 );
234 }
235 }
236
237 return $url;
238 }
239
240
241 /**
242 * Return true is $url is translatable
243 *
244 * @param string $url
245 * @return bool
246 */
247 function wplng_url_is_translatable( $url = '' ) {
248
249 global $wplng_request_uri;
250
251 // Get current URL if $url is empty
252 if ( '' === $url ) {
253 $url = sanitize_url( $wplng_request_uri );
254 }
255
256 $url = trailingslashit( $url );
257 $url = wp_make_link_relative( $url );
258 $url = strtolower( $url );
259
260 return apply_filters(
261 'wplng_url_is_translatable',
262 wplng_url_is_translatable_no_filter( $url ),
263 $url
264 );
265 }
266
267
268 /**
269 * Return true is $url is translatable - No apply 'wplng_url_translate' filter
270 *
271 * @param string $url
272 * @return bool
273 */
274 function wplng_url_is_translatable_no_filter( $url ) {
275
276 // Check if is an admin page
277 if ( wplng_str_contains( $url, wp_make_link_relative( get_admin_url() ) ) ) {
278 return false;
279 }
280
281 // Check if URL is an anchor link for the current page
282 if ( wplng_str_starts_with( $url, '#' ) ) {
283 return false;
284 }
285
286 // Don't translate some WordPress and WooCommerce URLs
287 // admin, REST API, rss and other special URLs
288 if ( wplng_str_contains( $url, 'wp-login.php' )
289 || wplng_str_contains( $url, 'wp-register.php' )
290 || wplng_str_contains( $url, 'wp-comments-post.php' )
291 || wplng_str_contains( $url, 'wp-cron.php' )
292 || wplng_str_contains( $url, 'xmlrpc.php' )
293 || wplng_str_ends_with( $url, '/feed/' )
294 || wplng_str_contains( $url, '/wp-json/' )
295 || wplng_str_contains( $url, '/wp-includes/' )
296 || wplng_str_contains( $url, '/oembed/' )
297 || wplng_str_contains( $url, '?wc-ajax=' )
298 || wplng_str_contains( $url, '?feed=' )
299 || wplng_str_contains( $url, '?embed=' )
300 || wplng_str_contains( $url, '&wc-ajax=' )
301 || wplng_str_contains( $url, '&feed=' )
302 || wplng_str_contains( $url, '&embed=' )
303 || wplng_str_contains( $url, 'builder=true&builder_id=' ) // Fusion builder
304 ) {
305 return false;
306 }
307
308 // Check if is Divi editor
309 if ( current_user_can( 'edit_posts' )
310 && (
311 wplng_str_contains( $url, '/?et_fb=1' )
312 || wplng_str_contains( $url, '&et_fb=1' )
313 )
314 ) {
315 return false;
316 }
317
318 // Check if is in wp-uploads
319 if ( wplng_str_contains( $url, wp_make_link_relative( content_url() ) ) ) {
320 return false;
321 }
322
323 // Exclude files URL
324 $regex_is_file = '#\.(avi|css|js|map|docx?|exe|gif|html?|jfif|jpe?g|webp|bmp|midi?|mp3|mpe?g|avif|mov|qt|pdf|png|ra?m|rar|tiff?|txt|wav|zip|ico|xml|rss|xls[x]?|ttf|otf|woff2?|eot|svg)\/$#i';
325
326 if ( preg_match( $regex_is_file, $url ) ) {
327 return false;
328 }
329
330 // Check if URL is excluded in option page
331 $url_original = wplng_get_url_original( $url );
332 $url_exclude_regex = wplng_get_url_exclude_regex();
333
334 foreach ( $url_exclude_regex as $regex ) {
335 if ( preg_match( $regex, $url_original ) ) {
336 return false;
337 }
338 }
339
340 return true;
341 }
342
343
344 /**
345 * Get the REGEX list of excluded URLs
346 *
347 * @return array
348 */
349 function wplng_get_url_exclude_regex() {
350
351 global $wplng_url_exclude_regex;
352
353 if ( null !== $wplng_url_exclude_regex ) {
354 return $wplng_url_exclude_regex;
355 }
356
357 $url_exclude = array();
358
359 // Get, check and sanitize excluded URL REGEX as string
360 $option = get_option( 'wplng_excluded_url' );
361 if ( empty( $option ) || ! is_string( $option ) ) {
362 $option = '';
363 } else {
364 $option = sanitize_textarea_field( $option );
365 }
366
367 // Get exclude URL REGEX as array
368 $option = explode( PHP_EOL, $option );
369
370 // Check each URL REGEX
371 foreach ( $option as $regex ) {
372 $regex = trim( $regex );
373 if ( '' !== $regex && false !== @preg_match( '#' . $regex . '#', '' ) ) {
374 $url_exclude[] = '#' . $regex . '#';
375 }
376 }
377
378 // Remove duplicate
379 $url_exclude = array_unique( $url_exclude );
380
381 // Apply wplng_url_exclude_regex filter
382 $url_exclude = apply_filters(
383 'wplng_url_exclude_regex',
384 $url_exclude
385 );
386
387 $wplng_url_exclude_regex = $url_exclude;
388
389 return $url_exclude;
390 }
391
392
393 /**
394 * Get the untranslated / original URL
395 *
396 * @param string $url
397 * @return string
398 */
399 function wplng_get_url_original( $url = '' ) {
400
401 if ( '' === $url ) {
402 $url = wplng_get_url_current();
403 }
404
405 $target = wplng_get_languages_target_ids();
406
407 foreach ( $target as $target_id ) {
408
409 if ( ! wplng_str_contains( $url, '/' . $target_id . '/' ) ) {
410 continue;
411 }
412
413 $url = str_replace(
414 '/' . $target_id . '/',
415 '/',
416 $url
417 );
418
419 $parsed_url = wp_parse_url( $url );
420
421 if ( isset( $parsed_url['path'] )
422 && $parsed_url['path'] !== ''
423 && $parsed_url['path'] !== '/'
424 ) {
425
426 $url = str_replace(
427 $parsed_url['path'],
428 wplng_slug_original_path(
429 $parsed_url['path'],
430 $target_id
431 ),
432 $url
433 );
434 }
435
436 break;
437 }
438
439 $url = apply_filters(
440 'wplng_url_original',
441 $url
442 );
443
444 return $url;
445 }
446
447
448 /**
449 * Get current path
450 *
451 * @return string
452 */
453 function wplng_get_path_current() {
454 global $wplng_request_uri;
455 return $wplng_request_uri;
456 }
457
458
459 /**
460 * Get current URL
461 *
462 * @return string
463 */
464 function wplng_get_url_current() {
465 return home_url( wplng_get_path_current() );
466 }
467
468
469 /**
470 * Get the URL translated in specified language
471 *
472 * @param string $language_id
473 * @return string
474 */
475 function wplng_get_url_current_for_language( $language_id ) {
476 return wplng_url_translate(
477 wplng_get_url_original(),
478 $language_id
479 );
480 }
481
482
483 /**
484 * Determines whether a given URL points to a sitemap XML file.
485 *
486 * This function checks if the URL matches common sitemap naming patterns such as:
487 * - sitemap.xml
488 * - sitemap_index.xml
489 * - sitemap-posts.xml
490 * - posts-sitemap.xml
491 * and similar variations.
492 *
493 * If no URL is provided, the current URL will be used.
494 * The check ignores query parameters and anchors.
495 *
496 * @param string $url The URL to check. Defaults to the current URL if empty.
497 * @return bool True if the URL is identified as a sitemap, false otherwise.
498 */
499 function wplng_url_is_sitemap_xml( $url = '' ) {
500
501 if ( '' === $url ) {
502 $url = wplng_get_url_current();
503 }
504
505 // Normalize the URL: convert to lowercase
506 $url = strtolower( $url );
507
508 // Remove GET parameters and anchors
509 $parsed_url = wp_parse_url( $url );
510 $url_path = isset( $parsed_url['path'] ) ? $parsed_url['path'] : '';
511
512 // Check if the URL matches common sitemap patterns
513 $is_sitemap = wplng_str_contains( $url_path, 'sitemap' ) && wplng_str_contains( $url_path, '.xml' );
514
515 /**
516 * Filter to allow customization of the sitemap detection logic
517 *
518 * @param bool $is_sitemap Whether the URL is a sitemap.
519 * @param string $url The URL being checked.
520 */
521 return apply_filters( 'wplng_url_is_sitemap_xml', $is_sitemap, $url );
522 }
523