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