Making WordPress.org


Ignore:
Timestamp:
02/24/2019 06:22:00 PM (8 years ago)
Author:
ocean90
Message:

Translate: Include JSON translations in language packs.

Props herregroen, ocean90.
See #3876, #3976.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-customizations/inc/cli/class-language-pack.php

    r8146 r8338  
    77use stdClass;
    88use WP_CLI;
     9use WP_CLI\Utils;
    910use WP_CLI_Command;
    1011use WP_Error;
     
    1819
    1920        /**
     21         * Whether a language pack should be enforced.
     22         *
     23         * @var bool
     24         */
     25        private $force = false;
     26
     27        /**
    2028         * Generates a language pack.
    2129         *
     
    3644         * [--version]
    3745         * : Current version of the theme or plugin.
     46         *
     47         * [--force]
     48         * : Generate language pack even when threshold is not reached or no updates exist.
    3849         */
    3950        public function generate( $args, $assoc_args ) {
    4051                $type = $args[0];
    4152                $slug = $args[1];
     53
     54                $this->force = Utils\get_flag_value( $assoc_args, 'force' );
    4255
    4356                $args = wp_parse_args( $assoc_args, [
     
    221234         *
    222235         * @param string $prefix Optional. The prefix for the directory, 'hello-dolly' for example.
    223          *
    224236         * @return string The temporary directory.
    225237         */
     
    246258         *
    247259         * @param string $dir The directory which should be removed.
    248          *
    249260         * @return bool False if directory is removed, false otherwise.
    250261         */
     
    261272         *
    262273         * @param string $plugin_slug Slug of a plugin.
    263          *
    264274         * @return false|string False on failure, stable tag on success.
    265275         */
     
    279289         *
    280290         * @param string $theme_slug Slug of a theme.
    281          *
    282291         * @return false|string False on failure, version on success.
    283292         */
     
    297306         *
    298307         * @param string $plugin_slug Slug of a plugin.
    299          *
    300308         * @return false|string False on failure, version on success.
    301309         */
     
    375383
    376384        /**
     385         * Build a mapping of JS files to translation entries occurring in those files.
     386         * Translation entries occurring in other files are added to the 'po' key.
     387         *
     388         * @param Translation_Entry[] $entries The translation entries to map.
     389         * @return array The mapping of sources to translation entries.
     390         */
     391        private function build_mapping( $entries ) {
     392                $mapping = [];
     393
     394                foreach ( $entries as $entry ) {
     395                        /** @var Translation_Entry $entry */
     396
     397                        // Find all unique sources this translation originates from.
     398                        $sources = array_map( function ( $reference ) {
     399                                $parts = explode( ':', $reference );
     400                                $file  = $parts[0];
     401
     402                                if ( substr( $file, -7 ) === '.min.js' ) {
     403                                        return substr( $file, 0, -7 ) . '.js';
     404                                }
     405
     406                                if ( substr( $file, -3 ) === '.js' ) {
     407                                        return $file;
     408                                }
     409                                return 'po';
     410                        }, $entry->references );
     411
     412                        // Always add all entries to the PO file.
     413                        $sources[] = 'po';
     414
     415                        $sources = array_unique( $sources );
     416
     417                        foreach ( $sources as $source ) {
     418                                $mapping[ $source ][] = $entry;
     419                        }
     420                }
     421
     422                return $mapping;
     423        }
     424
     425        /**
     426         * Builds a a separate JSON file with translations for each JavaScript file.
     427         *
     428         * @param GP_Project          $gp_project The GlotPress project.
     429         * @param GP_Locale           $gp_locale  The GlotPress locale.
     430         * @param GP_Translation_Set  $set        The translation set.
     431         * @param array               $mapping    A mapping of files to translation entries.
     432         * @param string              $base_dest  Destination file name.
     433         * @return array An array of translation files built, may be empty if no translations in JS files exist.
     434         */
     435        private function build_json_files( $gp_project, $gp_locale, $set, $mapping, $base_dest ) {
     436                $files  = array();
     437                $format = gp_array_get( GP::$formats, 'jed1x' );
     438
     439                foreach ( $mapping as $file => $entries ) {
     440                        // Don't create JSON files for source files.
     441                        if ( 0 === strpos( $file, 'src/' ) || false !== strpos( $file, '/src/' ) ) {
     442                                continue;
     443                        }
     444
     445                        // Get the translations in Jed 1.x compatible JSON format.
     446                        $json_content = $format->print_exported_file( $gp_project, $gp_locale, $set, $entries );
     447
     448                        // Decode and add comment with file reference for debugging.
     449                        $json_content_decoded          = json_decode( $json_content );
     450                        $json_content_decoded->comment = [ 'reference' => $file ];
     451
     452                        $json_content = wp_json_encode( $json_content_decoded );
     453
     454                        $hash = md5( $file );
     455                        $dest = "{$base_dest}-{$hash}.json";
     456
     457                        file_put_contents( $dest, $json_content );
     458
     459                        $files[] = $dest;
     460                }
     461
     462                return $files;
     463        }
     464
     465        /**
    377466         * Builds a PO file for translations.
    378467         *
    379          * @param GP_Project         $gp_project The GlotPress project.
    380          * @param GP_Locale          $gp_locale  The GlotPress locale.
    381          * @param GP_Translation_Set $set        The translation set.
    382          * @param string             $dest       Destination file name.
     468         * @param GP_Project          $gp_project The GlotPress project.
     469         * @param GP_Locale           $gp_locale  The GlotPress locale.
     470         * @param GP_Translation_Set  $set        The translation set.
     471         * @param Translation_Entry[] $entries    The translation entries.
     472         * @param string              $dest       Destination file name.
    383473         * @return string|WP_Error Last updated date on success, WP_Error on failure.
    384474         */
    385         private function build_po_file( $gp_project, $gp_locale, $set, $dest ) {
    386                 $entries = GP::$translation->for_export( $gp_project, $set, [ 'status' => 'current' ] );
    387                 if ( ! $entries ) {
    388                         return new WP_Error( 'no_translations', 'No current translations available.' );
    389                 }
    390 
     475        private function build_po_file( $gp_project, $gp_locale, $set, $entries, $dest ) {
    391476                $format     = gp_array_get( GP::$formats, 'po' );
    392477                $po_content = $format->print_exported_file( $gp_project, $gp_locale, $set, $entries );
     
    407492         *
    408493         * @param string $command The escaped command to execute.
    409          *
    410494         * @return true|WP_Error True on success, WP_Error on failure.
    411495         */
     
    428512         * @param string $version  Version of the theme/plugin.
    429513         * @param string $updated  Last updated.
    430          * @return string|WP_Error 'updated' when language pack was updated, 'inserted' if it's a new
    431          *                         language pack. WP_Error on failure.
     514         * @return true|WP_Error true when language pack was updated, WP_Error on failure.
    432515         */
    433516        private function insert_language_pack( $type, $domain, $language, $version, $updated ) {
     
    444527
    445528                if ( $existing ) {
    446                         return new WP_Error( 'language_pack_exists', 'The language pack is already imported for this version.' );
     529                        return true;
    447530                }
    448531
     
    474557                ) );
    475558
    476                 if ( $wpdb->rows_affected ) {
    477                         return 'updated';
    478                 } else {
    479                         return 'inserted';
    480                 }
     559                return true;
    481560        }
    482561
     
    488567        private function build_language_packs( $data ) {
    489568                $existing_packs = $this->get_active_language_packs( $data->type, $data->domain, $data->version );
    490                 $svn_command = $this->get_svn_command();
     569                $svn_command    = $this->get_svn_command();
    491570
    492571                foreach ( $data->translation_sets as $set ) {
     
    511590                        // Check if percent translated is above threshold.
    512591                        $percent_translated = $set->percent_translated();
    513                         if ( $percent_translated < self::PACKAGE_THRESHOLD ) {
     592                        if ( ! $this->force && $percent_translated < self::PACKAGE_THRESHOLD ) {
    514593                                WP_CLI::log( "Skip {$wp_locale}, translations below threshold ({$percent_translated}%)." );
    515594                                continue;
     
    517596
    518597                        // Check if new translations are available since last build.
    519                         if ( isset( $existing_packs[ $wp_locale ] ) ) {
     598                        if ( ! $this->force && isset( $existing_packs[ $wp_locale ] ) ) {
    520599                                $pack_time = strtotime( $existing_packs[ $wp_locale ]->updated );
    521600                                $glotpress_time = strtotime( $set->last_modified() );
     
    527606                        }
    528607
    529                         $export_directory = "{$data->svn_checkout}/{$data->domain}/{$data->version}/{$wp_locale}";
    530                         $build_directory  = self::BUILD_DIR . "/{$data->type}s/{$data->domain}/{$data->version}";
    531                         $filename         = "{$data->domain}-{$wp_locale}";
    532                         $po_file          = "{$export_directory}/{$filename}.po";
    533                         $mo_file          = "{$export_directory}/{$filename}.mo";
    534                         $zip_file         = "{$export_directory}/{$filename}.zip";
    535                         $build_zip_file   = "{$build_directory}/{$wp_locale}.zip";
     608                        $entries = GP::$translation->for_export( $data->gp_project, $set, [ 'status' => 'current' ] );
     609                        if ( ! $entries ) {
     610                                WP_CLI::warning( "No current translations available for {$wp_locale}." );
     611                                continue;
     612                        }
     613
     614                        $working_directory = "{$data->svn_checkout}/{$data->domain}";
     615                        $export_directory  = "{$working_directory}/{$data->version}/{$wp_locale}";
     616                        $build_directory   = self::BUILD_DIR . "/{$data->type}s/{$data->domain}/{$data->version}";
     617
     618                        $filename       = "{$data->domain}-{$wp_locale}";
     619                        $json_file_base = "{$export_directory}/{$filename}";
     620                        $po_file        = "{$export_directory}/{$filename}.po";
     621                        $mo_file        = "{$export_directory}/{$filename}.mo";
     622                        $zip_file       = "{$export_directory}/{$filename}.zip";
     623                        $build_zip_file = "{$build_directory}/{$wp_locale}.zip";
    536624
    537625                        // Update/create directories.
    538626                        $this->update_svn_directory( $export_directory );
    539627
     628                        // Build a mapping based on where the translation entries occur and separate the po entries.
     629                        $mapping    = $this->build_mapping( $entries );
     630                        $po_entries = array_key_exists( 'po', $mapping ) ? $mapping['po'] : [];
     631
     632                        unset( $mapping['po'] );
     633
     634                        // Create JED json files for each JS file.
     635                        $json_files = $this->build_json_files( $data->gp_project, $gp_locale, $set, $mapping, $json_file_base );
     636
    540637                        // Create PO file.
    541                         $last_modified = $this->build_po_file( $data->gp_project, $gp_locale, $set, $po_file );
     638                        $last_modified = $this->build_po_file( $data->gp_project, $gp_locale, $set, $po_entries, $po_file );
    542639
    543640                        if ( is_wp_error( $last_modified ) ) {
     
    545642
    546643                                // Clean up.
    547                                 $this->execute_command( "rm -rf {$data->svn_checkout}/{$data->domain}" );
     644                                $this->execute_command( sprintf( 'rm -rf %s', escapeshellarg( $working_directory ) ) );
    548645
    549646                                continue;
     
    562659
    563660                                // Clean up.
    564                                 $this->execute_command( "rm -rf {$data->svn_checkout}/{$data->domain}" );
     661                                $this->execute_command( sprintf( 'rm -rf %s', escapeshellarg( $working_directory ) ) );
    565662
    566663                                continue;
     
    569666                        // Create ZIP file.
    570667                        $result = $this->execute_command( sprintf(
    571                                 'zip -9 -j %s %s %s 2>&1',
     668                                'zip -9 -j %s %s %s %s 2>&1',
    572669                                escapeshellarg( $zip_file ),
    573670                                escapeshellarg( $po_file ),
    574                                 escapeshellarg( $mo_file )
     671                                escapeshellarg( $mo_file ),
     672                                implode( ' ', array_map( 'escapeshellarg', $json_files ) )
    575673                        ) );
    576674
     
    580678
    581679                                // Clean up.
    582                                 $this->execute_command( "rm -rf {$data->svn_checkout}/{$data->domain}" );
     680                                $this->execute_command( sprintf( 'rm -rf %s', escapeshellarg( $working_directory ) ) );
    583681
    584682                                continue;
     
    596694
    597695                                // Clean up.
    598                                 $this->execute_command( "rm -rf {$data->svn_checkout}/{$data->domain}" );
     696                                $this->execute_command( sprintf( 'rm -rf %s', escapeshellarg( $working_directory ) ) );
    599697
    600698                                continue;
     
    613711
    614712                                // Clean up.
    615                                 $this->execute_command( "rm -rf {$data->svn_checkout}/{$data->domain}" );
     713                                $this->execute_command( sprintf( 'rm -rf %s', escapeshellarg( $working_directory ) ) );
    616714
    617715                                continue;
     
    625723
    626724                                // Clean up.
    627                                 $this->execute_command( "rm -rf {$data->svn_checkout}/{$data->domain}" );
     725                                $this->execute_command( sprintf( 'rm -rf %s', escapeshellarg( $working_directory ) ) );
    628726
    629727                                continue;
     
    650748
    651749                                // Clean up.
    652                                 $this->execute_command( "rm -rf {$data->svn_checkout}/{$data->domain}" );
     750                                $this->execute_command( sprintf( 'rm -rf %s', escapeshellarg( $working_directory ) ) );
    653751
    654752                                continue;
     
    656754
    657755                        // Clean up.
    658                         $this->execute_command( "rm -rf {$data->svn_checkout}/{$data->domain}" );
     756                        $this->execute_command( sprintf( 'rm -rf %s', escapeshellarg( $working_directory ) ) );
    659757
    660758                        WP_CLI::success( "Language pack for {$wp_locale} generated." );
Note: See TracChangeset for help on using the changeset viewer.