| @@ -1,195 +1,195 @@ | ||
| 1 | -<?php | |
| 2 | - | |
| 3 | -// If this file is called directly, abort. | |
| 4 | -if ( ! defined( 'WPINC' ) ) { | |
| 5 | - die; | |
| 6 | -} | |
| 7 | - | |
| 8 | - | |
| 9 | -/** | |
| 10 | - * Outputs a JavaScript snippet to handle browser language-based redirection. | |
| 11 | - * | |
| 12 | - * This function is designed to inject a JavaScript script into the front-end | |
| 13 | - * for redirecting users based on their browser's language settings. It ensures | |
| 14 | - * that the script is only output under specific conditions: | |
| 15 | - * - The user is not logged in with editing permissions. | |
| 16 | - * - The user is not a bot. | |
| 17 | - * - The current page is the front page. | |
| 18 | - * - The current language matches the website's default language. | |
| 19 | - * | |
| 20 | - * If the JavaScript file is missing or empty, the function exits early. | |
| 21 | - * | |
| 22 | - * @return void | |
| 23 | - */ | |
| 24 | -function wplng_browser_language_redirect_js_only() { | |
| 25 | - | |
| 26 | - if ( current_user_can( 'edit_posts' ) | |
| 27 | - || wplng_is_bot() | |
| 28 | - || ! is_front_page() | |
| 29 | - || ( wplng_get_language_website_id() !== wplng_get_language_current_id() ) | |
| 30 | - ) { | |
| 31 | - return; | |
| 32 | - } | |
| 33 | - | |
| 34 | - $script = file_get_contents( WPLNG_PLUGIN_DIR . '/assets/js/browser-redirect.js' ); | |
| 35 | - | |
| 36 | - if ( empty( $script ) ) { | |
| 37 | - return; | |
| 38 | - } | |
| 39 | - | |
| 40 | - $script = str_replace( | |
| 41 | - array( '[wplng-cookie-path]', '//# sourceMappingURL=browser-redirect.js.map' ), | |
| 42 | - array( COOKIEPATH, '' ), | |
| 43 | - $script | |
| 44 | - ); | |
| 45 | - | |
| 46 | - echo '<script id="wplingue-browser-redirect-js">' . rtrim( $script ) . '</script>'; | |
| 47 | -} | |
| 48 | - | |
| 49 | - | |
| 50 | -/** | |
| 51 | - * Redirects the user to the translated version of the page based on browser language or a user-defined cookie. | |
| 52 | - * | |
| 53 | - * This function is designed to run on the 'template_redirect' hook. It handles the logic for | |
| 54 | - * redirecting users to the appropriate language version of a page. The redirection is based on | |
| 55 | - * a stored cookie (if available) or the browser's 'Accept-Language' header. | |
| 56 | - * | |
| 57 | - * It prevents redirection for specific user types (e.g., logged-in users with 'edit_posts' capability) | |
| 58 | - * and for known web crawlers and bots. | |
| 59 | - * | |
| 60 | - * @return void | |
| 61 | - */ | |
| 62 | -function wplng_browser_language_redirect_php_js() { | |
| 63 | - | |
| 64 | - // Exit early for users with editing permissions or for bots to prevent redirection loops. | |
| 65 | - if ( current_user_can( 'edit_posts' ) | |
| 66 | - || wplng_is_bot() | |
| 67 | - ) { | |
| 68 | - return; | |
| 69 | - } | |
| 70 | - | |
| 71 | - $language_website_id = wplng_get_language_website_id(); | |
| 72 | - $language_current_id = wplng_get_language_current_id(); | |
| 73 | - $is_translated_page = $language_website_id !== $language_current_id; | |
| 74 | - $cookie = wplng_browser_language_cookie_get(); | |
| 75 | - | |
| 76 | - // If the user is on a translated page. | |
| 77 | - if ( $is_translated_page ) { | |
| 78 | - // Update the cookie with the selected language code. | |
| 79 | - // This is a voluntary choice by the user. | |
| 80 | - wplng_browser_language_cookie_set( $language_current_id ); | |
| 81 | - return; | |
| 82 | - } | |
| 83 | - | |
| 84 | - // The user is on the original language page (no language prefix). | |
| 85 | - | |
| 86 | - // Determine the language to redirect to. | |
| 87 | - $redirect_language_id = false; | |
| 88 | - | |
| 89 | - if ( $cookie !== false ) { | |
| 90 | - // If the cookie contains a valid language code, use it for redirection. | |
| 91 | - // This honors the user's last choice. | |
| 92 | - if ( wplng_is_valid_language_id( $cookie ) ) { | |
| 93 | - | |
| 94 | - if ( $cookie === $language_website_id ) { | |
| 95 | - return; | |
| 96 | - } else { | |
| 97 | - $redirect_language_id = $cookie; | |
| 98 | - } | |
| 99 | - } | |
| 100 | - } else { | |
| 101 | - // This is the very first visit (no cookie). Use the browser's language. | |
| 102 | - if ( ! empty( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) ) { | |
| 103 | - $langs = explode( ',', sanitize_text_field( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) ); | |
| 104 | - if ( ! empty( $langs ) ) { | |
| 105 | - $language_browser_id = strtolower( substr( trim( $langs[0] ), 0, 2 ) ); | |
| 106 | - $redirect_language_id = $language_browser_id; | |
| 107 | - } | |
| 108 | - } | |
| 109 | - } | |
| 110 | - | |
| 111 | - // Check if the determined language is a valid target language. | |
| 112 | - if ( empty( $redirect_language_id ) | |
| 113 | - || ( $redirect_language_id === $language_website_id ) | |
| 114 | - || ! in_array( $redirect_language_id, wplng_get_languages_target_ids(), true ) | |
| 115 | - ) { | |
| 116 | - // If not, set the cookie to 'original' to prevent future redirects on the original page. | |
| 117 | - wplng_browser_language_cookie_set( $language_website_id ); | |
| 118 | - return; | |
| 119 | - } | |
| 120 | - | |
| 121 | - // Check if the URL to redirect is translatable. | |
| 122 | - $url_to_redirect = wplng_get_url_current_for_language( $redirect_language_id ); | |
| 123 | - | |
| 124 | - if ( $url_to_redirect === wplng_get_url_original() ) { | |
| 125 | - wplng_browser_language_cookie_set( $language_website_id ); | |
| 126 | - return; | |
| 127 | - } | |
| 128 | - | |
| 129 | - // Perform the redirection. | |
| 130 | - wplng_browser_language_cookie_set( $redirect_language_id ); | |
| 131 | - wp_safe_redirect( | |
| 132 | - $url_to_redirect, | |
| 133 | - 302 | |
| 134 | - ); | |
| 135 | - exit; | |
| 136 | -} | |
| 137 | - | |
| 138 | - | |
| 139 | -/** | |
| 140 | - * Retrieves the language code from the 'wplingua-lang' cookie. | |
| 141 | - * | |
| 142 | - * This function safely retrieves the language code stored in the user's cookie, | |
| 143 | - * sanitizing the value to prevent security issues. It returns the language code | |
| 144 | - * if it's valid ('original' or a known language ID), otherwise it returns false. | |
| 145 | - * | |
| 146 | - * @return string|false The language code from the cookie, or false if the cookie is not set or invalid. | |
| 147 | - */ | |
| 148 | -function wplng_browser_language_cookie_get() { | |
| 149 | - $cookie = false; | |
| 150 | - if ( ! empty( $_COOKIE['wplingua-lang'] ) ) { | |
| 151 | - $cookie = sanitize_text_field( $_COOKIE['wplingua-lang'] ); | |
| 152 | - if ( ! wplng_is_valid_language_id( $cookie ) ) { | |
| 153 | - $cookie = false; | |
| 154 | - } | |
| 155 | - } | |
| 156 | - | |
| 157 | - return $cookie; | |
| 158 | -} | |
| 159 | - | |
| 160 | - | |
| 161 | -/** | |
| 162 | - * Sets the 'wplingua-lang' cookie with a specified language ID. | |
| 163 | - * | |
| 164 | - * This function creates or updates a cookie to store the user's preferred language choice. | |
| 165 | - * The cookie is set to expire in 30 days and is accessible across the site's domain. | |
| 166 | - * | |
| 167 | - * @param string $language_id The language code to be stored (e.g., 'en', 'fr', 'es', or 'original'). | |
| 168 | - * @return void | |
| 169 | - */ | |
| 170 | -function wplng_browser_language_cookie_set( $language_id ) { | |
| 171 | - setcookie( | |
| 172 | - 'wplingua-lang', | |
| 173 | - $language_id, | |
| 174 | - time() + 30 * DAY_IN_SECONDS, | |
| 175 | - COOKIEPATH, | |
| 176 | - COOKIE_DOMAIN | |
| 177 | - ); | |
| 178 | -} | |
| 179 | - | |
| 180 | - | |
| 181 | -/** | |
| 182 | - * Determines if the current user agent is a bot or a web crawler. | |
| 183 | - * | |
| 184 | - * This function checks the user agent string against a predefined list of known bots | |
| 185 | - * using a regular expression. The result is cached for the duration of the request | |
| 186 | - * to improve performance on multiple calls. | |
| 187 | - * | |
| 188 | - * @return bool True if the user agent is a bot, false otherwise. | |
| 189 | - */ | |
| 190 | -function wplng_is_bot() { | |
| 191 | - return (bool) preg_match( | |
| 192 | - '/googlebot|bingbot|slurp|duckduckbot|baiduspider|yandex|semrushbot|ahrefsbot|mj12bot|dotbot|exabot|facebookexternalhit/i', | |
| 193 | - strtolower( $_SERVER['HTTP_USER_AGENT'] ?? '' ) | |
| 194 | - ); | |
| 195 | -} | |
| 1 | +<?php | |
| 2 | + | |
| 3 | +// If this file is called directly, abort. | |
| 4 | +if ( ! defined( 'WPINC' ) ) { | |
| 5 | + die; | |
| 6 | +} | |
| 7 | + | |
| 8 | + | |
| 9 | +/** | |
| 10 | + * Outputs a JavaScript snippet to handle browser language-based redirection. | |
| 11 | + * | |
| 12 | + * This function is designed to inject a JavaScript script into the front-end | |
| 13 | + * for redirecting users based on their browser's language settings. It ensures | |
| 14 | + * that the script is only output under specific conditions: | |
| 15 | + * - The user is not logged in with editing permissions. | |
| 16 | + * - The user is not a bot. | |
| 17 | + * - The current page is the front page. | |
| 18 | + * - The current language matches the website's default language. | |
| 19 | + * | |
| 20 | + * If the JavaScript file is missing or empty, the function exits early. | |
| 21 | + * | |
| 22 | + * @return void | |
| 23 | + */ | |
| 24 | +function wplng_browser_language_redirect_js_only() { | |
| 25 | + | |
| 26 | + if ( current_user_can( 'edit_posts' ) | |
| 27 | + || wplng_is_bot() | |
| 28 | + || ! is_front_page() | |
| 29 | + || ( wplng_get_language_website_id() !== wplng_get_language_current_id() ) | |
| 30 | + ) { | |
| 31 | + return; | |
| 32 | + } | |
| 33 | + | |
| 34 | + $script = file_get_contents( WPLNG_PLUGIN_DIR . '/assets/js/browser-redirect.js' ); | |
| 35 | + | |
| 36 | + if ( empty( $script ) ) { | |
| 37 | + return; | |
| 38 | + } | |
| 39 | + | |
| 40 | + $script = str_replace( | |
| 41 | + array( '[wplng-cookie-path]', '//# sourceMappingURL=browser-redirect.js.map' ), | |
| 42 | + array( COOKIEPATH, '' ), | |
| 43 | + $script | |
| 44 | + ); | |
| 45 | + | |
| 46 | + echo '<script id="wplingue-browser-redirect-js">' . rtrim( $script ) . '</script>'; | |
| 47 | +} | |
| 48 | + | |
| 49 | + | |
| 50 | +/** | |
| 51 | + * Redirects the user to the translated version of the page based on browser language or a user-defined cookie. | |
| 52 | + * | |
| 53 | + * This function is designed to run on the 'template_redirect' hook. It handles the logic for | |
| 54 | + * redirecting users to the appropriate language version of a page. The redirection is based on | |
| 55 | + * a stored cookie (if available) or the browser's 'Accept-Language' header. | |
| 56 | + * | |
| 57 | + * It prevents redirection for specific user types (e.g., logged-in users with 'edit_posts' capability) | |
| 58 | + * and for known web crawlers and bots. | |
| 59 | + * | |
| 60 | + * @return void | |
| 61 | + */ | |
| 62 | +function wplng_browser_language_redirect_php_js() { | |
| 63 | + | |
| 64 | + // Exit early for users with editing permissions or for bots to prevent redirection loops. | |
| 65 | + if ( current_user_can( 'edit_posts' ) | |
| 66 | + || wplng_is_bot() | |
| 67 | + ) { | |
| 68 | + return; | |
| 69 | + } | |
| 70 | + | |
| 71 | + $language_website_id = wplng_get_language_website_id(); | |
| 72 | + $language_current_id = wplng_get_language_current_id(); | |
| 73 | + $is_translated_page = $language_website_id !== $language_current_id; | |
| 74 | + $cookie = wplng_browser_language_cookie_get(); | |
| 75 | + | |
| 76 | + // If the user is on a translated page. | |
| 77 | + if ( $is_translated_page ) { | |
| 78 | + // Update the cookie with the selected language code. | |
| 79 | + // This is a voluntary choice by the user. | |
| 80 | + wplng_browser_language_cookie_set( $language_current_id ); | |
| 81 | + return; | |
| 82 | + } | |
| 83 | + | |
| 84 | + // The user is on the original language page (no language prefix). | |
| 85 | + | |
| 86 | + // Determine the language to redirect to. | |
| 87 | + $redirect_language_id = false; | |
| 88 | + | |
| 89 | + if ( $cookie !== false ) { | |
| 90 | + // If the cookie contains a valid language code, use it for redirection. | |
| 91 | + // This honors the user's last choice. | |
| 92 | + if ( wplng_is_valid_language_id( $cookie ) ) { | |
| 93 | + | |
| 94 | + if ( $cookie === $language_website_id ) { | |
| 95 | + return; | |
| 96 | + } else { | |
| 97 | + $redirect_language_id = $cookie; | |
| 98 | + } | |
| 99 | + } | |
| 100 | + } else { | |
| 101 | + // This is the very first visit (no cookie). Use the browser's language. | |
| 102 | + if ( ! empty( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) ) { | |
| 103 | + $langs = explode( ',', sanitize_text_field( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) ); | |
| 104 | + if ( ! empty( $langs ) ) { | |
| 105 | + $language_browser_id = strtolower( substr( trim( $langs[0] ), 0, 2 ) ); | |
| 106 | + $redirect_language_id = $language_browser_id; | |
| 107 | + } | |
| 108 | + } | |
| 109 | + } | |
| 110 | + | |
| 111 | + // Check if the determined language is a valid target language. | |
| 112 | + if ( empty( $redirect_language_id ) | |
| 113 | + || ( $redirect_language_id === $language_website_id ) | |
| 114 | + || ! in_array( $redirect_language_id, wplng_get_languages_target_ids(), true ) | |
| 115 | + ) { | |
| 116 | + // If not, set the cookie to 'original' to prevent future redirects on the original page. | |
| 117 | + wplng_browser_language_cookie_set( $language_website_id ); | |
| 118 | + return; | |
| 119 | + } | |
| 120 | + | |
| 121 | + // Check if the URL to redirect is translatable. | |
| 122 | + $url_to_redirect = wplng_get_url_current_for_language( $redirect_language_id ); | |
| 123 | + | |
| 124 | + if ( $url_to_redirect === wplng_get_url_original() ) { | |
| 125 | + wplng_browser_language_cookie_set( $language_website_id ); | |
| 126 | + return; | |
| 127 | + } | |
| 128 | + | |
| 129 | + // Perform the redirection. | |
| 130 | + wplng_browser_language_cookie_set( $redirect_language_id ); | |
| 131 | + wp_safe_redirect( | |
| 132 | + $url_to_redirect, | |
| 133 | + 302 | |
| 134 | + ); | |
| 135 | + exit; | |
| 136 | +} | |
| 137 | + | |
| 138 | + | |
| 139 | +/** | |
| 140 | + * Retrieves the language code from the 'wplingua-lang' cookie. | |
| 141 | + * | |
| 142 | + * This function safely retrieves the language code stored in the user's cookie, | |
| 143 | + * sanitizing the value to prevent security issues. It returns the language code | |
| 144 | + * if it's valid ('original' or a known language ID), otherwise it returns false. | |
| 145 | + * | |
| 146 | + * @return string|false The language code from the cookie, or false if the cookie is not set or invalid. | |
| 147 | + */ | |
| 148 | +function wplng_browser_language_cookie_get() { | |
| 149 | + $cookie = false; | |
| 150 | + if ( ! empty( $_COOKIE['wplingua-lang'] ) ) { | |
| 151 | + $cookie = sanitize_text_field( $_COOKIE['wplingua-lang'] ); | |
| 152 | + if ( ! wplng_is_valid_language_id( $cookie ) ) { | |
| 153 | + $cookie = false; | |
| 154 | + } | |
| 155 | + } | |
| 156 | + | |
| 157 | + return $cookie; | |
| 158 | +} | |
| 159 | + | |
| 160 | + | |
| 161 | +/** | |
| 162 | + * Sets the 'wplingua-lang' cookie with a specified language ID. | |
| 163 | + * | |
| 164 | + * This function creates or updates a cookie to store the user's preferred language choice. | |
| 165 | + * The cookie is set to expire in 30 days and is accessible across the site's domain. | |
| 166 | + * | |
| 167 | + * @param string $language_id The language code to be stored (e.g., 'en', 'fr', 'es', or 'original'). | |
| 168 | + * @return void | |
| 169 | + */ | |
| 170 | +function wplng_browser_language_cookie_set( $language_id ) { | |
| 171 | + setcookie( | |
| 172 | + 'wplingua-lang', | |
| 173 | + $language_id, | |
| 174 | + time() + 30 * DAY_IN_SECONDS, | |
| 175 | + COOKIEPATH, | |
| 176 | + COOKIE_DOMAIN | |
| 177 | + ); | |
| 178 | +} | |
| 179 | + | |
| 180 | + | |
| 181 | +/** | |
| 182 | + * Determines if the current user agent is a bot or a web crawler. | |
| 183 | + * | |
| 184 | + * This function checks the user agent string against a predefined list of known bots | |
| 185 | + * using a regular expression. The result is cached for the duration of the request | |
| 186 | + * to improve performance on multiple calls. | |
| 187 | + * | |
| 188 | + * @return bool True if the user agent is a bot, false otherwise. | |
| 189 | + */ | |
| 190 | +function wplng_is_bot() { | |
| 191 | + return (bool) preg_match( | |
| 192 | + '/googlebot|bingbot|slurp|duckduckbot|baiduspider|yandex|semrushbot|ahrefsbot|mj12bot|dotbot|exabot|facebookexternalhit/i', | |
| 193 | + strtolower( $_SERVER['HTTP_USER_AGENT'] ?? '' ) | |
| 194 | + ); | |
| 195 | +} | |