| | 64 | /** |
| | 65 | * Generates a language pack for core. |
| | 66 | * |
| | 67 | * Examples: |
| | 68 | * wp @translate wporg-translate language-pack generate core 5.0 |
| | 69 | * wp @translate wporg-translate language-pack generate core 5.0 --locale=de |
| | 70 | * |
| | 71 | * @param string $slug Slug of the core version. |
| | 72 | * @param array $args Extra arguments. |
| | 73 | */ |
| | 74 | private function generate_core( $slug, $args ) { |
| | 75 | $projects = [ |
| | 76 | "wp/$slug" => 'default', |
| | 77 | "wp/$slug/admin" => 'admin', |
| | 78 | "wp/$slug/admin/network" => 'admin-netwrok', |
| | 79 | "wp/$slug/cc" => 'continents-cities', |
| | 80 | ]; |
| | 81 | |
| | 82 | foreach ( $projects as $path => $domain ) { |
| | 83 | $gp_project = GP::$project->by_path( $path ); |
| | 84 | if ( ! $gp_project ) { |
| | 85 | WP_CLI::error( "Invalid core path: $path." ); |
| | 86 | } |
| | 87 | |
| | 88 | $translation_sets = GP::$translation_set->by_project_id( $gp_project->id ); |
| | 89 | if ( ! $translation_sets ) { |
| | 90 | WP_CLI::error( 'No translation sets available.' ); |
| | 91 | } |
| | 92 | |
| | 93 | /** |
| | 94 | * Filters the arguments passed to the WP-CLI command. |
| | 95 | * |
| | 96 | * @param array $args CLI arguments. |
| | 97 | * @param string $path Path of the GP Project. |
| | 98 | */ |
| | 99 | $args = apply_filters( 'wporg_translate_language_pack_core_args', $args, $path ); |
| | 100 | |
| | 101 | if ( $args['locale'] ) { |
| | 102 | $translation_sets = wp_list_filter( $translation_sets, [ |
| | 103 | 'locale' => $args['locale'], |
| | 104 | 'slug' => $args['locale-slug'], |
| | 105 | ] ); |
| | 106 | } |
| | 107 | |
| | 108 | if ( $domain === 'continents-cities' ) { |
| | 109 | $translation_sets = array_filter( $translation_sets, function ( $set ) { |
| | 110 | return substr( $set->locale, 0, 3 ) !== 'en_'; |
| | 111 | } ); |
| | 112 | } |
| | 113 | |
| | 114 | $svn_command = $this->get_svn_command(); |
| | 115 | $svn_checkout = self::get_temp_directory( $slug ); |
| | 116 | |
| | 117 | $result = $this->execute_command( sprintf( |
| | 118 | '%s checkout --quiet --depth=empty %s %s 2>&1', |
| | 119 | $svn_command, |
| | 120 | escapeshellarg( self::SVN_URL . '/core' ), |
| | 121 | escapeshellarg( $svn_checkout ) |
| | 122 | ) ); |
| | 123 | |
| | 124 | if ( is_wp_error( $result ) ) { |
| | 125 | WP_CLI::error_multi_line( $result->get_error_data() ); |
| | 126 | WP_CLI::error( 'SVN export failed.' ); |
| | 127 | } |
| | 128 | |
| | 129 | $data = new stdClass(); |
| | 130 | $data->type = 'core'; |
| | 131 | $data->domain = $domain; |
| | 132 | $data->version = $slug; |
| | 133 | $data->translation_sets = $translation_sets; |
| | 134 | $data->gp_project = $gp_project; |
| | 135 | $data->svn_checkout = $svn_checkout; |
| | 136 | $this->build_language_packs( $data ); |
| | 137 | } |
| | 138 | } |
| | 139 | |
| 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.' ); |
| | 463 | private function build_mapping( $entries ) { |
| | 464 | $mapping = array(); |
| | 465 | foreach ( $entries as $entry ) { |
| | 466 | /** @var Translation_Entry $entry */ |
| | 467 | |
| | 468 | // Find all unique sources this translation originates from. |
| | 469 | $sources = array_map( function ( $reference ) { |
| | 470 | $parts = explode( ':', $reference ); |
| | 471 | $file = $parts[0]; |
| | 472 | |
| | 473 | if ( substr( $file, -7 ) === '.min.js' ) { |
| | 474 | return substr( $file, 0, -7 ) . '.js'; |
| | 475 | } |
| | 476 | |
| | 477 | if ( substr( $file, -3 ) === '.js' ) { |
| | 478 | return $file; |
| | 479 | } |
| | 480 | return 'po'; |
| | 481 | }, $entry->references ); |
| | 482 | // Always add all entries to the PO file. |
| | 483 | $sources[] = 'po'; |
| | 484 | $sources = array_unique( $sources ); |
| | 485 | |
| | 486 | foreach ( $sources as $source ) { |
| | 487 | $mapping[ $source ][] = $entry; |
| | 488 | } |
| | 491 | return $mapping; |
| | 492 | } |
| | 493 | |
| | 494 | /** |
| | 495 | * Builds a mapping of JS file names to translation entries. |
| | 496 | * |
| | 497 | * @param GP_Project $gp_project The GlotPress project. |
| | 498 | * @param GP_Locale $gp_locale The GlotPress locale. |
| | 499 | * @param GP_Translation_Set $set The translation set. |
| | 500 | * @param array $mapping A mapping of files to translation entries. |
| | 501 | * @param string $base_dest Destination file name. |
| | 502 | * @return array An array of translation files built, may be empty if no translations in JS files exist. |
| | 503 | */ |
| | 504 | private function build_json_files( $gp_project, $gp_locale, $set, $mapping, $base_dest ) { |
| | 505 | // Export translations for each JS file to a separate translation file. |
| | 506 | $files = array(); |
| | 507 | $format = gp_array_get( GP::$formats, 'jed1x' ); |
| | 508 | foreach ( $mapping as $file => $entries ) { |
| | 509 | $json_content = $format->print_exported_file( $gp_project, $gp_locale, $set, $entries ); |
| | 510 | |
| | 511 | $hash = md5( $file ); |
| | 512 | $dest = "{$base_dest}-{$hash}.json"; |
| | 513 | |
| | 514 | file_put_contents( $dest, $json_content ); |
| | 515 | |
| | 516 | $files[] = $dest; |
| | 517 | } |
| | 518 | |
| | 519 | return $files; |
| | 520 | } |
| | 521 | |
| | 522 | /** |
| | 523 | * Builds a PO file for translations. |
| | 524 | * |
| | 525 | * @param GP_Project $gp_project The GlotPress project. |
| | 526 | * @param GP_Locale $gp_locale The GlotPress locale. |
| | 527 | * @param GP_Translation_Set $set The translation set. |
| | 528 | * @param Translation_Entry[] $entries The translation entries. |
| | 529 | * @param string $dest Destination file name. |
| | 530 | * @return string|WP_Error Last updated date on success, WP_Error on failure. |
| | 531 | */ |
| | 532 | private function build_po_file( $gp_project, $gp_locale, $set, $entries, $dest ) { |
| 523 | | $export_directory = "{$data->svn_checkout}/{$data->domain}/{$data->version}/{$wp_locale}"; |
| 524 | | $build_directory = self::BUILD_DIR . "/{$data->type}s/{$data->domain}/{$data->version}"; |
| 525 | | $filename = "{$data->domain}-{$wp_locale}"; |
| 526 | | $po_file = "{$export_directory}/{$filename}.po"; |
| 527 | | $mo_file = "{$export_directory}/{$filename}.mo"; |
| 528 | | $zip_file = "{$export_directory}/{$filename}.zip"; |
| 529 | | $build_zip_file = "{$build_directory}/{$wp_locale}.zip"; |
| | 665 | $working_directory = $data->type === 'core' ?$data->svn_checkout : "{$data->svn_checkout}/{$data->domain}"; |
| | 666 | $export_directory = "{$working_directory}/{$data->version}/{$wp_locale}"; |
| | 667 | $build_directory = $data->type === 'core' |
| | 668 | ? self::BUILD_DIR . "/core/{$data->version}" |
| | 669 | : self::BUILD_DIR . "/{$data->type}s/{$data->domain}/{$data->version}"; |
| | 670 | |
| | 671 | $filename = $data->domain === 'default' ? "{$data->domain}-{$wp_locale}" : $wp_locale; |
| | 672 | $json_file_base = "{$export_directory}/{$filename}"; |
| | 673 | $po_file = "{$export_directory}/{$filename}.po"; |
| | 674 | $mo_file = "{$export_directory}/{$filename}.mo"; |
| | 675 | $zip_file = "{$export_directory}/{$filename}.zip"; |
| | 676 | $build_zip_file = "{$build_directory}/{$wp_locale}.zip"; |
| | 681 | $entries = GP::$translation->for_export( $data->gp_project, $set, [ 'status' => 'current' ] ); |
| | 682 | if ( ! $entries ) { |
| | 683 | WP_CLI::warning( "No current translations available for {$wp_locale}." ); |
| | 684 | |
| | 685 | continue; |
| | 686 | } |
| | 687 | |
| | 688 | // Build a mapping based on where the translation entries occur and separate the po entries. |
| | 689 | $mapping = $this->build_mapping( $entries ); |
| | 690 | $po_entries = array_key_exists( 'po', $mapping ) ? $mapping['po'] : array(); |
| | 691 | |
| | 692 | unset( $mapping['po'] ); |
| | 693 | |
| | 694 | // Create JED json files for each JS file. |
| | 695 | $json_files = $this->build_json_files( $data->gp_project, $gp_locale, $set, $mapping, $json_file_base ); |
| | 696 | |