Make WordPress Core

Changeset 44169


Ignore:
Timestamp:
12/14/2018 05:51:31 AM (8 years ago)
Author:
pento
Message:

I18N: Add JavaScript translation support.

Adds the wp_set_script_translations() function which registers translations for a JavaScript file. This function takes a handle, domain and optionally a path and ensures JavaScript translation files are loaded if they exist.

Merges [43825,43828,43859,43898] from the 5.0 branch to trunk.

Props herregroen, atimmer, omarreiss, nerrad, swissspidy, ocean90, georgestephanis.
Fixes #45103, #45256.

Location:
trunk
Files:
6 edited
4 copied

Legend:

Unmodified
Added
Removed
  • trunk

  • trunk/src/wp-includes/class-wp-dependency.php

    r42343 r44169  
    6969
    7070        /**
     71         * Translation textdomain set for this dependency.
     72         *
     73         * @since 5.0.0
     74         * @var string
     75         */
     76        public $textdomain;
     77
     78        /**
     79         * Translation path set for this dependency.
     80         *
     81         * @since 5.0.0
     82         * @var string
     83         */
     84        public $translations_path;
     85
     86        /**
    7187         * Setup dependencies.
    7288         *
    … …  
    97113        }
    98114
     115        public function set_translations( $domain, $path = null ) {
     116                if ( ! is_string( $domain ) ) {
     117                        return false;
     118                }
     119                $this->textdomain        = $domain;
     120                $this->translations_path = $path;
     121                return true;
     122        }
    99123}
  • trunk/src/wp-includes/class.wp-scripts.php

    r43583 r44169  
    331331                }
    332332
     333                $translations = $this->print_translations( $handle, false );
     334                if ( $translations ) {
     335                        $translations = sprintf( "<script type='text/javascript'>\n%s\n</script>\n", $translations );
     336                }
     337
    333338                if ( ! preg_match( '|^(https?:)?//|', $src ) && ! ( $this->content_url && 0 === strpos( $src, $this->content_url ) ) ) {
    334339                        $src = $this->base_url . $src;
    … …  
    346351                }
    347352
    348                 $tag = "{$cond_before}{$before_handle}<script type='text/javascript' src='$src'></script>\n{$after_handle}{$cond_after}";
     353                $tag = "{$translations}{$cond_before}{$before_handle}<script type='text/javascript' src='$src'></script>\n{$after_handle}{$cond_after}";
    349354
    350355                /**
    … …  
    489494
    490495                return parent::set_group( $handle, $recursion, $grp );
     496        }
     497
     498        /**
     499         * Sets a translation textdomain.
     500         *
     501         * @since 5.0.0
     502         *
     503         * @param string $handle Name of the script to register a translation domain to.
     504         * @param string $domain The textdomain.
     505         * @param string $path   Optional. The full file path to the directory containing translation files.
     506         *
     507         * @return bool True if the textdomain was registered, false if not.
     508         */
     509        public function set_translations( $handle, $domain, $path = null ) {
     510                if ( ! isset( $this->registered[ $handle ] ) ) {
     511                        return false;
     512                }
     513
     514                /** @var \_WP_Dependency $obj */
     515                $obj = $this->registered[ $handle ];
     516
     517                if ( ! in_array( 'wp-i18n', $obj->deps, true ) ) {
     518                        $obj->deps[] = 'wp-i18n';
     519                }
     520                return $obj->set_translations( $domain, $path );
     521        }
     522
     523        /**
     524         * Prints translations set for a specific handle.
     525         *
     526         * @since 5.0.0
     527         *
     528         * @param string $handle Name of the script to add the inline script to. Must be lowercase.
     529         * @param bool   $echo   Optional. Whether to echo the script instead of just returning it.
     530         *                       Default true.
     531         * @return string|false Script on success, false otherwise.
     532         */
     533        public function print_translations( $handle, $echo = true ) {
     534                if ( ! isset( $this->registered[ $handle ] ) || empty( $this->registered[ $handle ]->textdomain ) ) {
     535                        return false;
     536                }
     537
     538                $domain = $this->registered[ $handle ]->textdomain;
     539                $path   = $this->registered[ $handle ]->translations_path;
     540
     541                $json_translations = load_script_textdomain( $handle, $domain, $path );
     542
     543                if ( ! $json_translations ) {
     544                        // Register empty locale data object to ensure the domain still exists.
     545                        $json_translations = '{ "locale_data": { "messages": { "": {} } } }';
     546                }
     547
     548                $output = '(function( translations ){' .
     549                                          'wp.i18n.setLocaleData( translations.locale_data, "' . $domain . '" );' .
     550                                  '})(' . $json_translations . ');';
     551
     552                if ( $echo ) {
     553                        printf( "<script type='text/javascript'>\n%s\n</script>\n", $output );
     554                }
     555
     556                return $output;
    491557        }
    492558
  • trunk/src/wp-includes/functions.wp-scripts.php

    r43661 r44169  
    199199
    200200        return $wp_scripts->localize( $handle, $object_name, $l10n );
     201}
     202
     203/**
     204 * Sets translated strings for a script.
     205 *
     206 * Works only if the script has already been added.
     207 *
     208 * @see WP_Scripts::set_translations()
     209 * @global WP_Scripts $wp_scripts The WP_Scripts object for printing scripts.
     210 *
     211 * @since 5.0.0
     212 *
     213 * @param string $handle Script handle the textdomain will be attached to.
     214 * @param string $domain The textdomain.
     215 * @param string $path   Optional. The full file path to the directory containing translation files.
     216 *
     217 * @return bool True if the textdomain was successfully localized, false otherwise.
     218 */
     219function wp_set_script_translations( $handle, $domain, $path = null ) {
     220        global $wp_scripts;
     221        if ( ! ( $wp_scripts instanceof WP_Scripts ) ) {
     222                _wp_scripts_maybe_doing_it_wrong( __FUNCTION__ );
     223                return false;
     224        }
     225
     226        return $wp_scripts->set_translations( $handle, $domain, $path );
    201227}
    202228
  • trunk/src/wp-includes/l10n.php

    r44134 r44169  
    892892        }
    893893        return load_theme_textdomain( $domain, $path );
     894}
     895
     896/**
     897 * Load the script translated strings.
     898 *
     899 * @see WP_Scripts::set_translations()
     900 * @link https://core.trac.wordpress.org/ticket/45103
     901 * @global WP_Scripts $wp_scripts The WP_Scripts object for printing scripts.
     902 *
     903 * @since 5.0.0
     904 *
     905 * @param string $handle Name of the script to register a translation domain to.
     906 * @param string $domain The textdomain.
     907 * @param string $path   Optional. The full file path to the directory containing translation files.
     908 *
     909 * @return false|string False if the script textdomain could not be loaded, the translated strings
     910 *                      in JSON encoding otherwise.
     911 */
     912function load_script_textdomain( $handle, $domain, $path = null ) {
     913        global $wp_scripts;
     914
     915        $path   = untrailingslashit( $path );
     916        $locale = is_admin() ? get_locale() : get_user_locale();
     917
     918        // If a path was given and the handle file exists simply return it.
     919        $file_base       = $domain === 'default' ? $locale : $domain . '-' . $locale;
     920        $handle_filename = $file_base . '-' . $handle . '.json';
     921        if ( $path && file_exists( $path . '/' . $handle_filename ) ) {
     922                return file_get_contents( $path . '/' . $handle_filename );
     923        }
     924
     925        $obj = $wp_scripts->registered[ $handle ];
     926
     927        /** This filter is documented in wp-includes/class.wp-scripts.php */
     928        $src = esc_url( apply_filters( 'script_loader_src', $obj->src, $handle ) );
     929
     930        $relative       = false;
     931        $languages_path = WP_LANG_DIR;
     932
     933        $src_url     = wp_parse_url( $src );
     934        $content_url = wp_parse_url( content_url() );
     935        $site_url    = wp_parse_url( site_url() );
     936
     937        // If the host is the same or it's a relative URL.
     938        if (
     939                strpos( $src_url['path'], $content_url['path'] ) === 0 &&
     940                ( ! isset( $src_url['host'] ) || $src_url['host'] !== $content_url['host'] )
     941        ) {
     942                // Make the src relative the specific plugin or theme.
     943                $relative = trim( substr( $src, strlen( $content_url['path'] ) ), '/' );
     944                $relative = explode( '/', $relative );
     945
     946                $languages_path = WP_LANG_DIR . '/' . $relative[0];
     947
     948                $relative = array_slice( $relative, 2 );
     949                $relative = implode( '/', $relative );
     950        } elseif ( ! isset( $src_url['host'] ) || $src_url['host'] !== $site_url['host'] ) {
     951                if ( ! isset( $site_url['path'] ) ) {
     952                        $relative = trim( $src_url['path'], '/' );
     953                } elseif ( ( strpos( $src_url['path'], $site_url['path'] ) === 0 ) ) {
     954                        // Make the src relative to the WP root.
     955                        $relative = substr( $src, strlen( $site_url['path'] ) );
     956                        $relative = trim( $relative, '/' );
     957                }
     958        }
     959
     960        // If the source is not from WP.
     961        if ( false === $relative ) {
     962                return false;
     963        }
     964
     965        // Translations are always based on the unminified filename.
     966        if ( substr( $relative, -7 ) === '.min.js' ) {
     967                $relative = substr( $relative, 0, -7 ) . '.js';
     968        }
     969
     970        $md5_filename = $file_base . '-' . md5( $relative ) . '.json';
     971        if ( $path && file_exists( $path . '/' . $md5_filename ) ) {
     972                return file_get_contents( $path . '/' . $md5_filename );
     973        }
     974        if ( file_exists( $languages_path . '/' . $md5_filename ) ) {
     975                return file_get_contents( $languages_path . '/' . $md5_filename );
     976        }
     977
     978        return false;
    894979}
    895980
  • trunk/tests/phpunit/tests/dependencies/scripts.php

    r44166 r44169  
    770770                $expected .= "<script type='text/javascript' src='/wp-includes/js/script3.js?ver={$ver}'></script>\n";
    771771                $expected .= "<script type='text/javascript' src='/wp-includes/js/script4.js?ver={$ver}'></script>\n";
     772
     773                $this->assertEquals( $expected, get_echo( 'wp_print_scripts' ) );
     774        }
     775
     776        /**
     777         * @ticket 45103
     778         */
     779        public function test_wp_set_script_translations() {
     780                wp_register_script( 'wp-i18n', '/wp-includes/js/dist/wp-i18n.js', array(), null );
     781                wp_enqueue_script( 'test-example', '/wp-includes/js/script.js', array(), null );
     782                wp_set_script_translations( 'test-example', 'default', DIR_TESTDATA . '/languages' );
     783
     784                $expected  = "<script type='text/javascript' src='/wp-includes/js/dist/wp-i18n.js'></script>";
     785                $expected .= "\n<script type='text/javascript'>\n(function( translations ){" .
     786                                'wp.i18n.setLocaleData( translations.locale_data, "default" );' .
     787                        '})(' . file_get_contents( DIR_TESTDATA . '/languages/en_US-813e104eb47e13dd4cc5af844c618754.json' ) . ");\n</script>\n";
     788                $expected .= "<script type='text/javascript' src='/wp-includes/js/script.js'></script>\n";
     789
     790                $this->assertEquals( $expected, get_echo( 'wp_print_scripts' ) );
     791        }
     792
     793        /**
     794         * @ticket 45103
     795         */
     796        public function test_wp_set_script_translations_for_plugin() {
     797                wp_register_script( 'wp-i18n', '/wp-includes/js/dist/wp-i18n.js', array(), null );
     798                wp_enqueue_script( 'plugin-example', '/wp-content/plugins/my-plugin/js/script.js', array(), null );
     799                wp_set_script_translations( 'plugin-example', 'internationalized-plugin', DIR_TESTDATA . '/languages/plugins' );
     800
     801                $expected  = "<script type='text/javascript' src='/wp-includes/js/dist/wp-i18n.js'></script>";
     802                $expected .= "\n<script type='text/javascript'>\n(function( translations ){" .
     803                                         'wp.i18n.setLocaleData( translations.locale_data, "internationalized-plugin" );' .
     804                                         '})(' . file_get_contents( DIR_TESTDATA . '/languages/plugins/internationalized-plugin-en_US-2f86cb96a0233e7cb3b6f03ad573be0b.json' ) . ");\n</script>\n";
     805                $expected .= "<script type='text/javascript' src='/wp-content/plugins/my-plugin/js/script.js'></script>\n";
     806
     807                $this->assertEquals( $expected, get_echo( 'wp_print_scripts' ) );
     808        }
     809
     810        /**
     811         * @ticket 45103
     812         */
     813        public function test_wp_set_script_translations_for_theme() {
     814                wp_register_script( 'wp-i18n', '/wp-includes/js/dist/wp-i18n.js', array(), null );
     815                wp_enqueue_script( 'theme-example', '/wp-content/themes/my-theme/js/script.js', array(), null );
     816                wp_set_script_translations( 'theme-example', 'internationalized-theme', DIR_TESTDATA . '/languages/themes' );
     817
     818                $expected  = "<script type='text/javascript' src='/wp-includes/js/dist/wp-i18n.js'></script>";
     819                $expected .= "\n<script type='text/javascript'>\n(function( translations ){" .
     820                                         'wp.i18n.setLocaleData( translations.locale_data, "internationalized-theme" );' .
     821                                         '})(' . file_get_contents( DIR_TESTDATA . '/languages/themes/internationalized-theme-en_US-2f86cb96a0233e7cb3b6f03ad573be0b.json' ) . ");\n</script>\n";
     822                $expected .= "<script type='text/javascript' src='/wp-content/themes/my-theme/js/script.js'></script>\n";
     823
     824                $this->assertEquals( $expected, get_echo( 'wp_print_scripts' ) );
     825        }
     826
     827        /**
     828         * @ticket 45103
     829         */
     830        public function test_wp_set_script_translations_with_handle_file() {
     831                wp_register_script( 'wp-i18n', '/wp-includes/js/dist/wp-i18n.js', array(), null );
     832                wp_enqueue_script( 'script-handle', '/wp-admin/js/script.js', array(), null );
     833                wp_set_script_translations( 'script-handle', 'admin', DIR_TESTDATA . '/languages/' );
     834
     835                $expected  = "<script type='text/javascript' src='/wp-includes/js/dist/wp-i18n.js'></script>";
     836                $expected .= "\n<script type='text/javascript'>\n(function( translations ){" .
     837                                         'wp.i18n.setLocaleData( translations.locale_data, "admin" );' .
     838                                         '})(' . file_get_contents( DIR_TESTDATA . '/languages/admin-en_US-script-handle.json' ) . ");\n</script>\n";
     839                $expected .= "<script type='text/javascript' src='/wp-admin/js/script.js'></script>\n";
     840
     841                $this->assertEquals( $expected, get_echo( 'wp_print_scripts' ) );
     842        }
     843
     844        /**
     845         * @ticket 45103
     846         */
     847        public function test_wp_set_script_translations_i18n_dependency() {
     848                global $wp_scripts;
     849
     850                wp_register_script( 'wp-i18n', '/wp-includes/js/dist/wp-i18n.js', array(), null );
     851                wp_enqueue_script( 'test-example', '/wp-includes/js/script.js', array(), null );
     852                wp_set_script_translations( 'test-example', 'default', DIR_TESTDATA . '/languages/' );
     853
     854                $script = $wp_scripts->registered['test-example'];
     855
     856                $this->assertContains( 'wp-i18n', $script->deps );
     857        }
     858
     859        /**
     860         * @ticket 45103
     861         */
     862        public function test_wp_set_script_translations_when_translation_file_does_not_exist() {
     863                wp_register_script( 'wp-i18n', '/wp-includes/js/dist/wp-i18n.js', array(), null );
     864                wp_enqueue_script( 'test-example', '/wp-admin/js/script.js', array(), null );
     865                wp_set_script_translations( 'test-example', 'admin', DIR_TESTDATA . '/languages/' );
     866
     867                $expected  = "<script type='text/javascript' src='/wp-includes/js/dist/wp-i18n.js'></script>";
     868                $expected .= "\n<script type='text/javascript'>\n(function( translations ){" .
     869                                         'translations.locale_data.messages[""].domain = "admin";' .
     870                                         'wp.i18n.setLocaleData( translations.locale_data.messages, "admin" );' .
     871                                         "})({ \"locale_data\": { \"messages\": { \"\": {} } } });\n</script>\n";
     872                $expected .= "<script type='text/javascript' src='/wp-admin/js/script.js'></script>\n";
     873
     874                $this->assertEquals( $expected, get_echo( 'wp_print_scripts' ) );
     875        }
     876
     877        /**
     878         * @ticket 45103
     879         */
     880        public function test_wp_set_script_translations_after_register() {
     881                wp_register_script( 'wp-i18n', '/wp-includes/js/dist/wp-i18n.js', array(), null );
     882                wp_register_script( 'test-example', '/wp-includes/js/script.js', array(), null );
     883                wp_set_script_translations( 'test-example', 'default', DIR_TESTDATA . '/languages' );
     884
     885                wp_enqueue_script( 'test-example' );
     886
     887                $expected  = "<script type='text/javascript' src='/wp-includes/js/dist/wp-i18n.js'></script>";
     888                $expected .= "\n<script type='text/javascript'>\n(function( translations ){" .
     889                                         'wp.i18n.setLocaleData( translations.locale_data, "default" );' .
     890                                         '})(' . file_get_contents( DIR_TESTDATA . '/languages/en_US-813e104eb47e13dd4cc5af844c618754.json' ) . ");\n</script>\n";
     891                $expected .= "<script type='text/javascript' src='/wp-includes/js/script.js'></script>\n";
     892
     893                $this->assertEquals( $expected, get_echo( 'wp_print_scripts' ) );
     894        }
     895
     896        /**
     897         * @ticket 45103
     898         */
     899        public function test_wp_set_script_translations_dependency() {
     900                wp_register_script( 'wp-i18n', '/wp-includes/js/dist/wp-i18n.js', array(), null );
     901                wp_register_script( 'test-dependency', '/wp-includes/js/script.js', array(), null );
     902                wp_set_script_translations( 'test-dependency', 'default', DIR_TESTDATA . '/languages' );
     903
     904                wp_enqueue_script( 'test-example', '/wp-includes/js/script2.js', array( 'test-dependency' ), null );
     905
     906                $expected  = "<script type='text/javascript' src='/wp-includes/js/dist/wp-i18n.js'></script>";
     907                $expected .= "\n<script type='text/javascript'>\n(function( translations ){" .
     908                                         'wp.i18n.setLocaleData( translations.locale_data, "default" );' .
     909                                         '})(' . file_get_contents( DIR_TESTDATA . '/languages/en_US-813e104eb47e13dd4cc5af844c618754.json' ) . ");\n</script>\n";
     910                $expected .= "<script type='text/javascript' src='/wp-includes/js/script.js'></script>\n";
     911                $expected .= "<script type='text/javascript' src='/wp-includes/js/script2.js'></script>\n";
    772912
    773913                $this->assertEquals( $expected, get_echo( 'wp_print_scripts' ) );
Note: See TracChangeset for help on using the changeset viewer.