Changeset 3478054
- Timestamp:
- 03/09/2026 12:13:50 PM (4 weeks ago)
- Location:
- lang-attribute-blocks
- Files:
-
- 8 added
- 2 deleted
- 14 edited
- 1 copied
-
tags/3.0 (copied) (copied from lang-attribute-blocks/trunk)
-
tags/3.0/assets (deleted)
-
tags/3.0/build/index.asset.php (modified) (1 diff)
-
tags/3.0/build/index.css (modified) (1 diff)
-
tags/3.0/build/index.js (modified) (1 diff)
-
tags/3.0/includes/class-lang-attribute-blocks.php (modified) (8 diffs)
-
tags/3.0/lang-attribute-blocks.php (modified) (1 diff)
-
tags/3.0/languages/lang-attribute-blocks.pot (modified) (4 diffs)
-
tags/3.0/readme.txt (modified) (7 diffs)
-
tags/3.0/src (added)
-
tags/3.0/src/index.js (added)
-
tags/3.0/src/index.php (added)
-
tags/3.0/src/index.scss (added)
-
trunk/assets (deleted)
-
trunk/build/index.asset.php (modified) (1 diff)
-
trunk/build/index.css (modified) (1 diff)
-
trunk/build/index.js (modified) (1 diff)
-
trunk/includes/class-lang-attribute-blocks.php (modified) (8 diffs)
-
trunk/lang-attribute-blocks.php (modified) (1 diff)
-
trunk/languages/lang-attribute-blocks.pot (modified) (4 diffs)
-
trunk/readme.txt (modified) (7 diffs)
-
trunk/src (added)
-
trunk/src/index.js (added)
-
trunk/src/index.php (added)
-
trunk/src/index.scss (added)
Legend:
- Unmodified
- Added
- Removed
-
lang-attribute-blocks/tags/3.0/build/index.asset.php
r3337921 r3478054 1 <?php return array('dependencies' => array('react', 'wp-block-editor', 'wp-components', 'wp-compose', 'wp- hooks', 'wp-i18n'), 'version' => '4f65e3d03de63b85e0a0');1 <?php return array('dependencies' => array('react', 'wp-block-editor', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-editor', 'wp-hooks', 'wp-i18n', 'wp-plugins'), 'version' => '4c50b3542257c21dfef2'); -
lang-attribute-blocks/tags/3.0/build/index.css
r3337921 r3478054 1 :root{--nakedcatplugins-lang-attr-highlight-color:rgba(255,0,0,. 75)}.editor-styles-wrapper .naked-cat-plugins-has-lang-attr,body:not(.block-editor-page) [lang]{outline:3px dashed var(--nakedcatplugins-lang-attr-highlight-color)!important;outline-offset:2px}1 :root{--nakedcatplugins-lang-attr-highlight-color:rgba(255,0,0,.65)}.editor-styles-wrapper .naked-cat-plugins-has-lang-attr,body:not(.block-editor-page) [lang]{outline:3px dashed var(--nakedcatplugins-lang-attr-highlight-color)!important;outline-offset:2px}html.naked-cat-plugins-post-has-lang-attr{outline:3px dashed var(--nakedcatplugins-lang-attr-highlight-color)!important;outline-offset:-3px} -
lang-attribute-blocks/tags/3.0/build/index.js
r3337921 r3478054 1 (()=>{"use strict";const t=window.React,e=window.wp.i18n,a=window.wp.blockEditor, n=window.wp.components,l=window.wp.compose,i=window.wp.hooks,o=(0,l.createHigherOrderComponent)((l=>i=>{if(window.nakedCatPluginsLangAttributeBlocks&&window.nakedCatPluginsLangAttributeBlocks.supportedBlocks&&!window.nakedCatPluginsLangAttributeBlocks.supportedBlocks.includes(i.name))return(0,t.createElement)(l,{...i});const{attributes:o,setAttributes:r}=i,s=o.lang||"",d=o.dir||"ltr";return(0,t.createElement)(t.Fragment,null,(0,t.createElement)(l,{...i}),(0,t.createElement)(a.InspectorControls,null,(0,t.createElement)(n.PanelBody,{title:(0,e.__)("Language Settings","lang-attribute-blocks"),initialOpen:!0},(0,t.createElement)(n.TextControl,{label:(0,e.__)("Language Code","lang-attribute-blocks"),value:s,onChange:t=>r({lang:t}),placeholder:window.nakedCatPluginsLangAttributeBlocks?.placeholderText||"en (default website language)",help:(0,e.__)("Valid language code, like “fr” or “pt-PT”, if different from the website main language (shown as a placeholder)","lang-attribute-blocks")}),(0,t.createElement)(n.SelectControl,{label:(0,e.__)("Text Direction","lang-attribute-blocks"),value:d,options:[{label:(0,e.__)("Left to right","lang-attribute-blocks"),value:"ltr"},{label:(0,e.__)("Right to left","lang-attribute-blocks"),value:"rtl"}],onChange:t=>r({dir:t})}))))}),"addLangAttributesToGroupBlock");(0,i.addFilter)("editor.BlockEdit","lang-attribute-blocks/add-lang-attributes-to-group-block",o);const r=(0,l.createHigherOrderComponent)((e=>a=>window.nakedCatPluginsLangAttributeBlocks&&window.nakedCatPluginsLangAttributeBlocks.supportedBlocks&&!window.nakedCatPluginsLangAttributeBlocks.supportedBlocks.includes(a.block.name)?(0,t.createElement)(e,{...a}):a.block.attributes.lang&&""!==a.block.attributes.lang.trim()?(0,t.createElement)(e,{...a,className:"naked-cat-plugins-has-lang-attr"}):(0,t.createElement)(e,{...a})),"withLangAttr");(0,i.addFilter)("blocks.registerBlockType","lang-attribute-blocks/add-lang-and-dir-attributes",(function(t,e){return window.nakedCatPluginsLangAttributeBlocks&&window.nakedCatPluginsLangAttributeBlocks.supportedBlocks&&window.nakedCatPluginsLangAttributeBlocks.supportedBlocks.includes(e)&&(t.attributes={...t.attributes,lang:{type:"string",default:""},dir:{type:"string",default:"ltr"}}),t})),window.nakedCatPluginsLangAttributeBlocks&&window.nakedCatPluginsLangAttributeBlocks.highlightEnabled&&(0,i.addFilter)("editor.BlockListBlock","lang-attribute-blocks/add-lang-and-dir-attributes",r)})();1 (()=>{"use strict";const t=window.React,e=window.wp.i18n,a=window.wp.blockEditor,l=window.wp.components,n=window.wp.compose,i=window.wp.hooks,o=window.wp.plugins,r=window.wp.editor,s=window.wp.coreData,g=window.wp.data,u=(0,n.createHigherOrderComponent)((n=>i=>{if(window.nakedCatPluginsLangAttributeBlocks&&window.nakedCatPluginsLangAttributeBlocks.supportedBlocks&&!window.nakedCatPluginsLangAttributeBlocks.supportedBlocks.includes(i.name))return(0,t.createElement)(n,{...i});const{attributes:o,setAttributes:r}=i,s=(o.lang||"").trim(),g=o.dir||"ltr";return(0,t.createElement)(t.Fragment,null,(0,t.createElement)(n,{...i}),(0,t.createElement)(a.InspectorControls,null,(0,t.createElement)(l.PanelBody,{title:(0,e.__)("Block Language","lang-attribute-blocks"),initialOpen:!0},(0,t.createElement)(l.TextControl,{label:(0,e.__)("Language Code","lang-attribute-blocks"),value:s,onChange:t=>r({lang:t.trim()}),placeholder:window.nakedCatPluginsLangAttributeBlocks?.placeholderText||"en (default website language)",help:(0,e.__)("Valid language code for this block, like “fr” or “pt-PT”, if different from the website's or page's main language (shown as a placeholder)","lang-attribute-blocks")}),(0,t.createElement)(l.SelectControl,{label:(0,e.__)("Text Direction","lang-attribute-blocks"),value:g,options:[{label:(0,e.__)("Left to right","lang-attribute-blocks"),value:"ltr"},{label:(0,e.__)("Right to left","lang-attribute-blocks"),value:"rtl"}],onChange:t=>r({dir:t})}))))}),"addLangAttributesToGroupBlock");(0,i.addFilter)("editor.BlockEdit","lang-attribute-blocks/add-lang-attributes-to-group-block",u);const d=(0,n.createHigherOrderComponent)((e=>a=>window.nakedCatPluginsLangAttributeBlocks&&window.nakedCatPluginsLangAttributeBlocks.supportedBlocks&&!window.nakedCatPluginsLangAttributeBlocks.supportedBlocks.includes(a.block.name)?(0,t.createElement)(e,{...a}):a.block.attributes.lang&&""!==a.block.attributes.lang.trim()?(0,t.createElement)(e,{...a,className:"naked-cat-plugins-has-lang-attr"}):(0,t.createElement)(e,{...a})),"withLangAttr");(0,i.addFilter)("blocks.registerBlockType","lang-attribute-blocks/add-lang-and-dir-attributes",(function(t,e){return window.nakedCatPluginsLangAttributeBlocks&&window.nakedCatPluginsLangAttributeBlocks.supportedBlocks&&window.nakedCatPluginsLangAttributeBlocks.supportedBlocks.includes(e)&&(t.attributes={...t.attributes,lang:{type:"string",default:""},dir:{type:"string",default:"ltr"}}),t})),window.nakedCatPluginsLangAttributeBlocks&&window.nakedCatPluginsLangAttributeBlocks.highlightEnabled&&(0,i.addFilter)("editor.BlockListBlock","lang-attribute-blocks/add-lang-and-dir-attributes",d),(0,o.registerPlugin)("nakedcatplugins-page-lang-controls",{render:()=>{var a,n;const i=(0,g.useSelect)((t=>t("core/editor").getCurrentPostType()),[]),[o,u]=(0,s.useEntityProp)("postType",i,"meta"),d=(null!==(a=o?._nakedcatplugins_page_lang)&&void 0!==a?a:"").trim(),c=null!==(n=o?._nakedcatplugins_page_dir)&&void 0!==n?n:"ltr";return(0,t.createElement)(r.PluginDocumentSettingPanel,{name:"nakedcatplugins-page-lang-panel",title:(0,e.__)("Page Language","lang-attribute-blocks")},(0,t.createElement)(l.TextControl,{label:(0,e.__)("Language Code","lang-attribute-blocks"),value:d,onChange:t=>u({...o,_nakedcatplugins_page_lang:t.trim()}),placeholder:window.nakedCatPluginsLangAttributeBlocks?.placeholderText||"en (default website language)",help:(0,e.__)("Valid language code for this page/post, like “fr” or “pt-PT”, if different from the website's main language (shown as a placeholder) - This overrides the HTML language attribute","lang-attribute-blocks")}),(0,t.createElement)(l.SelectControl,{label:(0,e.__)("Text Direction","lang-attribute-blocks"),value:c,options:[{label:(0,e.__)("Left to right","lang-attribute-blocks"),value:"ltr"},{label:(0,e.__)("Right to left","lang-attribute-blocks"),value:"rtl"}],onChange:t=>u({...o,_nakedcatplugins_page_dir:t})}))}})})(); -
lang-attribute-blocks/tags/3.0/includes/class-lang-attribute-blocks.php
r3342031 r3478054 1 1 <?php 2 2 /** 3 * Main plugin class for Language Attribute for Container Blocks 3 * Main plugin class for Language Attribute for Container Blocks and Pages/Posts 4 4 * 5 5 * This file contains the core functionality for adding language and direction … … 136 136 add_filter( 'render_block_' . $block_name, array( $this, 'process_blocks' ), 10, 2 ); 137 137 } 138 // Register post meta for page-level language settings 139 add_action( 'init', array( $this, 'register_page_lang_meta' ) ); 140 // Apply page-level language attribute to the <html> element 141 add_filter( 'language_attributes', array( $this, 'apply_page_lang_attribute' ) ); 138 142 // Enqueues JavaScript and CSS assets for the WordPress block editor 139 143 add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue_block_editor_assets' ) ); … … 144 148 // Add settings link to the plugin action links 145 149 add_filter( 'plugin_action_links_' . plugin_basename( NAKEDCATPLUGINS_LANG_ATTRIBUTE_BLOCKS_FILE ), array( $this, 'add_plugin_action_links' ) ); 150 // Add classic editor metabox for page-level language settings 151 add_action( 'add_meta_boxes', array( $this, 'add_classic_editor_metabox' ) ); 152 add_action( 'save_post', array( $this, 'save_classic_editor_metabox' ), 10, 2 ); 153 } 154 155 /** 156 * Register post meta fields for page-level language settings. 157 * 158 * Registers '_nakedcatplugins_page_lang' and '_nakedcatplugins_page_dir' meta 159 * for all public post types, exposed via the REST API so the block editor 160 * can read and write them. 161 * 162 * @since 3.0 163 * @hook init 164 * @return void 165 */ 166 public function register_page_lang_meta() { 167 $args_lang = array( 168 'show_in_rest' => true, 169 'single' => true, 170 'type' => 'string', 171 'default' => '', 172 'auth_callback' => function () { 173 return current_user_can( 'edit_posts' ); 174 }, 175 ); 176 $args_dir = array( 177 'show_in_rest' => true, 178 'single' => true, 179 'type' => 'string', 180 'default' => 'ltr', 181 'auth_callback' => function () { 182 return current_user_can( 'edit_posts' ); 183 }, 184 ); 185 // Register for all public post types 186 foreach ( get_post_types( array( 'public' => true ) ) as $post_type ) { 187 register_post_meta( $post_type, '_nakedcatplugins_page_lang', $args_lang ); 188 register_post_meta( $post_type, '_nakedcatplugins_page_dir', $args_dir ); 189 } 190 } 191 192 /** 193 * Override the HTML lang (and optionally dir) attribute for singular pages/posts. 194 * 195 * When a page or post has the '_nakedcatplugins_page_lang' meta set, this method 196 * replaces the lang attribute on the <html> element with the stored value. 197 * When '_nakedcatplugins_page_dir' is set to 'rtl', the dir attribute is also applied. 198 * 199 * @since 3.0 200 * @hook language_attributes 201 * @param string $output The existing language attributes string, e.g. 'lang="en-US"'. 202 * @return string Modified language attributes string. 203 */ 204 public function apply_page_lang_attribute( $output ) { 205 if ( ! is_singular() ) { 206 return $output; 207 } 208 $post_id = get_queried_object_id(); 209 $page_lang = trim( get_post_meta( $post_id, '_nakedcatplugins_page_lang', true ) ); 210 $page_dir = trim( get_post_meta( $post_id, '_nakedcatplugins_page_dir', true ) ); 211 212 if ( ! empty( $page_lang ) ) { 213 $safe_lang = esc_attr( $page_lang ); 214 if ( strpos( $output, 'lang=' ) !== false ) { 215 $output = preg_replace( '/lang="[^"]*"/', 'lang="' . $safe_lang . '"', $output ); 216 } else { 217 $output .= ' lang="' . $safe_lang . '"'; 218 } 219 // Add our class name 220 if ( strpos( $output, 'class=' ) !== false ) { 221 $output = preg_replace( '/class="([^"]*)"/', 'class="$1 naked-cat-plugins-post-has-lang-attr"', $output ); 222 } else { 223 $output .= ' class="naked-cat-plugins-post-has-lang-attr"'; 224 } 225 } 226 227 if ( ! empty( $page_dir ) && 'rtl' === $page_dir ) { 228 $safe_dir = esc_attr( $page_dir ); 229 if ( strpos( $output, 'dir=' ) !== false ) { 230 $output = preg_replace( '/dir="[^"]*"/', 'dir="' . $safe_dir . '"', $output ); 231 } else { 232 $output .= ' dir="' . $safe_dir . '"'; 233 } 234 } 235 236 return $output; 146 237 } 147 238 … … 196 287 public function process_blocks( $block_content, $block ) { 197 288 if ( isset( $block['attrs']['lang'] ) && ! empty( $block['attrs']['lang'] ) ) { 198 $lang = esc_attr( $block['attrs']['lang']);199 $dir = isset( $block['attrs']['dir'] ) ? esc_attr( $block['attrs']['dir'] ) : 'ltr';289 $lang = trim( esc_attr( $block['attrs']['lang'] ) ); 290 $dir = trim( isset( $block['attrs']['dir'] ) ? esc_attr( $block['attrs']['dir'] ) : 'ltr' ); 200 291 $tag_processor = new \WP_HTML_Tag_Processor( $block_content ); 201 292 // Depending on the block type, we will set the tag to be processed … … 251 342 'nakedcatplugins-lang-attribute-blocks-script', 252 343 plugins_url( 'build/index.js', NAKEDCATPLUGINS_LANG_ATTRIBUTE_BLOCKS_FILE ), 253 array( 'wp-blocks', 'wp-dom', 'wp-dom-ready', 'wp-edit-post', 'wp-e lement', 'wp-i18n', 'wp-block-editor' ),344 array( 'wp-blocks', 'wp-dom', 'wp-dom-ready', 'wp-edit-post', 'wp-editor', 'wp-element', 'wp-i18n', 'wp-block-editor', 'wp-plugins', 'wp-data', 'wp-core-data' ), 254 345 filemtime( plugin_dir_path( NAKEDCATPLUGINS_LANG_ATTRIBUTE_BLOCKS_FILE ) . 'build/index.js' ), 255 346 true … … 361 452 add_settings_section( 362 453 'nakedcatplugins_lang_attr_section', 363 __( 'Language Attribute for Container Blocks ', 'lang-attribute-blocks' ),454 __( 'Language Attribute for Container Blocks and Pages/Posts', 'lang-attribute-blocks' ), 364 455 array( $this, 'settings_section_callback' ), 365 456 'writing' … … 383 474 */ 384 475 public function settings_section_callback() { 385 echo '<p>' . esc_html__( 'Configure Language Attribute for Container Blocks plugin settings.', 'lang-attribute-blocks' ) . '</p>';476 echo '<p>' . esc_html__( 'Configure Language Attribute for Container Blocks and Pages/Posts plugin settings.', 'lang-attribute-blocks' ) . '</p>'; 386 477 } 387 478 /** … … 405 496 406 497 /** 498 * Register the page language metabox for the classic editor. 499 * 500 * Added to all public post types so editors can set the page-level 501 * lang and dir meta without the block editor. 502 * 503 * @since 3.0 504 * @hook add_meta_boxes 505 * @return void 506 */ 507 public function add_classic_editor_metabox() { 508 // Only register the metabox in the classic editor; the block editor 509 // provides its own Document Settings panel for the same fields. 510 $screen = get_current_screen(); 511 if ( $screen && $screen->is_block_editor() ) { 512 return; 513 } 514 515 foreach ( get_post_types( array( 'public' => true ), 'names' ) as $post_type ) { 516 add_meta_box( 517 'nakedcatplugins_page_language', 518 __( 'Page Language', 'lang-attribute-blocks' ), 519 array( $this, 'render_classic_editor_metabox' ), 520 $post_type, 521 'side', 522 'default' 523 ); 524 } 525 } 526 527 /** 528 * Render the classic editor metabox HTML. 529 * 530 * @since 3.0 531 * @param \WP_Post $post The current post object. 532 * @return void 533 */ 534 public function render_classic_editor_metabox( \WP_Post $post ) { 535 $lang = trim( get_post_meta( $post->ID, '_nakedcatplugins_page_lang', true ) ); 536 $dir = trim( get_post_meta( $post->ID, '_nakedcatplugins_page_dir', true ) ); 537 if ( empty( $dir ) ) { 538 $dir = 'ltr'; 539 } 540 541 wp_nonce_field( 'nakedcatplugins_page_language_metabox', 'nakedcatplugins_page_language_nonce' ); 542 $placeholder = sprintf( 543 /* translators: %s: The website's default language code */ 544 __( '%s (default website language)', 'lang-attribute-blocks' ), 545 get_bloginfo( 'language' ) 546 ); 547 ?> 548 <p> 549 <label for="nakedcatplugins_page_lang"> 550 <?php esc_html_e( 'Language Code', 'lang-attribute-blocks' ); ?> 551 </label> 552 <input type="text" id="nakedcatplugins_page_lang" name="nakedcatplugins_page_lang" value="<?php echo esc_attr( $lang ); ?>" placeholder="<?php echo esc_attr( $placeholder ); ?>" class="widefat"/> 553 <span class="description"> 554 <?php esc_html_e( "Valid language code for this page/post, like “fr” or “pt-PT”, if different from the website's main language (shown as a placeholder) - This overrides the HTML language attribute", 'lang-attribute-blocks' ); ?> 555 </span> 556 </p> 557 <p> 558 <label for="nakedcatplugins_page_dir"> 559 <?php esc_html_e( 'Text Direction', 'lang-attribute-blocks' ); ?> 560 </label> 561 <select id="nakedcatplugins_page_dir" name="nakedcatplugins_page_dir" class="widefat"> 562 <option value="ltr" <?php selected( $dir, 'ltr' ); ?>> 563 <?php esc_html_e( 'Left to right', 'lang-attribute-blocks' ); ?> 564 </option> 565 <option value="rtl" <?php selected( $dir, 'rtl' ); ?>> 566 <?php esc_html_e( 'Right to left', 'lang-attribute-blocks' ); ?> 567 </option> 568 </select> 569 </p> 570 <?php 571 } 572 573 /** 574 * Save the classic editor metabox values. 575 * 576 * Validates the nonce to confirm the metabox was present in the form 577 * (guards against quick edit, bulk actions, REST and programmatic saves 578 * that would otherwise wipe the meta), checks capabilities, then saves 579 * or deletes the page language meta fields. 580 * 581 * @since 3.0 582 * @hook save_post 583 * @param int $post_id The post ID being saved. 584 * @param \WP_Post $post The post object being saved. 585 * @return void 586 */ 587 public function save_classic_editor_metabox( int $post_id, \WP_Post $post ) { 588 // If our metabox was not present in the request, bail out to avoid 589 // accidentally wiping the meta (e.g. quick edit, REST, wp_update_post()). 590 if ( ! isset( $_POST['nakedcatplugins_page_language_nonce'] ) ) { 591 return; 592 } 593 594 // WordPress has already verified the post nonce, but we verify our own 595 // as extra confirmation that our metabox submitted these values. 596 if ( ! wp_verify_nonce( sanitize_key( $_POST['nakedcatplugins_page_language_nonce'] ), 'nakedcatplugins_page_language_metabox' ) ) { 597 return; 598 } 599 600 // Bail on autosave and revisions. 601 if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { 602 return; 603 } 604 if ( wp_is_post_revision( $post_id ) ) { 605 return; 606 } 607 608 // Check the user has permission to edit this specific post. 609 $post_type_object = get_post_type_object( $post->post_type ); 610 if ( ! current_user_can( $post_type_object->cap->edit_post, $post_id ) ) { 611 return; 612 } 613 614 // Save lang — trim and sanitize; delete if empty so the DB stays clean. 615 if ( isset( $_POST['nakedcatplugins_page_lang'] ) ) { 616 $lang = trim( sanitize_text_field( wp_unslash( $_POST['nakedcatplugins_page_lang'] ) ) ); 617 if ( ! empty( $lang ) ) { 618 update_post_meta( $post_id, '_nakedcatplugins_page_lang', $lang ); 619 } else { 620 delete_post_meta( $post_id, '_nakedcatplugins_page_lang' ); 621 delete_post_meta( $post_id, '_nakedcatplugins_page_dir' ); 622 return; // If lang is empty, we also delete dir and skip saving it since it doesn't make sense to have a dir without a lang. 623 } 624 } 625 626 // Save dir — whitelist to known values only. 627 if ( isset( $_POST['nakedcatplugins_page_dir'] ) ) { 628 $dir = sanitize_text_field( wp_unslash( $_POST['nakedcatplugins_page_dir'] ) ); 629 $dir = in_array( $dir, array( 'ltr', 'rtl' ), true ) ? $dir : 'ltr'; 630 update_post_meta( $post_id, '_nakedcatplugins_page_dir', $dir ); 631 } 632 } 633 634 /** 407 635 * Add settings link to the plugin action links. 408 636 * 409 637 * This function adds a "Settings" link to the plugin's row on the Plugins page 410 * that points to the Language Attribute for Container Blocks settings section638 * that points to the Language Attribute for Container Blocks and Pages/Posts settings section 411 639 * on the Settings > Writing page. 412 640 * -
lang-attribute-blocks/tags/3.0/lang-attribute-blocks.php
r3381456 r3478054 1 1 <?php 2 2 /** 3 * Plugin Name: Language Attribute for Container Blocks 3 * Plugin Name: Language Attribute for Container Blocks and Pages/Posts 4 4 * Plugin URI: 5 * Description: Add “lang” and “dir” attributes on Group, Columns, and Cover WordPress Blocks6 * Version: 2.25 * Description: Add `lang` and `dir` attributes to Group, Columns, Cover, and other specific WordPress Blocks, or to the whole page/post. 6 * Version: 3.0 7 7 * Author: Naked Cat Plugins (by Webdados) 8 8 * Author URI: https://nakedcatplugins.com 9 9 * Text Domain: lang-attribute-blocks 10 10 * Requires at least: 5.9 11 * Tested up to: 6.911 * Tested up to: 7.0 12 12 * Requires PHP: 7.2 13 13 * License: GPLv3 -
lang-attribute-blocks/tags/3.0/languages/lang-attribute-blocks.pot
r3381456 r3478054 1 # Copyright (C) 202 5Naked Cat Plugins (by Webdados)1 # Copyright (C) 2026 Naked Cat Plugins (by Webdados) 2 2 # This file is distributed under the GPLv3. 3 3 msgid "" 4 4 msgstr "" 5 "Project-Id-Version: Language Attribute for Container Blocks 2.2\n"5 "Project-Id-Version: Language Attribute for Container Blocks and Pages/Posts 2.2\n" 6 6 "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/lang-attribute-blocks\n" 7 7 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" … … 10 10 "Content-Type: text/plain; charset=UTF-8\n" 11 11 "Content-Transfer-Encoding: 8bit\n" 12 "POT-Creation-Date: 202 5-10-20T16:43:04+00:00\n"12 "POT-Creation-Date: 2026-03-09T12:04:28+00:00\n" 13 13 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 14 "X-Generator: WP-CLI 2.11.0\n" … … 17 17 #. Plugin Name of the plugin 18 18 #: lang-attribute-blocks.php 19 #: includes/class-lang-attribute-blocks.php: 36320 msgid "Language Attribute for Container Blocks "19 #: includes/class-lang-attribute-blocks.php:454 20 msgid "Language Attribute for Container Blocks and Pages/Posts" 21 21 msgstr "" 22 22 23 23 #. Description of the plugin 24 24 #: lang-attribute-blocks.php 25 msgid "Add “lang” and “dir” attributes on Group, Columns, and Cover WordPress Blocks"25 msgid "Add `lang` and `dir` attributes to Group, Columns, Cover, and other specific WordPress Blocks, or to the whole page/post." 26 26 msgstr "" 27 27 … … 37 37 38 38 #. translators: %s: The website's default language code 39 #: includes/class-lang-attribute-blocks.php:268 39 #: includes/class-lang-attribute-blocks.php:359 40 #: includes/class-lang-attribute-blocks.php:544 40 41 msgid "%s (default website language)" 41 42 msgstr "" 42 43 43 #: includes/class-lang-attribute-blocks.php: 37144 #: includes/class-lang-attribute-blocks.php:462 44 45 msgid "Highlight blocks with lang attribute" 45 46 msgstr "" 46 47 47 #: includes/class-lang-attribute-blocks.php: 38548 msgid "Configure Language Attribute for Container Blocks plugin settings."48 #: includes/class-lang-attribute-blocks.php:476 49 msgid "Configure Language Attribute for Container Blocks and Pages/Posts plugin settings." 49 50 msgstr "" 50 51 51 #: includes/class-lang-attribute-blocks.php: 39852 #: includes/class-lang-attribute-blocks.php:489 52 53 msgid "Show visual outline around blocks that have a language attribute set" 53 54 msgstr "" 54 55 55 #: includes/class-lang-attribute-blocks.php:4 0156 #: includes/class-lang-attribute-blocks.php:492 56 57 msgid "When enabled, blocks with a language attribute will be visually highlighted with a red dashed outline in both the editor and frontend (only for Administrators and Editors)." 57 58 msgstr "" 58 59 59 #: includes/class-lang-attribute-blocks.php:421 60 #: includes/class-lang-attribute-blocks.php:518 61 #: build/index.js:1 62 #: src/index.js:165 63 msgid "Page Language" 64 msgstr "" 65 66 #: includes/class-lang-attribute-blocks.php:550 67 #: build/index.js:1 68 #: src/index.js:49 69 #: src/index.js:168 70 msgid "Language Code" 71 msgstr "" 72 73 #: includes/class-lang-attribute-blocks.php:554 74 #: build/index.js:1 75 #: src/index.js:172 76 msgid "Valid language code for this page/post, like “fr” or “pt-PT”, if different from the website's main language (shown as a placeholder) - This overrides the HTML language attribute" 77 msgstr "" 78 79 #: includes/class-lang-attribute-blocks.php:559 80 #: build/index.js:1 81 #: src/index.js:56 82 #: src/index.js:175 83 msgid "Text Direction" 84 msgstr "" 85 86 #: includes/class-lang-attribute-blocks.php:563 87 #: build/index.js:1 88 #: src/index.js:59 89 #: src/index.js:178 90 msgid "Left to right" 91 msgstr "" 92 93 #: includes/class-lang-attribute-blocks.php:566 94 #: build/index.js:1 95 #: src/index.js:60 96 #: src/index.js:179 97 msgid "Right to left" 98 msgstr "" 99 100 #: includes/class-lang-attribute-blocks.php:649 60 101 msgid "Settings" 61 102 msgstr "" 62 103 63 #: assets/index.js:4164 104 #: build/index.js:1 65 msgid "Language Settings" 105 #: src/index.js:45 106 msgid "Block Language" 66 107 msgstr "" 67 108 68 #: assets/index.js:4569 109 #: build/index.js:1 70 msgid "Language Code" 110 #: src/index.js:53 111 msgid "Valid language code for this block, like “fr” or “pt-PT”, if different from the website's or page's main language (shown as a placeholder)" 71 112 msgstr "" 72 73 #: assets/index.js:4974 #: build/index.js:175 msgid "Valid language code, like “fr” or “pt-PT”, if different from the website main language (shown as a placeholder)"76 msgstr ""77 78 #: assets/index.js:5279 #: build/index.js:180 msgid "Text Direction"81 msgstr ""82 83 #: assets/index.js:5584 #: build/index.js:185 msgid "Left to right"86 msgstr ""87 88 #: assets/index.js:5689 #: build/index.js:190 msgid "Right to left"91 msgstr "" -
lang-attribute-blocks/tags/3.0/readme.txt
r3381456 r3478054 1 === Language Attribute for Container Blocks ===1 === Language Attribute for Container Blocks and Pages/Posts === 2 2 Contributors: nakedcatplugins, webdados 3 Tags: language, accessibility, block editor 3 Tags: language, accessibility, block editor, gutenberg, classic editor 4 4 Requires at least: 5.9 5 Tested up to: 6.95 Tested up to: 7.0 6 6 Requires PHP: 7.2 7 Stable tag: 2.27 Stable tag: 3.0 8 8 License: GPLv3 9 9 License URI: https://www.gnu.org/licenses/gpl-3.0.html 10 10 11 Add “lang” and “dir” attributes to Group, Columns, Cover, and other specific WordPress Blocks.11 Add `lang` and `dir` attributes to Group, Columns, Cover, and other specific WordPress Blocks, or to the whole page/post. 12 12 13 13 == Description == 14 14 15 This plugin aims to provide a way to ensure that any language change in the content of a pageis indicated to assistive technologies at the container block level, helping a website comply with WCAG guidelines.15 This plugin aims to ensure that any language change in a page’s content is indicated to assistive technologies at the container block level, helping a website comply with WCAG guidelines. 16 16 17 17 This feature is available on the core block editor only at a text formatting level after code from [Jb Audras plugin “Lang Attribute for the Block Editor”](https://wordpress.org/plugins/lang-attribute/) was merged into core. The objective of this plugin is to provide the same functionality at a container block level (Group - including all its variants, Columns, Cover, and other specific block types) so that the language applies to all child elements, no matter the kind of content inside. 18 18 19 Th is plugin is heavily inspired by the Jb Audras plugin (including this readme file), and the development started at WordCamp Europe 2025 Contributor Day, by Marco Almeida from [Naked Cat Plugins](https://profiles.wordpress.org/nakedcatplugins/) / [Webdados](https://profiles.wordpress.org/webdados/), and the help from [Ryan Welcher](https://profiles.wordpress.org/welcher/) on the code side and [Amber Hinds](https://profiles.wordpress.org/alh0319/) on the accessibility compliance side.19 The plugin also supports setting the language at the page or post level, both on the blocks and classic editor. When an entire page is written in a different language than the website’s default, you can override the HTML `lang` and `dir` attributes for that specific page directly from the Document Settings sidebar, without needing to wrap everything in a container block. 20 20 21 For more context: this plugin helps you to make your website compliant with the Web Content Accessibility Guidelines (WCAG) success criterion 3.1.2: “Language of Parts”. The purpose of this success Criterion is to ensure that user agents can correctly present content written in multiple languages.21 This plugin is heavily inspired by the Jb Audras plugin (including this readme file). The development started at WordCamp Europe 2025 Contributor Day, by Marco Almeida from [Naked Cat Plugins](https://profiles.wordpress.org/nakedcatplugins/) / [Webdados](https://profiles.wordpress.org/webdados/), and the help from [Ryan Welcher](https://profiles.wordpress.org/welcher/) on the code side and [Amber Hinds](https://profiles.wordpress.org/alh0319/) on the accessibility compliance side. 22 22 23 Keep in mind that you should only set the lang and dir attributes to a container block if the content you’re going to insert inside it is written in a different language than that set globally on your website. 23 For more context: this plugin helps you to make your website compliant with the Web Content Accessibility Guidelines (WCAG) success criteria: 24 24 25 As per Web Content Accessibility Guidelines: 25 * **3.1.1 – Language of Page**: The default human language of each web page can be programmatically determined. Use the page-level setting when an entire page or post is written in a language other than the website’s default. 26 * **3.1.2 – Language of Parts**: The human language of each passage or phrase in the content can be programmatically determined. Use the block-level setting when only specific sections within a page are in a different language. 26 27 27 Th is makes it possible for user agents and assistive technologies to present content according to the presentation and pronunciation rules for that language. This applies to graphical browsers as well as screen readers, braille displays, and other voice browsers.28 The purpose of these success criteria is to ensure that user agents can correctly present content written in multiple languages. 28 29 29 Both assistive technologies and conventional user agents can render text more accurately if the language of each passage of text is identified. Screen readers can use the pronunciation rules of the language of the text. Visual browsers can display characters and scripts in appropriate ways. 30 Keep in mind that you should set the `lang` and `dir` attributes only on a container block or page if the content is written in a language different from the one set globally on your website. 31 32 **As per Web Content Accessibility Guidelines:** 33 34 This enables user agents and assistive technologies to present content according to the presentation and pronunciation rules of that language. This applies to graphical browsers, screen readers, braille displays, and other voice browsers. 35 36 Both assistive technologies and conventional user agents can render text more accurately if the language of each passage of text is identified. Screen readers can use the language’s pronunciation rules. Visual browsers can display characters and scripts appropriately. 30 37 31 38 This is especially important when switching between languages that read from left to right and languages that read from right to left, or when text is rendered in a language that uses a different alphabet. Users with disabilities who know all the languages used in the Web page will be better able to understand the content when each passage is rendered appropriately. … … 36 43 37 44 == Supported block types == 45 38 46 * **Group** (`core/group`): Group contents together and set a language for them 39 47 * **Columns** (`core/columns` and `core/column`): Organize content into a set of columns and set a language for all the columns or a specific column … … 45 53 46 54 == Features == 47 * Add “lang” and “dir” attributes to Group, Columns, Cover, and other specific WordPress Blocks 55 56 * Set the language and text direction for an entire page or post, both on the blocks and classic editor: a “Page Language” panel in the Document Settings sidebar overrides the HTML `lang` and `dir` attributes for that specific page 57 * Add `lang` and `dir` attributes to Group, Columns, Cover, and other specific WordPress Blocks, mentioned above 48 58 * Show visual outline around blocks that have a language attribute set - For easy identification of blocks you have already set to a different language during your editing process, only for Administrators and Editors, and if enabled in Settings - Writing 49 59 … … 51 61 52 62 1. Using the block editor to add a language attribute to a Group block 53 2. The lang anddir attributes rendered on the frontend63 2. The `lang` and `dir attributes rendered on the frontend 54 64 3. Using the highlighting option during the editing process 55 65 … … 57 67 58 68 1. Install the plugin and activate it. 59 2. Insert a Group, Columns, Cover (or other specific) block, and use the “Language Settings” sidebar panel to set the language for all the content inside that container 69 2. To set the language for an entire page or post: open the Document Settings sidebar (the panel icon at the top right of the editor) and use the “Page Language” panel. 70 3. To set the language for a specific section within a page: insert a Group, Columns, Cover (or other specific) block, and use the “Block Language” sidebar panel to set the language for all the content inside that container. 60 71 61 72 == Frequently Asked Questions == 73 74 = When should I use the page-level language setting instead of a block-level one? = 75 76 Use the **Page Language** setting (in the Document Settings sidebar) when the entire page or post is written in a different language than the website default. This overrides the `lang` attribute on the HTML element itself, which corresponds to WCAG 3.1.1 (Language of Page). 77 In this case, we also recommend creating a dedicated template in the Site Editor (Appearance → Editor → Templates) where shared template parts — such as the header and footer — are also in that same language. 78 79 Use the **Block Language** setting (in the block’s sidebar panel) when only a specific section within a page is in a different language, while the rest of the page remains in the site’s default language. This corresponds to WCAG 3.1.2 (Language of Parts). 62 80 63 81 = Why not have the option to set the language attribute on all block types? = … … 71 89 72 90 If your are working on a WordCamp website, or you don’t want to mess around with PHP, you can also add custom CSS to change the color, overriding our `--nakedcatplugins-lang-attr-highlight-color` variable. 73 Here’s a [Gist example](https://gist.github.com/webdados/ 61197dd2e98f399ba2cfeefbac518851).91 Here’s a [Gist example](https://gist.github.com/webdados/7179f5be4e224ba84867cf77e9bc9174). 74 92 75 93 = How can I contribute to this plugin? = … … 82 100 83 101 == Changelog == 102 103 = 3.0 - 2026-03-09 = 104 * [NEW] Plugin renamed from “Language Attribute for Container Blocks” to “Language Attribute for Container Blocks and Pages/Posts” 105 * [NEW] Set the page/post language at the document level: a new “Page Language” panel in the Document Settings sidebar allows overriding the HTML `lang` and `dir` attributes for a specific page or post, independently of the website’s default language 106 * [TWEAK] Rename “Language Settings” sidebar block panel to “Block Language” 107 * [FIX] Gist URL for changing the highlight color using plain CSS 108 * [DEV] Tested up to 7.0-beta3-61865 84 109 85 110 = 2.2 - 2025-10-20 = -
lang-attribute-blocks/trunk/build/index.asset.php
r3337921 r3478054 1 <?php return array('dependencies' => array('react', 'wp-block-editor', 'wp-components', 'wp-compose', 'wp- hooks', 'wp-i18n'), 'version' => '4f65e3d03de63b85e0a0');1 <?php return array('dependencies' => array('react', 'wp-block-editor', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-editor', 'wp-hooks', 'wp-i18n', 'wp-plugins'), 'version' => '4c50b3542257c21dfef2'); -
lang-attribute-blocks/trunk/build/index.css
r3337921 r3478054 1 :root{--nakedcatplugins-lang-attr-highlight-color:rgba(255,0,0,. 75)}.editor-styles-wrapper .naked-cat-plugins-has-lang-attr,body:not(.block-editor-page) [lang]{outline:3px dashed var(--nakedcatplugins-lang-attr-highlight-color)!important;outline-offset:2px}1 :root{--nakedcatplugins-lang-attr-highlight-color:rgba(255,0,0,.65)}.editor-styles-wrapper .naked-cat-plugins-has-lang-attr,body:not(.block-editor-page) [lang]{outline:3px dashed var(--nakedcatplugins-lang-attr-highlight-color)!important;outline-offset:2px}html.naked-cat-plugins-post-has-lang-attr{outline:3px dashed var(--nakedcatplugins-lang-attr-highlight-color)!important;outline-offset:-3px} -
lang-attribute-blocks/trunk/build/index.js
r3337921 r3478054 1 (()=>{"use strict";const t=window.React,e=window.wp.i18n,a=window.wp.blockEditor, n=window.wp.components,l=window.wp.compose,i=window.wp.hooks,o=(0,l.createHigherOrderComponent)((l=>i=>{if(window.nakedCatPluginsLangAttributeBlocks&&window.nakedCatPluginsLangAttributeBlocks.supportedBlocks&&!window.nakedCatPluginsLangAttributeBlocks.supportedBlocks.includes(i.name))return(0,t.createElement)(l,{...i});const{attributes:o,setAttributes:r}=i,s=o.lang||"",d=o.dir||"ltr";return(0,t.createElement)(t.Fragment,null,(0,t.createElement)(l,{...i}),(0,t.createElement)(a.InspectorControls,null,(0,t.createElement)(n.PanelBody,{title:(0,e.__)("Language Settings","lang-attribute-blocks"),initialOpen:!0},(0,t.createElement)(n.TextControl,{label:(0,e.__)("Language Code","lang-attribute-blocks"),value:s,onChange:t=>r({lang:t}),placeholder:window.nakedCatPluginsLangAttributeBlocks?.placeholderText||"en (default website language)",help:(0,e.__)("Valid language code, like “fr” or “pt-PT”, if different from the website main language (shown as a placeholder)","lang-attribute-blocks")}),(0,t.createElement)(n.SelectControl,{label:(0,e.__)("Text Direction","lang-attribute-blocks"),value:d,options:[{label:(0,e.__)("Left to right","lang-attribute-blocks"),value:"ltr"},{label:(0,e.__)("Right to left","lang-attribute-blocks"),value:"rtl"}],onChange:t=>r({dir:t})}))))}),"addLangAttributesToGroupBlock");(0,i.addFilter)("editor.BlockEdit","lang-attribute-blocks/add-lang-attributes-to-group-block",o);const r=(0,l.createHigherOrderComponent)((e=>a=>window.nakedCatPluginsLangAttributeBlocks&&window.nakedCatPluginsLangAttributeBlocks.supportedBlocks&&!window.nakedCatPluginsLangAttributeBlocks.supportedBlocks.includes(a.block.name)?(0,t.createElement)(e,{...a}):a.block.attributes.lang&&""!==a.block.attributes.lang.trim()?(0,t.createElement)(e,{...a,className:"naked-cat-plugins-has-lang-attr"}):(0,t.createElement)(e,{...a})),"withLangAttr");(0,i.addFilter)("blocks.registerBlockType","lang-attribute-blocks/add-lang-and-dir-attributes",(function(t,e){return window.nakedCatPluginsLangAttributeBlocks&&window.nakedCatPluginsLangAttributeBlocks.supportedBlocks&&window.nakedCatPluginsLangAttributeBlocks.supportedBlocks.includes(e)&&(t.attributes={...t.attributes,lang:{type:"string",default:""},dir:{type:"string",default:"ltr"}}),t})),window.nakedCatPluginsLangAttributeBlocks&&window.nakedCatPluginsLangAttributeBlocks.highlightEnabled&&(0,i.addFilter)("editor.BlockListBlock","lang-attribute-blocks/add-lang-and-dir-attributes",r)})();1 (()=>{"use strict";const t=window.React,e=window.wp.i18n,a=window.wp.blockEditor,l=window.wp.components,n=window.wp.compose,i=window.wp.hooks,o=window.wp.plugins,r=window.wp.editor,s=window.wp.coreData,g=window.wp.data,u=(0,n.createHigherOrderComponent)((n=>i=>{if(window.nakedCatPluginsLangAttributeBlocks&&window.nakedCatPluginsLangAttributeBlocks.supportedBlocks&&!window.nakedCatPluginsLangAttributeBlocks.supportedBlocks.includes(i.name))return(0,t.createElement)(n,{...i});const{attributes:o,setAttributes:r}=i,s=(o.lang||"").trim(),g=o.dir||"ltr";return(0,t.createElement)(t.Fragment,null,(0,t.createElement)(n,{...i}),(0,t.createElement)(a.InspectorControls,null,(0,t.createElement)(l.PanelBody,{title:(0,e.__)("Block Language","lang-attribute-blocks"),initialOpen:!0},(0,t.createElement)(l.TextControl,{label:(0,e.__)("Language Code","lang-attribute-blocks"),value:s,onChange:t=>r({lang:t.trim()}),placeholder:window.nakedCatPluginsLangAttributeBlocks?.placeholderText||"en (default website language)",help:(0,e.__)("Valid language code for this block, like “fr” or “pt-PT”, if different from the website's or page's main language (shown as a placeholder)","lang-attribute-blocks")}),(0,t.createElement)(l.SelectControl,{label:(0,e.__)("Text Direction","lang-attribute-blocks"),value:g,options:[{label:(0,e.__)("Left to right","lang-attribute-blocks"),value:"ltr"},{label:(0,e.__)("Right to left","lang-attribute-blocks"),value:"rtl"}],onChange:t=>r({dir:t})}))))}),"addLangAttributesToGroupBlock");(0,i.addFilter)("editor.BlockEdit","lang-attribute-blocks/add-lang-attributes-to-group-block",u);const d=(0,n.createHigherOrderComponent)((e=>a=>window.nakedCatPluginsLangAttributeBlocks&&window.nakedCatPluginsLangAttributeBlocks.supportedBlocks&&!window.nakedCatPluginsLangAttributeBlocks.supportedBlocks.includes(a.block.name)?(0,t.createElement)(e,{...a}):a.block.attributes.lang&&""!==a.block.attributes.lang.trim()?(0,t.createElement)(e,{...a,className:"naked-cat-plugins-has-lang-attr"}):(0,t.createElement)(e,{...a})),"withLangAttr");(0,i.addFilter)("blocks.registerBlockType","lang-attribute-blocks/add-lang-and-dir-attributes",(function(t,e){return window.nakedCatPluginsLangAttributeBlocks&&window.nakedCatPluginsLangAttributeBlocks.supportedBlocks&&window.nakedCatPluginsLangAttributeBlocks.supportedBlocks.includes(e)&&(t.attributes={...t.attributes,lang:{type:"string",default:""},dir:{type:"string",default:"ltr"}}),t})),window.nakedCatPluginsLangAttributeBlocks&&window.nakedCatPluginsLangAttributeBlocks.highlightEnabled&&(0,i.addFilter)("editor.BlockListBlock","lang-attribute-blocks/add-lang-and-dir-attributes",d),(0,o.registerPlugin)("nakedcatplugins-page-lang-controls",{render:()=>{var a,n;const i=(0,g.useSelect)((t=>t("core/editor").getCurrentPostType()),[]),[o,u]=(0,s.useEntityProp)("postType",i,"meta"),d=(null!==(a=o?._nakedcatplugins_page_lang)&&void 0!==a?a:"").trim(),c=null!==(n=o?._nakedcatplugins_page_dir)&&void 0!==n?n:"ltr";return(0,t.createElement)(r.PluginDocumentSettingPanel,{name:"nakedcatplugins-page-lang-panel",title:(0,e.__)("Page Language","lang-attribute-blocks")},(0,t.createElement)(l.TextControl,{label:(0,e.__)("Language Code","lang-attribute-blocks"),value:d,onChange:t=>u({...o,_nakedcatplugins_page_lang:t.trim()}),placeholder:window.nakedCatPluginsLangAttributeBlocks?.placeholderText||"en (default website language)",help:(0,e.__)("Valid language code for this page/post, like “fr” or “pt-PT”, if different from the website's main language (shown as a placeholder) - This overrides the HTML language attribute","lang-attribute-blocks")}),(0,t.createElement)(l.SelectControl,{label:(0,e.__)("Text Direction","lang-attribute-blocks"),value:c,options:[{label:(0,e.__)("Left to right","lang-attribute-blocks"),value:"ltr"},{label:(0,e.__)("Right to left","lang-attribute-blocks"),value:"rtl"}],onChange:t=>u({...o,_nakedcatplugins_page_dir:t})}))}})})(); -
lang-attribute-blocks/trunk/includes/class-lang-attribute-blocks.php
r3342031 r3478054 1 1 <?php 2 2 /** 3 * Main plugin class for Language Attribute for Container Blocks 3 * Main plugin class for Language Attribute for Container Blocks and Pages/Posts 4 4 * 5 5 * This file contains the core functionality for adding language and direction … … 136 136 add_filter( 'render_block_' . $block_name, array( $this, 'process_blocks' ), 10, 2 ); 137 137 } 138 // Register post meta for page-level language settings 139 add_action( 'init', array( $this, 'register_page_lang_meta' ) ); 140 // Apply page-level language attribute to the <html> element 141 add_filter( 'language_attributes', array( $this, 'apply_page_lang_attribute' ) ); 138 142 // Enqueues JavaScript and CSS assets for the WordPress block editor 139 143 add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue_block_editor_assets' ) ); … … 144 148 // Add settings link to the plugin action links 145 149 add_filter( 'plugin_action_links_' . plugin_basename( NAKEDCATPLUGINS_LANG_ATTRIBUTE_BLOCKS_FILE ), array( $this, 'add_plugin_action_links' ) ); 150 // Add classic editor metabox for page-level language settings 151 add_action( 'add_meta_boxes', array( $this, 'add_classic_editor_metabox' ) ); 152 add_action( 'save_post', array( $this, 'save_classic_editor_metabox' ), 10, 2 ); 153 } 154 155 /** 156 * Register post meta fields for page-level language settings. 157 * 158 * Registers '_nakedcatplugins_page_lang' and '_nakedcatplugins_page_dir' meta 159 * for all public post types, exposed via the REST API so the block editor 160 * can read and write them. 161 * 162 * @since 3.0 163 * @hook init 164 * @return void 165 */ 166 public function register_page_lang_meta() { 167 $args_lang = array( 168 'show_in_rest' => true, 169 'single' => true, 170 'type' => 'string', 171 'default' => '', 172 'auth_callback' => function () { 173 return current_user_can( 'edit_posts' ); 174 }, 175 ); 176 $args_dir = array( 177 'show_in_rest' => true, 178 'single' => true, 179 'type' => 'string', 180 'default' => 'ltr', 181 'auth_callback' => function () { 182 return current_user_can( 'edit_posts' ); 183 }, 184 ); 185 // Register for all public post types 186 foreach ( get_post_types( array( 'public' => true ) ) as $post_type ) { 187 register_post_meta( $post_type, '_nakedcatplugins_page_lang', $args_lang ); 188 register_post_meta( $post_type, '_nakedcatplugins_page_dir', $args_dir ); 189 } 190 } 191 192 /** 193 * Override the HTML lang (and optionally dir) attribute for singular pages/posts. 194 * 195 * When a page or post has the '_nakedcatplugins_page_lang' meta set, this method 196 * replaces the lang attribute on the <html> element with the stored value. 197 * When '_nakedcatplugins_page_dir' is set to 'rtl', the dir attribute is also applied. 198 * 199 * @since 3.0 200 * @hook language_attributes 201 * @param string $output The existing language attributes string, e.g. 'lang="en-US"'. 202 * @return string Modified language attributes string. 203 */ 204 public function apply_page_lang_attribute( $output ) { 205 if ( ! is_singular() ) { 206 return $output; 207 } 208 $post_id = get_queried_object_id(); 209 $page_lang = trim( get_post_meta( $post_id, '_nakedcatplugins_page_lang', true ) ); 210 $page_dir = trim( get_post_meta( $post_id, '_nakedcatplugins_page_dir', true ) ); 211 212 if ( ! empty( $page_lang ) ) { 213 $safe_lang = esc_attr( $page_lang ); 214 if ( strpos( $output, 'lang=' ) !== false ) { 215 $output = preg_replace( '/lang="[^"]*"/', 'lang="' . $safe_lang . '"', $output ); 216 } else { 217 $output .= ' lang="' . $safe_lang . '"'; 218 } 219 // Add our class name 220 if ( strpos( $output, 'class=' ) !== false ) { 221 $output = preg_replace( '/class="([^"]*)"/', 'class="$1 naked-cat-plugins-post-has-lang-attr"', $output ); 222 } else { 223 $output .= ' class="naked-cat-plugins-post-has-lang-attr"'; 224 } 225 } 226 227 if ( ! empty( $page_dir ) && 'rtl' === $page_dir ) { 228 $safe_dir = esc_attr( $page_dir ); 229 if ( strpos( $output, 'dir=' ) !== false ) { 230 $output = preg_replace( '/dir="[^"]*"/', 'dir="' . $safe_dir . '"', $output ); 231 } else { 232 $output .= ' dir="' . $safe_dir . '"'; 233 } 234 } 235 236 return $output; 146 237 } 147 238 … … 196 287 public function process_blocks( $block_content, $block ) { 197 288 if ( isset( $block['attrs']['lang'] ) && ! empty( $block['attrs']['lang'] ) ) { 198 $lang = esc_attr( $block['attrs']['lang']);199 $dir = isset( $block['attrs']['dir'] ) ? esc_attr( $block['attrs']['dir'] ) : 'ltr';289 $lang = trim( esc_attr( $block['attrs']['lang'] ) ); 290 $dir = trim( isset( $block['attrs']['dir'] ) ? esc_attr( $block['attrs']['dir'] ) : 'ltr' ); 200 291 $tag_processor = new \WP_HTML_Tag_Processor( $block_content ); 201 292 // Depending on the block type, we will set the tag to be processed … … 251 342 'nakedcatplugins-lang-attribute-blocks-script', 252 343 plugins_url( 'build/index.js', NAKEDCATPLUGINS_LANG_ATTRIBUTE_BLOCKS_FILE ), 253 array( 'wp-blocks', 'wp-dom', 'wp-dom-ready', 'wp-edit-post', 'wp-e lement', 'wp-i18n', 'wp-block-editor' ),344 array( 'wp-blocks', 'wp-dom', 'wp-dom-ready', 'wp-edit-post', 'wp-editor', 'wp-element', 'wp-i18n', 'wp-block-editor', 'wp-plugins', 'wp-data', 'wp-core-data' ), 254 345 filemtime( plugin_dir_path( NAKEDCATPLUGINS_LANG_ATTRIBUTE_BLOCKS_FILE ) . 'build/index.js' ), 255 346 true … … 361 452 add_settings_section( 362 453 'nakedcatplugins_lang_attr_section', 363 __( 'Language Attribute for Container Blocks ', 'lang-attribute-blocks' ),454 __( 'Language Attribute for Container Blocks and Pages/Posts', 'lang-attribute-blocks' ), 364 455 array( $this, 'settings_section_callback' ), 365 456 'writing' … … 383 474 */ 384 475 public function settings_section_callback() { 385 echo '<p>' . esc_html__( 'Configure Language Attribute for Container Blocks plugin settings.', 'lang-attribute-blocks' ) . '</p>';476 echo '<p>' . esc_html__( 'Configure Language Attribute for Container Blocks and Pages/Posts plugin settings.', 'lang-attribute-blocks' ) . '</p>'; 386 477 } 387 478 /** … … 405 496 406 497 /** 498 * Register the page language metabox for the classic editor. 499 * 500 * Added to all public post types so editors can set the page-level 501 * lang and dir meta without the block editor. 502 * 503 * @since 3.0 504 * @hook add_meta_boxes 505 * @return void 506 */ 507 public function add_classic_editor_metabox() { 508 // Only register the metabox in the classic editor; the block editor 509 // provides its own Document Settings panel for the same fields. 510 $screen = get_current_screen(); 511 if ( $screen && $screen->is_block_editor() ) { 512 return; 513 } 514 515 foreach ( get_post_types( array( 'public' => true ), 'names' ) as $post_type ) { 516 add_meta_box( 517 'nakedcatplugins_page_language', 518 __( 'Page Language', 'lang-attribute-blocks' ), 519 array( $this, 'render_classic_editor_metabox' ), 520 $post_type, 521 'side', 522 'default' 523 ); 524 } 525 } 526 527 /** 528 * Render the classic editor metabox HTML. 529 * 530 * @since 3.0 531 * @param \WP_Post $post The current post object. 532 * @return void 533 */ 534 public function render_classic_editor_metabox( \WP_Post $post ) { 535 $lang = trim( get_post_meta( $post->ID, '_nakedcatplugins_page_lang', true ) ); 536 $dir = trim( get_post_meta( $post->ID, '_nakedcatplugins_page_dir', true ) ); 537 if ( empty( $dir ) ) { 538 $dir = 'ltr'; 539 } 540 541 wp_nonce_field( 'nakedcatplugins_page_language_metabox', 'nakedcatplugins_page_language_nonce' ); 542 $placeholder = sprintf( 543 /* translators: %s: The website's default language code */ 544 __( '%s (default website language)', 'lang-attribute-blocks' ), 545 get_bloginfo( 'language' ) 546 ); 547 ?> 548 <p> 549 <label for="nakedcatplugins_page_lang"> 550 <?php esc_html_e( 'Language Code', 'lang-attribute-blocks' ); ?> 551 </label> 552 <input type="text" id="nakedcatplugins_page_lang" name="nakedcatplugins_page_lang" value="<?php echo esc_attr( $lang ); ?>" placeholder="<?php echo esc_attr( $placeholder ); ?>" class="widefat"/> 553 <span class="description"> 554 <?php esc_html_e( "Valid language code for this page/post, like “fr” or “pt-PT”, if different from the website's main language (shown as a placeholder) - This overrides the HTML language attribute", 'lang-attribute-blocks' ); ?> 555 </span> 556 </p> 557 <p> 558 <label for="nakedcatplugins_page_dir"> 559 <?php esc_html_e( 'Text Direction', 'lang-attribute-blocks' ); ?> 560 </label> 561 <select id="nakedcatplugins_page_dir" name="nakedcatplugins_page_dir" class="widefat"> 562 <option value="ltr" <?php selected( $dir, 'ltr' ); ?>> 563 <?php esc_html_e( 'Left to right', 'lang-attribute-blocks' ); ?> 564 </option> 565 <option value="rtl" <?php selected( $dir, 'rtl' ); ?>> 566 <?php esc_html_e( 'Right to left', 'lang-attribute-blocks' ); ?> 567 </option> 568 </select> 569 </p> 570 <?php 571 } 572 573 /** 574 * Save the classic editor metabox values. 575 * 576 * Validates the nonce to confirm the metabox was present in the form 577 * (guards against quick edit, bulk actions, REST and programmatic saves 578 * that would otherwise wipe the meta), checks capabilities, then saves 579 * or deletes the page language meta fields. 580 * 581 * @since 3.0 582 * @hook save_post 583 * @param int $post_id The post ID being saved. 584 * @param \WP_Post $post The post object being saved. 585 * @return void 586 */ 587 public function save_classic_editor_metabox( int $post_id, \WP_Post $post ) { 588 // If our metabox was not present in the request, bail out to avoid 589 // accidentally wiping the meta (e.g. quick edit, REST, wp_update_post()). 590 if ( ! isset( $_POST['nakedcatplugins_page_language_nonce'] ) ) { 591 return; 592 } 593 594 // WordPress has already verified the post nonce, but we verify our own 595 // as extra confirmation that our metabox submitted these values. 596 if ( ! wp_verify_nonce( sanitize_key( $_POST['nakedcatplugins_page_language_nonce'] ), 'nakedcatplugins_page_language_metabox' ) ) { 597 return; 598 } 599 600 // Bail on autosave and revisions. 601 if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { 602 return; 603 } 604 if ( wp_is_post_revision( $post_id ) ) { 605 return; 606 } 607 608 // Check the user has permission to edit this specific post. 609 $post_type_object = get_post_type_object( $post->post_type ); 610 if ( ! current_user_can( $post_type_object->cap->edit_post, $post_id ) ) { 611 return; 612 } 613 614 // Save lang — trim and sanitize; delete if empty so the DB stays clean. 615 if ( isset( $_POST['nakedcatplugins_page_lang'] ) ) { 616 $lang = trim( sanitize_text_field( wp_unslash( $_POST['nakedcatplugins_page_lang'] ) ) ); 617 if ( ! empty( $lang ) ) { 618 update_post_meta( $post_id, '_nakedcatplugins_page_lang', $lang ); 619 } else { 620 delete_post_meta( $post_id, '_nakedcatplugins_page_lang' ); 621 delete_post_meta( $post_id, '_nakedcatplugins_page_dir' ); 622 return; // If lang is empty, we also delete dir and skip saving it since it doesn't make sense to have a dir without a lang. 623 } 624 } 625 626 // Save dir — whitelist to known values only. 627 if ( isset( $_POST['nakedcatplugins_page_dir'] ) ) { 628 $dir = sanitize_text_field( wp_unslash( $_POST['nakedcatplugins_page_dir'] ) ); 629 $dir = in_array( $dir, array( 'ltr', 'rtl' ), true ) ? $dir : 'ltr'; 630 update_post_meta( $post_id, '_nakedcatplugins_page_dir', $dir ); 631 } 632 } 633 634 /** 407 635 * Add settings link to the plugin action links. 408 636 * 409 637 * This function adds a "Settings" link to the plugin's row on the Plugins page 410 * that points to the Language Attribute for Container Blocks settings section638 * that points to the Language Attribute for Container Blocks and Pages/Posts settings section 411 639 * on the Settings > Writing page. 412 640 * -
lang-attribute-blocks/trunk/lang-attribute-blocks.php
r3381456 r3478054 1 1 <?php 2 2 /** 3 * Plugin Name: Language Attribute for Container Blocks 3 * Plugin Name: Language Attribute for Container Blocks and Pages/Posts 4 4 * Plugin URI: 5 * Description: Add “lang” and “dir” attributes on Group, Columns, and Cover WordPress Blocks6 * Version: 2.25 * Description: Add `lang` and `dir` attributes to Group, Columns, Cover, and other specific WordPress Blocks, or to the whole page/post. 6 * Version: 3.0 7 7 * Author: Naked Cat Plugins (by Webdados) 8 8 * Author URI: https://nakedcatplugins.com 9 9 * Text Domain: lang-attribute-blocks 10 10 * Requires at least: 5.9 11 * Tested up to: 6.911 * Tested up to: 7.0 12 12 * Requires PHP: 7.2 13 13 * License: GPLv3 -
lang-attribute-blocks/trunk/languages/lang-attribute-blocks.pot
r3381456 r3478054 1 # Copyright (C) 202 5Naked Cat Plugins (by Webdados)1 # Copyright (C) 2026 Naked Cat Plugins (by Webdados) 2 2 # This file is distributed under the GPLv3. 3 3 msgid "" 4 4 msgstr "" 5 "Project-Id-Version: Language Attribute for Container Blocks 2.2\n"5 "Project-Id-Version: Language Attribute for Container Blocks and Pages/Posts 2.2\n" 6 6 "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/lang-attribute-blocks\n" 7 7 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" … … 10 10 "Content-Type: text/plain; charset=UTF-8\n" 11 11 "Content-Transfer-Encoding: 8bit\n" 12 "POT-Creation-Date: 202 5-10-20T16:43:04+00:00\n"12 "POT-Creation-Date: 2026-03-09T12:04:28+00:00\n" 13 13 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 14 "X-Generator: WP-CLI 2.11.0\n" … … 17 17 #. Plugin Name of the plugin 18 18 #: lang-attribute-blocks.php 19 #: includes/class-lang-attribute-blocks.php: 36320 msgid "Language Attribute for Container Blocks "19 #: includes/class-lang-attribute-blocks.php:454 20 msgid "Language Attribute for Container Blocks and Pages/Posts" 21 21 msgstr "" 22 22 23 23 #. Description of the plugin 24 24 #: lang-attribute-blocks.php 25 msgid "Add “lang” and “dir” attributes on Group, Columns, and Cover WordPress Blocks"25 msgid "Add `lang` and `dir` attributes to Group, Columns, Cover, and other specific WordPress Blocks, or to the whole page/post." 26 26 msgstr "" 27 27 … … 37 37 38 38 #. translators: %s: The website's default language code 39 #: includes/class-lang-attribute-blocks.php:268 39 #: includes/class-lang-attribute-blocks.php:359 40 #: includes/class-lang-attribute-blocks.php:544 40 41 msgid "%s (default website language)" 41 42 msgstr "" 42 43 43 #: includes/class-lang-attribute-blocks.php: 37144 #: includes/class-lang-attribute-blocks.php:462 44 45 msgid "Highlight blocks with lang attribute" 45 46 msgstr "" 46 47 47 #: includes/class-lang-attribute-blocks.php: 38548 msgid "Configure Language Attribute for Container Blocks plugin settings."48 #: includes/class-lang-attribute-blocks.php:476 49 msgid "Configure Language Attribute for Container Blocks and Pages/Posts plugin settings." 49 50 msgstr "" 50 51 51 #: includes/class-lang-attribute-blocks.php: 39852 #: includes/class-lang-attribute-blocks.php:489 52 53 msgid "Show visual outline around blocks that have a language attribute set" 53 54 msgstr "" 54 55 55 #: includes/class-lang-attribute-blocks.php:4 0156 #: includes/class-lang-attribute-blocks.php:492 56 57 msgid "When enabled, blocks with a language attribute will be visually highlighted with a red dashed outline in both the editor and frontend (only for Administrators and Editors)." 57 58 msgstr "" 58 59 59 #: includes/class-lang-attribute-blocks.php:421 60 #: includes/class-lang-attribute-blocks.php:518 61 #: build/index.js:1 62 #: src/index.js:165 63 msgid "Page Language" 64 msgstr "" 65 66 #: includes/class-lang-attribute-blocks.php:550 67 #: build/index.js:1 68 #: src/index.js:49 69 #: src/index.js:168 70 msgid "Language Code" 71 msgstr "" 72 73 #: includes/class-lang-attribute-blocks.php:554 74 #: build/index.js:1 75 #: src/index.js:172 76 msgid "Valid language code for this page/post, like “fr” or “pt-PT”, if different from the website's main language (shown as a placeholder) - This overrides the HTML language attribute" 77 msgstr "" 78 79 #: includes/class-lang-attribute-blocks.php:559 80 #: build/index.js:1 81 #: src/index.js:56 82 #: src/index.js:175 83 msgid "Text Direction" 84 msgstr "" 85 86 #: includes/class-lang-attribute-blocks.php:563 87 #: build/index.js:1 88 #: src/index.js:59 89 #: src/index.js:178 90 msgid "Left to right" 91 msgstr "" 92 93 #: includes/class-lang-attribute-blocks.php:566 94 #: build/index.js:1 95 #: src/index.js:60 96 #: src/index.js:179 97 msgid "Right to left" 98 msgstr "" 99 100 #: includes/class-lang-attribute-blocks.php:649 60 101 msgid "Settings" 61 102 msgstr "" 62 103 63 #: assets/index.js:4164 104 #: build/index.js:1 65 msgid "Language Settings" 105 #: src/index.js:45 106 msgid "Block Language" 66 107 msgstr "" 67 108 68 #: assets/index.js:4569 109 #: build/index.js:1 70 msgid "Language Code" 110 #: src/index.js:53 111 msgid "Valid language code for this block, like “fr” or “pt-PT”, if different from the website's or page's main language (shown as a placeholder)" 71 112 msgstr "" 72 73 #: assets/index.js:4974 #: build/index.js:175 msgid "Valid language code, like “fr” or “pt-PT”, if different from the website main language (shown as a placeholder)"76 msgstr ""77 78 #: assets/index.js:5279 #: build/index.js:180 msgid "Text Direction"81 msgstr ""82 83 #: assets/index.js:5584 #: build/index.js:185 msgid "Left to right"86 msgstr ""87 88 #: assets/index.js:5689 #: build/index.js:190 msgid "Right to left"91 msgstr "" -
lang-attribute-blocks/trunk/readme.txt
r3381456 r3478054 1 === Language Attribute for Container Blocks ===1 === Language Attribute for Container Blocks and Pages/Posts === 2 2 Contributors: nakedcatplugins, webdados 3 Tags: language, accessibility, block editor 3 Tags: language, accessibility, block editor, gutenberg, classic editor 4 4 Requires at least: 5.9 5 Tested up to: 6.95 Tested up to: 7.0 6 6 Requires PHP: 7.2 7 Stable tag: 2.27 Stable tag: 3.0 8 8 License: GPLv3 9 9 License URI: https://www.gnu.org/licenses/gpl-3.0.html 10 10 11 Add “lang” and “dir” attributes to Group, Columns, Cover, and other specific WordPress Blocks.11 Add `lang` and `dir` attributes to Group, Columns, Cover, and other specific WordPress Blocks, or to the whole page/post. 12 12 13 13 == Description == 14 14 15 This plugin aims to provide a way to ensure that any language change in the content of a pageis indicated to assistive technologies at the container block level, helping a website comply with WCAG guidelines.15 This plugin aims to ensure that any language change in a page’s content is indicated to assistive technologies at the container block level, helping a website comply with WCAG guidelines. 16 16 17 17 This feature is available on the core block editor only at a text formatting level after code from [Jb Audras plugin “Lang Attribute for the Block Editor”](https://wordpress.org/plugins/lang-attribute/) was merged into core. The objective of this plugin is to provide the same functionality at a container block level (Group - including all its variants, Columns, Cover, and other specific block types) so that the language applies to all child elements, no matter the kind of content inside. 18 18 19 Th is plugin is heavily inspired by the Jb Audras plugin (including this readme file), and the development started at WordCamp Europe 2025 Contributor Day, by Marco Almeida from [Naked Cat Plugins](https://profiles.wordpress.org/nakedcatplugins/) / [Webdados](https://profiles.wordpress.org/webdados/), and the help from [Ryan Welcher](https://profiles.wordpress.org/welcher/) on the code side and [Amber Hinds](https://profiles.wordpress.org/alh0319/) on the accessibility compliance side.19 The plugin also supports setting the language at the page or post level, both on the blocks and classic editor. When an entire page is written in a different language than the website’s default, you can override the HTML `lang` and `dir` attributes for that specific page directly from the Document Settings sidebar, without needing to wrap everything in a container block. 20 20 21 For more context: this plugin helps you to make your website compliant with the Web Content Accessibility Guidelines (WCAG) success criterion 3.1.2: “Language of Parts”. The purpose of this success Criterion is to ensure that user agents can correctly present content written in multiple languages.21 This plugin is heavily inspired by the Jb Audras plugin (including this readme file). The development started at WordCamp Europe 2025 Contributor Day, by Marco Almeida from [Naked Cat Plugins](https://profiles.wordpress.org/nakedcatplugins/) / [Webdados](https://profiles.wordpress.org/webdados/), and the help from [Ryan Welcher](https://profiles.wordpress.org/welcher/) on the code side and [Amber Hinds](https://profiles.wordpress.org/alh0319/) on the accessibility compliance side. 22 22 23 Keep in mind that you should only set the lang and dir attributes to a container block if the content you’re going to insert inside it is written in a different language than that set globally on your website. 23 For more context: this plugin helps you to make your website compliant with the Web Content Accessibility Guidelines (WCAG) success criteria: 24 24 25 As per Web Content Accessibility Guidelines: 25 * **3.1.1 – Language of Page**: The default human language of each web page can be programmatically determined. Use the page-level setting when an entire page or post is written in a language other than the website’s default. 26 * **3.1.2 – Language of Parts**: The human language of each passage or phrase in the content can be programmatically determined. Use the block-level setting when only specific sections within a page are in a different language. 26 27 27 Th is makes it possible for user agents and assistive technologies to present content according to the presentation and pronunciation rules for that language. This applies to graphical browsers as well as screen readers, braille displays, and other voice browsers.28 The purpose of these success criteria is to ensure that user agents can correctly present content written in multiple languages. 28 29 29 Both assistive technologies and conventional user agents can render text more accurately if the language of each passage of text is identified. Screen readers can use the pronunciation rules of the language of the text. Visual browsers can display characters and scripts in appropriate ways. 30 Keep in mind that you should set the `lang` and `dir` attributes only on a container block or page if the content is written in a language different from the one set globally on your website. 31 32 **As per Web Content Accessibility Guidelines:** 33 34 This enables user agents and assistive technologies to present content according to the presentation and pronunciation rules of that language. This applies to graphical browsers, screen readers, braille displays, and other voice browsers. 35 36 Both assistive technologies and conventional user agents can render text more accurately if the language of each passage of text is identified. Screen readers can use the language’s pronunciation rules. Visual browsers can display characters and scripts appropriately. 30 37 31 38 This is especially important when switching between languages that read from left to right and languages that read from right to left, or when text is rendered in a language that uses a different alphabet. Users with disabilities who know all the languages used in the Web page will be better able to understand the content when each passage is rendered appropriately. … … 36 43 37 44 == Supported block types == 45 38 46 * **Group** (`core/group`): Group contents together and set a language for them 39 47 * **Columns** (`core/columns` and `core/column`): Organize content into a set of columns and set a language for all the columns or a specific column … … 45 53 46 54 == Features == 47 * Add “lang” and “dir” attributes to Group, Columns, Cover, and other specific WordPress Blocks 55 56 * Set the language and text direction for an entire page or post, both on the blocks and classic editor: a “Page Language” panel in the Document Settings sidebar overrides the HTML `lang` and `dir` attributes for that specific page 57 * Add `lang` and `dir` attributes to Group, Columns, Cover, and other specific WordPress Blocks, mentioned above 48 58 * Show visual outline around blocks that have a language attribute set - For easy identification of blocks you have already set to a different language during your editing process, only for Administrators and Editors, and if enabled in Settings - Writing 49 59 … … 51 61 52 62 1. Using the block editor to add a language attribute to a Group block 53 2. The lang anddir attributes rendered on the frontend63 2. The `lang` and `dir attributes rendered on the frontend 54 64 3. Using the highlighting option during the editing process 55 65 … … 57 67 58 68 1. Install the plugin and activate it. 59 2. Insert a Group, Columns, Cover (or other specific) block, and use the “Language Settings” sidebar panel to set the language for all the content inside that container 69 2. To set the language for an entire page or post: open the Document Settings sidebar (the panel icon at the top right of the editor) and use the “Page Language” panel. 70 3. To set the language for a specific section within a page: insert a Group, Columns, Cover (or other specific) block, and use the “Block Language” sidebar panel to set the language for all the content inside that container. 60 71 61 72 == Frequently Asked Questions == 73 74 = When should I use the page-level language setting instead of a block-level one? = 75 76 Use the **Page Language** setting (in the Document Settings sidebar) when the entire page or post is written in a different language than the website default. This overrides the `lang` attribute on the HTML element itself, which corresponds to WCAG 3.1.1 (Language of Page). 77 In this case, we also recommend creating a dedicated template in the Site Editor (Appearance → Editor → Templates) where shared template parts — such as the header and footer — are also in that same language. 78 79 Use the **Block Language** setting (in the block’s sidebar panel) when only a specific section within a page is in a different language, while the rest of the page remains in the site’s default language. This corresponds to WCAG 3.1.2 (Language of Parts). 62 80 63 81 = Why not have the option to set the language attribute on all block types? = … … 71 89 72 90 If your are working on a WordCamp website, or you don’t want to mess around with PHP, you can also add custom CSS to change the color, overriding our `--nakedcatplugins-lang-attr-highlight-color` variable. 73 Here’s a [Gist example](https://gist.github.com/webdados/ 61197dd2e98f399ba2cfeefbac518851).91 Here’s a [Gist example](https://gist.github.com/webdados/7179f5be4e224ba84867cf77e9bc9174). 74 92 75 93 = How can I contribute to this plugin? = … … 82 100 83 101 == Changelog == 102 103 = 3.0 - 2026-03-09 = 104 * [NEW] Plugin renamed from “Language Attribute for Container Blocks” to “Language Attribute for Container Blocks and Pages/Posts” 105 * [NEW] Set the page/post language at the document level: a new “Page Language” panel in the Document Settings sidebar allows overriding the HTML `lang` and `dir` attributes for a specific page or post, independently of the website’s default language 106 * [TWEAK] Rename “Language Settings” sidebar block panel to “Block Language” 107 * [FIX] Gist URL for changing the highlight color using plain CSS 108 * [DEV] Tested up to 7.0-beta3-61865 84 109 85 110 = 2.2 - 2025-10-20 =
Note: See TracChangeset
for help on using the changeset viewer.