PluginProbe
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN / 1.3.3
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN v1.3.3
1.3.3 1.3.2 1.3.1 1.3.0 1.2.4 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 1.1.8 All 29 releases
← All changes | includes/modules/Fonts/FontsModule.php +49 -15 1.0.61.3.3 View file →
@@ -31,11 +31,11 @@
31 31 public const VERSION = '1.0.0';
32 32
33 33 public function ui_metadata(): array {
34 34 return array(
35 - 'label' => 'Fonts',
35 + 'label' => __( 'Fonts', 'xspeed' ),
36 36 'icon' => 'Type',
37 - 'description' => 'Stop web fonts from blocking text. Adds display=swap to Google Fonts and preloads the fonts you mark critical.',
37 + 'description' => __( 'Stop web fonts from blocking text. Adds display=swap to Google Fonts and preloads the fonts you mark critical.', 'xspeed' ),
38 38 );
39 39 }
40 40
41 41 public function settings_schema(): array {
@@ -42,22 +42,41 @@
42 42 return array(
43 43 'font_display_swap' => array(
44 44 'type' => 'bool',
45 45 'default' => true,
46 - 'label' => 'Add font-display: swap',
47 - 'description' => 'Append display=swap to Google Fonts URLs so text renders immediately in a fallback face while the web font loads. No effect on URLs that already declare a display value.',
46 + 'label' => __( 'Add font-display: swap', 'xspeed' ),
47 + 'description' => __( 'Append display=swap to Google Fonts URLs so text renders immediately in a fallback face while the web font loads. Blocking values already on the URL (auto, block) are rewritten to swap; a deliberate non-blocking choice (fallback, optional) is left alone.', 'xspeed' ),
48 48 ),
49 49 'preload_fonts' => array(
50 50 'type' => 'list',
51 51 'default' => array(),
52 52 'item_type' => 'url',
53 - 'label' => 'Preload Font URLs',
54 - 'description' => 'One absolute font URL per line (woff2/woff/ttf/otf). Each becomes a <link rel="preload" as="font" crossorigin> in the head so the browser starts downloading before the CSS parses. Use only for fonts that render above the fold.',
53 + 'label' => __( 'Preload Font URLs', 'xspeed' ),
54 + 'description' => __( 'One absolute font URL per line (woff2/woff/ttf/otf). Each becomes a <link rel="preload" as="font" crossorigin> in the head so the browser starts downloading before the CSS parses. Use only for fonts that render above the fold.', 'xspeed' ),
55 55 ),
56 56 );
57 57 }
58 58
59 59 public function boot(): void {
60 + /*
61 + * Deferred to `init`. This module reads its own settings to decide
62 + * what to hook, and reading settings builds settings_schema(), whose
63 + * labels are declared through __(). boot() runs on `plugins_loaded`,
64 + * before `after_setup_theme` — the point WordPress 6.7+ treats as the
65 + * earliest safe moment to translate — so doing that here fires
66 + * _load_textdomain_just_in_time on every request AND resolves the
67 + * labels against a domain that is not loaded yet.
68 + *
69 + * Everything below hooks actions that fire after `init`, so running
70 + * one hook later is equivalent.
71 + */
72 + add_action( 'init', array( $this, 'boot_on_init' ) );
73 + }
74 +
75 + /**
76 + * The real boot body — see boot() for why it runs on `init`.
77 + */
78 + public function boot_on_init(): void {
60 79 // Frontend-only rewriting. Admin / cron / AJAX / REST never
61 80 // render <link rel="stylesheet"> tags we should touch.
62 81 if ( is_admin()
63 82 || ( defined( 'DOING_AJAX' ) && DOING_AJAX )
@@ -62,8 +81,12 @@
62 81 if ( is_admin()
63 82 || ( defined( 'DOING_AJAX' ) && DOING_AJAX )
64 83 || ( defined( 'DOING_CRON' ) && DOING_CRON )
65 84 || ( defined( 'REST_REQUEST' ) && REST_REQUEST )
85 + // A builder editing screen is a front-end URL none of the above
86 + // catch; swapping font-display under it changes what the editor
87 + // measures. (#281)
88 + || \XSpeed\Builder_Editor::is_active()
66 89 ) {
67 90 return;
68 91 }
69 92
@@ -105,18 +128,28 @@
105 128 }
106 129
107 130 $href = $m[2];
108 131
109 - // Already has a display param — leave it alone (respect the theme /
110 - // plugin that set it). The href here has been through esc_url(),
111 - // which encodes "&" as the entity "&#038;", so a real URL like
112 - // ...?family=Roboto&display=optional arrives as
113 - // ...?family=Roboto&#038;display=optional — the char before
114 - // "display=" is then ";" (tail of the entity), not "&", and the old
115 - // [?&]display= guard missed it, double-appending a second display.
116 - // Decode entities before the check so it matches either form.
117 - // (FBS-82161)
132 + // A display param the theme set is respected ONLY when it is one of
133 + // the non-blocking choices (swap / fallback / optional) — someone
134 + // picked those deliberately and each is a defensible trade. `auto`
135 + // and `block` are the values this setting exists to remove: `auto`
136 + // IS block behavior in every engine, and it is almost never a
137 + // choice — it is the default a theme's enqueue happened to emit.
138 + // "Respecting" it turned the switch into a no-op on exactly the
139 + // sites that need it: a live text-LCP measured a 5.5s render delay
140 + // behind flatsome's `display=auto` Poppins URL while this option
141 + // was on and its label promised the opposite. WP Rocket and
142 + // LiteSpeed rewrite these too. The href here has been through
143 + // esc_url(), which encodes "&" as "&#038;" — decode before
144 + // matching, rewrite on the ORIGINAL encoded href so str_replace
145 + // finds it in the tag verbatim. (FBS-82161)
118 146 $href_decoded = html_entity_decode( $href, ENT_QUOTES | ENT_HTML5 );
147 + if ( preg_match( '/([?&])display=(auto|block)(&|$)/i', $href_decoded ) ) {
148 + $new_href = preg_replace( '/((?:[?&]|&#0*38;|&#[xX]0*26;|&amp;)display=)(?:auto|block)(?=&|$)/i', '$1swap', $href );
149 +
150 + return str_replace( $href, $new_href, $tag );
151 + }
119 152 if ( preg_match( '/[?&]display=/i', $href_decoded ) ) {
120 153 return $tag;
121 154 }
122 155
@@ -190,8 +223,9 @@
190 223 array(
191 224 'name' => 'xspeed fonts',
192 225 'callback' => array( $this, 'cli_handler' ),
193 226 'shortdesc' => 'Show font-optimization settings.',
227 + 'ai_hint' => 'How are web fonts being optimized (font-display swap, preloading, local hosting)? Use for questions about invisible text while loading (FOIT/FOUT) or render-blocking fonts.',
194 228 'synopsis' => array(),
195 229 ),
196 230 );
197 231 }