WordPress 4.3
[autoinstalls/wordpress.git] / wp-includes / l10n.php
1 <?php
2 /**
3  * WordPress Translation API
4  *
5  * @package WordPress
6  * @subpackage i18n
7  */
8
9 /**
10  * Get the current locale.
11  *
12  * If the locale is set, then it will filter the locale in the 'locale' filter
13  * hook and return the value.
14  *
15  * If the locale is not set already, then the WPLANG constant is used if it is
16  * defined. Then it is filtered through the 'locale' filter hook and the value
17  * for the locale global set and the locale is returned.
18  *
19  * The process to get the locale should only be done once, but the locale will
20  * always be filtered using the 'locale' hook.
21  *
22  * @since 1.5.0
23  *
24  * @global string $locale
25  * @global string $wp_local_package
26  *
27  * @return string The locale of the blog or from the 'locale' hook.
28  */
29 function get_locale() {
30         global $locale, $wp_local_package;
31
32         if ( isset( $locale ) ) {
33                 /**
34                  * Filter WordPress install's locale ID.
35                  *
36                  * @since 1.5.0
37                  *
38                  * @param string $locale The locale ID.
39                  */
40                 return apply_filters( 'locale', $locale );
41         }
42
43         if ( isset( $wp_local_package ) ) {
44                 $locale = $wp_local_package;
45         }
46
47         // WPLANG was defined in wp-config.
48         if ( defined( 'WPLANG' ) ) {
49                 $locale = WPLANG;
50         }
51
52         // If multisite, check options.
53         if ( is_multisite() ) {
54                 // Don't check blog option when installing.
55                 if ( defined( 'WP_INSTALLING' ) || ( false === $ms_locale = get_option( 'WPLANG' ) ) ) {
56                         $ms_locale = get_site_option( 'WPLANG' );
57                 }
58
59                 if ( $ms_locale !== false ) {
60                         $locale = $ms_locale;
61                 }
62         } else {
63                 $db_locale = get_option( 'WPLANG' );
64                 if ( $db_locale !== false ) {
65                         $locale = $db_locale;
66                 }
67         }
68
69         if ( empty( $locale ) ) {
70                 $locale = 'en_US';
71         }
72
73         /** This filter is documented in wp-includes/l10n.php */
74         return apply_filters( 'locale', $locale );
75 }
76
77 /**
78  * Retrieve the translation of $text.
79  *
80  * If there is no translation, or the text domain isn't loaded, the original text is returned.
81  *
82  * *Note:* Don't use {@see translate()} directly, use `{@see __()} or related functions.
83  *
84  * @since 2.2.0
85  *
86  * @param string $text   Text to translate.
87  * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings.
88  * @return string Translated text
89  */
90 function translate( $text, $domain = 'default' ) {
91         $translations = get_translations_for_domain( $domain );
92         $translations = $translations->translate( $text );
93
94         /**
95          * Filter text with its translation.
96          *
97          * @since 2.0.11
98          *
99          * @param string $translations Translated text.
100          * @param string $text         Text to translate.
101          * @param string $domain       Text domain. Unique identifier for retrieving translated strings.
102          */
103         return apply_filters( 'gettext', $translations, $text, $domain );
104 }
105
106 /**
107  * Remove last item on a pipe-delimited string.
108  *
109  * Meant for removing the last item in a string, such as 'Role name|User role'. The original
110  * string will be returned if no pipe '|' characters are found in the string.
111  *
112  * @since 2.8.0
113  *
114  * @param string $string A pipe-delimited string.
115  * @return string Either $string or everything before the last pipe.
116  */
117 function before_last_bar( $string ) {
118         $last_bar = strrpos( $string, '|' );
119         if ( false === $last_bar )
120                 return $string;
121         else
122                 return substr( $string, 0, $last_bar );
123 }
124
125 /**
126  * Retrieve the translation of $text in the context defined in $context.
127  *
128  * If there is no translation, or the text domain isn't loaded the original
129  * text is returned.
130  *
131  * @since 2.8.0
132  *
133  * @param string $text    Text to translate.
134  * @param string $context Context information for the translators.
135  * @param string $domain  Optional. Text domain. Unique identifier for retrieving translated strings.
136  * @return string Translated text on success, original text on failure.
137  */
138 function translate_with_gettext_context( $text, $context, $domain = 'default' ) {
139         $translations = get_translations_for_domain( $domain );
140         $translations = $translations->translate( $text, $context );
141         /**
142          * Filter text with its translation based on context information.
143          *
144          * @since 2.8.0
145          *
146          * @param string $translations Translated text.
147          * @param string $text         Text to translate.
148          * @param string $context      Context information for the translators.
149          * @param string $domain       Text domain. Unique identifier for retrieving translated strings.
150          */
151         return apply_filters( 'gettext_with_context', $translations, $text, $context, $domain );
152 }
153
154 /**
155  * Retrieve the translation of $text. If there is no translation,
156  * or the text domain isn't loaded, the original text is returned.
157  *
158  * @since 2.1.0
159  *
160  * @param string $text   Text to translate.
161  * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings.
162  * @return string Translated text.
163  */
164 function __( $text, $domain = 'default' ) {
165         return translate( $text, $domain );
166 }
167
168 /**
169  * Retrieve the translation of $text and escapes it for safe use in an attribute.
170  *
171  * If there is no translation, or the text domain isn't loaded, the original text is returned.
172  *
173  * @since 2.8.0
174  *
175  * @param string $text   Text to translate.
176  * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings.
177  * @return string Translated text on success, original text on failure.
178  */
179 function esc_attr__( $text, $domain = 'default' ) {
180         return esc_attr( translate( $text, $domain ) );
181 }
182
183 /**
184  * Retrieve the translation of $text and escapes it for safe use in HTML output.
185  *
186  * If there is no translation, or the text domain isn't loaded, the original text is returned.
187  *
188  * @since 2.8.0
189  *
190  * @param string $text   Text to translate.
191  * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings.
192  * @return string Translated text
193  */
194 function esc_html__( $text, $domain = 'default' ) {
195         return esc_html( translate( $text, $domain ) );
196 }
197
198 /**
199  * Display translated text.
200  *
201  * @since 1.2.0
202  *
203  * @param string $text   Text to translate.
204  * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings.
205  */
206 function _e( $text, $domain = 'default' ) {
207         echo translate( $text, $domain );
208 }
209
210 /**
211  * Display translated text that has been escaped for safe use in an attribute.
212  *
213  * @since 2.8.0
214  *
215  * @param string $text   Text to translate.
216  * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings.
217  */
218 function esc_attr_e( $text, $domain = 'default' ) {
219         echo esc_attr( translate( $text, $domain ) );
220 }
221
222 /**
223  * Display translated text that has been escaped for safe use in HTML output.
224  *
225  * @since 2.8.0
226  *
227  * @param string $text   Text to translate.
228  * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings.
229  */
230 function esc_html_e( $text, $domain = 'default' ) {
231         echo esc_html( translate( $text, $domain ) );
232 }
233
234 /**
235  * Retrieve translated string with gettext context.
236  *
237  * Quite a few times, there will be collisions with similar translatable text
238  * found in more than two places, but with different translated context.
239  *
240  * By including the context in the pot file, translators can translate the two
241  * strings differently.
242  *
243  * @since 2.8.0
244  *
245  * @param string $text    Text to translate.
246  * @param string $context Context information for the translators.
247  * @param string $domain  Optional. Text domain. Unique identifier for retrieving translated strings.
248  * @return string Translated context string without pipe.
249  */
250 function _x( $text, $context, $domain = 'default' ) {
251         return translate_with_gettext_context( $text, $context, $domain );
252 }
253
254 /**
255  * Display translated string with gettext context.
256  *
257  * @since 3.0.0
258  *
259  * @param string $text    Text to translate.
260  * @param string $context Context information for the translators.
261  * @param string $domain  Optional. Text domain. Unique identifier for retrieving translated strings.
262  * @return string Translated context string without pipe.
263  */
264 function _ex( $text, $context, $domain = 'default' ) {
265         echo _x( $text, $context, $domain );
266 }
267
268 /**
269  * Translate string with gettext context, and escapes it for safe use in an attribute.
270  *
271  * @since 2.8.0
272  *
273  * @param string $text    Text to translate.
274  * @param string $context Context information for the translators.
275  * @param string $domain  Optional. Text domain. Unique identifier for retrieving translated strings.
276  * @return string Translated text
277  */
278 function esc_attr_x( $text, $context, $domain = 'default' ) {
279         return esc_attr( translate_with_gettext_context( $text, $context, $domain ) );
280 }
281
282 /**
283  * Translate string with gettext context, and escapes it for safe use in HTML output.
284  *
285  * @since 2.9.0
286  *
287  * @param string $text    Text to translate.
288  * @param string $context Context information for the translators.
289  * @param string $domain  Optional. Text domain. Unique identifier for retrieving translated strings.
290  * @return string Translated text.
291  */
292 function esc_html_x( $text, $context, $domain = 'default' ) {
293         return esc_html( translate_with_gettext_context( $text, $context, $domain ) );
294 }
295
296 /**
297  * Retrieve the plural or single form based on the supplied amount.
298  *
299  * If the text domain is not set in the $l10n list, then a comparison will be made
300  * and either $plural or $single parameters returned.
301  *
302  * If the text domain does exist, then the parameters $single, $plural, and $number
303  * will first be passed to the text domain's ngettext method. Then it will be passed
304  * to the 'ngettext' filter hook along with the same parameters. The expected
305  * type will be a string.
306  *
307  * @since 2.8.0
308  *
309  * @param string $single The text that will be used if $number is 1.
310  * @param string $plural The text that will be used if $number is not 1.
311  * @param int    $number The number to compare against to use either $single or $plural.
312  * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings.
313  * @return string Either $single or $plural translated text.
314  */
315 function _n( $single, $plural, $number, $domain = 'default' ) {
316         $translations = get_translations_for_domain( $domain );
317         $translation = $translations->translate_plural( $single, $plural, $number );
318         /**
319          * Filter text with its translation when plural option is available.
320          *
321          * @since 2.2.0
322          *
323          * @param string $translation Translated text.
324          * @param string $single      The text that will be used if $number is 1.
325          * @param string $plural      The text that will be used if $number is not 1.
326          * @param string $number      The number to compare against to use either $single or $plural.
327          * @param string $domain      Text domain. Unique identifier for retrieving translated strings.
328          */
329         return apply_filters( 'ngettext', $translation, $single, $plural, $number, $domain );
330 }
331
332 /**
333  * Retrieve the plural or single form based on the supplied amount with gettext context.
334  *
335  * This is a hybrid of _n() and _x(). It supports contexts and plurals.
336  *
337  * @since 2.8.0
338  *
339  * @param string $single  The text that will be used if $number is 1.
340  * @param string $plural  The text that will be used if $number is not 1.
341  * @param int    $number  The number to compare against to use either $single or $plural.
342  * @param string $context Context information for the translators.
343  * @param string $domain  Optional. Text domain. Unique identifier for retrieving translated strings.
344  * @return string Either $single or $plural translated text with context.
345  */
346 function _nx($single, $plural, $number, $context, $domain = 'default') {
347         $translations = get_translations_for_domain( $domain );
348         $translation = $translations->translate_plural( $single, $plural, $number, $context );
349         /**
350          * Filter text with its translation while plural option and context are available.
351          *
352          * @since 2.8.0
353          *
354          * @param string $translation Translated text.
355          * @param string $single      The text that will be used if $number is 1.
356          * @param string $plural      The text that will be used if $number is not 1.
357          * @param string $number      The number to compare against to use either $single or $plural.
358          * @param string $context     Context information for the translators.
359          * @param string $domain      Text domain. Unique identifier for retrieving translated strings.
360          */
361         return apply_filters( 'ngettext_with_context', $translation, $single, $plural, $number, $context, $domain );
362 }
363
364 /**
365  * Register plural strings in POT file, but don't translate them.
366  *
367  * Used when you want to keep structures with translatable plural
368  * strings and use them later.
369  *
370  * Example:
371  *
372  *     $messages = array(
373  *              'post' => _n_noop( '%s post', '%s posts' ),
374  *              'page' => _n_noop( '%s pages', '%s pages' ),
375  *     );
376  *     ...
377  *     $message = $messages[ $type ];
378  *     $usable_text = sprintf( translate_nooped_plural( $message, $count ), $count );
379  *
380  * @since 2.5.0
381  *
382  * @param string $singular Single form to be i18ned.
383  * @param string $plural   Plural form to be i18ned.
384  * @param string $domain   Optional. Text domain. Unique identifier for retrieving translated strings.
385  * @return array array($singular, $plural)
386  */
387 function _n_noop( $singular, $plural, $domain = null ) {
388         return array( 0 => $singular, 1 => $plural, 'singular' => $singular, 'plural' => $plural, 'context' => null, 'domain' => $domain );
389 }
390
391 /**
392  * Register plural strings with context in POT file, but don't translate them.
393  *
394  * @since 2.8.0
395  * @param string $singular
396  * @param string $plural
397  * @param string $context
398  * @param string|null $domain
399  * @return array
400  */
401 function _nx_noop( $singular, $plural, $context, $domain = null ) {
402         return array( 0 => $singular, 1 => $plural, 2 => $context, 'singular' => $singular, 'plural' => $plural, 'context' => $context, 'domain' => $domain );
403 }
404
405 /**
406  * Translate the result of _n_noop() or _nx_noop().
407  *
408  * @since 3.1.0
409  *
410  * @param array  $nooped_plural Array with singular, plural and context keys, usually the result of _n_noop() or _nx_noop()
411  * @param int    $count         Number of objects
412  * @param string $domain        Optional. Text domain. Unique identifier for retrieving translated strings. If $nooped_plural contains
413  *                              a text domain passed to _n_noop() or _nx_noop(), it will override this value.
414  * @return string Either $single or $plural translated text.
415  */
416 function translate_nooped_plural( $nooped_plural, $count, $domain = 'default' ) {
417         if ( $nooped_plural['domain'] )
418                 $domain = $nooped_plural['domain'];
419
420         if ( $nooped_plural['context'] )
421                 return _nx( $nooped_plural['singular'], $nooped_plural['plural'], $count, $nooped_plural['context'], $domain );
422         else
423                 return _n( $nooped_plural['singular'], $nooped_plural['plural'], $count, $domain );
424 }
425
426 /**
427  * Load a .mo file into the text domain $domain.
428  *
429  * If the text domain already exists, the translations will be merged. If both
430  * sets have the same string, the translation from the original value will be taken.
431  *
432  * On success, the .mo file will be placed in the $l10n global by $domain
433  * and will be a MO object.
434  *
435  * @since 1.5.0
436  *
437  * @global array $l10n
438  *
439  * @param string $domain Text domain. Unique identifier for retrieving translated strings.
440  * @param string $mofile Path to the .mo file.
441  * @return bool True on success, false on failure.
442  */
443 function load_textdomain( $domain, $mofile ) {
444         global $l10n;
445
446         /**
447          * Filter text domain and/or MO file path for loading translations.
448          *
449          * @since 2.9.0
450          *
451          * @param bool   $override Whether to override the text domain. Default false.
452          * @param string $domain   Text domain. Unique identifier for retrieving translated strings.
453          * @param string $mofile   Path to the MO file.
454          */
455         $plugin_override = apply_filters( 'override_load_textdomain', false, $domain, $mofile );
456
457         if ( true == $plugin_override ) {
458                 return true;
459         }
460
461         /**
462          * Fires before the MO translation file is loaded.
463          *
464          * @since 2.9.0
465          *
466          * @param string $domain Text domain. Unique identifier for retrieving translated strings.
467          * @param string $mofile Path to the .mo file.
468          */
469         do_action( 'load_textdomain', $domain, $mofile );
470
471         /**
472          * Filter MO file path for loading translations for a specific text domain.
473          *
474          * @since 2.9.0
475          *
476          * @param string $mofile Path to the MO file.
477          * @param string $domain Text domain. Unique identifier for retrieving translated strings.
478          */
479         $mofile = apply_filters( 'load_textdomain_mofile', $mofile, $domain );
480
481         if ( !is_readable( $mofile ) ) return false;
482
483         $mo = new MO();
484         if ( !$mo->import_from_file( $mofile ) ) return false;
485
486         if ( isset( $l10n[$domain] ) )
487                 $mo->merge_with( $l10n[$domain] );
488
489         $l10n[$domain] = &$mo;
490
491         return true;
492 }
493
494 /**
495  * Unload translations for a text domain.
496  *
497  * @since 3.0.0
498  *
499  * @global array $l10n
500  *
501  * @param string $domain Text domain. Unique identifier for retrieving translated strings.
502  * @return bool Whether textdomain was unloaded.
503  */
504 function unload_textdomain( $domain ) {
505         global $l10n;
506
507         /**
508          * Filter the text domain for loading translation.
509          *
510          * @since 3.0.0
511          *
512          * @param bool   $override Whether to override unloading the text domain. Default false.
513          * @param string $domain   Text domain. Unique identifier for retrieving translated strings.
514          */
515         $plugin_override = apply_filters( 'override_unload_textdomain', false, $domain );
516
517         if ( $plugin_override )
518                 return true;
519
520         /**
521          * Fires before the text domain is unloaded.
522          *
523          * @since 3.0.0
524          *
525          * @param string $domain Text domain. Unique identifier for retrieving translated strings.
526          */
527         do_action( 'unload_textdomain', $domain );
528
529         if ( isset( $l10n[$domain] ) ) {
530                 unset( $l10n[$domain] );
531                 return true;
532         }
533
534         return false;
535 }
536
537 /**
538  * Load default translated strings based on locale.
539  *
540  * Loads the .mo file in WP_LANG_DIR constant path from WordPress root.
541  * The translated (.mo) file is named based on the locale.
542  *
543  * @see load_textdomain()
544  *
545  * @since 1.5.0
546  *
547  * @param string $locale Optional. Locale to load. Default is the value of {@see get_locale()}.
548  * @return bool Whether the textdomain was loaded.
549  */
550 function load_default_textdomain( $locale = null ) {
551         if ( null === $locale ) {
552                 $locale = get_locale();
553         }
554
555         // Unload previously loaded strings so we can switch translations.
556         unload_textdomain( 'default' );
557
558         $return = load_textdomain( 'default', WP_LANG_DIR . "/$locale.mo" );
559
560         if ( ( is_multisite() || ( defined( 'WP_INSTALLING_NETWORK' ) && WP_INSTALLING_NETWORK ) ) && ! file_exists(  WP_LANG_DIR . "/admin-$locale.mo" ) ) {
561                 load_textdomain( 'default', WP_LANG_DIR . "/ms-$locale.mo" );
562                 return $return;
563         }
564
565         if ( is_admin() || defined( 'WP_INSTALLING' ) || ( defined( 'WP_REPAIRING' ) && WP_REPAIRING ) ) {
566                 load_textdomain( 'default', WP_LANG_DIR . "/admin-$locale.mo" );
567         }
568
569         if ( is_network_admin() || ( defined( 'WP_INSTALLING_NETWORK' ) && WP_INSTALLING_NETWORK ) )
570                 load_textdomain( 'default', WP_LANG_DIR . "/admin-network-$locale.mo" );
571
572         return $return;
573 }
574
575 /**
576  * Load a plugin's translated strings.
577  *
578  * If the path is not given then it will be the root of the plugin directory.
579  *
580  * The .mo file should be named based on the text domain with a dash, and then the locale exactly.
581  *
582  * @since 1.5.0
583  *
584  * @param string $domain          Unique identifier for retrieving translated strings
585  * @param string $deprecated      Use the $plugin_rel_path parameter instead.
586  * @param string $plugin_rel_path Optional. Relative path to WP_PLUGIN_DIR where the .mo file resides.
587  *                                Default false.
588  * @return bool True when textdomain is successfully loaded, false otherwise.
589  */
590 function load_plugin_textdomain( $domain, $deprecated = false, $plugin_rel_path = false ) {
591         $locale = get_locale();
592         /**
593          * Filter a plugin's locale.
594          *
595          * @since 3.0.0
596          *
597          * @param string $locale The plugin's current locale.
598          * @param string $domain Text domain. Unique identifier for retrieving translated strings.
599          */
600         $locale = apply_filters( 'plugin_locale', $locale, $domain );
601
602         if ( false !== $plugin_rel_path ) {
603                 $path = WP_PLUGIN_DIR . '/' . trim( $plugin_rel_path, '/' );
604         } elseif ( false !== $deprecated ) {
605                 _deprecated_argument( __FUNCTION__, '2.7' );
606                 $path = ABSPATH . trim( $deprecated, '/' );
607         } else {
608                 $path = WP_PLUGIN_DIR;
609         }
610
611         // Load the textdomain according to the plugin first
612         $mofile = $domain . '-' . $locale . '.mo';
613         if ( $loaded = load_textdomain( $domain, $path . '/'. $mofile ) )
614                 return $loaded;
615
616         // Otherwise, load from the languages directory
617         $mofile = WP_LANG_DIR . '/plugins/' . $mofile;
618         return load_textdomain( $domain, $mofile );
619 }
620
621 /**
622  * Load the translated strings for a plugin residing in the mu-plugins directory.
623  *
624  * @since 3.0.0
625  *
626  * @param string $domain             Text domain. Unique identifier for retrieving translated strings.
627  * @param string $mu_plugin_rel_path Relative to WPMU_PLUGIN_DIR directory in which the .mo file resides.
628  *                                   Default empty string.
629  * @return bool True when textdomain is successfully loaded, false otherwise.
630  */
631 function load_muplugin_textdomain( $domain, $mu_plugin_rel_path = '' ) {
632         /** This filter is documented in wp-includes/l10n.php */
633         $locale = apply_filters( 'plugin_locale', get_locale(), $domain );
634         $path = trailingslashit( WPMU_PLUGIN_DIR . '/' . ltrim( $mu_plugin_rel_path, '/' ) );
635
636         // Load the textdomain according to the plugin first
637         $mofile = $domain . '-' . $locale . '.mo';
638         if ( $loaded = load_textdomain( $domain, $path . $mofile ) )
639                 return $loaded;
640
641         // Otherwise, load from the languages directory
642         $mofile = WP_LANG_DIR . '/plugins/' . $mofile;
643         return load_textdomain( $domain, $mofile );
644 }
645
646 /**
647  * Load the theme's translated strings.
648  *
649  * If the current locale exists as a .mo file in the theme's root directory, it
650  * will be included in the translated strings by the $domain.
651  *
652  * The .mo files must be named based on the locale exactly.
653  *
654  * @since 1.5.0
655  *
656  * @param string $domain Text domain. Unique identifier for retrieving translated strings.
657  * @param string $path   Optional. Path to the directory containing the .mo file.
658  *                       Default false.
659  * @return bool True when textdomain is successfully loaded, false otherwise.
660  */
661 function load_theme_textdomain( $domain, $path = false ) {
662         $locale = get_locale();
663         /**
664          * Filter a theme's locale.
665          *
666          * @since 3.0.0
667          *
668          * @param string $locale The theme's current locale.
669          * @param string $domain Text domain. Unique identifier for retrieving translated strings.
670          */
671         $locale = apply_filters( 'theme_locale', $locale, $domain );
672
673         if ( ! $path )
674                 $path = get_template_directory();
675
676         // Load the textdomain according to the theme
677         $mofile = untrailingslashit( $path ) . "/{$locale}.mo";
678         if ( $loaded = load_textdomain( $domain, $mofile ) )
679                 return $loaded;
680
681         // Otherwise, load from the languages directory
682         $mofile = WP_LANG_DIR . "/themes/{$domain}-{$locale}.mo";
683         return load_textdomain( $domain, $mofile );
684 }
685
686 /**
687  * Load the child themes translated strings.
688  *
689  * If the current locale exists as a .mo file in the child themes
690  * root directory, it will be included in the translated strings by the $domain.
691  *
692  * The .mo files must be named based on the locale exactly.
693  *
694  * @since 2.9.0
695  *
696  * @param string $domain Text domain. Unique identifier for retrieving translated strings.
697  * @param string $path   Optional. Path to the directory containing the .mo file.
698  *                       Default false.
699  * @return bool True when the theme textdomain is successfully loaded, false otherwise.
700  */
701 function load_child_theme_textdomain( $domain, $path = false ) {
702         if ( ! $path )
703                 $path = get_stylesheet_directory();
704         return load_theme_textdomain( $domain, $path );
705 }
706
707 /**
708  * Return the Translations instance for a text domain.
709  *
710  * If there isn't one, returns empty Translations instance.
711  *
712  * @since 2.8.0
713  *
714  * @global array $l10n
715  *
716  * @param string $domain Text domain. Unique identifier for retrieving translated strings.
717  * @return NOOP_Translations A Translations instance.
718  */
719 function get_translations_for_domain( $domain ) {
720         global $l10n;
721         if ( !isset( $l10n[$domain] ) ) {
722                 $l10n[$domain] = new NOOP_Translations;
723         }
724         return $l10n[$domain];
725 }
726
727 /**
728  * Whether there are translations for the text domain.
729  *
730  * @since 3.0.0
731  *
732  * @global array $l10n
733  *
734  * @param string $domain Text domain. Unique identifier for retrieving translated strings.
735  * @return bool Whether there are translations.
736  */
737 function is_textdomain_loaded( $domain ) {
738         global $l10n;
739         return isset( $l10n[$domain] );
740 }
741
742 /**
743  * Translates role name.
744  *
745  * Since the role names are in the database and not in the source there
746  * are dummy gettext calls to get them into the POT file and this function
747  * properly translates them back.
748  *
749  * The before_last_bar() call is needed, because older installs keep the roles
750  * using the old context format: 'Role name|User role' and just skipping the
751  * content after the last bar is easier than fixing them in the DB. New installs
752  * won't suffer from that problem.
753  *
754  * @since 2.8.0
755  *
756  * @param string $name The role name.
757  * @return string Translated role name on success, original name on failure.
758  */
759 function translate_user_role( $name ) {
760         return translate_with_gettext_context( before_last_bar($name), 'User role' );
761 }
762
763 /**
764  * Get all available languages based on the presence of *.mo files in a given directory.
765  *
766  * The default directory is WP_LANG_DIR.
767  *
768  * @since 3.0.0
769  *
770  * @param string $dir A directory to search for language files.
771  *                    Default WP_LANG_DIR.
772  * @return array An array of language codes or an empty array if no languages are present. Language codes are formed by stripping the .mo extension from the language file names.
773  */
774 function get_available_languages( $dir = null ) {
775         $languages = array();
776
777         $lang_files = glob( ( is_null( $dir) ? WP_LANG_DIR : $dir ) . '/*.mo' );
778         if ( $lang_files ) {
779                 foreach( $lang_files as $lang_file ) {
780                         $lang_file = basename( $lang_file, '.mo' );
781                         if ( 0 !== strpos( $lang_file, 'continents-cities' ) && 0 !== strpos( $lang_file, 'ms-' ) &&
782                                 0 !== strpos( $lang_file, 'admin-' ) ) {
783                                 $languages[] = $lang_file;
784                         }
785                 }
786         }
787
788         return $languages;
789 }
790
791 /**
792  * Get installed translations.
793  *
794  * Looks in the wp-content/languages directory for translations of
795  * plugins or themes.
796  *
797  * @since 3.7.0
798  *
799  * @param string $type What to search for. Accepts 'plugins', 'themes', 'core'.
800  * @return array Array of language data.
801  */
802 function wp_get_installed_translations( $type ) {
803         if ( $type !== 'themes' && $type !== 'plugins' && $type !== 'core' )
804                 return array();
805
806         $dir = 'core' === $type ? '' : "/$type";
807
808         if ( ! is_dir( WP_LANG_DIR ) )
809                 return array();
810
811         if ( $dir && ! is_dir( WP_LANG_DIR . $dir ) )
812                 return array();
813
814         $files = scandir( WP_LANG_DIR . $dir );
815         if ( ! $files )
816                 return array();
817
818         $language_data = array();
819
820         foreach ( $files as $file ) {
821                 if ( '.' === $file[0] || is_dir( $file ) ) {
822                         continue;
823                 }
824                 if ( substr( $file, -3 ) !== '.po' ) {
825                         continue;
826                 }
827                 if ( ! preg_match( '/(?:(.+)-)?([a-z]{2,3}(?:_[A-Z]{2})?(?:_[a-z0-9]+)?).po/', $file, $match ) ) {
828                         continue;
829                 }
830                 if ( ! in_array( substr( $file, 0, -3 ) . '.mo', $files ) )  {
831                         continue;
832                 }
833
834                 list( , $textdomain, $language ) = $match;
835                 if ( '' === $textdomain ) {
836                         $textdomain = 'default';
837                 }
838                 $language_data[ $textdomain ][ $language ] = wp_get_pomo_file_data( WP_LANG_DIR . "$dir/$file" );
839         }
840         return $language_data;
841 }
842
843 /**
844  * Extract headers from a PO file.
845  *
846  * @since 3.7.0
847  *
848  * @param string $po_file Path to PO file.
849  * @return array PO file headers.
850  */
851 function wp_get_pomo_file_data( $po_file ) {
852         $headers = get_file_data( $po_file, array(
853                 'POT-Creation-Date'  => '"POT-Creation-Date',
854                 'PO-Revision-Date'   => '"PO-Revision-Date',
855                 'Project-Id-Version' => '"Project-Id-Version',
856                 'X-Generator'        => '"X-Generator',
857         ) );
858         foreach ( $headers as $header => $value ) {
859                 // Remove possible contextual '\n' and closing double quote.
860                 $headers[ $header ] = preg_replace( '~(\\\n)?"$~', '', $value );
861         }
862         return $headers;
863 }
864
865 /**
866  * Language selector.
867  *
868  * @since 4.0.0
869  * @since 4.3.0 Introduced the `echo` argument.
870  *
871  * @see get_available_languages()
872  * @see wp_get_available_translations()
873  *
874  * @param string|array $args {
875  *     Optional. Array or string of arguments for outputting the language selector.
876  *
877  *     @type string   $id                           ID attribute of the select element. Default empty.
878  *     @type string   $name                         Name attribute of the select element. Default empty.
879  *     @type array    $languages                    List of installed languages, contain only the locales.
880  *                                                  Default empty array.
881  *     @type array    $translations                 List of available translations. Default result of
882  *                                                  wp_get_available_translations().
883  *     @type string   $selected                     Language which should be selected. Default empty.
884  *     @type bool|int $echo                         Whether to echo or return the generated markup. Accepts 0, 1, or their
885  *                                                  bool equivalents. Default 1.
886  *     @type bool     $show_available_translations  Whether to show available translations. Default true.
887  * }
888  * @return string HTML content only if 'echo' argument is 0.
889  */
890 function wp_dropdown_languages( $args = array() ) {
891
892         $args = wp_parse_args( $args, array(
893                 'id'           => '',
894                 'name'         => '',
895                 'languages'    => array(),
896                 'translations' => array(),
897                 'selected'     => '',
898                 'echo'         => 1,
899                 'show_available_translations' => true,
900         ) );
901
902         $translations = $args['translations'];
903         if ( empty( $translations ) ) {
904                 require_once( ABSPATH . 'wp-admin/includes/translation-install.php' );
905                 $translations = wp_get_available_translations();
906         }
907
908         /*
909          * $args['languages'] should only contain the locales. Find the locale in
910          * $translations to get the native name. Fall back to locale.
911          */
912         $languages = array();
913         foreach ( $args['languages'] as $locale ) {
914                 if ( isset( $translations[ $locale ] ) ) {
915                         $translation = $translations[ $locale ];
916                         $languages[] = array(
917                                 'language'    => $translation['language'],
918                                 'native_name' => $translation['native_name'],
919                                 'lang'        => current( $translation['iso'] ),
920                         );
921
922                         // Remove installed language from available translations.
923                         unset( $translations[ $locale ] );
924                 } else {
925                         $languages[] = array(
926                                 'language'    => $locale,
927                                 'native_name' => $locale,
928                                 'lang'        => '',
929                         );
930                 }
931         }
932
933         $translations_available = ( ! empty( $translations ) && $args['show_available_translations'] );
934
935         $output = sprintf( '<select name="%s" id="%s">', esc_attr( $args['name'] ), esc_attr( $args['id'] ) );
936
937         // Holds the HTML markup.
938         $structure = array();
939
940         // List installed languages.
941         if ( $translations_available ) {
942                 $structure[] = '<optgroup label="' . esc_attr_x( 'Installed', 'translations' ) . '">';
943         }
944         $structure[] = '<option value="" lang="en" data-installed="1">English (United States)</option>';
945         foreach ( $languages as $language ) {
946                 $structure[] = sprintf(
947                         '<option value="%s" lang="%s"%s data-installed="1">%s</option>',
948                         esc_attr( $language['language'] ),
949                         esc_attr( $language['lang'] ),
950                         selected( $language['language'], $args['selected'], false ),
951                         esc_html( $language['native_name'] )
952                 );
953         }
954         if ( $translations_available ) {
955                 $structure[] = '</optgroup>';
956         }
957
958         // List available translations.
959         if ( $translations_available ) {
960                 $structure[] = '<optgroup label="' . esc_attr_x( 'Available', 'translations' ) . '">';
961                 foreach ( $translations as $translation ) {
962                         $structure[] = sprintf(
963                                 '<option value="%s" lang="%s"%s>%s</option>',
964                                 esc_attr( $translation['language'] ),
965                                 esc_attr( current( $translation['iso'] ) ),
966                                 selected( $translation['language'], $args['selected'], false ),
967                                 esc_html( $translation['native_name'] )
968                         );
969                 }
970                 $structure[] = '</optgroup>';
971         }
972
973         $output .= join( "\n", $structure );
974
975         $output .= '</select>';
976
977         if ( $args['echo'] ) {
978                 echo $output;
979         }
980
981         return $output;
982 }