manager = $manager; $this->catalog = $catalog; } public function register() { add_shortcode( 'glvr_locale_switcher', array( $this, 'shortcode' ) ); add_action( 'init', array( $this, 'register_block' ) ); add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_style' ) ); add_action( 'wp_body_open', array( $this, 'render_global' ), 20 ); add_action( 'wp_footer', array( $this, 'render_global' ), 1 ); add_filter( 'body_class', array( $this, 'body_classes' ) ); } public function register_block() { wp_register_style( 'glovario-locale-switcher', GLOVARIO_URL . 'assets/locale-switcher.css', array(), GLOVARIO_VERSION ); register_block_type( GLOVARIO_PATH . 'blocks/locale-switcher', array( 'render_callback' => array( $this, 'render' ) ) ); } /** * Loads the stylesheet in the head only for the site-wide flag, which is * printed at the top of every page. The block and the shortcode enqueue it * themselves when they render, so other pages load no Glovario asset. */ public function enqueue_style() { if ( $this->manager->global_switcher_enabled() && $this->manager->standalone_available() ) { wp_enqueue_style( 'glovario-locale-switcher' ); } } public function body_classes( $classes ) { if ( $this->manager->global_switcher_enabled() && $this->manager->standalone_available() ) { $classes[] = 'glovario-global-locale-switcher-enabled'; } return $classes; } public function shortcode( $attributes ) { $attributes = shortcode_atts( array( 'show_codes' => 'false', ), $attributes, 'glvr_locale_switcher' ); return $this->render( array( 'showCodes' => 'true' === strtolower( (string) $attributes['show_codes'] ) ) ); } public function render( $attributes = array() ) { if ( ! $this->manager->standalone_available() ) { return current_user_can( 'manage_options' ) && ( is_admin() || $this->is_rest_request() ) ? '

' . /* translators: Glovario is the product name; do not translate it. */ esc_html__( 'Enable Standalone Glovario selector mode under Locale Settings to display this switcher.', 'glovario' ) . '

' : ''; } wp_enqueue_style( 'glovario-locale-switcher' ); $show_codes = ! empty( $attributes['showCodes'] ); /** * Short-circuits the inline switcher markup. * * @param string|null $markup Null to render the default and the second language. * @param bool $show_codes Whether locale codes follow the names. * @param LocaleSwitcher $switcher This switcher, for its link helpers. */ $markup = apply_filters( 'glovario_locale_switcher_markup', null, $show_codes, $this ); if ( is_string( $markup ) ) { return wp_kses( $markup, self::allowed_html() ); } $settings = $this->manager->settings(); $links = $this->link_markup( $settings['default_locale'], 'inline', $show_codes ); if ( '' !== $settings['second_locale'] ) { $links .= $this->link_markup( $settings['second_locale'], 'inline', $show_codes ); } return $this->inline_container( $links ); } /** * One language link, shared by the inline and site-wide switchers. * * @param string $locale Locale code. * @param string $variant 'inline' or 'global'. * @param bool $show_codes Whether the locale code follows the name. * @return string Escaped markup. */ public function link_markup( $locale, $variant, $show_codes = false ) { $active = $this->active_locale(); $name = $this->catalog->name( $locale ); $label = $show_codes ? $name . ' (' . $locale . ')' : $name; $global = 'global' === $variant; return sprintf( '%6$s%8$s', $global ? 'glovario-global-locale-switcher__option' : 'glovario-locale-switcher__link', $locale === $active ? ' is-active' : '', esc_url( add_query_arg( StandaloneLocale::QUERY, $locale, $this->current_url() ) ), esc_attr( str_replace( '_', '-', $locale ) ), $locale === $active ? ' aria-current="true"' : '', $this->flag_markup( $locale, $global ? 'glovario-global-locale-switcher__flag' : 'glovario-locale-switcher__flag' ), $global ? '' : ' class="glovario-locale-switcher__label"', esc_html( $label ) ); } /** * Wrap language links in the inline switcher container. * * @param string $links Escaped link markup. * @return string */ public function inline_container( $links ) { return sprintf( '', esc_attr__( 'Language', 'glovario' ), $links ); } public function render_global() { if ( $this->global_rendered || ! $this->manager->global_switcher_enabled() || ! $this->manager->standalone_available() ) { return; } $this->global_rendered = true; echo $this->global_markup(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Markup is assembled exclusively from escaped values below. } public function global_markup() { if ( ! $this->manager->global_switcher_enabled() || ! $this->manager->standalone_available() ) { return ''; } wp_enqueue_style( 'glovario-locale-switcher' ); /** * Short-circuits the language links of the site-wide switcher. * * @param string|null $links Null to list the default and the second language. * @param LocaleSwitcher $switcher This switcher, for its link helpers. */ $links = apply_filters( 'glovario_global_locale_switcher_links', null, $this ); if ( is_string( $links ) ) { $links = wp_kses( $links, self::allowed_html() ); } else { $settings = $this->manager->settings(); $links = $this->link_markup( $settings['default_locale'], 'global' ); if ( '' !== $settings['second_locale'] ) { $links .= $this->link_markup( $settings['second_locale'], 'global' ); } } $active = $this->active_locale(); /* translators: %s: current language name. */ $summary = sprintf( __( 'Choose language. Current language: %s.', 'glovario' ), $this->catalog->name( $active ) ); return sprintf( '
%3$s%1$s
', esc_attr( $summary ), esc_attr__( 'Choose language', 'glovario' ), $this->flag_markup( $active, 'glovario-global-locale-switcher__flag' ), esc_attr__( 'Available languages', 'glovario' ), $links ); } /** * Tags and attributes the switchers print, for markup that filters return. * * @return array Allowed HTML for wp_kses(). */ public static function allowed_html() { $common = array( 'class' => true, 'id' => true, 'lang' => true, 'title' => true, 'aria-label' => true, 'aria-hidden' => true, 'aria-current' => true, ); return array( 'nav' => $common, 'ul' => $common, 'li' => $common, 'span' => $common, 'a' => array_merge( $common, array( 'href' => true, 'hreflang' => true, ) ), 'img' => array_merge( $common, array( 'src' => true, 'alt' => true, 'width' => true, 'height' => true, ) ), ); } private function current_url() { return remove_query_arg( array( StandaloneLocale::QUERY, LocaleManager::PREVIEW_QUERY, '_glovario_locale_nonce' ), StandaloneLocale::request_path() ); } private function active_locale() { return apply_filters( 'glovario_current_locale', determine_locale(), array( 'source' => 'locale_switcher' ) ); } private function flag_markup( $locale, $class_name ) { $flag = $this->flag_for_locale( $locale ); if ( $flag ) { return ''; } $parts = preg_split( '/[_-]/', sanitize_locale_name( $locale ) ); $code = strtoupper( substr( (string) ( $parts[0] ?? $locale ), 0, 2 ) ); return ''; } private function flag_for_locale( $locale ) { $parts = preg_split( '/[_-]/', sanitize_locale_name( $locale ) ); $region = ''; foreach ( array_slice( $parts, 1 ) as $part ) { if ( preg_match( '/^[A-Za-z]{2}$/', $part ) ) { $region = strtoupper( $part ); break; } } if ( in_array( $region, array( 'UN', 'XA', 'XB', 'ZZ' ), true ) ) { $region = ''; } if ( ! $region ) { $defaults = array( 'af' => 'ZA', 'am' => 'ET', 'ar' => 'SA', 'az' => 'AZ', 'bel' => 'BY', 'bg' => 'BG', 'bn' => 'BD', 'bs' => 'BA', 'ca' => 'ES', 'ceb' => 'PH', 'cs' => 'CZ', 'cy' => 'GB', 'da' => 'DK', 'de' => 'DE', 'el' => 'GR', 'en' => 'US', 'es' => 'ES', 'et' => 'EE', 'eu' => 'ES', 'fa' => 'IR', 'fi' => 'FI', 'fil' => 'PH', 'fr' => 'FR', 'gd' => 'GB', 'gl' => 'ES', 'gu' => 'IN', 'he' => 'IL', 'hi' => 'IN', 'hr' => 'HR', 'hu' => 'HU', 'hy' => 'AM', 'id' => 'ID', 'is' => 'IS', 'it' => 'IT', 'ja' => 'JP', 'jv' => 'ID', 'ka' => 'GE', 'kk' => 'KZ', 'km' => 'KH', 'kn' => 'IN', 'ko' => 'KR', 'lo' => 'LA', 'lt' => 'LT', 'lv' => 'LV', 'mk' => 'MK', 'ml' => 'IN', 'mn' => 'MN', 'mr' => 'IN', 'ms' => 'MY', 'my' => 'MM', 'nb' => 'NO', 'ne' => 'NP', 'nl' => 'NL', 'nn' => 'NO', 'pa' => 'IN', 'pl' => 'PL', 'ps' => 'AF', 'pt' => 'PT', 'ro' => 'RO', 'ru' => 'RU', 'si' => 'LK', 'sk' => 'SK', 'sl' => 'SI', 'sq' => 'AL', 'sr' => 'RS', 'sv' => 'SE', 'sw' => 'TZ', 'ta' => 'IN', 'te' => 'IN', 'th' => 'TH', 'tl' => 'PH', 'tr' => 'TR', 'tt' => 'RU', 'ug' => 'CN', 'uk' => 'UA', 'ur' => 'PK', 'uz' => 'UZ', 'vi' => 'VN', 'zh' => 'CN', 'yor' => 'NG', ); $region = $defaults[ strtolower( (string) ( $parts[0] ?? '' ) ) ] ?? ''; } if ( ! preg_match( '/^[A-Z]{2}$/', $region ) ) { return ''; } $entities = ''; foreach ( str_split( $region ) as $letter ) { $entities .= '&#' . ( 127397 + ord( $letter ) ) . ';'; } return html_entity_decode( $entities, ENT_NOQUOTES, 'UTF-8' ); } private function is_rest_request() { return defined( 'REST_REQUEST' ) && REST_REQUEST; } }