PluginProbe ʕ •ᴥ•ʔ
GPTranslate – Multilingual AI Translation for WordPress: Automatically Translate Websites / 2.12
GPTranslate – Multilingual AI Translation for WordPress: Automatically Translate Websites v2.12
2.33.6 2.33.5 2.33.2 2.32.10 2.33 2.33.1 2.32.6 2.32.7 2.32.8 trunk 2.10.3 2.10.4 2.10.5 2.10.6 2.11 2.12 2.13 2.14 2.14.1 2.15 2.15.1 2.16.1 2.16.2 2.17 2.18 2.18.1 2.18.2 2.19 2.20 2.21 2.22 2.23 2.24 2.25 2.25.1 2.25.2 2.26 2.27 2.27.10 2.27.5 2.28 2.28.1 2.29 2.30 2.31 2.32 2.32.5
gptranslate / multilang-routing.php
gptranslate Last commit date
assets 11 months ago flags 11 months ago language 11 months ago gptranslate.php 11 months ago multilang-routing.php 11 months ago readme.txt 11 months ago serverside-translations.php 11 months ago settings.php 11 months ago simplehtmldom.php 11 months ago uninstall.php 11 months ago
multilang-routing.php
566 lines
1 <?php
2 /**
3 * GPTranslate - Multilang Routing System for WordPress
4 * Handles language prefixes in URLs like /it/, /fr/, /en/, etc.
5 * and maps them to internal pages, similar to Joomla LanguageFilter plugin.
6 */
7
8 if (!defined('ABSPATH')) exit;
9
10 // Load only in frontend
11 if (! is_admin ()) {
12 $settings = get_option("gptranslate_options");
13
14 if ( ! function_exists( 'is_plugin_active' ) ) {
15 include_once ABSPATH . 'wp-admin/includes/plugin.php';
16 }
17 $polylangActive = is_plugin_active( 'polylang/polylang.php' );
18
19 $enabledLanguages = $settings['languages'] ?? [];
20
21 // Define supported languages (lowercase)
22 $gpt_supported_languages = array_map ( 'strtolower', [
23 'AF',
24 'SQ',
25 'AM',
26 'AR',
27 'HY',
28 'AZ',
29 'EU',
30 'BE',
31 'BN',
32 'BS',
33 'BG',
34 'CA',
35 'CEB',
36 'NY',
37 'ZH',
38 'CO',
39 'HR',
40 'CS',
41 'DA',
42 'NL',
43 'EN',
44 'EO',
45 'ET',
46 'TL',
47 'FI',
48 'FR',
49 'FY',
50 'GL',
51 'KA',
52 'DE',
53 'EL',
54 'GU',
55 'HT',
56 'HA',
57 'HAW',
58 'IW',
59 'HI',
60 'HMN',
61 'HU',
62 'IS',
63 'IG',
64 'ID',
65 'GA',
66 'IT',
67 'JA',
68 'JW',
69 'KN',
70 'KK',
71 'KM',
72 'KO',
73 'KU',
74 'KY',
75 'LO',
76 'LA',
77 'LV',
78 'LT',
79 'LB',
80 'MK',
81 'MG',
82 'MS',
83 'ML',
84 'MT',
85 'MI',
86 'MR',
87 'MN',
88 'MY',
89 'NE',
90 'NO',
91 'PS',
92 'FA',
93 'PL',
94 'PT',
95 'PA',
96 'RO',
97 'RU',
98 'SM',
99 'GD',
100 'SR',
101 'ST',
102 'SN',
103 'SD',
104 'SI',
105 'SK',
106 'SL',
107 'SO',
108 'ES',
109 'SU',
110 'SW',
111 'SV',
112 'TG',
113 'TA',
114 'TE',
115 'TH',
116 'TR',
117 'UK',
118 'UR',
119 'UZ',
120 'VI',
121 'CY',
122 'XH',
123 'YI',
124 'YO',
125 'ZU'
126 ] );
127
128 /**
129 * GPTranslate - Alias Translation Routing for WordPress
130 * This section augments the language prefix routing by checking translated aliases
131 * and routing them to the original page link.
132 */
133
134 add_action('init', function () use ($gpt_supported_languages, $polylangActive, $settings) {
135 if (is_admin()) return;
136
137 global $wpdb;
138
139 $subfolder = $settings['subfolder_installation'] ?? 0;
140 $rewriteAlias = $settings['rewrite_language_alias'] ?? 0;
141 $rewriteUrl = $settings['rewrite_language_url'] ?? 0;
142 $originalLang = $settings['language'] ?? 'en';
143
144 if (! $rewriteAlias || ! $rewriteUrl) return;
145
146 $requestUri = urldecode($_SERVER['REQUEST_URI']) ?? '';
147 $requestUri = sanitize_text_field(wp_unslash($requestUri));
148
149 // Remove subfolder if present
150 if ($subfolder) {
151 $homePath = wp_parse_url(home_url(), PHP_URL_PATH) ?? '';
152 if ($homePath && strpos($requestUri, $homePath) === 0) {
153 $requestUri = substr($requestUri, strlen($homePath));
154 }
155 }
156
157 // Normalize path
158 $rawUri = rtrim(home_url() . $requestUri, '/');
159 $rawUriWithSlash = $rawUri . '/';
160
161 $uri = isset($_SERVER['REQUEST_URI']) ? sanitize_text_field(wp_unslash($_SERVER['REQUEST_URI'])) : '';
162 $pattern = '#^/(' . implode ( '|', array_map ( 'preg_quote', $gpt_supported_languages ) ) . ')(/.*)?$#i';
163
164 // Optional: remove subfolder if set
165 $subfolder_prefix = ''; // To hold subfolder if any
166 if ($settings['subfolder_installation']) {
167 $uri_parts = explode('/', ltrim($uri, '/'));
168
169 // Remove the first part (subfolder name)
170 $subfolder_prefix = '/' . array_shift($uri_parts);
171
172 // Rebuild URI without subfolder
173 $uri_to_match = '/' . implode('/', $uri_parts);
174 } else {
175 $uri_to_match = $uri;
176 }
177
178 if (preg_match ( $pattern, $uri_to_match, $matches )) {
179 $currentLang = strtolower ( $matches [1] );
180 } else {
181 return;
182 }
183
184 // Check translated alias
185 $table = $wpdb->prefix . 'gptranslate';
186 $query = $wpdb->prepare(
187 "SELECT pagelink FROM $table WHERE published = 1 AND languagetranslated = %s AND (
188 translated_alias = %s OR translated_alias = %s)",
189 $currentLang, $rawUri, $rawUriWithSlash );
190
191 $originalPageLink = $wpdb->get_var($query);
192
193 if (! $originalPageLink) return;
194
195 // Extract path from URL
196 $parsed = wp_parse_url($originalPageLink);
197 $originalPath = $parsed['path'] ?? '';
198
199 // Remove subfolder prefix again from final path
200 if ($subfolder && !empty($homePath) && strpos($originalPath, $homePath) === 0) {
201 $originalPath = substr($originalPath, strlen($homePath));
202 }
203
204 // Clean path
205 $originalPath = preg_replace('#^/?index\.php/?#i', '', $originalPath);
206 $originalPath = '/' . ltrim($originalPath, '/');
207
208 // Rewrite the request
209 $_SERVER['REQUEST_URI'] = $originalPath;
210 }, 1);
211
212 /**
213 * Detect language from URI and rewrite $_SERVER['REQUEST_URI'] accordingly defining a GPTRANSLATE_CURRENT_LANG constant
214 */
215 add_action ( 'init', function () use ($gpt_supported_languages, $polylangActive, $settings) {
216 if (is_admin()) return;
217
218 $uri = isset($_SERVER['REQUEST_URI']) ? sanitize_text_field(wp_unslash($_SERVER['REQUEST_URI'])) : '';
219 $pattern = '#^/(' . implode ( '|', array_map ( 'preg_quote', $gpt_supported_languages ) ) . ')(/.*)?$#i';
220
221 // Optional: remove subfolder if set
222 $subfolder_prefix = ''; // To hold subfolder if any
223 if ($settings['subfolder_installation']) {
224 $uri_parts = explode('/', ltrim($uri, '/'));
225
226 // Remove the first part (subfolder name)
227 $subfolder_prefix = '/' . array_shift($uri_parts);
228
229 // Rebuild URI without subfolder
230 $uri_to_match = '/' . implode('/', $uri_parts);
231 } else {
232 $uri_to_match = $uri;
233 }
234
235 if (preg_match ( $pattern, $uri_to_match, $matches )) {
236 $lang = strtolower ( $matches [1] );
237 $new_uri = isset ( $matches [2] ) ? $matches [2] : '/';
238 if ($settings ['rewrite_language_url'] == 1 && !$polylangActive) {
239 $_SERVER ['REQUEST_URI'] = $subfolder_prefix . $new_uri;
240 }
241
242 if (! defined ( 'GPTRANSLATE_CURRENT_LANG' )) {
243 define ( 'GPTRANSLATE_CURRENT_LANG', $lang );
244 }
245 }
246 }, 2);
247
248 /**
249 * Replace and manage the lang attribute of the html tag
250 */
251 add_filter ( 'language_attributes', function ($output) use ($settings, $polylangActive) {
252 if (! defined ( 'GPTRANSLATE_CURRENT_LANG' ) || (!$settings['set_html_lang'] && !$settings ['rewrite_language_url']) || $polylangActive)
253 return $output;
254
255 $altflags_array = get_option ( 'gptranslate_options' ) ['alt_flags'] ?? [ ];
256
257 $lang_map = [
258 // Variazioni per EN
259 'en' => (in_array ( 'usa', $altflags_array ) || in_array ( 'canada', $altflags_array )) ? 'en-US' : 'en-GB',
260
261 // Variazioni per FR
262 'fr' => in_array ( 'quebec', $altflags_array ) ? 'fr-CA' : 'fr-FR',
263
264 // Variazioni per PT
265 'pt' => in_array ( 'brazil', $altflags_array ) ? 'pt-BR' : 'pt-PT',
266
267 // Variazioni per ES
268 'es' => in_array ( 'mexico', $altflags_array ) ? 'es-MX' : (in_array ( 'argentina', $altflags_array ) ? 'es-AR' : (in_array ( 'colombia', $altflags_array ) ? 'es-CO' : 'es-ES')),
269
270 // Tutte le altre senza variazioni
271 'af' => 'af-ZA',
272 'sq' => 'sq-AL',
273 'am' => 'am-ET',
274 'ar' => 'ar-SA',
275 'hy' => 'hy-AM',
276 'az' => 'az-AZ',
277 'eu' => 'eu-ES',
278 'be' => 'be-BY',
279 'bn' => 'bn-BD',
280 'bs' => 'bs-BA',
281 'bg' => 'bg-BG',
282 'ca' => 'ca-ES',
283 'ceb' => 'ceb-PH',
284 'ny' => 'ny-MW',
285 'zh' => 'zh-CN',
286 'co' => 'co-FR',
287 'hr' => 'hr-HR',
288 'cs' => 'cs-CZ',
289 'da' => 'da-DK',
290 'nl' => 'nl-NL',
291 'eo' => 'eo-EO',
292 'et' => 'et-EE',
293 'tl' => 'tl-PH',
294 'fi' => 'fi-FI',
295 'fy' => 'fy-NL',
296 'gl' => 'gl-ES',
297 'ka' => 'ka-GE',
298 'de' => 'de-DE',
299 'el' => 'el-GR',
300 'gu' => 'gu-IN',
301 'ht' => 'ht-HT',
302 'ha' => 'ha-NG',
303 'haw' => 'haw-US',
304 'iw' => 'iw-IL',
305 'hi' => 'hi-IN',
306 'hmn' => 'hmn-US',
307 'hu' => 'hu-HU',
308 'is' => 'is-IS',
309 'ig' => 'ig-NG',
310 'id' => 'id-ID',
311 'ga' => 'ga-IE',
312 'it' => 'it-IT',
313 'ja' => 'ja-JP',
314 'jw' => 'jw-ID',
315 'kn' => 'kn-IN',
316 'kk' => 'kk-KZ',
317 'km' => 'km-KH',
318 'ko' => 'ko-KR',
319 'ku' => 'ku-TR',
320 'ky' => 'ky-KG',
321 'lo' => 'lo-LA',
322 'la' => 'la-VA',
323 'lv' => 'lv-LV',
324 'lt' => 'lt-LT',
325 'lb' => 'lb-LU',
326 'mk' => 'mk-MK',
327 'mg' => 'mg-MG',
328 'ms' => 'ms-MY',
329 'ml' => 'ml-IN',
330 'mt' => 'mt-MT',
331 'mi' => 'mi-NZ',
332 'mr' => 'mr-IN',
333 'mn' => 'mn-MN',
334 'my' => 'my-MM',
335 'ne' => 'ne-NP',
336 'no' => 'no-NO',
337 'ps' => 'ps-AF',
338 'fa' => 'fa-IR',
339 'pl' => 'pl-PL',
340 'pa' => 'pa-IN',
341 'ro' => 'ro-RO',
342 'ru' => 'ru-RU',
343 'sm' => 'sm-WS',
344 'gd' => 'gd-GB',
345 'sr' => 'sr-RS',
346 'st' => 'st-LS',
347 'sn' => 'sn-ZW',
348 'sd' => 'sd-PK',
349 'si' => 'si-LK',
350 'sk' => 'sk-SK',
351 'sl' => 'sl-SI',
352 'so' => 'so-SO',
353 'su' => 'su-ID',
354 'sw' => 'sw-TZ',
355 'sv' => 'sv-SE',
356 'tg' => 'tg-TJ',
357 'ta' => 'ta-IN',
358 'te' => 'te-IN',
359 'th' => 'th-TH',
360 'tr' => 'tr-TR',
361 'uk' => 'uk-UA',
362 'ur' => 'ur-PK',
363 'uz' => 'uz-UZ',
364 'vi' => 'vi-VN',
365 'cy' => 'cy-GB',
366 'xh' => 'xh-ZA',
367 'yi' => 'yi-US',
368 'yo' => 'yo-NG',
369 'zu' => 'zu-ZA'
370 ];
371
372 $gpt_lang = strtolower ( GPTRANSLATE_CURRENT_LANG );
373 $full_lang = $lang_map [$gpt_lang] ?? $gpt_lang;
374
375 $output = 'lang="' . esc_attr ( $full_lang ) . '"';
376
377 return $output;
378 } );
379
380 /**
381 * Inject alternate hreflang links in <head>
382 */
383 add_action ( 'wp_head', function () use ($enabledLanguages, $polylangActive, $settings) {
384 if (! defined ( 'GPTRANSLATE_CURRENT_LANG' ) || ! $settings ['add_alternate'] || ! $settings ['serverside_translations'] || $polylangActive) {
385 return;
386 }
387
388 $current_lang = GPTRANSLATE_CURRENT_LANG;
389 $request_uri = isset($_SERVER['REQUEST_URI']) ? sanitize_text_field(wp_unslash($_SERVER['REQUEST_URI'])) : '';
390 $request_path = sanitize_text_field ( wp_unslash ( $request_uri ) );
391
392 // Remove query string
393 $request_path = strtok ( $request_path, '?' );
394
395 // Optional subfolder cleanup
396 if (!empty($settings['subfolder_installation'])) {
397 $home_path = wp_parse_url(home_url(), PHP_URL_PATH) ?? '';
398 if ($home_path && strpos($request_path, $home_path) === 0) {
399 $request_path = substr($request_path, strlen($home_path));
400 }
401 }
402
403 // Remove current language code from URL if present
404 $current_path = preg_replace ( '#^/' . preg_quote ( $current_lang, '#' ) . '(/|$)#', '/', $request_path );
405
406 $site_url = home_url ();
407
408 foreach ( $enabledLanguages as $lang ) {
409 $translated_url = rtrim ( $site_url, '/' ) . '/' . $lang . rtrim ( $current_path, '/' );
410
411 echo '<link rel="alternate" hreflang="' . esc_attr ( $lang ) . '" href="' . esc_url ( $translated_url . '/' ) . "\" />\n";
412
413 if ($lang === $settings ['language']) {
414 echo '<link rel="alternate" hreflang="x-default" href="' . esc_url ( $translated_url . '/' ) . "\" />\n";
415 }
416 }
417 } );
418
419 /**
420 * Handle the multilanguage URLs
421 */
422 if ($settings ['rewrite_language_url'] == 1 && !$polylangActive) {
423 // Prevent WP to redirect
424 add_filter ( 'redirect_canonical', function ($redirect_url, $requested_url) use ($gpt_supported_languages) {
425 if (defined ( 'GPTRANSLATE_CURRENT_LANG' )) {
426 return false;
427 }
428 }, 10, 2 );
429
430 /**
431 * Add rewrite rules to allow /lang/ URLs
432 */
433 add_filter ( 'rewrite_rules_array', function ($rules) use ($gpt_supported_languages) {
434 $new_rules = [ ];
435 foreach ( $gpt_supported_languages as $lang ) {
436 $new_rules ["{$lang}/(.*)$"] = 'index.php?pagename=$matches[1]&gptlang=' . $lang;
437 $new_rules ["{$lang}$"] = 'index.php?gptlang=' . $lang;
438 }
439 return $new_rules + $rules;
440 } );
441
442 /**
443 * Redirect URLs without language prefix to default language
444 */
445 add_action ( 'template_redirect', function () use ($gpt_supported_languages, $settings) {
446 $defaultOriginalLanguage = get_option ( 'gptranslate_options' ) ['language'] ?? 'en';
447
448 $default_lang = $defaultOriginalLanguage;
449 $uri = isset($_SERVER['REQUEST_URI']) ? sanitize_text_field(wp_unslash($_SERVER['REQUEST_URI'])) : '';
450
451 // Exceptions and management for subfolder installations
452 if($settings['subfolder_installation']) {
453 // Explode URI into parts
454 $uri_parts = explode('/', ltrim($uri, '/'));
455
456 // Remove the first part (subfolder)
457 array_shift($uri_parts);
458
459 // Rebuild the URI
460 $uri = '/' . implode('/', $uri_parts);
461 }
462
463 // Avoid multiple redirects in the same session
464 if (! session_id ()) {
465 session_start ();
466 }
467
468 if (! empty ( $_SESSION ['gptranslate_redirected'] ) && ($_SESSION ['gptranslate_redirected'] === $uri || $settings ['subfolder_installation'])) {
469 unset ( $_SESSION ['gptranslate_redirected'] );
470 return;
471 }
472
473 // Skip static assets and admin/API routes
474 if (preg_match ( '#\.(ico|png|jpe?g|gif|svg|css|js|woff2?|ttf|eot|mp4|webm)$#i', $uri ) || strpos ( $uri, '/wp-' ) === 0 || strpos ( $uri, '/wp-json/' ) === 0) {
475 return;
476 }
477
478 // If a language has already been detected and defined, skip redirect
479 if (defined ( 'GPTRANSLATE_CURRENT_LANG' )) {
480 return;
481 }
482
483 // Redirect to default language
484 $_SESSION ['gptranslate_redirected'] = $uri;
485 wp_redirect ( home_url ( "/{$default_lang}{$uri}" ), 301 );
486 exit ();
487 } );
488
489 /**
490 * Add gptlang as a recognized query var
491 */
492 add_filter ( 'query_vars', function ($vars) {
493 $vars [] = 'gptlang';
494 return $vars;
495 } );
496
497 /**
498 * Helper function to get current language
499 */
500 function gpt_get_current_lang() {
501 if (defined ( 'GPTRANSLATE_CURRENT_LANG' ))
502 return GPTRANSLATE_CURRENT_LANG;
503 $lang = get_query_var ( 'gptlang' );
504 return $lang ?: 'en';
505 }
506
507 /**
508 * Rewrite internal links to include language prefix
509 */
510 if ($settings ['rewrite_page_links'] == 1 && !$polylangActive) {
511 add_action('template_redirect', function () use ($gpt_supported_languages, $settings) {
512 ob_start(function ($html) use ($gpt_supported_languages, $settings) {
513 $current_lang = defined('GPTRANSLATE_CURRENT_LANG') ? GPTRANSLATE_CURRENT_LANG : ($settings['language'] ?? 'en');
514
515 $site_url = home_url();
516 $parsed_site_url = wp_parse_url($site_url);
517 $base_path = rtrim($parsed_site_url['path'] ?? '', '');
518
519 return preg_replace_callback('#<a\s[^>]*href=["\']([^"\']+)["\']#i', function ($matches) use ($current_lang, $gpt_supported_languages, $parsed_site_url, $base_path, $site_url) {
520 $original_url = $matches[1];
521 $url = $original_url;
522
523 // Skip external URLs
524 if (preg_match('#^https?://#', $url) && strpos($url, $parsed_site_url['host']) === false) {
525 return $matches[0];
526 }
527
528 // Skip admin-related URLs
529 if (strpos($url, 'wp-admin') !== false || strpos($url, 'wp-login.php') !== false || strpos($url, 'wp-json') !== false) {
530 return $matches[0];
531 }
532
533 // Convert absolute local URL to relative
534 if (strpos($url, $parsed_site_url['scheme'] . '://' . $parsed_site_url['host']) === 0) {
535 $url = substr($url, strlen($parsed_site_url['scheme'] . '://' . $parsed_site_url['host']));
536 }
537
538 // Remove base path if present
539 if (!empty($base_path) && strpos($url, $base_path) === 0) {
540 $url = substr($url, strlen($base_path));
541 }
542
543 // Ensure it starts with /
544 if (strpos($url, '/') !== 0) {
545 return $matches[0]; // ignore non-relative, non-local URLs
546 }
547
548 // Skip if already has a language prefix
549 foreach ($gpt_supported_languages as $lang) {
550 if (preg_match("#^/{$lang}(/|\$)#i", $url)) {
551 return $matches[0];
552 }
553 }
554
555 // Build new full absolute URL
556 $new_url = rtrim($site_url, '/') . '/' . $current_lang . $url;
557
558 return str_replace($original_url, $new_url, $matches[0]);
559
560 }, $html);
561 });
562 }, 15);
563 }
564 }
565 } // end is_admin check
566