// ========================================================================= // Reiss Multilingual Helpers // ========================================================================= if (!function_exists('reiss_get_current_request_path')) { function reiss_get_current_request_path() { $current_request_uri = isset($_SERVER['REQUEST_URI']) ? wp_unslash($_SERVER['REQUEST_URI']) : '/'; $current_path = strtok($current_request_uri, '?'); if ($current_path === false || $current_path === '') { $current_path = '/'; } $current_path = '/' . ltrim((string) $current_path, '/'); $current_path = preg_replace('#/+#', '/', $current_path); return $current_path; } } if (!function_exists('reiss_get_default_lang')) { function reiss_get_default_lang() { return 'en'; } } if (!function_exists('reiss_get_host')) { function reiss_get_host() { return home_url('/'); } } if (!function_exists('reiss_get_language_codes')) { function reiss_get_language_codes() { return array( 'af','ar','az','de','el','es','fa','fr','id','it','ja','ko','ms','nl','pl','pt','ru','sv','th','tr','vi','zh-cn' ); } } if (!function_exists('reiss_get_supported_langs')) { function reiss_get_supported_langs() { return array_values(array_unique(array_merge(array(reiss_get_default_lang()), reiss_get_language_codes()))); } } if (!function_exists('reiss_get_locale_map')) { function reiss_get_locale_map() { return array( 'en' => 'en_US', 'af' => 'af_ZA', 'ar' => 'ar_SA', 'az' => 'az_AZ', 'de' => 'de_DE', 'el' => 'el_GR', 'es' => 'es_ES', 'fa' => 'fa_IR', 'fr' => 'fr_FR', 'id' => 'id_ID', 'it' => 'it_IT', 'ja' => 'ja_JP', 'ko' => 'ko_KR', 'ms' => 'ms_MY', 'nl' => 'nl_NL', 'pl' => 'pl_PL', 'pt' => 'pt_PT', 'ru' => 'ru_RU', 'sv' => 'sv_SE', 'th' => 'th_TH', 'tr' => 'tr_TR', 'vi' => 'vi_VN', 'zh-cn' => 'zh_CN', ); } } if (!function_exists('reiss_get_html_lang_map')) { function reiss_get_html_lang_map() { return array( 'en' => 'en', 'af' => 'af', 'ar' => 'ar', 'az' => 'az', 'de' => 'de', 'el' => 'el', 'es' => 'es', 'fa' => 'fa', 'fr' => 'fr', 'id' => 'id', 'it' => 'it', 'ja' => 'ja', 'ko' => 'ko', 'ms' => 'ms', 'nl' => 'nl', 'pl' => 'pl', 'pt' => 'pt', 'ru' => 'ru', 'sv' => 'sv', 'th' => 'th', 'tr' => 'tr', 'vi' => 'vi', 'zh-cn' => 'zh-CN', ); } } if (!function_exists('reiss_normalize_path')) { function reiss_normalize_path($path) { $path = '/' . ltrim((string) $path, '/'); $path = preg_replace('#/+#', '/', $path); if ($path !== '/' && substr($path, -1) !== '/') { $path .= '/'; } return $path; } } if (!function_exists('reiss_get_current_lang')) { function reiss_get_current_lang() { $current_path = reiss_get_current_request_path(); $current_lang = reiss_get_default_lang(); if (preg_match('#^/([a-zA-Z]{2,3}(?:-[a-zA-Z]{2})?)(?:/|$)#', $current_path, $matches)) { $current_lang = strtolower($matches[1]); } if (!in_array($current_lang, reiss_get_supported_langs(), true)) { $current_lang = reiss_get_default_lang(); } return $current_lang; } } if (!function_exists('reiss_get_current_lang_path')) { function reiss_get_current_lang_path() { $current_path = reiss_get_current_request_path(); $current_lang = reiss_get_current_lang(); $default_lang = reiss_get_default_lang(); $path = $current_path; if ($current_lang !== $default_lang) { $path = preg_replace('#^/' . preg_quote($current_lang, '#') . '(?=/|$)#i', '', $path); } if ($path === '' || $path === false) { $path = '/'; } return reiss_normalize_path($path); } } if (!function_exists('reiss_lang_url')) { function reiss_lang_url($host, $lang, $default, $path) { $host = rtrim((string) $host, '/'); $path = reiss_normalize_path($path); if ($lang === $default) { return $host . $path; } return $host . '/' . rawurlencode($lang) . $path; } } if (!function_exists('reiss_strip_lang_from_path')) { function reiss_strip_lang_from_path($path, $supported_langs) { $path = (string) $path; $path_only = parse_url($path, PHP_URL_PATH); if (!is_string($path_only) || $path_only === '') { $path_only = '/'; } foreach ($supported_langs as $lang_code) { if ($lang_code === reiss_get_default_lang()) { continue; } $pattern = '#^/' . preg_quote($lang_code, '#') . '(?=/|$)#i'; if (preg_match($pattern, $path_only)) { $path_only = preg_replace($pattern, '', $path_only); break; } } if ($path_only === '' || $path_only === false) { $path_only = '/'; } return reiss_normalize_path($path_only); } } if (!function_exists('reiss_localize_url')) { function reiss_localize_url($url, $target_lang, $default_lang, $host, $supported_langs) { $url = trim((string) $url); if ($url === '') { return $url; } $parsed = wp_parse_url($url); if (!is_array($parsed)) { return $url; } $target_host = wp_parse_url($host, PHP_URL_HOST); $url_host = isset($parsed['host']) ? $parsed['host'] : ''; if ($url_host && $target_host && strtolower($url_host) !== strtolower($target_host)) { return $url; } $path = isset($parsed['path']) ? $parsed['path'] : '/'; $clean = reiss_strip_lang_from_path($path, $supported_langs); $localized = reiss_lang_url($host, $target_lang, $default_lang, $clean); if (!empty($parsed['query'])) { $localized .= '?' . $parsed['query']; } if (!empty($parsed['fragment'])) { $localized .= '#' . $parsed['fragment']; } return $localized; } } if (!function_exists('reiss_replace_host_urls_in_value')) { function reiss_replace_host_urls_in_value($value, $current_lang, $default_lang, $host, $supported_langs) { if (is_array($value)) { foreach ($value as $k => $v) { $value[$k] = reiss_replace_host_urls_in_value($v, $current_lang, $default_lang, $host, $supported_langs); } return $value; } if (is_string($value) && preg_match('#^https?://#i', $value)) { return reiss_localize_url($value, $current_lang, $default_lang, $host, $supported_langs); } return $value; } } if (!function_exists('reiss_replace_host_urls_in_json_string')) { function reiss_replace_host_urls_in_json_string($json, $current_lang, $default_lang, $host, $supported_langs) { if (!is_string($json) || $json === '') { return $json; } $host_no_trailing = rtrim($host, '/'); $host_quoted = preg_quote($host_no_trailing, '#'); foreach ($supported_langs as $lang_code) { if ($lang_code === $default_lang) { continue; } $json = preg_replace( '#'.$host_quoted.'/' . preg_quote($lang_code, '#') . '(/?)#i', $host_no_trailing . '$1', $json ); } $path = reiss_get_current_lang_path(); $current_url = reiss_lang_url($host, $current_lang, $default_lang, $path); $default_url = reiss_lang_url($host, $default_lang, $default_lang, $path); $json = str_replace( array( wp_json_encode(rtrim($default_url, '/') . '/#article'), wp_json_encode(rtrim($default_url, '/') . '/#breadcrumb'), wp_json_encode(rtrim($default_url, '/') . '/#primaryimage'), wp_json_encode(rtrim($default_url, '/') . '/#webpage'), wp_json_encode(rtrim($default_url, '/') . '/') ), array( wp_json_encode(rtrim($current_url, '/') . '/#article'), wp_json_encode(rtrim($current_url, '/') . '/#breadcrumb'), wp_json_encode(rtrim($current_url, '/') . '/#primaryimage'), wp_json_encode(rtrim($current_url, '/') . '/#webpage'), wp_json_encode(rtrim($current_url, '/') . '/') ), $json ); $decoded = json_decode($json, true); if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) { $decoded = reiss_replace_host_urls_in_value($decoded, $current_lang, $default_lang, $host, $supported_langs); return wp_json_encode($decoded, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); } return $json; } } if (!function_exists('reiss_get_current_url')) { function reiss_get_current_url() { return reiss_lang_url( reiss_get_host(), reiss_get_current_lang(), reiss_get_default_lang(), reiss_get_current_lang_path() ); } } if (!function_exists('reiss_get_default_url')) { function reiss_get_default_url() { return reiss_lang_url( reiss_get_host(), reiss_get_default_lang(), reiss_get_default_lang(), reiss_get_current_lang_path() ); } } if (!function_exists('reiss_get_current_og_locale')) { function reiss_get_current_og_locale() { $map = reiss_get_locale_map(); $lang = reiss_get_current_lang(); return isset($map[$lang]) ? $map[$lang] : 'en_US'; } } if (!function_exists('reiss_get_current_html_lang')) { function reiss_get_current_html_lang() { $map = reiss_get_html_lang_map(); $lang = reiss_get_current_lang(); return isset($map[$lang]) ? $map[$lang] : 'en'; } } if (!function_exists('reiss_get_schema_language')) { function reiss_get_schema_language() { $lang = reiss_get_current_lang(); if ($lang === 'en') { return 'en-US'; } $map = array( 'zh-cn' => 'zh-CN', ); return isset($map[$lang]) ? $map[$lang] : $lang; } } // ========================================================================= // Language Attributes // ========================================================================= function reiss_filter_language_attributes($output) { $html_lang = reiss_get_current_html_lang(); $output = preg_replace('/lang=("|\')[^"\']*("|\')/i', 'lang="' . esc_attr($html_lang) . '"', $output); if (stripos($output, 'lang=') === false) { $output .= ' lang="' . esc_attr($html_lang) . '"'; } return $output; } add_filter('language_attributes', 'reiss_filter_language_attributes', 20); // ========================================================================= // Yoast SEO Multilingual Fix - Base Filters // ========================================================================= function reiss_filter_wpseo_canonical($canonical) { if (is_search() || is_404()) { return $canonical; } $current_lang = reiss_get_current_lang(); $default_lang = reiss_get_default_lang(); $host = reiss_get_host(); $current_path = reiss_get_current_lang_path(); $supported_langs = reiss_get_supported_langs(); if (!empty($canonical)) { return reiss_localize_url($canonical, $current_lang, $default_lang, $host, $supported_langs); } return reiss_lang_url($host, $current_lang, $default_lang, $current_path); } add_filter('wpseo_canonical', 'reiss_filter_wpseo_canonical', 99); function reiss_filter_wpseo_opengraph_url($url) { $current_lang = reiss_get_current_lang(); $default_lang = reiss_get_default_lang(); $host = reiss_get_host(); $current_path = reiss_get_current_lang_path(); $supported_langs = reiss_get_supported_langs(); if (!empty($url)) { return reiss_localize_url($url, $current_lang, $default_lang, $host, $supported_langs); } return reiss_lang_url($host, $current_lang, $default_lang, $current_path); } add_filter('wpseo_opengraph_url', 'reiss_filter_wpseo_opengraph_url', 99); function reiss_filter_wpseo_locale($locale) { return reiss_get_current_og_locale(); } add_filter('wpseo_locale', 'reiss_filter_wpseo_locale', 99); function reiss_filter_wpseo_schema_graph($graph) { if (!is_array($graph)) { return $graph; } $current_lang = reiss_get_current_lang(); $default_lang = reiss_get_default_lang(); $host = reiss_get_host(); $supported_langs = reiss_get_supported_langs(); $schema_lang = reiss_get_schema_language(); foreach ($graph as $i => $piece) { $graph[$i] = reiss_replace_host_urls_in_value($piece, $current_lang, $default_lang, $host, $supported_langs); if (is_array($graph[$i]) && isset($graph[$i]['inLanguage'])) { $graph[$i]['inLanguage'] = $schema_lang; } } return $graph; } add_filter('wpseo_schema_graph', 'reiss_filter_wpseo_schema_graph', 999); function reiss_filter_wpseo_json_ld_output($data) { if (!is_array($data)) { return $data; } $current_lang = reiss_get_current_lang(); $default_lang = reiss_get_default_lang(); $host = reiss_get_host(); $supported_langs = reiss_get_supported_langs(); $schema_lang = reiss_get_schema_language(); $data = reiss_replace_host_urls_in_value($data, $current_lang, $default_lang, $host, $supported_langs); if (!empty($data['@graph']) && is_array($data['@graph'])) { foreach ($data['@graph'] as $index => $node) { if (!is_array($node)) { continue; } if (isset($node['inLanguage'])) { $data['@graph'][$index]['inLanguage'] = $schema_lang; } $data['@graph'][$index] = reiss_replace_host_urls_in_value($node, $current_lang, $default_lang, $host, $supported_langs); } } return $data; } add_filter('wpseo_json_ld_output', 'reiss_filter_wpseo_json_ld_output', 999); // ========================================================================= // Yoast SEO Multilingual Fix - Final Head Output Override // ========================================================================= if (!function_exists('reiss_start_wpseo_head_buffer')) { function reiss_start_wpseo_head_buffer() { ob_start('reiss_filter_wpseo_head_output'); } } if (!function_exists('reiss_end_wpseo_head_buffer')) { function reiss_end_wpseo_head_buffer() { if (ob_get_level() > 0) { @ob_end_flush(); } } } if (!function_exists('reiss_filter_wpseo_head_output')) { function reiss_filter_wpseo_head_output($html) { if (!is_string($html) || trim($html) === '') { return $html; } if (strpos($html, 'yoast') === false && strpos($html, 'og:locale') === false && strpos($html, 'application/ld+json') === false) { return $html; } $current_lang = reiss_get_current_lang(); $default_lang = reiss_get_default_lang(); $host = reiss_get_host(); $supported_langs = reiss_get_supported_langs(); $current_url = reiss_get_current_url(); $current_og_locale = reiss_get_current_og_locale(); $schema_lang = reiss_get_schema_language(); $html = preg_replace( '##i', '', $html ); $html = preg_replace( '##i', '', $html ); $html = preg_replace( '##i', '', $html ); $html = preg_replace_callback( '#]*type=("|\')application/ld\+json\1[^>]*>(.*?)#is', function ($matches) use ($current_lang, $default_lang, $host, $supported_langs, $schema_lang) { $script_open = preg_replace('#>(.*?)$#s', '>', $matches[0]); $json = $matches[2]; $decoded = json_decode($json, true); if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) { $decoded = reiss_replace_host_urls_in_value($decoded, $current_lang, $default_lang, $host, $supported_langs); if (!empty($decoded['@graph']) && is_array($decoded['@graph'])) { foreach ($decoded['@graph'] as $i => $node) { if (!is_array($node)) { continue; } $decoded['@graph'][$i] = reiss_replace_host_urls_in_value($node, $current_lang, $default_lang, $host, $supported_langs); if (isset($decoded['@graph'][$i]['inLanguage'])) { $decoded['@graph'][$i]['inLanguage'] = $schema_lang; } } } elseif (isset($decoded['inLanguage'])) { $decoded['inLanguage'] = $schema_lang; } $json = wp_json_encode($decoded, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); } else { $json = reiss_replace_host_urls_in_json_string($json, $current_lang, $default_lang, $host, $supported_langs); } return preg_replace( '#]*type=("|\')application/ld\+json\1[^>]*>.*?#is', $script_open . $json . '', $matches[0] ); }, $html ); return $html; } } add_action('wpseo_head', 'reiss_start_wpseo_head_buffer', 0); add_action('wpseo_head', 'reiss_end_wpseo_head_buffer', 999999);