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 +112 -0 3.0.15 → 3.0.17 View file →
@@ -449,8 +449,9 @@
449 449 // WP_LANG_DIR/plugins) for the admin UI. Without it, translated
450 450 // admin strings never reach the SPA. Mirrors FrontendAssetsProvider.
451 451 if (function_exists('wp_set_script_translations')) {
452 452 wp_set_script_translations('yatra-admin', 'yatra', YATRA_PLUGIN_PATH . 'i18n/languages');
453 + $this->backfillScriptTranslations('yatra-admin');
453 454 }
454 455
455 456 // Localize script data
456 457 wp_localize_script('yatra-admin', 'yatraAdmin', $localized_data);
@@ -547,8 +548,119 @@
547 548 * Enqueue setup wizard assets
548 549 *
549 550 * @return void
550 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 +
551 663 public function enqueueSetupWizardAssets(): void
552 664 {
553 665 // Enqueue setup wizard CSS
554 666 $cssPath = YATRA_PLUGIN_PATH . 'assets/admin/css/setup-wizard.css';