PluginProbe
Translate and Go multilingual – Automatic AI translation – wpLingua / 2.12.1
Translate and Go multilingual – Automatic AI translation – wpLingua v2.12.1
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.12.1, at inc/url.php

518 lines 11.9 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 $preg_domain = '';
122 $parsed_url_home = wp_parse_url( home_url() );
123 $home_base_path = wplng_get_home_base_path();
124
125 if ( isset( $parsed_url_home['host'] )
126 && is_string( $parsed_url_home['host'] )
127 ) {
128 $preg_domain = preg_quote( $parsed_url_home['host'] );
129 }
130
131 if ( ! empty( $preg_domain )
132 && preg_match( '#^(http:\/\/|https:\/\/)?' . $preg_domain . '(.*)$#', $url )
133 ) {
134
135 /**
136 * It's an URL starting y a domain name
137 */
138
139 // Check if URL is already translated
140 foreach ( $languages_target as $language_target ) {
141 if ( wplng_str_contains( $url, '/' . $language_target['id'] . '/' ) ) {
142 return $url;
143 }
144 }
145
146 $parsed_url = wp_parse_url( $url );
147
148 if ( '' === $home_base_path ) {
149
150 if ( ! empty( $parsed_url['path'] ) ) {
151 $url = str_replace(
152 $parsed_url['path'],
153 wplng_slug_translate_path(
154 $parsed_url['path'],
155 $language_target_id
156 ),
157 $url
158 );
159 }
160
161 $url = preg_replace(
162 '#^(http:\/\/|https:\/\/)?' . $preg_domain . '(.*)$#',
163 '${1}' . $parsed_url_home['host'] . '/' . $language_target_id . '${2}',
164 $url
165 );
166
167 } else {
168
169 $path = '/';
170
171 if ( ! empty( $parsed_url['path'] ) ) {
172 $path = $parsed_url['path'];
173 }
174
175 $path = wplng_slug_translate_path(
176 $path,
177 $language_target_id
178 );
179
180 $path = wplng_add_language_to_path(
181 $path,
182 $language_target_id
183 );
184
185 if ( ! empty( $parsed_url['path'] ) ) {
186 $url = str_replace( $parsed_url['path'], $path, $url );
187 } else {
188 $url = untrailingslashit( $url ) . $path;
189 }
190 }
191
192 if ( empty( $parsed_url['fragment'] )
193 && empty( $parsed_url['query'] )
194 ) {
195 // Add slash at the end if is not an anchor link
196 $url = trailingslashit( $url );
197 }
198 } elseif ( preg_match( '#^[^\/]+\/[^\/].*$|^\/[^\/].*$#', $url ) ) {
199
200 /**
201 * It's a path
202 */
203
204 // Check if URL is already translated
205 foreach ( $languages_target as $language_target ) {
206 if ( substr( $url, 0, 4 ) === ( '/' . $language_target['id'] . '/' )
207 || substr( $url, 0, 3 ) === ( $language_target['id'] . '/' )
208 ) {
209 return $url;
210 }
211 }
212
213 $url = wplng_slug_translate_path(
214 $url,
215 $language_target_id
216 );
217
218 if ( '' === $home_base_path ) {
219 if ( wplng_str_starts_with( $url, '/' ) ) {
220 $url = '/' . $language_target_id . $url;
221 } else {
222 $url = $language_target_id . '/' . $url;
223 }
224 } else {
225 $url = wplng_add_language_to_path(
226 $url,
227 $language_target_id
228 );
229 }
230 }
231
232 return $url;
233 }
234
235
236 /**
237 * Return true is $url is translatable
238 *
239 * @param string $url
240 * @return bool
241 */
242 function wplng_url_is_translatable( $url = '' ) {
243
244 global $wplng_request_uri;
245
246 // Get current URL if $url is empty
247 if ( '' === $url ) {
248 $url = sanitize_url( $wplng_request_uri );
249 }
250
251 $url = trailingslashit( $url );
252 $url = wp_make_link_relative( $url );
253 $url = strtolower( $url );
254
255 return apply_filters(
256 'wplng_url_is_translatable',
257 wplng_url_is_translatable_no_filter( $url ),
258 $url
259 );
260 }
261
262
263 /**
264 * Return true is $url is translatable - No apply 'wplng_url_translate' filter
265 *
266 * @param string $url
267 * @return bool
268 */
269 function wplng_url_is_translatable_no_filter( $url ) {
270
271 // Check if is an admin page
272 if ( wplng_str_contains( $url, wp_make_link_relative( get_admin_url() ) ) ) {
273 return false;
274 }
275
276 // Check if URL is an anchor link for the current page
277 if ( wplng_str_starts_with( $url, '#' ) ) {
278 return false;
279 }
280
281 // Don't translate some WordPress and WooCommerce URLs
282 // admin, REST API, rss and other special URLs
283 if ( wplng_str_contains( $url, 'wp-login.php' )
284 || wplng_str_contains( $url, 'wp-register.php' )
285 || wplng_str_contains( $url, 'wp-comments-post.php' )
286 || wplng_str_contains( $url, 'wp-cron.php' )
287 || wplng_str_contains( $url, 'xmlrpc.php' )
288 || wplng_str_ends_with( $url, '/feed/' )
289 || wplng_str_contains( $url, '/wp-json/' )
290 || wplng_str_contains( $url, '/wp-includes/' )
291 || wplng_str_contains( $url, '/oembed/' )
292 || wplng_str_contains( $url, '?wc-ajax=' )
293 || wplng_str_contains( $url, '?feed=' )
294 || wplng_str_contains( $url, '?embed=' )
295 || wplng_str_contains( $url, '&wc-ajax=' )
296 || wplng_str_contains( $url, '&feed=' )
297 || wplng_str_contains( $url, '&embed=' )
298 || wplng_str_contains( $url, 'builder=true&builder_id=' ) // Fusion builder
299 ) {
300 return false;
301 }
302
303 // Check if is Divi editor
304 if ( current_user_can( 'edit_posts' )
305 && (
306 wplng_str_contains( $url, '/?et_fb=1' )
307 || wplng_str_contains( $url, '&et_fb=1' )
308 )
309 ) {
310 return false;
311 }
312
313 // Check if is in wp-uploads
314 if ( wplng_str_contains( $url, wp_make_link_relative( content_url() ) ) ) {
315 return false;
316 }
317
318 // Exclude files URL
319 $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';
320
321 if ( preg_match( $regex_is_file, $url ) ) {
322 return false;
323 }
324
325 // Check if URL is excluded in option page
326 $url_original = wplng_get_url_original( $url );
327 $url_exclude_regex = wplng_get_url_exclude_regex();
328
329 foreach ( $url_exclude_regex as $regex ) {
330 if ( preg_match( $regex, $url_original ) ) {
331 return false;
332 }
333 }
334
335 return true;
336 }
337
338
339 /**
340 * Get the REGEX list of excluded URLs
341 *
342 * @return array
343 */
344 function wplng_get_url_exclude_regex() {
345
346 global $wplng_url_exclude_regex;
347
348 if ( null !== $wplng_url_exclude_regex ) {
349 return $wplng_url_exclude_regex;
350 }
351
352 $url_exclude = array();
353
354 // Get, check and sanitize excluded URL REGEX as string
355 $option = get_option( 'wplng_excluded_url' );
356 if ( empty( $option ) || ! is_string( $option ) ) {
357 $option = '';
358 } else {
359 $option = sanitize_textarea_field( $option );
360 }
361
362 // Get exclude URL REGEX as array
363 $option = explode( PHP_EOL, $option );
364
365 // Check each URL REGEX
366 foreach ( $option as $regex ) {
367 $regex = trim( $regex );
368 if ( '' !== $regex ) {
369 $url_exclude[] = '#' . $regex . '#';
370 }
371 }
372
373 // Remove duplicate
374 $url_exclude = array_unique( $url_exclude );
375
376 // Apply wplng_url_exclude_regex filter
377 $url_exclude = apply_filters(
378 'wplng_url_exclude_regex',
379 $url_exclude
380 );
381
382 $wplng_url_exclude_regex = $url_exclude;
383
384 return $url_exclude;
385 }
386
387
388 /**
389 * Get the untranslated / original URL
390 *
391 * @param string $url
392 * @return string
393 */
394 function wplng_get_url_original( $url = '' ) {
395
396 if ( '' === $url ) {
397 $url = wplng_get_url_current();
398 }
399
400 $target = wplng_get_languages_target_ids();
401
402 foreach ( $target as $target_id ) {
403
404 if ( ! wplng_str_contains( $url, '/' . $target_id . '/' ) ) {
405 continue;
406 }
407
408 $url = str_replace(
409 '/' . $target_id . '/',
410 '/',
411 $url
412 );
413
414 $parsed_url = wp_parse_url( $url );
415
416 if ( isset( $parsed_url['path'] )
417 && $parsed_url['path'] !== ''
418 && $parsed_url['path'] !== '/'
419 ) {
420
421 $url = str_replace(
422 $parsed_url['path'],
423 wplng_slug_original_path(
424 $parsed_url['path'],
425 $target_id
426 ),
427 $url
428 );
429 }
430
431 break;
432 }
433
434 $url = apply_filters(
435 'wplng_url_original',
436 $url
437 );
438
439 return $url;
440 }
441
442
443 /**
444 * Get current path
445 *
446 * @return string
447 */
448 function wplng_get_path_current() {
449 global $wplng_request_uri;
450 return $wplng_request_uri;
451 }
452
453
454 /**
455 * Get current URL
456 *
457 * @return string
458 */
459 function wplng_get_url_current() {
460 return home_url( wplng_get_path_current() );
461 }
462
463
464 /**
465 * Get the URL translated in specified language
466 *
467 * @param string $language_id
468 * @return string
469 */
470 function wplng_get_url_current_for_language( $language_id ) {
471 return wplng_url_translate(
472 wplng_get_url_original(),
473 $language_id
474 );
475 }
476
477
478 /**
479 * Determines whether a given URL points to a sitemap XML file.
480 *
481 * This function checks if the URL matches common sitemap naming patterns such as:
482 * - sitemap.xml
483 * - sitemap_index.xml
484 * - sitemap-posts.xml
485 * - posts-sitemap.xml
486 * and similar variations.
487 *
488 * If no URL is provided, the current URL will be used.
489 * The check ignores query parameters and anchors.
490 *
491 * @param string $url The URL to check. Defaults to the current URL if empty.
492 * @return bool True if the URL is identified as a sitemap, false otherwise.
493 */
494 function wplng_url_is_sitemap_xml( $url = '' ) {
495
496 if ( '' === $url ) {
497 $url = wplng_get_url_current();
498 }
499
500 // Normalize the URL: convert to lowercase
501 $url = strtolower( $url );
502
503 // Remove GET parameters and anchors
504 $parsed_url = wp_parse_url( $url );
505 $url_path = isset( $parsed_url['path'] ) ? $parsed_url['path'] : '';
506
507 // Check if the URL matches common sitemap patterns
508 $is_sitemap = str_contains( $url_path, 'sitemap' ) && str_contains( $url_path, '.xml' );
509
510 /**
511 * Filter to allow customization of the sitemap detection logic
512 *
513 * @param bool $is_sitemap Whether the URL is a sitemap.
514 * @param string $url The URL being checked.
515 */
516 return apply_filters( 'wplng_url_is_sitemap_xml', $is_sitemap, $url );
517 }
518