WordPress.org

Making WordPress.org

Changeset 8338


Ignore:
Timestamp:
02/24/2019 06:22:00 PM (4 months 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.