*/ private $internal_blocks; /** * Constructor. * * @since 3.8.10 * * @param \PLL_Base $polylang Polylang object. */ public function __construct( &$polylang ) { parent::__construct( $polylang ); $this->internal_blocks = new SplObjectStorage(); } /** * Adds the required hooks specific to the navigation language switcher. * * @since 3.2 * * @return self */ public function init() { parent::init(); add_action( 'rest_api_init', array( $this, 'register_switcher_menu_item_options_meta_rest_field' ) ); add_filter( 'render_block_core/navigation-link', array( $this, 'render_custom_attributes' ), 10, 3 ); add_filter( 'render_block_core/navigation-submenu', array( $this, 'render_custom_attributes' ), 10, 3 ); add_action( 'init', array( $this, 'register_editor_style' ) ); return $this; } /** * Registers the editor style for the navigation language switcher block. * * @since 3.8 * * @return void */ public function register_editor_style(): void { $suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min'; wp_register_style( 'pll-navigation-language-switcher-editor-style', plugins_url( 'css/build/navigation-language-switcher-editor-style' . $suffix . '.css', POLYLANG_ROOT_FILE ), array(), POLYLANG_VERSION ); } /** * Returns the navigation language switcher block name with the Polylang's namespace. * * @since 3.2 * * @return string The block name. */ protected function get_block_name() { return 'polylang/navigation-language-switcher'; } /** * Renders the `polylang/navigation-language-switcher` block on server. * * @since 3.1 * @since 3.3 Accepts two new parameters, $content and $block. * * @param array $attributes The block attributes. * @param string $content The saved content. Unused. * @param WP_Block $block The parsed block. * @return string The HTML string output to serve. */ public function render( $attributes, $content, $block ) { $attributes = $this->set_attributes_for_block( $attributes ); $switcher = new PLL_Switcher(); $switcher_elements = (array) $switcher->the_languages( $this->links, array_merge( $attributes, array( 'raw' => true ) ) ); if ( empty( $switcher_elements ) ) { return ''; } $generated_class = wp_apply_generated_classname_support( $block->block_type )['class']; if ( $attributes['dropdown'] ) { $inner_nav_link_blocks = array(); $top_level_lang = reset( $switcher_elements ); foreach ( $switcher_elements as $switcher_element ) { $inner_nav_link_blocks[] = $this->create_inner_block( 'core/navigation-link', $this->get_core_block_attributes( $switcher_element ), $switcher_element, $block->context, $attributes ); if ( $switcher_element['current_lang'] && ! $attributes['hide_current'] ) { $top_level_lang = $switcher_element; } } $submenu_attributes = $this->get_core_block_attributes( $top_level_lang ); $submenu_attributes['className'] .= ' ' . $generated_class; $submenu_block = $this->create_inner_block( 'core/navigation-submenu', $submenu_attributes, $top_level_lang, $block->context, $attributes, $inner_nav_link_blocks ); $output = $submenu_block->render(); } else { $output = ''; foreach ( $switcher_elements as $switcher_element ) { $link_attributes = $this->get_core_block_attributes( $switcher_element ); $link_attributes['className'] .= ' ' . $generated_class; $output .= $this->create_inner_block( 'core/navigation-link', $link_attributes, $switcher_element, $block->context, $attributes )->render(); } } return $output; } /** * Register switcher menu item meta options as a REST API field. * * @since 3.2 * * @return void */ public function register_switcher_menu_item_options_meta_rest_field() { register_post_meta( 'nav_menu_item', '_pll_menu_item', array( 'object_subtype' => 'nav_menu_item', 'description' => __( 'Language switcher settings', 'polylang' ), 'single' => true, 'show_in_rest' => array( 'schema' => array( 'type' => 'object', 'additionalProperties' => array( 'type' => 'boolean', ), ), ), ) ); } /** * Renders a core/naviagation-link or core/naviagation-submenu block by adding hreflang and lang attributes to the tag * and also the language flag if required. * * @since 3.6 * * @param string $block_content The block content. * @param array $block The full block, including name and attributes. * @param WP_Block $instance The block instance. * * @return string A formatted HTML string representing the core/navigation-link or core/navigation-submenu block. */ public function render_custom_attributes( $block_content, $block, $instance ) { if ( ! $this->internal_blocks->offsetExists( $instance ) ) { return $block_content; } $snapshot = $this->internal_blocks->offsetGet( $instance ); $this->internal_blocks->offsetUnset( $instance ); $content_tags = new WP_HTML_Tag_Processor( $block_content ); if ( 'core/navigation-submenu' === $instance->name ) { // If `openSubmenusOnClick`, the submenu is rendered as a button, so there are no `` to process. if ( empty( $instance->context['openSubmenusOnClick'] ) && $content_tags->next_tag( array( 'tag_name' => 'a' ) ) ) { $content_tags->set_attribute( 'hreflang', $snapshot['locale'] ); $content_tags->set_attribute( 'lang', $snapshot['locale'] ); } if ( $content_tags->next_tag( array( 'tag_name' => 'button' ) ) ) { $content_tags->set_attribute( 'aria-label', str_replace( static::PLACEHOLDER, __( 'Languages', 'polylang' ), (string) $content_tags->get_attribute( 'aria-label' ) ) ); } } elseif ( $content_tags->next_tag( array( 'tag_name' => 'a' ) ) ) { $content_tags->set_attribute( 'hreflang', $snapshot['locale'] ); $content_tags->set_attribute( 'lang', $snapshot['locale'] ); } $overridden_block_content = $content_tags->get_updated_html(); return str_replace( static::PLACEHOLDER, $snapshot['label'], $overridden_block_content ); } /** * Returns the path to the block JSON file directory. * The directory name being used to register a block. * * @since 3.8 * * @return string The path to the block. */ protected function get_path(): string { return __DIR__; } /** * Creates an inner navigation block and stores the locale and label to render for it. * * @since 3.8.10 * * @param string $block_name Core block name. * @param array $core_attributes Attributes to be rendered by core. * @param array $switcher_item Array of a switcher item data. * @param array $context Block context. * @param array $attributes Array of polylang/navigation-language-switcher attributes. * @param WP_Block[] $inner_blocks Optional inner blocks. * @return WP_Block */ private function create_inner_block( $block_name, $core_attributes, $switcher_item, $context, $attributes, $inner_blocks = array() ) { $block = new WP_Block( array( 'blockName' => $block_name, 'attrs' => $core_attributes, 'innerBlocks' => $inner_blocks, ), $context ); $this->internal_blocks->offsetSet( $block, array( 'locale' => $switcher_item['locale'], 'label' => $this->get_link_label( $switcher_item, $attributes ), ) ); return $block; } /** * Builds the language switcher link label from a trusted switcher item. * * @since 3.8.10 * * @param array $switcher_item Array of a switcher item data. * @param array $attributes Array of polylang/navigation-language-switcher attributes. * @return string */ private function get_link_label( $switcher_item, $attributes ) { $show_flags = ! empty( $attributes['show_flags'] ); $show_names = ! empty( $attributes['show_names'] ); $link_label = ''; if ( $show_flags ) { $link_label .= $switcher_item['flag']; } if ( $show_names ) { $name = esc_html( $switcher_item['name'] ); $link_label .= $show_flags ? ' ' . $name : $name; } return $link_label; } /** * Returns attributes that fit for core/navigation-link or core/navigation-submenu and specific to polylang/navigation-language-switcher. * * @since 3.6 * * @param array $switcher_item Array of a switcher item data. * @return array Attributes to be rendered by core. */ private function get_core_block_attributes( $switcher_item ) { return array( 'label' => static::PLACEHOLDER, 'url' => $switcher_item['url'], 'className' => trim( implode( ' ', (array) $switcher_item['classes'] ) ), ); } }