Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Include translations for JavaScript files in language packs #108

Merged
merged 16 commits into from
Nov 24, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
256 changes: 256 additions & 0 deletions inc/Export.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
<?php
/**
* Translation export class
*
* @since 3.0.0
*
* @package Required\Traduttore
*/

namespace Required\Traduttore;

use GP;
use GP_Locales;
use GP_Translation_Set;
use Translation_Entry;

/**
* Export strings to translation files in PO, MO, and JSON format.
*
* @since 3.0.0
*/
class Export {
/**
* The current translation set.
*
* @since 3.0.0
*
* @var \GP_Translation_Set
*/
protected $translation_set;

/**
* The current locale.
*
* @since 3.0.0
*
* @var \GP_Locale
*/
protected $locale;

/**
* The current Project instance.
*
* @since 3.0.0
*
* @var Project
*/
protected $project;

/**
* List of generated files.
*
* @since 3.0.0
*
* @var string[]
*/
protected $files;

/**
* Export constructor.
*
* @param GP_Translation_Set $translation_set The translation set this export is for.
*/
public function __construct( GP_Translation_Set $translation_set ) {
$this->translation_set = $translation_set;
$this->locale = GP_Locales::by_slug( $translation_set->locale );
$this->project = new Project( GP::$project->get( $translation_set->project_id ) );
}

/**
* Saves strings to different file formats and returns a list of generated files.
*
* @since 3.0.0
*
* @return array List of files with names as key and temporary file location as value.
*/
public function export_strings(): ?array {
$entries = GP::$translation->for_export( $this->project->get_project(), $this->translation_set, [ 'status' => 'current' ] );

if ( ! $entries ) {
return null;
}

// Build a mapping based on where the translation entries occur and separate the po entries.
$mapping = $this->map_entries_to_source( $entries );

$php_entries = array_key_exists( 'php', $mapping ) ? $mapping['php'] : [];

unset( $mapping['php'] );

$this->build_json_files( $mapping );
$this->build_po_file( $php_entries );
$this->build_mo_file( $php_entries );

return $this->files;
}

/**
* Writes content to a file using the WordPress Filesystem Abstraction interface.
*
* @since 3.0.0
*
* @param string $file File path.
* @param string $contents File contents.
*
* @return bool True on success, false otherwise.
*/
protected function write_to_file( string $file, string $contents ): bool {
/* @var \WP_Filesystem_Base $wp_filesystem */
global $wp_filesystem;

if ( ! $wp_filesystem ) {
require_once ABSPATH . '/wp-admin/includes/admin.php';

if ( ! \WP_Filesystem() ) {
return false;
}
}

return $wp_filesystem->put_contents( $file, $contents, FS_CHMOD_FILE );
}

/**
* Returns the base name for translation files.
*
* @since 3.0.0
*
* @return string Base file name without extension.
*/
protected function get_base_file_name(): string {
$slug = $this->project->get_slug();
$text_domain = $this->project->get_text_domain();

if ( $text_domain ) {
$slug = $text_domain;
}

return "{$slug}-{$this->locale->wp_locale}";
}

/**
* Build a mapping of JS files to translation entries occurring in those files.
*
* Translation entries occurring in other files are added to the 'php' key.
*
* @since 3.0.0
*
* @param Translation_Entry[] $entries The translation entries to map.
*
* @return array The mapping of sources to translation entries.
*/
protected function map_entries_to_source( $entries ): array {
$mapping = [];

foreach ( $entries as $entry ) {
// Find all unique sources this translation originates from.
$sources = array_map(
function ( $reference ) {
$parts = explode( ':', $reference );
$file = $parts[0];

if ( substr( $file, -7 ) === '.min.js' ) {
return substr( $file, 0, -7 ) . '.js';
}

if ( substr( $file, -3 ) === '.js' ) {
return $file;
}

return 'php';
},
$entry->references
);

$sources = array_unique( $sources );

foreach ( $sources as $source ) {
$mapping[ $source ][] = $entry;
}
}

return $mapping;
}

/**
* Builds a mapping of JS file names to translation entries.
*
* Exports translations for each JS file to a separate translation file.
*
* @since 3.0.0
*
* @param array $mapping A mapping of files to translation entries.
*/
protected function build_json_files( $mapping ): void {
/* @var \GP_Format $format */
$format = gp_array_get( GP::$formats, 'jed1x' );

$base_file_name = $this->get_base_file_name();

foreach ( $mapping as $file => $entries ) {
$contents = $format->print_exported_file( $this->project->get_project(), $this->locale, $this->translation_set, $entries );

$hash = md5( $file );
$file_name = "{$base_file_name}-{$hash}.json";
$temp_file = wp_tempnam( $file_name );

if ( $this->write_to_file( $temp_file, $contents ) ) {
$this->files[ $file_name ] = $temp_file;
}
}
}

/**
* Builds a PO file for translations.
*
* @since 3.0.0
*
* @param Translation_Entry[] $entries The translation entries.
*/
protected function build_po_file( $entries ): void {
/* @var \GP_Format $format */
$format = gp_array_get( GP::$formats, 'po' );

$base_file_name = $this->get_base_file_name();
$file_name = "{$base_file_name}.po";
$temp_file = wp_tempnam( $file_name );

$contents = $format->print_exported_file( $this->project->get_project(), $this->locale, $this->translation_set, $entries );

if ( $this->write_to_file( $temp_file, $contents ) ) {
$this->files[ $file_name ] = $temp_file;
}
}

/**
* Builds a MO file for translations.
*
* @since 3.0.0
*
* @param Translation_Entry[] $entries The translation entries.
*/
protected function build_mo_file( $entries ): void {
/* @var \GP_Format $format */
$format = gp_array_get( GP::$formats, 'mo' );

$base_file_name = $this->get_base_file_name();
$file_name = "{$base_file_name}.mo";
$temp_file = wp_tempnam( $file_name );

$contents = $format->print_exported_file( $this->project->get_project(), $this->locale, $this->translation_set, $entries );

if ( $this->write_to_file( $temp_file, $contents ) ) {
$this->files[ $file_name ] = $temp_file;
}
}
}
53 changes: 6 additions & 47 deletions inc/ZipProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
namespace Required\Traduttore;

