35 const T15S_API_EXPORT_URL =
'https://translationspress.com/app/gravitykit/{plugin_slug}/{language_slug}/default/export-translations';
78 private function __construct( $slug, $translations_path =
'' ) {
81 if ( $translations_path ) {
82 $this->_translations_path = $translations_path;
85 add_action(
'upgrader_process_complete', [ $this,
'on_upgrader_process_complete' ], 10, 2 );
87 add_filter(
'translations_api', [ $this,
'translations_api' ], 10, 3 );
89 add_filter(
'pre_set_site_transient_update_plugins', [ $this,
'site_transient_update_plugins' ] );
102 public static function get_instance( $slug, $translations_path =
'' ) {
103 if ( empty( self::$_instances[ $slug ] ) ) {
104 self::$_instances[ $slug ] =
new self( $slug, $translations_path );
107 return self::$_instances[ $slug ];
124 if (
'plugins' !== $requested_type || $this->_slug !==
$args[
'slug'] ) {
143 return isset( $this->_all_translations->projects[ $this->_slug ] ) ? $this->_all_translations->projects[
$this->_slug ] : [];
164 if ( ! isset(
$value->translations ) ) {
165 $value->translations = [];
170 if ( empty( $translations[
'translations'] ) ) {
174 foreach ( $translations[
'translations'] as $translation ) {
179 $translation[
'type'] =
'plugin';
182 $value->translations[] = $translation;
204 $this->_all_translations = null;
219 $cache_lifespan = self::T15S_TRANSIENT_EXPIRY;
221 return ! isset( $this->_all_translations->_last_checked ) || ( time() - $this->_all_translations->_last_checked ) > $cache_lifespan;
234 $request = wp_remote_get( self::T15S_API_PACKAGES_URL, [
238 if ( is_wp_error( $request ) ) {
240 $this->
get_exception(
'Unable to reach TranslationsPress API: %s.', __METHOD__, $request->get_error_message() )
244 if ( 200 !== wp_remote_retrieve_response_code( $request ) ) {
246 $this->
get_exception(
'TranslationsPress API returned an invalid response: %s.', __METHOD__, $request[
'response'][
'message'] )
251 $result = json_decode( wp_remote_retrieve_body( $request ),
true );
253 if ( ! is_array( $result ) ) {
258 $this->
get_exception(
'Could not decode the response received from TranslationsPress API: $s.', __METHOD__, wp_remote_retrieve_body( $request ) )
275 if ( is_object( $this->_all_translations ) ) {
279 $this->_all_translations = get_site_transient( self::T15S_TRANSIENT_KEY );
285 $this->_all_translations = new \stdClass();
287 $this->_all_translations->_last_checked = time();
289 set_site_transient( self::T15S_TRANSIENT_KEY, $this->_all_translations );
305 self::get_instance( $slug )->install( $locale );
322 if ( empty( $translations[
'translations'] ) ) {
324 $this->
get_exception(
'No translations found for %s.', __METHOD__, $this->_slug )
328 foreach ( $translations[
'translations'] as $translation ) {
354 global $wp_filesystem;
356 if ( ! $wp_filesystem ) {
357 require_once ABSPATH .
'/wp-admin/includes/admin.php';
361 $this->
get_exception(
'Aborting translation package installation; unable to init WP_Filesystem.', __METHOD__ )
366 if ( ! $wp_filesystem->is_dir( $this->_translations_path ) ) {
367 $wp_filesystem->mkdir( $this->_translations_path, FS_CHMOD_DIR );
370 $temp_package_file = download_url( $translation[
'package'] );
372 if ( is_wp_error( $temp_package_file ) ) {
374 $this->
get_exception(
'Error downloading translation package. Code: %s; Message %s.', __METHOD__, $temp_package_file->get_error_code(), $temp_package_file->get_error_message() )
378 $_get_file_name =
function ( $extension, $language ) {
381 untrailingslashit( $this->_translations_path ),
388 $zip_file = $_get_file_name(
'zip', $translation[
'language'] );
390 $copy_result = $wp_filesystem->copy( $temp_package_file, $zip_file,
true, FS_CHMOD_FILE );
392 $wp_filesystem->delete( $temp_package_file );
394 if ( ! $copy_result ) {
396 $this->
get_exception(
'Unable to move translation package to %s.', __METHOD__, $this->_translations_path )
400 $result = unzip_file( $zip_file, $this->_translations_path );
402 $wp_filesystem->delete( $zip_file );
404 if ( is_wp_error( $result ) ) {
406 $this->
get_exception(
'Error extracting translation package. Code: %s; Message %s.', __METHOD__, $temp_package_file->get_error_code(), $temp_package_file->get_error_message() )
417 if ( ! $T15S_language_slug ) {
418 LoggerFramework::get_instance()->warn(
'Unable to get the T15S language slug for ' . $translation[
'language'] .
' locale.' );
423 $temp_po_file = download_url( strtr(
424 self::T15S_API_EXPORT_URL,
426 '{plugin_slug}' => $this->_slug,
427 '{language_slug}' => $T15S_language_slug
431 if ( is_wp_error( $temp_po_file ) ) {
433 $this->
get_exception(
'Error downloading translation PO file. Code: %s; Message %s.', __METHOD__, $temp_package_file->get_error_code(), $temp_package_file->get_error_message() )
437 $json_file = $_get_file_name(
'json', $translation[
'language'] );
441 $wp_filesystem->delete( $temp_po_file );
458 if ( ! file_exists( $po_file ) ) {
460 $this->
get_exception(
'PO file %s does not exist. Code: %s; Message %s.', __METHOD__, $po_file )
464 $original_translations = Translations::fromPoFile( $po_file );
466 $filtered_translations = [];
468 foreach ( $original_translations as $original_translation ) {
470 if ( strpos( json_encode( $original_translation->getReferences() ),
'.js' ) ===
false || ! $original_translation->getTranslation() ) {
475 $filtered_translation =
new Translation(
'', $original_translation->getOriginal(), $original_translation->getPlural() );
476 $filtered_translation->setTranslation( $original_translation->getTranslation() );
478 $filtered_translations[] = $filtered_translation;
481 if ( empty( $filtered_translations ) ) {
485 $converted_translations =
new Translations( $filtered_translations );
487 foreach ( $original_translations->getHeaders() as $key => $header ) {
488 $converted_translations->setHeader( $key, $header );
491 $converted_translations = [
492 'translation-revision-date' => $converted_translations->getHeader(
'PO-Revision-Date' ),
493 'generator' =>
'GravityKit Translations',
494 'locale_data' => json_decode( $converted_translations->toJedString(), true )
497 if ( ! file_put_contents( $json_file, json_encode( $converted_translations ) ) ) {
499 $this->
get_exception(
'Unable to save JSON file %s.', __METHOD__, $json_file )
515 if ( ( $locale !== $translation[
'language'] ) || ! in_array( $translation[
'language'], $this->
get_available_languages(),
true ) ) {
519 if ( empty( $translation[
'updated'] ) ) {
525 if ( ! isset( $installed[ $translation[
'language'] ] ) ) {
529 $local = date_create( $installed[ $translation[
'language'] ][
'PO-Revision-Date'] );
530 $remote = date_create( $translation[
'updated'] );
532 return $remote > $local;
543 static $languages = [];
545 if ( empty( $languages ) ) {
562 if ( isset( $data[ $this->_slug ] ) ) {
569 foreach ( $translations as $locale => $mo_file ) {
570 $po_file = str_replace(
'.mo',
'.po', $mo_file );
571 if ( ! file_exists( $po_file ) ) {
575 $data[
$this->_slug ][ $locale ] = wp_get_pomo_file_data( $po_file );
591 $files = glob( sprintf(
593 $this->_translations_path,
597 if ( empty( $files ) ) {
603 foreach ( $files as $file ) {
604 $translations[ str_replace( $this->_slug .
'-',
'', basename( $file,
'.mo' ) ) ] = $file;
607 return $return_files ? $translations : array_keys( $translations );
622 $message =
"%s(): ${message}";
643 $slug_to_locale_map = [
661 'ca_valencia' =>
'ca-valencia',
677 'art_xemoji' =>
'art-xemoji',
778 'pap_CW' =>
'pap-cw',
779 'pap_AW' =>
'pap-aw',
782 'art_xpirate' =>
'pirate',
785 'pt_PT_ao90' =>
'pt-ao90',
843 return Arr::get( $slug_to_locale_map, $locale, null );
set_all_translations()
Caches the TranslationsPress data, if not already cached.
get_installed_translations_data()
Returns header data from the installed translations for the current plugin.
install( $locale='')
Installs translations for a given locale.
convert_po_to_json( $po_file, $json_file)
Converts the PO file to JSON file by extracting only JavaScript translations.
get_plugin_translations()
Gets the TranslationsPress data for the current plugin.
get_exception( $message, $method,... $args)
Formats and returns an exception message.
static get( $array, $key, $default=null)
{}
static get_instance( $slug, $translations_path='')
Returns an instance of this class for the given slug.
get_slug_from_locale( $locale)
Returns T15S slug for the language based on the WP locale.
get_available_languages()
Returns an array of locales the site has installed.
refresh_all_translations()
Refreshes the cached TranslationsPress data, if expired.
get_remote_translations_data()
Gets the translation data from the TranslationsPress API.
should_install( $translation, $locale='')
Determines if a translation should be installed.
install_translation( $translation)
Downloads and installs the given translation.
Allows downloading and installing translations from TranslationsPress.
const T15S_API_EXPORT_URL
get_installed_translations( $return_files=false)
Returns an array of locales or .mo translation files found in the translations folder.
translations_api( $result, $requested_type, $args)
Short-circuits translations API requests for private projects.
const T15S_API_PACKAGES_URL
__construct( $slug, $translations_path='')
Class constructor.
download_package( $slug, $locale='')
Downloads and installs the translations package for the specified plugin.
site_transient_update_plugins( $value)
Filters the translations transients to include the current plugin.
const T15S_TRANSIENT_EXPIRY
is_transient_expired()
Determines if the cached TranslationsPress data needs refreshing.
Logging framework for GravityKit.