PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.17
Yatra – Travel Booking & Tour Operator Software v3.0.17
3.0.17 3.0.16 3.0.15 3.0.14 3.0.14.1 3.0.14.2 3.0.12 3.0.13 3.0.11 3.0.10 3.0.9 3.0.8 3.0.7 3.0.6 3.0.5 3.0.5.1 3.0.4 3.0.3 3.0.2.9 3.0.2.7 3.0.2.8 3.0.2.6 trunk 1.0.0 2.0.0 All 85 releases
← All changes | app/Providers/AdminAssetsProvider.php +124 -0 3.0.14.2 → 3.0.17 View file →
@@ -439,8 +439,21 @@
439 439 $jsVersion,
440 440 true
441 441 );
442 442
443 + // The bundle calls the global wp.i18n.__() (it never ships its
444 + // own copy), and scripts/extract-js-pot.mjs writes every admin
445 + // string's `#:` reference as this bundle's path precisely so
446 + // WordPress's md5(handle src) JSON lookup matches. This call is
447 + // the missing last link: it tells WordPress to load
448 + // i18n/languages/yatra-{locale}-{md5}.json (or the copy under
449 + // WP_LANG_DIR/plugins) for the admin UI. Without it, translated
450 + // admin strings never reach the SPA. Mirrors FrontendAssetsProvider.
451 + if (function_exists('wp_set_script_translations')) {
452 + wp_set_script_translations('yatra-admin', 'yatra', YATRA_PLUGIN_PATH . 'i18n/languages');
453 + $this->backfillScriptTranslations('yatra-admin');
454 + }
455 +
443 456 // Localize script data
444 457 wp_localize_script('yatra-admin', 'yatraAdmin', $localized_data);
445 458
446 459 // Phone dataset for admin displays (flag + dial-code detection of
@@ -535,8 +548,119 @@
535 548 * Enqueue setup wizard assets
536 549 *
537 550 * @return void
538 551 */
552 + /**
553 + * Feed the admin bundle its translations when no JSON file exists.
554 + *
555 + * wp_set_script_translations() can only read a `yatra-{locale}-{md5}.json`
556 + * file. WordPress.org language packs ship one, so translations from
557 + * translate.wordpress.org simply work — but a site translated by hand, with
558 + * Loco Translate or a .po/.mo dropped into wp-content/languages, has only
559 + * the PHP catalogue. For those sites every string in the React admin stayed
560 + * in English no matter how complete the translation was, which looked like
561 + * the plugin ignoring the translation altogether.
562 + *
563 + * The strings are the same ones the PHP catalogue already holds, so they are
564 + * handed to wp.i18n directly. Core is asked first and left in charge
565 + * whenever it can find a JSON file: that path is cached by the browser as a
566 + * separate request, and this one is not.
567 + *
568 + * Nothing is emitted on an untranslated site — the map comes back empty and
569 + * English sites carry no extra weight.
570 + */
571 + private function backfillScriptTranslations(string $handle): void
572 + {
573 + if (!function_exists('load_script_textdomain')) {
574 + return;
575 + }
576 +
577 + // A real JSON file beats this: let WordPress load it as it normally would.
578 + if (load_script_textdomain($handle, 'yatra', YATRA_PLUGIN_PATH . 'i18n/languages')) {
579 + return;
580 + }
581 +
582 + $localeData = $this->localeDataFromTextdomain('yatra');
583 + if ($localeData === []) {
584 + return;
585 + }
586 +
587 + wp_add_inline_script(
588 + $handle,
589 + 'wp.i18n.setLocaleData(' . wp_json_encode($localeData) . ', "yatra");',
590 + 'before'
591 + );
592 + }
593 +
594 + /**
595 + * The loaded PHP catalogue, in the shape wp.i18n.setLocaleData() expects.
596 + *
597 + * Entries that were never translated are left out: they would only restate
598 + * the English the bundle already carries. Contexts use the same NUL
599 + * separator Jed and gettext use, so _x() resolves too.
600 + *
601 + * @return array<string, mixed>
602 + */
603 + private function localeDataFromTextdomain(string $domain): array
604 + {
605 + $translations = get_translations_for_domain($domain);
606 + if (!is_object($translations)) {
607 + return [];
608 + }
609 + // Deliberately duck-typed, and deliberately not isset()/??. WordPress
610 + // returns Translations, NOOP_Translations or — since the performant
611 + // translations work — WP_Translations, which neither extends
612 + // Translations nor declares `entries`: it serves that property through
613 + // __get() and defines no __isset(), so both instanceof and isset()
614 + // report nothing is there and quietly disable this fallback. Only a
615 + // direct read reaches the magic getter.
616 + if (!property_exists($translations, 'entries') && !method_exists($translations, '__get')) {
617 + return [];
618 + }
619 +
620 + $entries = $translations->entries;
621 + if (!is_array($entries) || $entries === []) {
622 + return [];
623 + }
624 +
625 + $data = [];
626 + foreach ($entries as $entry) {
627 + if (!is_object($entry) || (string) $entry->singular === '') {
628 + continue;
629 + }
630 +
631 + $forms = array_values(array_filter(
632 + (array) $entry->translations,
633 + static fn($t) => is_string($t) && $t !== ''
634 + ));
635 + if ($forms === []) {
636 + continue;
637 + }
638 + // Untranslated entries come back as the original string.
639 + if (count($forms) === 1 && $forms[0] === $entry->singular) {
640 + continue;
641 + }
642 +
643 + $key = ($entry->context !== null && $entry->context !== '')
644 + ? $entry->context . "\u{0004}" . $entry->singular
645 + : $entry->singular;
646 +
647 + $data[$key] = $forms;
648 + }
649 +
650 + if ($data === []) {
651 + return [];
652 + }
653 +
654 + $data[''] = [
655 + 'domain' => $domain,
656 + 'lang' => determine_locale(),
657 + 'plural-forms' => 'nplurals=2; plural=(n != 1);',
658 + ];
659 +
660 + return $data;
661 + }
662 +
539 663 public function enqueueSetupWizardAssets(): void
540 664 {
541 665 // Enqueue setup wizard CSS
542 666 $cssPath = YATRA_PLUGIN_PATH . 'assets/admin/css/setup-wizard.css';