PluginProbe
Translate and Go multilingual – Automatic AI translation – wpLingua / 2.16.7
Translate and Go multilingual – Automatic AI translation – wpLingua v2.16.7
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
← All changes | inc/url.php +339 -81 1.0.32.16.7 View file →
@@ -6,49 +6,132 @@
6 6 }
7 7
8 8
9 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 +/**
10 68 * Get the translated URL
11 69 *
12 70 * @param string $url
13 71 * @param string $language_target_id
14 - * @return string
72 + * @return string Translated URL
15 73 */
16 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 +}
17 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 +
18 94 // Check if URL is an empty string
19 95 if ( '' === $url ) {
20 96 return '';
21 97 }
22 98
23 - // Check if URL is translatable (exclude /admin/, ...)
24 - if ( ! wplng_url_is_translatable( $url ) ) {
25 - return $url;
99 + // Get current target ID if not set
100 + if ( '' === $language_target_id ) {
101 + $language_target_id = wplng_get_language_current_id();
26 102 }
27 103
28 - // Check if it's an WooCommece AJAX URL
29 - if ( str_contains( $url, '?wc-ajax=' ) ) {
30 - return $url;
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;
31 113 }
32 114
33 - // Check if URL is an anchor link for the current page
34 - if ( '#' === substr( $url, 0, 1 ) ) {
115 + // Check if URL is not translatable (exclude /admin/, ...)
116 + if ( ! wplng_url_is_translatable( $url ) ) {
35 117 return $url;
36 118 }
37 119
38 120 $languages_target = wplng_get_languages_target();
121 + $home_base_path = wplng_get_home_base_path();
39 122
40 - if ( '' === $language_target_id ) {
41 - $language_target_id = wplng_get_language_current_id();
42 - }
123 + static $preg_domain = null;
124 + static $parsed_url_home = null;
43 125
44 - $preg_domain = '';
45 - $parsed_url = wp_parse_url( home_url() );
46 -
47 - if ( isset( $parsed_url['host'] )
48 - && is_string( $parsed_url['host'] )
49 - ) {
50 - $preg_domain = preg_quote( $parsed_url['host'] );
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 + }
51 134 }
52 135
53 136 if ( ! empty( $preg_domain )
54 137 && preg_match( '#^(http:\/\/|https:\/\/)?' . $preg_domain . '(.*)$#', $url )
@@ -53,46 +136,105 @@
53 136 if ( ! empty( $preg_domain )
54 137 && preg_match( '#^(http:\/\/|https:\/\/)?' . $preg_domain . '(.*)$#', $url )
55 138 ) {
56 139
140 + /**
141 + * It's an URL starting y a domain name
142 + */
143 +
57 144 // Check if URL is already translated
58 145 foreach ( $languages_target as $language_target ) {
59 - if ( str_contains( $url, '/' . $language_target['id'] . '/' ) ) {
146 + if ( wplng_str_contains( $url, '/' . $language_target['id'] . '/' ) ) {
60 147 return $url;
61 148 }
62 149 }
63 150
64 - $url = preg_replace(
65 - '#^(http:\/\/|https:\/\/)?' . $preg_domain . '(.*)$#',
66 - '$1' . $preg_domain . '/' . $language_target_id . '$2',
67 - $url
68 - );
151 + $parsed_url = wp_parse_url( $url );
69 152
70 - $url = trailingslashit( $url );
153 + if ( '' === $home_base_path ) {
71 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 + }
72 203 } elseif ( preg_match( '#^[^\/]+\/[^\/].*$|^\/[^\/].*$#', $url ) ) {
73 204
205 + /**
206 + * It's a path
207 + */
208 +
74 209 // Check if URL is already translated
75 210 foreach ( $languages_target as $language_target ) {
76 - if ( substr( $url, 0, 4 ) == '/' . $language_target['id'] . '/'
77 - || substr( $url, 0, 3 ) == $language_target['id'] . '/'
211 + if ( wplng_str_starts_with( $url, '/' . $language_target['id'] . '/' )
212 + || wplng_str_starts_with( $url, $language_target['id'] . '/' )
78 213 ) {
79 214 return $url;
80 215 }
81 216 }
82 217
83 - if ( '/' === substr( $url, 0, 1 ) ) {
84 - $url = '/' . $language_target_id . $url;
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 + }
85 229 } else {
86 - $url = $language_target_id . '/' . $url;
230 + $url = wplng_add_language_to_path(
231 + $url,
232 + $language_target_id
233 + );
87 234 }
88 235 }
89 236
90 - $url = apply_filters(
91 - 'wplng_url_translate',
92 - $url
93 - );
94 -
95 237 return $url;
96 238 }
97 239
98 240
@@ -104,64 +246,101 @@
104 246 */
105 247 function wplng_url_is_translatable( $url = '' ) {
106 248
107 249 global $wplng_request_uri;
108 - $is_translatable = true;
109 250
110 251 // Get current URL if $url is empty
111 - if ( empty( $url ) ) {
252 + if ( '' === $url ) {
112 253 $url = sanitize_url( $wplng_request_uri );
113 254 }
114 255
256 + $url = trailingslashit( $url );
115 257 $url = wp_make_link_relative( $url );
258 + $url = strtolower( $url );
116 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 +
117 276 // Check if is an admin page
118 - if ( str_contains( $url, wp_make_link_relative( get_admin_url() ) ) ) {
119 - $is_translatable = false;
277 + if ( wplng_str_contains( $url, wp_make_link_relative( get_admin_url() ) ) ) {
278 + return false;
120 279 }
121 280
122 - // Check if is wp-comments-post.php
123 - if ( $is_translatable
124 - && str_contains( $url, 'wp-comments-post.php' )
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, '?doing_wp_cron=' )
293 + || wplng_str_contains( $url, '&doing_wp_cron=' )
294 + || wplng_str_contains( $url, 'xmlrpc.php' )
295 + || wplng_str_ends_with( $url, '/feed/' )
296 + || wplng_str_contains( $url, '/wp-json/' )
297 + || wplng_str_contains( $url, '/wp-includes/' )
298 + || wplng_str_contains( $url, '/oembed/' )
299 + || wplng_str_contains( $url, '?wc-ajax=' )
300 + || wplng_str_contains( $url, '?feed=' )
301 + || wplng_str_contains( $url, '?embed=' )
302 + || wplng_str_contains( $url, '&wc-ajax=' )
303 + || wplng_str_contains( $url, '&feed=' )
304 + || wplng_str_contains( $url, '&embed=' )
305 + || wplng_str_contains( $url, 'builder=true&builder_id=' ) // Fusion builder
125 306 ) {
126 - $is_translatable = false;
307 + return false;
127 308 }
128 309
310 + // Check if is Divi editor
311 + if ( current_user_can( 'edit_posts' )
312 + && (
313 + wplng_str_contains( $url, '/?et_fb=1' )
314 + || wplng_str_contains( $url, '&et_fb=1' )
315 + )
316 + ) {
317 + return false;
318 + }
319 +
129 320 // Check if is in wp-uploads
130 - if (
131 - $is_translatable
132 - && str_contains( $url, wp_make_link_relative( content_url() ) )
133 - ) {
134 - $is_translatable = false;
321 + if ( wplng_str_contains( $url, wp_make_link_relative( content_url() ) ) ) {
322 + return false;
135 323 }
136 324
137 325 // Exclude files URL
138 - $regex_is_file = '#\.(avi|css|doc|exe|gif|html|jpg|jpeg|mid|midi|mp3|mpg|mpeg|mov|qt|pdf|png|ram|rar|tiff|txt|wav|zip|ico)$#Uis';
139 - if ( $is_translatable && preg_match( $regex_is_file, $url ) ) {
140 - $is_translatable = false;
326 + $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';
327 +
328 + if ( preg_match( $regex_is_file, $url ) ) {
329 + return false;
141 330 }
142 331
143 332 // Check if URL is excluded in option page
144 - if ( $is_translatable ) {
333 + $url_original = wplng_get_url_original( $url );
334 + $url_exclude_regex = wplng_get_url_exclude_regex();
145 335
146 - $url_original = wplng_get_url_original( $url );
147 - $url_exclude_regex = wplng_get_url_exclude_regex();
148 -
149 - foreach ( $url_exclude_regex as $regex ) {
150 - if ( preg_match( $regex, $url_original ) ) {
151 - $is_translatable = false;
152 - break;
153 - }
336 + foreach ( $url_exclude_regex as $regex ) {
337 + if ( preg_match( $regex, $url_original ) ) {
338 + return false;
154 339 }
155 340 }
156 341
157 - $is_translatable = apply_filters(
158 - 'wplng_url_is_translatable',
159 - $is_translatable,
160 - $url
161 - );
162 -
163 - return $is_translatable;
342 + return true;
164 343 }
165 344
166 345
167 346 /**
@@ -170,8 +349,14 @@
170 349 * @return array
171 350 */
172 351 function wplng_get_url_exclude_regex() {
173 352
353 + global $wplng_url_exclude_regex;
354 +
355 + if ( null !== $wplng_url_exclude_regex ) {
356 + return $wplng_url_exclude_regex;
357 + }
358 +
174 359 $url_exclude = array();
175 360
176 361 // Get, check and sanitize excluded URL REGEX as string
177 362 $option = get_option( 'wplng_excluded_url' );
@@ -186,9 +371,9 @@
186 371
187 372 // Check each URL REGEX
188 373 foreach ( $option as $regex ) {
189 374 $regex = trim( $regex );
190 - if ( '' !== $regex ) {
375 + if ( '' !== $regex && false !== @preg_match( '#' . $regex . '#', '' ) ) {
191 376 $url_exclude[] = '#' . $regex . '#';
192 377 }
193 378 }
194 379
@@ -200,8 +385,10 @@
200 385 'wplng_url_exclude_regex',
201 386 $url_exclude
202 387 );
203 388
389 + $wplng_url_exclude_regex = $url_exclude;
390 +
204 391 return $url_exclude;
205 392 }
206 393
207 394
@@ -219,13 +406,39 @@
219 406
220 407 $target = wplng_get_languages_target_ids();
221 408
222 409 foreach ( $target as $target_id ) {
223 - $url = str_replace( '/' . $target_id . '/', '/', $url );
410 +
411 + if ( ! wplng_str_contains( $url, '/' . $target_id . '/' ) ) {
412 + continue;
413 + }
414 +
415 + $url = str_replace(
416 + '/' . $target_id . '/',
417 + '/',
418 + $url
419 + );
420 +
421 + $parsed_url = wp_parse_url( $url );
422 +
423 + if ( isset( $parsed_url['path'] )
424 + && $parsed_url['path'] !== ''
425 + && $parsed_url['path'] !== '/'
426 + ) {
427 +
428 + $url = str_replace(
429 + $parsed_url['path'],
430 + wplng_slug_original_path(
431 + $parsed_url['path'],
432 + $target_id
433 + ),
434 + $url
435 + );
436 + }
437 +
438 + break;
224 439 }
225 440
226 - $url = esc_url( $url );
227 -
228 441 $url = apply_filters(
229 442 'wplng_url_original',
230 443 $url
231 444 );
@@ -234,15 +447,25 @@
234 447 }
235 448
236 449
237 450 /**
451 + * Get current path
452 + *
453 + * @return string
454 + */
455 +function wplng_get_path_current() {
456 + global $wplng_request_uri;
457 + return $wplng_request_uri;
458 +}
459 +
460 +
461 +/**
238 462 * Get current URL
239 463 *
240 464 * @return string
241 465 */
242 466 function wplng_get_url_current() {
243 - global $wplng_request_uri;
244 - return home_url( $wplng_request_uri );
467 + return home_url( wplng_get_path_current() );
245 468 }
246 469
247 470
248 471 /**
@@ -251,16 +474,51 @@
251 474 * @param string $language_id
252 475 * @return string
253 476 */
254 477 function wplng_get_url_current_for_language( $language_id ) {
478 + return wplng_url_translate(
479 + wplng_get_url_original(),
480 + $language_id
481 + );
482 +}
255 483
256 - global $wplng_request_uri;
257 - $path = wplng_get_url_original( $wplng_request_uri );
258 484
259 - if ( wplng_url_is_translatable( $path )
260 - && ( wplng_get_language_website_id() !== $language_id )
261 - ) {
262 - $path = '/' . $language_id . $path;
485 +/**
486 + * Determines whether a given URL points to a sitemap XML file.
487 + *
488 + * This function checks if the URL matches common sitemap naming patterns such as:
489 + * - sitemap.xml
490 + * - sitemap_index.xml
491 + * - sitemap-posts.xml
492 + * - posts-sitemap.xml
493 + * and similar variations.
494 + *
495 + * If no URL is provided, the current URL will be used.
496 + * The check ignores query parameters and anchors.
497 + *
498 + * @param string $url The URL to check. Defaults to the current URL if empty.
499 + * @return bool True if the URL is identified as a sitemap, false otherwise.
500 + */
501 +function wplng_url_is_sitemap_xml( $url = '' ) {
502 +
503 + if ( '' === $url ) {
504 + $url = wplng_get_url_current();
263 505 }
264 506
265 - return home_url( $path );
507 + // Normalize the URL: convert to lowercase
508 + $url = strtolower( $url );
509 +
510 + // Remove GET parameters and anchors
511 + $parsed_url = wp_parse_url( $url );
512 + $url_path = isset( $parsed_url['path'] ) ? $parsed_url['path'] : '';
513 +
514 + // Check if the URL matches common sitemap patterns
515 + $is_sitemap = wplng_str_contains( $url_path, 'sitemap' ) && wplng_str_contains( $url_path, '.xml' );
516 +
517 + /**
518 + * Filter to allow customization of the sitemap detection logic
519 + *
520 + * @param bool $is_sitemap Whether the URL is a sitemap.
521 + * @param string $url The URL being checked.
522 + */
523 + return apply_filters( 'wplng_url_is_sitemap_xml', $is_sitemap, $url );
266 524 }