| @@ -38,12 +38,12 @@ | ||
| 38 | 38 | public const VERSION = '1.0.0'; |
| 39 | 39 | |
| 40 | 40 | public function ui_metadata(): array { |
| 41 | 41 | return array( |
| 42 | - 'label' => 'Media Optimization', | |
| 43 | - 'tab_label' => 'Lazy Loading', // its own tab on the Media Optimization page | |
| 42 | + 'label' => __( 'Media Optimization', 'xspeed' ), | |
| 43 | + 'tab_label' => __( 'Lazy Loading', 'xspeed' ), // its own tab on the Media Optimization page | |
| 44 | 44 | 'icon' => 'Image', |
| 45 | - 'description' => 'Control how images, iframes, and videos load — lazy-loading, missing dimensions, and format optimization.', | |
| 45 | + 'description' => __( 'Control how images, iframes, and videos load — lazy-loading, missing dimensions, and format optimization.', 'xspeed' ), | |
| 46 | 46 | // Host page: Lazy Loading (this module) + Image Optimization (Pro) |
| 47 | 47 | // + AI Suggestions (Pro) as tabs — everything a page loads on one |
| 48 | 48 | // page instead of separate rows (FBS-83633). Fonts is NOT here: it |
| 49 | 49 | // has its own Optimization card (#86). |
| @@ -55,28 +55,28 @@ | ||
| 55 | 55 | return array( |
| 56 | 56 | 'lazy_images' => array( |
| 57 | 57 | 'type' => 'bool', |
| 58 | 58 | 'default' => true, |
| 59 | - 'label' => 'Lazy-load Images', | |
| 60 | - 'description' => 'Add loading="lazy" + decoding="async" to <img> tags in post content. The first images on the page get loading="eager" so the LCP image is not deferred.', | |
| 59 | + 'label' => __( 'Lazy-load Images', 'xspeed' ), | |
| 60 | + 'description' => __( 'Add loading="lazy" + decoding="async" to <img> tags in post content. The first images on the page get loading="eager" so the LCP image is not deferred.', 'xspeed' ), | |
| 61 | 61 | ), |
| 62 | 62 | 'lazy_iframes' => array( |
| 63 | 63 | 'type' => 'bool', |
| 64 | 64 | 'default' => true, |
| 65 | - 'label' => 'Lazy-load Iframes', | |
| 66 | - 'description' => 'Add loading="lazy" to <iframe> tags. Useful for YouTube / Vimeo embeds + map widgets that pull a lot of bytes.', | |
| 65 | + 'label' => __( 'Lazy-load Iframes', 'xspeed' ), | |
| 66 | + 'description' => __( 'Add loading="lazy" to <iframe> tags. Useful for YouTube / Vimeo embeds + map widgets that pull a lot of bytes.', 'xspeed' ), | |
| 67 | 67 | ), |
| 68 | 68 | 'video_facade' => array( |
| 69 | 69 | 'type' => 'bool', |
| 70 | 70 | 'default' => false, |
| 71 | - 'label' => 'Click-to-Play Video Facade', | |
| 72 | - 'description' => 'Replace YouTube and Vimeo embeds — and self-hosted <video> tags that have a poster — with the poster image and a play button. The video only loads when a visitor clicks it, so a page with embeds no longer pays ~1MB of third-party JavaScript, or the full weight of a hosted video file, for visitors who never press play. Autoplaying videos are left alone. Falls back to the normal embed when JavaScript is off.', | |
| 71 | + 'label' => __( 'Click-to-Play Video Facade', 'xspeed' ), | |
| 72 | + 'description' => __( 'Replace YouTube and Vimeo embeds — and self-hosted <video> tags that have a poster — with the poster image and a play button. The video only loads when a visitor clicks it, so a page with embeds no longer pays ~1MB of third-party JavaScript, or the full weight of a hosted video file, for visitors who never press play. Autoplaying videos are left alone. Falls back to the normal embed when JavaScript is off.', 'xspeed' ), | |
| 73 | 73 | ), |
| 74 | 74 | 'lazy_videos' => array( |
| 75 | 75 | 'type' => 'bool', |
| 76 | 76 | 'default' => true, |
| 77 | - 'label' => 'Lazy-load HTML5 Videos', | |
| 78 | - 'description' => 'Set preload="none" on self-hosted <video> tags, overriding a player\'s own preload="auto"/"metadata". Autoplaying videos are left alone — they need their bytes regardless. Browsers do not yet support loading="lazy" on video; preload="none" is the closest equivalent.', | |
| 77 | + 'label' => __( 'Lazy-load HTML5 Videos', 'xspeed' ), | |
| 78 | + 'description' => __( 'Set preload="none" on self-hosted <video> tags, overriding a player\'s own preload="auto"/"metadata". Autoplaying videos are left alone — they need their bytes regardless. Browsers do not yet support loading="lazy" on video; preload="none" is the closest equivalent.', 'xspeed' ), | |
| 79 | 79 | ), |
| 80 | 80 | 'eager_first_n' => array( |
| 81 | 81 | 'type' => 'int', |
| 82 | 82 | 'default' => 1, |
| @@ -81,23 +81,23 @@ | ||
| 81 | 81 | 'type' => 'int', |
| 82 | 82 | 'default' => 1, |
| 83 | 83 | 'min' => 0, |
| 84 | 84 | 'max' => 10, |
| 85 | - 'label' => 'Eager-load First N Images', | |
| 86 | - 'description' => 'How many images at the top of the post get loading="eager". 1 is usually right (the LCP hero image). 0 to lazy-load everything.', | |
| 85 | + 'label' => __( 'Eager-load First N Images', 'xspeed' ), | |
| 86 | + 'description' => __( 'How many images at the top of the post get loading="eager". 1 is usually right (the LCP hero image). 0 to lazy-load everything.', 'xspeed' ), | |
| 87 | 87 | ), |
| 88 | 88 | 'add_missing_dimensions' => array( |
| 89 | 89 | 'type' => 'bool', |
| 90 | 90 | 'default' => true, |
| 91 | - 'label' => 'Add Missing Image Dimensions', | |
| 92 | - 'description' => 'When an <img class="wp-image-N"> has no width/height, look the values up from the media library and inject them. Prevents the page-layout shift that hurts CLS scores.', | |
| 91 | + 'label' => __( 'Add Missing Image Dimensions', 'xspeed' ), | |
| 92 | + 'description' => __( 'When an <img class="wp-image-N"> has no width/height, look the values up from the media library and inject them. Prevents the page-layout shift that hurts CLS scores.', 'xspeed' ), | |
| 93 | 93 | ), |
| 94 | 94 | 'excluded_images' => array( |
| 95 | 95 | 'type' => 'list', |
| 96 | 96 | 'default' => array(), |
| 97 | 97 | 'item_type' => 'string', |
| 98 | - 'label' => 'Excluded Images', | |
| 99 | - 'description' => 'Substring patterns that, if found anywhere in the <img> / <iframe> tag (typically a class or filename), exempt that element from lazy-loading. Useful for hero / logo / sprite images. Or add data-skip-lazy to the tag directly.', | |
| 98 | + 'label' => __( 'Excluded Images', 'xspeed' ), | |
| 99 | + 'description' => __( 'Substring patterns that, if found anywhere in the <img> / <iframe> tag (typically a class or filename), exempt that element from lazy-loading. Useful for hero / logo / sprite images. Or add data-skip-lazy to the tag directly.', 'xspeed' ), | |
| 100 | 100 | ), |
| 101 | 101 | ); |
| 102 | 102 | } |
| 103 | 103 | |
| @@ -118,8 +118,27 @@ | ||
| 118 | 118 | ); |
| 119 | 119 | } |
| 120 | 120 | |
| 121 | 121 | public function boot(): void { |
| 122 | + /* | |
| 123 | + * Deferred to `init`. This module reads its own settings to decide | |
| 124 | + * what to hook, and reading settings builds settings_schema(), whose | |
| 125 | + * labels are declared through __(). boot() runs on `plugins_loaded`, | |
| 126 | + * before `after_setup_theme` — the point WordPress 6.7+ treats as the | |
| 127 | + * earliest safe moment to translate — so doing that here fires | |
| 128 | + * _load_textdomain_just_in_time on every request AND resolves the | |
| 129 | + * labels against a domain that is not loaded yet. | |
| 130 | + * | |
| 131 | + * Everything below hooks actions that fire after `init`, so running | |
| 132 | + * one hook later is equivalent. | |
| 133 | + */ | |
| 134 | + add_action( 'init', array( $this, 'boot_on_init' ) ); | |
| 135 | + } | |
| 136 | + | |
| 137 | + /** | |
| 138 | + * The real boot body — see boot() for why it runs on `init`. | |
| 139 | + */ | |
| 140 | + public function boot_on_init(): void { | |
| 122 | 141 | // Bail entirely on admin / feed / cron / REST — same scope as |
| 123 | 142 | // Minifier. Lazy-loading rendered HTML only matters on real |
| 124 | 143 | // frontend page renders. |
| 125 | 144 | if ( is_admin() || ( defined( 'DOING_AJAX' ) && DOING_AJAX ) || ( defined( 'DOING_CRON' ) && DOING_CRON ) || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ) { |