PluginProbe
Booktics – Appointment Booking Calendar for Service Businesses / 1.0.29
Booktics – Appointment Booking Calendar for Service Businesses v1.0.29
1.0.29 1.0.28 1.0.27 1.0.26 1.0.25 1.0.24 1.0.23 1.0.22 1.0.21 1.0.20 1.0.19 trunk 1.0.0 1.0.1 1.0.10 1.0.11 1.0.12 1.0.13 1.0.14 1.0.15 1.0.16 1.0.17 1.0.18 1.0.2 1.0.3 All 31 releases
← All changes | core/assets/base-assets.php +107 -33 1.0.22 → 1.0.29 View file →
@@ -105,8 +105,9 @@
105 105 'locale' => determine_locale(),
106 106 'domainMap' => [],
107 107 'domainPaths' => [],
108 108 'translationMap' => (object) [],
109 + 'preloaded' => false,
109 110 ];
110 111
111 112 $lang_dir = WP_LANG_DIR;
112 113 $content_dir = WP_CONTENT_DIR;
@@ -121,49 +122,27 @@
121 122 substr( trailingslashit( $lang_dir ), strlen( untrailingslashit( $abspath ) ) )
122 123 );
123 124 }
124 125
125 - $data['translationMap'] = (object) $this->get_translation_map();
126 - $data['domainPaths'] = (object) $this->get_translation_directories();
126 + $locale_data = $this->get_preloaded_locale_data( $data['locale'] );
127 + $data['preloaded'] = ! empty( $locale_data );
127 128
129 + // The consolidated locale data already covers every string in the bundles,
130 + // so the per-chunk hash map is only needed when it could not be built.
131 + if ( ! $data['preloaded'] ) {
132 + $data['translationMap'] = (object) $this->get_translation_map();
133 + }
134 +
128 135 wp_enqueue_script( 'booktics-i18n-loader' );
129 136
130 137 $data['domainMap'] = (object) $data['domainMap'];
131 138 $data['domainPaths'] = (object) $data['domainPaths'];
132 139
133 - $preload = null;
134 - $l10n_file = trailingslashit( WP_LANG_DIR ) . 'plugins/booktics-' . $data['locale'] . '.l10n.php';
135 - $l10n_file_alt = trailingslashit( WP_LANG_DIR ) . 'plugins/booktics-' . str_replace( '-', '_', $data['locale'] ) . '.l10n.php';
136 - $l10n_file_legacy = trailingslashit( WP_LANG_DIR ) . 'plugins/booktics-' . str_replace( '_', '-', $data['locale'] ) . '.l10n.php';
137 - $l10n_candidates = array_unique( array_filter( [ $l10n_file, $l10n_file_alt, $l10n_file_legacy ] ) );
138 -
139 - foreach ( $l10n_candidates as $candidate ) {
140 - if ( file_exists( $candidate ) ) {
141 - $preload = include $candidate;
142 - break;
143 - }
144 - }
145 -
146 140 $inline = '(function(){var k="bookticsI18nLoader"; if (typeof window[k] === "undefined") { window[k] = {}; } window[k].state = ' . wp_json_encode( $data, JSON_UNESCAPED_SLASHES ) . ';';
147 141
148 - if ( is_array( $preload ) && ! empty( $preload['messages'] ) && is_array( $preload['messages'] ) ) {
149 - $messages = [];
150 - foreach ( $preload['messages'] as $msgid => $msgstr ) {
151 - if ( is_string( $msgid ) && is_string( $msgstr ) ) {
152 - $messages[ $msgid ] = [ $msgstr ];
153 - }
154 - }
155 -
156 - $locale_data = [
157 - '' => [
158 - 'domain' => 'booktics',
159 - 'lang' => $data['locale'],
160 - 'plural-forms' => isset( $preload['plural-forms'] ) ? $preload['plural-forms'] : 'nplurals=2; plural=n != 1;',
161 - ],
162 - ] + $messages;
163 -
164 - $encoded_locale_data = wp_json_encode( $locale_data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE );
165 - $inline .= ' window[k].preloadedLocaleData = ' . $encoded_locale_data . '; window.bookticsPreloadedLocaleData = ' . $encoded_locale_data . ';';
142 + if ( ! empty( $locale_data ) ) {
143 + $inline .= ' window[k].preloadedLocaleData = window.bookticsPreloadedLocaleData = '
144 + . wp_json_encode( $locale_data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ) . ';';
166 145 }
167 146
168 147 $inline .= '})();';
169 148
@@ -270,8 +249,103 @@
270 249 return false;
271 250 }
272 251
273 252 return $file_path;
253 + }
254 +
255 + /**
256 + * Build the Jed locale data for every translated string in the text domain.
257 + *
258 + * Script translations are normally split into one JSON file per bundled
259 + * script, keyed by an md5 of that script's path. Async chunk filenames carry
260 + * a content hash and change on every build, so those JSON files go stale as
261 + * soon as the plugin is updated. Registering the whole domain up front keeps
262 + * the React apps translated regardless of how the bundles were chunked.
263 + *
264 + * @param string $locale Locale being rendered.
265 + *
266 + * @return array Jed formatted locale data, empty when there is nothing to preload.
267 + */
268 + private function get_preloaded_locale_data( $locale ) {
269 + static $cache = [];
270 +
271 + if ( isset( $cache[ $locale ] ) ) {
272 + return $cache[ $locale ];
273 + }
274 +
275 + $cache[ $locale ] = [];
276 +
277 + if ( 'en_US' === $locale ) {
278 + return $cache[ $locale ];
279 + }
280 +
281 + if ( ! is_textdomain_loaded( 'booktics' ) ) {
282 + // Registers the plugin's own /languages path. Just-in-time loading only
283 + // finds WP.org language packs, so without this a bundled .mo is ignored.
284 + // phpcs:ignore PluginCheck.CodeAnalysis.DiscouragedFunctions.load_plugin_textdomainFound
285 + load_plugin_textdomain( 'booktics', false, dirname( plugin_basename( BOOKTICS_FILE ) ) . '/languages' );
286 + }
287 +
288 + $translations = get_translations_for_domain( 'booktics' );
289 +
290 + // WP_Translations (WP 6.5+, .l10n.php files) does not extend Translations,
291 + // so check for the entries it exposes rather than the class.
292 + if ( ! is_object( $translations ) || $translations instanceof \NOOP_Translations ) {
293 + return $cache[ $locale ];
294 + }
295 +
296 + $entries = (array) $translations->entries;
297 + $messages = [];
298 +
299 + foreach ( $entries as $entry ) {
300 + if ( ! $entry instanceof \Translation_Entry || ! is_string( $entry->singular ) || '' === $entry->singular ) {
301 + continue;
302 + }
303 +
304 + $entry_translations = array_values( array_filter(
305 + (array) $entry->translations,
306 + function ( $translation ) {
307 + return is_string( $translation ) && '' !== $translation;
308 + }
309 + ) );
310 +
311 + if ( empty( $entry_translations ) ) {
312 + continue;
313 + }
314 +
315 + // wp.i18n keys contextual strings as "contextsingular".
316 + $key = ( is_string( $entry->context ) && '' !== $entry->context )
317 + ? $entry->context . "\4" . $entry->singular
318 + : $entry->singular;
319 +
320 + $messages[ $key ] = $entry_translations;
321 + }
322 +
323 + if ( empty( $messages ) ) {
324 + return $cache[ $locale ];
325 + }
326 +
327 + $headers = (array) $translations->headers;
328 +
329 + $locale_data = [
330 + '' => [
331 + 'domain' => 'booktics',
332 + 'lang' => $locale,
333 + 'plural-forms' => ! empty( $headers['Plural-Forms'] ) ? $headers['Plural-Forms'] : 'nplurals=2; plural=n != 1;',
334 + ],
335 + ] + $messages;
336 +
337 + /**
338 + * Filter the locale data preloaded for the React apps.
339 + *
340 + * Useful to trim the payload down to the strings a site actually renders.
341 + *
342 + * @param array $locale_data Jed formatted locale data.
343 + * @param string $locale Locale being rendered.
344 + */
345 + $cache[ $locale ] = apply_filters( 'booktics_preloaded_locale_data', $locale_data, $locale );
346 +
347 + return $cache[ $locale ];
274 348 }
275 349
276 350 /**
277 351 * Get map of translation file hashes to their URLs.