use GP;
use GP_Format;
use GP_Locale;
use GP_Locales;
use GP_Translation_Set;
Expand Down Expand Up @@ -98,10 +97,6 @@ public function schedule_generation(): void {
* @return bool True on success, false on failure.
*/
public function generate_zip_file() : bool {
if ( ! class_exists( '\ZipArchive' ) ) {
return false;
}

/* @var WP_Filesystem_Base $wp_filesystem */
global $wp_filesystem;

Expand All @@ -118,36 +113,20 @@ public function generate_zip_file() : bool {
$wp_filesystem->mkdir( static::get_cache_dir(), FS_CHMOD_DIR );
}

/* @var GP_Locale $locale */
$locale = GP_Locales::by_slug( $this->translation_set->locale );
$gp_project = GP::$project->get( $this->translation_set->project_id );
$entries = GP::$translation->for_export( $gp_project, $this->translation_set, [ 'status' => 'current' ] );

if ( ! $entries ) {
return false;
}

$files_for_zip = [];

/* @var GP_Format $format */
foreach ( [ GP::$formats['po'], GP::$formats['mo'] ] as $format ) {
$file_name = sprintf( '%1$s.%2$s', $this->get_base_file_name(), $format->extension );

$temp_file = wp_tempnam( $file_name );

$contents = $format->print_exported_file( $gp_project, $locale, $this->translation_set, $entries );
$export = new Export( $this->translation_set );

$wp_filesystem->put_contents( $temp_file, $contents, FS_CHMOD_FILE );
$files_for_zip = $export->export_strings();

$files_for_zip[ $temp_file ] = $file_name;
if ( ! $files_for_zip ) {
return false;
}

$zip = new ZipArchive();

$temp_zip_file = wp_tempnam( $this->get_zip_filename() );

if ( $zip->open( $temp_zip_file, ZipArchive::CREATE ) === true ) {
foreach ( $files_for_zip as $temp_file => $file_name ) {
foreach ( $files_for_zip as $file_name => $temp_file ) {
$zip->addFile( $temp_file, $file_name );
}

Expand All @@ -156,7 +135,7 @@ public function generate_zip_file() : bool {

$wp_filesystem->move( $temp_zip_file, $this->get_zip_path(), true );

foreach ( $files_for_zip as $temp_file => $file_name ) {
foreach ( $files_for_zip as $temp_file ) {
$wp_filesystem->delete( $temp_file );
}

Expand Down Expand Up @@ -210,26 +189,6 @@ public function remove_zip_file() : bool {
return $success;
}

/**
* Returns the base name for translation files.
*
* @since 3.0.0
*
* @return string Base file name without extension.
*/
private function get_base_file_name(): string {
$locale = GP_Locales::by_slug( $this->translation_set->locale );
$project = new Project( GP::$project->get( $this->translation_set->project_id ) );
$slug = $project->get_slug();
$text_domain = $project->get_text_domain();

if ( $text_domain ) {
$slug = $text_domain;
}

return "{$slug}-{$locale->wp_locale}";
}

/**
* Returns the name of the ZIP file without the path.
*
Expand Down
Loading