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