GravityView  2.17
The best, easiest way to display Gravity Forms entries on your website.
TranslationsPress_Updater.php
Go to the documentation of this file.
1 <?php
2 /**
3  * @license GPL-2.0-or-later
4  *
5  * Modified by gravityview on 13-January-2023 using Strauss.
6  * @see https://github.com/BrianHenryIE/strauss
7  */
8 
10 
11 use Exception;
12 use WP_Filesystem;
17 
18 /**
19  * Allows downloading and installing translations from TranslationsPress.
20  *
21  * This is a modified version of the Gravity Forms' TranslationsPress_Updater class, which is based on the T15S library.
22  *
23  * @since 1.0.0
24  *
25  * @see https://github.com/WP-Translations/t15s-registry
26  * @see gravityforms/includes/class-translationspress-updater.php
27  */
29  const T15S_TRANSIENT_KEY = 't15s-registry-gravitykit';
30 
31  const T15S_TRANSIENT_EXPIRY = 12 * HOUR_IN_SECONDS;
32 
33  const T15S_API_PACKAGES_URL = 'https://packages.translationspress.com/gravitykit/packages.json';
34 
35  const T15S_API_EXPORT_URL = 'https://translationspress.com/app/gravitykit/{plugin_slug}/{language_slug}/default/export-translations';
36 
37  /**
38  * The plugin slug.
39  *
40  * @since 1.0.0
41  *
42  * @var string
43  */
44  private $_slug = '';
45 
46  /**
47  * Translations storage path.
48  *
49  * @since 1.0.0
50  *
51  * @var string
52  */
53  private $_translations_path = WP_LANG_DIR . '/plugins/';
54 
55  /**
56  * Cached TranslationsPress data for all GravityKit plugins.
57  *
58  * @var null|object
59  */
61 
62  /**
63  * The current instances of this class keyed by plugin slugs.
64  *
65  * @var TranslationsPress_Updater[]
66  */
67  private static $_instances = [];
68 
69  /**
70  * Class constructor.
71  *
72  * @since 1.0.0
73  *
74  * @param string $slug The plugin slug.
75  * @param string $translations_path Translations storage path.
76  *
77  */
78  private function __construct( $slug, $translations_path = '' ) {
79  $this->_slug = $slug;
80 
81  if ( $translations_path ) {
82  $this->_translations_path = $translations_path;
83  }
84 
85  add_action( 'upgrader_process_complete', [ $this, 'on_upgrader_process_complete' ], 10, 2 );
86 
87  add_filter( 'translations_api', [ $this, 'translations_api' ], 10, 3 );
88 
89  add_filter( 'pre_set_site_transient_update_plugins', [ $this, 'site_transient_update_plugins' ] );
90  }
91 
92  /**
93  * Returns an instance of this class for the given slug.
94  *
95  * @since 1.0.0
96  *
97  * @param string $slug The plugin slug.
98  * @param string $translations_path Translations storage path.
99  *
100  * @return TranslationsPress_Updater
101  */
102  public static function get_instance( $slug, $translations_path = '' ) {
103  if ( empty( self::$_instances[ $slug ] ) ) {
104  self::$_instances[ $slug ] = new self( $slug, $translations_path );
105  }
106 
107  return self::$_instances[ $slug ];
108  }
109 
110  /**
111  * Short-circuits translations API requests for private projects.
112  *
113  * @since 1.0.0
114  *
115  * @param bool|array $result The result object (default: false).
116  * @param string $requested_type The type of translations being requested.
117  * @param object $args Translation API arguments.
118  *
119  * @throws Exception
120  *
121  * @return bool|array
122  */
123  public function translations_api( $result, $requested_type, $args ) {
124  if ( 'plugins' !== $requested_type || $this->_slug !== $args['slug'] ) {
125  return $result;
126  }
127 
128  return $this->get_plugin_translations();
129  }
130 
131  /**
132  * Gets the TranslationsPress data for the current plugin.
133  *
134  * @since 1.0.0
135  *
136  * @throws Exception
137  *
138  * @return array
139  */
140  public function get_plugin_translations() {
141  $this->set_all_translations();
142 
143  return isset( $this->_all_translations->projects[ $this->_slug ] ) ? $this->_all_translations->projects[ $this->_slug ] : [];
144  }
145 
146  /**
147  * Filters the translations transients to include the current plugin.
148  *
149  * @since 1.0.0
150  *
151  * @see wp_get_translation_updates()
152  *
153  * @param mixed $value The transient value.
154  *
155  * @throws Exception
156  *
157  * @return object
158  */
160  if ( ! $value ) {
161  $value = new \stdClass();
162  }
163 
164  if ( ! isset( $value->translations ) ) {
165  $value->translations = [];
166  }
167 
168  $translations = $this->get_plugin_translations();
169 
170  if ( empty( $translations['translations'] ) ) {
171  return $value;
172  }
173 
174  foreach ( $translations['translations'] as $translation ) {
175  if ( ! $this->should_install( $translation ) ) {
176  continue;
177  }
178 
179  $translation['type'] = 'plugin';
180  $translation['slug'] = $this->_slug;
181 
182  $value->translations[] = $translation;
183  }
184 
185  return $value;
186  }
187 
188  /**
189  * Refreshes the cached TranslationsPress data, if expired.
190  *
191  * @since 1.0.0
192  *
193  * @throws Exception
194  *
195  * @return void
196  */
197  public function refresh_all_translations() {
198  static $done;
199 
200  if ( $done ) {
201  return;
202  }
203 
204  $this->_all_translations = null;
205 
206  $this->set_all_translations();
207 
208  $done = true;
209  }
210 
211  /**
212  * Determines if the cached TranslationsPress data needs refreshing.
213  *
214  * @since 1.0.0
215  *
216  * @return bool
217  */
218  public function is_transient_expired() {
219  $cache_lifespan = self::T15S_TRANSIENT_EXPIRY;
220 
221  return ! isset( $this->_all_translations->_last_checked ) || ( time() - $this->_all_translations->_last_checked ) > $cache_lifespan;
222  }
223 
224  /**
225  * Gets the translation data from the TranslationsPress API.
226  *
227  * @since 1.0.0
228  *
229  * @throws Exception
230  *
231  * @return array
232  */
233  public function get_remote_translations_data() {
234  $request = wp_remote_get( self::T15S_API_PACKAGES_URL, [
235  'timeout' => 3
236  ] );
237 
238  if ( is_wp_error( $request ) ) {
239  throw new Exception(
240  $this->get_exception( 'Unable to reach TranslationsPress API: %s.', __METHOD__, $request->get_error_message() )
241  );
242  }
243 
244  if ( 200 !== wp_remote_retrieve_response_code( $request ) ) {
245  throw new Exception(
246  $this->get_exception( 'TranslationsPress API returned an invalid response: %s.', __METHOD__, $request['response']['message'] )
247  );
248  }
249 
250  try {
251  $result = json_decode( wp_remote_retrieve_body( $request ), true );
252 
253  if ( ! is_array( $result ) ) {
254  throw new Exception();
255  }
256  } catch ( Exception $e ) {
257  throw new Exception(
258  $this->get_exception( 'Could not decode the response received from TranslationsPress API: $s.', __METHOD__, wp_remote_retrieve_body( $request ) )
259  );
260  }
261 
262  return $result;
263  }
264 
265  /**
266  * Caches the TranslationsPress data, if not already cached.
267  *
268  * @since 1.0.0
269  *
270  * @throws Exception
271  *
272  * @return void
273  */
274  public function set_all_translations() {
275  if ( is_object( $this->_all_translations ) ) {
276  return;
277  }
278 
279  $this->_all_translations = get_site_transient( self::T15S_TRANSIENT_KEY );
280 
281  if ( is_object( $this->_all_translations ) && ! $this->is_transient_expired() ) {
282  return;
283  }
284 
285  $this->_all_translations = new \stdClass();
286  $this->_all_translations->projects = $this->get_remote_translations_data();
287  $this->_all_translations->_last_checked = time();
288 
289  set_site_transient( self::T15S_TRANSIENT_KEY, $this->_all_translations );
290  }
291 
292  /**
293  * Downloads and installs the translations package for the specified plugin.
294  *
295  * @since 1.0.0
296  *
297  * @param string $slug The plugin slug.
298  * @param string $locale The locale when the site locale is changed or an empty string to install all the user available locales.
299  *
300  * @throws Exception
301  *
302  * @return void
303  */
304  public function download_package( $slug, $locale = '' ) {
305  self::get_instance( $slug )->install( $locale );
306  }
307 
308  /**
309  * Installs translations for a given locale.
310  *
311  * @since 1.0.0
312  *
313  * @param string $locale Locale for which to install the translation.
314  *
315  * @throws Exception
316  *
317  * @return void
318  */
319  public function install( $locale = '' ) {
320  $translations = $this->get_plugin_translations();
321 
322  if ( empty( $translations['translations'] ) ) {
323  throw new Exception(
324  $this->get_exception( 'No translations found for %s.', __METHOD__, $this->_slug )
325  );
326  }
327 
328  foreach ( $translations['translations'] as $translation ) {
329  if ( ! $this->should_install( $translation, $locale ) ) {
330  continue;
331  }
332 
333  $this->install_translation( $translation );
334 
335  if ( $locale ) {
336  return;
337  }
338  }
339  }
340 
341  /**
342  * Downloads and installs the given translation.
343  *
344  * @since 1.0.0
345  * @since 1.0.1 Download an extra file from the T15S API.
346  *
347  * @param array $translation The translation data.
348  *
349  * @throws Exception
350  *
351  * @return void
352  */
353  public function install_translation( $translation ) {
354  global $wp_filesystem;
355 
356  if ( ! $wp_filesystem ) {
357  require_once ABSPATH . '/wp-admin/includes/admin.php';
358 
359  if ( ! WP_Filesystem() ) {
360  throw new Exception(
361  $this->get_exception( 'Aborting translation package installation; unable to init WP_Filesystem.', __METHOD__ )
362  );
363  }
364  }
365 
366  if ( ! $wp_filesystem->is_dir( $this->_translations_path ) ) {
367  $wp_filesystem->mkdir( $this->_translations_path, FS_CHMOD_DIR );
368  }
369 
370  $temp_package_file = download_url( $translation['package'] );
371 
372  if ( is_wp_error( $temp_package_file ) ) {
373  throw new Exception(
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() )
375  );
376  }
377 
378  $_get_file_name = function ( $extension, $language ) {
379  return sprintf(
380  '%s/%s-%s.%s',
381  untrailingslashit( $this->_translations_path ),
382  $this->_slug,
383  $language,
384  $extension
385  );
386  };
387 
388  $zip_file = $_get_file_name( 'zip', $translation['language'] );
389 
390  $copy_result = $wp_filesystem->copy( $temp_package_file, $zip_file, true, FS_CHMOD_FILE );
391 
392  $wp_filesystem->delete( $temp_package_file );
393 
394  if ( ! $copy_result ) {
395  throw new Exception(
396  $this->get_exception( 'Unable to move translation package to %s.', __METHOD__, $this->_translations_path )
397  );
398  }
399 
400  $result = unzip_file( $zip_file, $this->_translations_path );
401 
402  $wp_filesystem->delete( $zip_file );
403 
404  if ( is_wp_error( $result ) ) {
405  throw new Exception(
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() )
407  );
408  }
409 
410  // Follows is a workaround for T15S purging JS translations from the .po file included in the translation package.
411  // This is typically what happens when WP CLI's `i18n make-json` command is used to generate JS translation files (.json).
412  // The filenames contain a hash of the source JS file, which in many of our cases is not the actual file that we end up loading since we tend to bundle UI assets.
413  // As a result, WP is unable to automatically load JS translations and what we do instead is manually create (and later load) a single .json file with all JS translations by extracting them from a .po file.
414  // Since the .po file in the translation package is missing some JS translations, we need to download an unprocessed .po file that's provided by T15S via an API endpoint.
415  $T15S_language_slug = $this->get_slug_from_locale( $translation['language'] );
416 
417  if ( ! $T15S_language_slug ) {
418  LoggerFramework::get_instance()->warn( 'Unable to get the T15S language slug for ' . $translation['language'] . ' locale.' );
419 
420  return;
421  }
422 
423  $temp_po_file = download_url( strtr(
424  self::T15S_API_EXPORT_URL,
425  [
426  '{plugin_slug}' => $this->_slug,
427  '{language_slug}' => $T15S_language_slug
428  ]
429  ) );
430 
431  if ( is_wp_error( $temp_po_file ) ) {
432  throw new Exception(
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() )
434  );
435  }
436 
437  $json_file = $_get_file_name( 'json', $translation['language'] );
438 
439  $this->convert_po_to_json( $temp_po_file, $json_file );
440 
441  $wp_filesystem->delete( $temp_po_file );
442  }
443 
444  /**
445  * Converts the PO file to JSON file by extracting only JavaScript translations.
446  *
447  * @since 1.0.0
448  * @since 1.0.1 New $json_file parameter and Exception when the PO file cannot be read/JSON file cannot be saved.
449  *
450  * @param string $po_file File with path to the PO file.
451  * @param string $json_file File with path where the JSON file will be saved.
452  *
453  * @throws Exception
454  *
455  * @return void
456  */
457  function convert_po_to_json( $po_file, $json_file ) {
458  if ( ! file_exists( $po_file ) ) {
459  throw new Exception(
460  $this->get_exception( 'PO file %s does not exist. Code: %s; Message %s.', __METHOD__, $po_file )
461  );
462  }
463 
464  $original_translations = Translations::fromPoFile( $po_file );
465 
466  $filtered_translations = [];
467 
468  foreach ( $original_translations as $original_translation ) {
469  // Get translations only for files with .js (or .js.php) extensions and only where translated data exists.
470  if ( strpos( json_encode( $original_translation->getReferences() ), '.js' ) === false || ! $original_translation->getTranslation() ) {
471  continue;
472  }
473 
474  // We need to re-create the translation without context or else it will be joined with the source string (see https://github.com/php-gettext/Gettext/blob/3f7bc5ef23302a9059e64934f3d59e454516bec0/src/Generators/Jed.php#L51).
475  $filtered_translation = new Translation( '', $original_translation->getOriginal(), $original_translation->getPlural() );
476  $filtered_translation->setTranslation( $original_translation->getTranslation() );
477 
478  $filtered_translations[] = $filtered_translation;
479  }
480 
481  if ( empty( $filtered_translations ) ) {
482  return;
483  }
484 
485  $converted_translations = new Translations( $filtered_translations );
486 
487  foreach ( $original_translations->getHeaders() as $key => $header ) {
488  $converted_translations->setHeader( $key, $header );
489  }
490 
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 )
495  ];
496 
497  if ( ! file_put_contents( $json_file, json_encode( $converted_translations ) ) ) {
498  throw new Exception(
499  $this->get_exception( 'Unable to save JSON file %s.', __METHOD__, $json_file )
500  );
501  }
502  }
503 
504  /**
505  * Determines if a translation should be installed.
506  *
507  * @since 1.0.0
508  *
509  * @param array $translation The translation data.
510  * @param string $locale The locale when the site locale is changed or an empty string to check all the user available locales.
511  *
512  * @return bool
513  */
514  public function should_install( $translation, $locale = '' ) {
515  if ( ( $locale !== $translation['language'] ) || ! in_array( $translation['language'], $this->get_available_languages(), true ) ) {
516  return false;
517  }
518 
519  if ( empty( $translation['updated'] ) ) {
520  return true;
521  }
522 
523  $installed = $this->get_installed_translations_data();
524 
525  if ( ! isset( $installed[ $translation['language'] ] ) ) {
526  return true;
527  }
528 
529  $local = date_create( $installed[ $translation['language'] ]['PO-Revision-Date'] );
530  $remote = date_create( $translation['updated'] );
531 
532  return $remote > $local;
533  }
534 
535  /**
536  * Returns an array of locales the site has installed.
537  *
538  * @since 1.0.0
539  *
540  * @return array
541  */
542  public function get_available_languages() {
543  static $languages = [];
544 
545  if ( empty( $languages ) ) {
546  $languages = get_available_languages();
547  }
548 
549  return $languages;
550  }
551 
552  /**
553  * Returns header data from the installed translations for the current plugin.
554  *
555  * @since 1.0.0
556  *
557  * @return array
558  */
560  static $data = [];
561 
562  if ( isset( $data[ $this->_slug ] ) ) {
563  return $data[ $this->_slug ];
564  }
565 
566  $data[ $this->_slug ] = [];
567  $translations = $this->get_installed_translations( true );
568 
569  foreach ( $translations as $locale => $mo_file ) {
570  $po_file = str_replace( '.mo', '.po', $mo_file );
571  if ( ! file_exists( $po_file ) ) {
572  continue;
573  }
574 
575  $data[ $this->_slug ][ $locale ] = wp_get_pomo_file_data( $po_file );
576  }
577 
578  return $data[ $this->_slug ];
579  }
580 
581  /**
582  * Returns an array of locales or .mo translation files found in the translations folder.
583  *
584  * @since 1.0.0
585  *
586  * @param bool $return_files Indicates if the object should be keyed by locale (e.g., 'en_EN').
587  *
588  * @return array
589  */
590  public function get_installed_translations( $return_files = false ) {
591  $files = glob( sprintf(
592  '%s/%s-*.mo',
593  $this->_translations_path,
594  $this->_slug
595  ) );
596 
597  if ( empty( $files ) ) {
598  return [];
599  }
600 
601  $translations = [];
602 
603  foreach ( $files as $file ) {
604  $translations[ str_replace( $this->_slug . '-', '', basename( $file, '.mo' ) ) ] = $file;
605  }
606 
607  return $return_files ? $translations : array_keys( $translations );
608  }
609 
610  /**
611  * Formats and returns an exception message.
612  *
613  * @since 1.0.0
614  *
615  * @param string $message Exception message with placeholders for sprintf() replacement.
616  * @param string $method Method throwing the exception.
617  * @param mixed $args,... Variable-length argument lists for sprintf() replacement.
618  *
619  * @return string
620  */
621  public function get_exception( $message, $method, ...$args ) {
622  $message = "%s(): ${message}";
623 
624  return sprintf(
625  $message,
626  $method,
627  ...$args
628  );
629  }
630 
631  /**
632  * Returns T15S slug for the language based on the WP locale.
633  * This is used to access the export URL (e.g., https://translationspress.com/app/gravitykit/gk-gravitycalendar/<slug>/default/export-translations/)
634  *
635  * @since 1.0.1
636  *
637  * @param string $locale WP language locale.
638  *
639  * @return string|null T15S slug.
640  */
641  public function get_slug_from_locale( $locale ) {
642  // Taken from GlotPress that powers T15S: https://github.com/GlotPress/GlotPress/blob/a4436a6169d9f6cba5bc0ed62abe31e4f3ef15b4/locales/locales.php
643  $slug_to_locale_map = [
644  'az' => 'az',
645  'azb' => 'azb',
646  'az_TR' => 'az-tr',
647  'ba' => 'ba',
648  'bal' => 'bal',
649  'bcc' => 'bcc',
650  'bel' => 'bel',
651  'bg_BG' => 'bg',
652  'bgn' => 'bgn',
653  'bho' => 'bho',
654  'bn_BD' => 'bn',
655  'bn_IN' => 'bn-in',
656  'bo' => 'bo',
657  'bre' => 'br',
658  'brx' => 'brx',
659  'bs_BA' => 'bs',
660  'ca' => 'ca',
661  'ca_valencia' => 'ca-valencia',
662  'ceb' => 'ceb',
663  'ckb' => 'ckb',
664  'co' => 'co',
665  'cor' => 'cor',
666  'cs_CZ' => 'cs',
667  'cy' => 'cy',
668  'da_DK' => 'da',
669  'de_DE' => 'de',
670  'de_AT' => 'de-at',
671  'de_CH' => 'de-ch',
672  'dsb' => 'dsb',
673  'dv' => 'dv',
674  'dzo' => 'dzo',
675  'ewe' => 'ee',
676  'el' => 'el',
677  'art_xemoji' => 'art-xemoji',
678  'en_US' => 'en',
679  'en_AU' => 'en-au',
680  'en_CA' => 'en-ca',
681  'en_GB' => 'en-gb',
682  'en_NZ' => 'en-nz',
683  'en_ZA' => 'en-za',
684  'eo' => 'eo',
685  'es_ES' => 'es',
686  'es_AR' => 'es-ar',
687  'es_CL' => 'es-cl',
688  'es_CO' => 'es-co',
689  'es_CR' => 'es-cr',
690  'es_DO' => 'es-do',
691  'es_EC' => 'es-ec',
692  'es_GT' => 'es-gt',
693  'es_HN' => 'es-hn',
694  'es_MX' => 'es-mx',
695  'es_PE' => 'es-pe',
696  'es_PR' => 'es-pr',
697  'es_UY' => 'es-uy',
698  'es_VE' => 'es-ve',
699  'et' => 'et',
700  'eu' => 'eu',
701  'fa_IR' => 'fa',
702  'fa_AF' => 'fa-af',
703  'fuc' => 'fuc',
704  'fi' => 'fi',
705  'fo' => 'fo',
706  'fon' => 'fon',
707  'fr_FR' => 'fr',
708  'fr_BE' => 'fr-be',
709  'fr_CA' => 'fr-ca',
710  'frp' => 'frp',
711  'fur' => 'fur',
712  'fy' => 'fy',
713  'ga' => 'ga',
714  'gax' => 'gax',
715  'gd' => 'gd',
716  'gl_ES' => 'gl',
717  'gu' => 'gu',
718  'hat' => 'hat',
719  'hau' => 'hau',
720  'haw_US' => 'haw',
721  'haz' => 'haz',
722  'he_IL' => 'he',
723  'hi_IN' => 'hi',
724  'hr' => 'hr',
725  'hsb' => 'hsb',
726  'hu_HU' => 'hu',
727  'hy' => 'hy',
728  'ibo' => 'ibo',
729  'id_ID' => 'id',
730  'ido' => 'ido',
731  'is_IS' => 'is',
732  'it_IT' => 'it',
733  'ja' => 'ja',
734  'jv_ID' => 'jv',
735  'ka_GE' => 'ka',
736  'kaa' => 'kaa',
737  'kab' => 'kab',
738  'kal' => 'kal',
739  'kin' => 'kin',
740  'kk' => 'kk',
741  'km' => 'km',
742  'kmr' => 'kmr',
743  'kn' => 'kn',
744  'ko_KR' => 'ko',
745  'kir' => 'kir',
746  'lb_LU' => 'lb',
747  'li' => 'li',
748  'lij' => 'lij',
749  'lin' => 'lin',
750  'lmo' => 'lmo',
751  'lo' => 'lo',
752  'lt_LT' => 'lt',
753  'lug' => 'lug',
754  'lv' => 'lv',
755  'mai' => 'mai',
756  'me_ME' => 'me',
757  'mfe' => 'mfe',
758  'mg_MG' => 'mg',
759  'mk_MK' => 'mk',
760  'ml_IN' => 'ml',
761  'mlt' => 'mlt',
762  'mn' => 'mn',
763  'mr' => 'mr',
764  'mri' => 'mri',
765  'ms_MY' => 'ms',
766  'my_MM' => 'mya',
767  'ne_NP' => 'ne',
768  'nb_NO' => 'nb',
769  'nl_NL' => 'nl',
770  'nl_BE' => 'nl-be',
771  'nn_NO' => 'nn',
772  'nqo' => 'nqo',
773  'oci' => 'oci',
774  'ory' => 'ory',
775  'os' => 'os',
776  'pa_IN' => 'pa',
777  'pa_PK' => 'pa-pk',
778  'pap_CW' => 'pap-cw',
779  'pap_AW' => 'pap-aw',
780  'pcd' => 'pcd',
781  'pcm' => 'pcm',
782  'art_xpirate' => 'pirate',
783  'pl_PL' => 'pl',
784  'pt_PT' => 'pt',
785  'pt_PT_ao90' => 'pt-ao90',
786  'pt_AO' => 'pt-ao',
787  'pt_BR' => 'pt-br',
788  'ps' => 'ps',
789  'rhg' => 'rhg',
790  'ro_RO' => 'ro',
791  'roh' => 'roh',
792  'ru_RU' => 'ru',
793  'sah' => 'sah',
794  'sa_IN' => 'sa-in',
795  'scn' => 'scn',
796  'si_LK' => 'si',
797  'sk_SK' => 'sk',
798  'skr' => 'skr',
799  'sl_SI' => 'sl',
800  'sna' => 'sna',
801  'snd' => 'snd',
802  'so_SO' => 'so',
803  'sq' => 'sq',
804  'sq_XK' => 'sq-xk',
805  'sr_RS' => 'sr',
806  'srd' => 'srd',
807  'ssw' => 'ssw',
808  'su_ID' => 'su',
809  'sv_SE' => 'sv',
810  'sw' => 'sw',
811  'syr' => 'syr',
812  'szl' => 'szl',
813  'ta_IN' => 'ta',
814  'ta_LK' => 'ta-lk',
815  'tah' => 'tah',
816  'te' => 'te',
817  'tg' => 'tg',
818  'th' => 'th',
819  'tir' => 'tir',
820  'tl' => 'tl',
821  'tr_TR' => 'tr',
822  'tt_RU' => 'tt',
823  'tuk' => 'tuk',
824  'twd' => 'twd',
825  'tzm' => 'tzm',
826  'ug_CN' => 'ug',
827  'uk' => 'uk',
828  'ur' => 'ur',
829  'uz_UZ' => 'uz',
830  'vec' => 'vec',
831  'vi' => 'vi',
832  'wol' => 'wol',
833  'xho' => 'xho',
834  'yor' => 'yor',
835  'zgh' => 'zgh',
836  'zh_CN' => 'zh-cn',
837  'zh_HK' => 'zh-hk',
838  'zh_SG' => 'zh-sg',
839  'zh_TW' => 'zh-tw',
840  'zul' => 'zul'
841  ];
842 
843  return Arr::get( $slug_to_locale_map, $locale, null );
844  }
845 }
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.
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)
{}
Definition: Arr.php:99
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.
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.
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.
is_transient_expired()
Determines if the cached TranslationsPress data needs refreshing.