Make WordPress Core

Ticket #45973: l10n.php

File l10n.php, 46.9 KB (added by jonasemde, 6 years ago)
Line 
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 */
30function get_locale() {
31        global $locale, $wp_local_package;
32
33        if ( isset( $locale ) ) {
34                /**
35                 * Filters the locale ID of the WordPress installation.
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 * Retrieves the locale of a user.
80 *
81 * If the user has a locale set to a non-empty string then it will be
82 * returned. Otherwise it returns the locale of get_locale().
83 *
84 * @since 4.7.0
85 *
86 * @param int|WP_User $user_id User's ID or a WP_User object. Defaults to current user.
87 * @return string The locale of the user.
88 */
89function get_user_locale( $user_id = 0 ) {
90        $user = false;
91        if ( 0 === $user_id && function_exists( 'wp_get_current_user' ) ) {
92                $user = wp_get_current_user();
93        } elseif ( $user_id instanceof WP_User ) {
94                $user = $user_id;
95        } elseif ( $user_id && is_numeric( $user_id ) ) {
96                $user = get_user_by( 'id', $user_id );
97        }
98
99        if ( ! $user ) {
100                return get_locale();
101        }
102
103        $locale = $user->locale;
104        return $locale ? $locale : get_locale();
105}
106
107/**
108 * Determine the current locale desired for the request.
109 *
110 * @since 5.0.0
111 *
112 * @global string $pagenow
113 *
114 * @return string The determined locale.
115 */
116function determine_locale() {
117        /**
118         * Filters the locale for the current request prior to the default determination process.
119         *
120         * Using this filter allows to override the default logic, effectively short-circuiting the function.
121         *
122         * @since 5.0.0
123         *
124         * @param string|null The locale to return and short-circuit, or null as default.
125         */
126        $determined_locale = apply_filters( 'pre_determine_locale', null );
127        if ( ! empty( $determined_locale ) && is_string( $determined_locale ) ) {
128                return $determined_locale;
129        }
130
131        $determined_locale = get_locale();
132
133        if ( is_admin() ) {
134                $determined_locale = get_user_locale();
135        }
136
137        if ( isset( $_GET['_locale'] ) && 'user' === $_GET['_locale'] && wp_is_json_request() ) {
138                $determined_locale = get_user_locale();
139        }
140
141        if ( ! empty( $_GET['wp_lang'] ) && 'wp-login.php' === $GLOBALS['pagenow'] ) {
142                $determined_locale = sanitize_text_field( $_GET['wp_lang'] );
143        }
144
145        /**
146         * Filters the locale for the current request.
147         *
148         * @since 5.0.0
149         *
150         * @param string $locale The locale.
151         */
152        return apply_filters( 'determine_locale', $determined_locale );
153}
154
155/**
156 * Retrieve the translation of $text.
157 *
158 * If there is no translation, or the text domain isn't loaded, the original text is returned.
159 *
160 * *Note:* Don't use translate() directly, use __() or related functions.
161 *
162 * @since 2.2.0
163 *
164 * @param string $text   Text to translate.
165 * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings.
166 *                       Default 'default'.
167 * @return string Translated text
168 */
169if (! function_exists('translate')) {
170    function translate( $text, $domain = 'default' ) {
171        $translations = get_translations_for_domain( $domain );
172        $translation  = $translations->translate( $text );
173   
174        /**
175         * Filters text with its translation.
176         *
177         * @since 2.0.11
178         *
179         * @param string $translation  Translated text.
180         * @param string $text         Text to translate.
181         * @param string $domain       Text domain. Unique identifier for retrieving translated strings.
182         */
183        return apply_filters( 'gettext', $translation, $text, $domain );
184    }
185}
186
187/**
188 * Remove last item on a pipe-delimited string.
189 *
190 * Meant for removing the last item in a string, such as 'Role name|User role'. The original
191 * string will be returned if no pipe '|' characters are found in the string.
192 *
193 * @since 2.8.0
194 *
195 * @param string $string A pipe-delimited string.
196 * @return string Either $string or everything before the last pipe.
197 */
198function before_last_bar( $string ) {
199        $last_bar = strrpos( $string, '|' );
200        if ( false === $last_bar ) {
201                return $string;
202        } else {
203                return substr( $string, 0, $last_bar );
204        }
205}
206
207/**
208 * Retrieve the translation of $text in the context defined in $context.
209 *
210 * If there is no translation, or the text domain isn't loaded the original
211 * text is returned.
212 *
213 * *Note:* Don't use translate_with_gettext_context() directly, use _x() or related functions.
214 *
215 * @since 2.8.0
216 *
217 * @param string $text    Text to translate.
218 * @param string $context Context information for the translators.
219 * @param string $domain  Optional. Text domain. Unique identifier for retrieving translated strings.
220 *                        Default 'default'.
221 * @return string Translated text on success, original text on failure.
222 */
223function translate_with_gettext_context( $text, $context, $domain = 'default' ) {
224        $translations = get_translations_for_domain( $domain );
225        $translation  = $translations->translate( $text, $context );
226        /**
227         * Filters text with its translation based on context information.
228         *
229         * @since 2.8.0
230         *
231         * @param string $translation  Translated text.
232         * @param string $text         Text to translate.
233         * @param string $context      Context information for the translators.
234         * @param string $domain       Text domain. Unique identifier for retrieving translated strings.
235         */
236        return apply_filters( 'gettext_with_context', $translation, $text, $context, $domain );
237}
238
239/**
240 * Retrieve the translation of $text.
241 *
242 * If there is no translation, or the text domain isn't loaded, the original text is returned.
243 *
244 * @since 2.1.0
245 *
246 * @param string $text   Text to translate.
247 * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings.
248 *                       Default 'default'.
249 * @return string Translated text.
250 */
251if (! function_exists('__')) {
252    function __( $text, $domain = 'default' ) {
253        return translate( $text, $domain );
254    }
255}
256
257/**
258 * Retrieve the translation of $text and escapes it for safe use in an attribute.
259 *
260 * If there is no translation, or the text domain isn't loaded, the original text is returned.
261 *
262 * @since 2.8.0
263 *
264 * @param string $text   Text to translate.
265 * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings.
266 *                       Default 'default'.
267 * @return string Translated text on success, original text on failure.
268 */
269function esc_attr__( $text, $domain = 'default' ) {
270        return esc_attr( translate( $text, $domain ) );
271}
272
273/**
274 * Retrieve the translation of $text and escapes it for safe use in HTML output.
275 *
276 * If there is no translation, or the text domain isn't loaded, the original text
277 * is escaped and returned..
278 *
279 * @since 2.8.0
280 *
281 * @param string $text   Text to translate.
282 * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings.
283 *                       Default 'default'.
284 * @return string Translated text
285 */
286function esc_html__( $text, $domain = 'default' ) {
287        return esc_html( translate( $text, $domain ) );
288}
289
290/**
291 * Display translated text.
292 *
293 * @since 1.2.0
294 *
295 * @param string $text   Text to translate.
296 * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings.
297 *                       Default 'default'.
298 */
299function _e( $text, $domain = 'default' ) {
300        echo translate( $text, $domain );
301}
302
303/**
304 * Display translated text that has been escaped for safe use in an attribute.
305 *
306 * @since 2.8.0
307 *
308 * @param string $text   Text to translate.
309 * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings.
310 *                       Default 'default'.
311 */
312function esc_attr_e( $text, $domain = 'default' ) {
313        echo esc_attr( translate( $text, $domain ) );
314}
315
316/**
317 * Display translated text that has been escaped for safe use in HTML output.
318 *
319 * @since 2.8.0
320 *
321 * @param string $text   Text to translate.
322 * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings.
323 *                       Default 'default'.
324 */
325function esc_html_e( $text, $domain = 'default' ) {
326        echo esc_html( translate( $text, $domain ) );
327}
328
329/**
330 * Retrieve translated string with gettext context.
331 *
332 * Quite a few times, there will be collisions with similar translatable text
333 * found in more than two places, but with different translated context.
334 *
335 * By including the context in the pot file, translators can translate the two
336 * strings differently.
337 *
338 * @since 2.8.0
339 *
340 * @param string $text    Text to translate.
341 * @param string $context Context information for the translators.
342 * @param string $domain  Optional. Text domain. Unique identifier for retrieving translated strings.
343 *                        Default 'default'.
344 * @return string Translated context string without pipe.
345 */
346function _x( $text, $context, $domain = 'default' ) {
347        return translate_with_gettext_context( $text, $context, $domain );
348}
349
350/**
351 * Display translated string with gettext context.
352 *
353 * @since 3.0.0
354 *
355 * @param string $text    Text to translate.
356 * @param string $context Context information for the translators.
357 * @param string $domain  Optional. Text domain. Unique identifier for retrieving translated strings.
358 *                        Default 'default'.
359 * @return string Translated context string without pipe.
360 */
361function _ex( $text, $context, $domain = 'default' ) {
362        echo _x( $text, $context, $domain );
363}
364
365/**
366 * Translate string with gettext context, and escapes it for safe use in an attribute.
367 *
368 * @since 2.8.0
369 *
370 * @param string $text    Text to translate.
371 * @param string $context Context information for the translators.
372 * @param string $domain  Optional. Text domain. Unique identifier for retrieving translated strings.
373 *                        Default 'default'.
374 * @return string Translated text
375 */
376function esc_attr_x( $text, $context, $domain = 'default' ) {
377        return esc_attr( translate_with_gettext_context( $text, $context, $domain ) );
378}
379
380/**
381 * Translate string with gettext context, and escapes it for safe use in HTML output.
382 *
383 * @since 2.9.0
384 *
385 * @param string $text    Text to translate.
386 * @param string $context Context information for the translators.
387 * @param string $domain  Optional. Text domain. Unique identifier for retrieving translated strings.
388 *                        Default 'default'.
389 * @return string Translated text.
390 */
391function esc_html_x( $text, $context, $domain = 'default' ) {
392        return esc_html( translate_with_gettext_context( $text, $context, $domain ) );
393}
394
395/**
396 * Translates and retrieves the singular or plural form based on the supplied number.
397 *
398 * Used when you want to use the appropriate form of a string based on whether a
399 * number is singular or plural.
400 *
401 * Example:
402 *
403 *     printf( _n( '%s person', '%s people', $count, 'text-domain' ), number_format_i18n( $count ) );
404 *
405 * @since 2.8.0
406 *
407 * @param string $single The text to be used if the number is singular.
408 * @param string $plural The text to be used if the number is plural.
409 * @param int    $number The number to compare against to use either the singular or plural form.
410 * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings.
411 *                       Default 'default'.
412 * @return string The translated singular or plural form.
413 */
414function _n( $single, $plural, $number, $domain = 'default' ) {
415        $translations = get_translations_for_domain( $domain );
416        $translation  = $translations->translate_plural( $single, $plural, $number );
417
418        /**
419         * Filters the singular or plural form of a string.
420         *
421         * @since 2.2.0
422         *
423         * @param string $translation Translated text.
424         * @param string $single      The text to be used if the number is singular.
425         * @param string $plural      The text to be used if the number is plural.
426         * @param string $number      The number to compare against to use either the singular or plural form.
427         * @param string $domain      Text domain. Unique identifier for retrieving translated strings.
428         */
429        return apply_filters( 'ngettext', $translation, $single, $plural, $number, $domain );
430}
431
432/**
433 * Translates and retrieves the singular or plural form based on the supplied number, with gettext context.
434 *
435 * This is a hybrid of _n() and _x(). It supports context and plurals.
436 *
437 * Used when you want to use the appropriate form of a string with context based on whether a
438 * number is singular or plural.
439 *
440 * Example of a generic phrase which is disambiguated via the context parameter:
441 *
442 *     printf( _nx( '%s group', '%s groups', $people, 'group of people', 'text-domain' ), number_format_i18n( $people ) );
443 *     printf( _nx( '%s group', '%s groups', $animals, 'group of animals', 'text-domain' ), number_format_i18n( $animals ) );
444 *
445 * @since 2.8.0
446 *
447 * @param string $single  The text to be used if the number is singular.
448 * @param string $plural  The text to be used if the number is plural.
449 * @param int    $number  The number to compare against to use either the singular or plural form.
450 * @param string $context Context information for the translators.
451 * @param string $domain  Optional. Text domain. Unique identifier for retrieving translated strings.
452 *                        Default 'default'.
453 * @return string The translated singular or plural form.
454 */
455function _nx($single, $plural, $number, $context, $domain = 'default') {
456        $translations = get_translations_for_domain( $domain );
457        $translation  = $translations->translate_plural( $single, $plural, $number, $context );
458
459        /**
460         * Filters the singular or plural form of a string with gettext context.
461         *
462         * @since 2.8.0
463         *
464         * @param string $translation Translated text.
465         * @param string $single      The text to be used if the number is singular.
466         * @param string $plural      The text to be used if the number is plural.
467         * @param string $number      The number to compare against to use either the singular or plural form.
468         * @param string $context     Context information for the translators.
469         * @param string $domain      Text domain. Unique identifier for retrieving translated strings.
470         */
471        return apply_filters( 'ngettext_with_context', $translation, $single, $plural, $number, $context, $domain );
472}
473
474/**
475 * Registers plural strings in POT file, but does not translate them.
476 *
477 * Used when you want to keep structures with translatable plural
478 * strings and use them later when the number is known.
479 *
480 * Example:
481 *
482 *     $message = _n_noop( '%s post', '%s posts', 'text-domain' );
483 *     ...
484 *     printf( translate_nooped_plural( $message, $count, 'text-domain' ), number_format_i18n( $count ) );
485 *
486 * @since 2.5.0
487 *
488 * @param string $singular Singular form to be localized.
489 * @param string $plural   Plural form to be localized.
490 * @param string $domain   Optional. Text domain. Unique identifier for retrieving translated strings.
491 *                         Default null.
492 * @return array {
493 *     Array of translation information for the strings.
494 *
495 *     @type string $0        Singular form to be localized. No longer used.
496 *     @type string $1        Plural form to be localized. No longer used.
497 *     @type string $singular Singular form to be localized.
498 *     @type string $plural   Plural form to be localized.
499 *     @type null   $context  Context information for the translators.
500 *     @type string $domain   Text domain.
501 * }
502 */
503function _n_noop( $singular, $plural, $domain = null ) {
504        return array( 0 => $singular, 1 => $plural, 'singular' => $singular, 'plural' => $plural, 'context' => null, 'domain' => $domain );
505}
506
507/**
508 * Registers plural strings with gettext context in POT file, but does not translate them.
509 *
510 * Used when you want to keep structures with translatable plural
511 * strings and use them later when the number is known.
512 *
513 * Example of a generic phrase which is disambiguated via the context parameter:
514 *
515 *     $messages = array(
516 *              'people'  => _nx_noop( '%s group', '%s groups', 'people', 'text-domain' ),
517 *              'animals' => _nx_noop( '%s group', '%s groups', 'animals', 'text-domain' ),
518 *     );
519 *     ...
520 *     $message = $messages[ $type ];
521 *     printf( translate_nooped_plural( $message, $count, 'text-domain' ), number_format_i18n( $count ) );
522 *
523 * @since 2.8.0
524 *
525 * @param string $singular Singular form to be localized.
526 * @param string $plural   Plural form to be localized.
527 * @param string $context  Context information for the translators.
528 * @param string $domain   Optional. Text domain. Unique identifier for retrieving translated strings.
529 *                         Default null.
530 * @return array {
531 *     Array of translation information for the strings.
532 *
533 *     @type string $0        Singular form to be localized. No longer used.
534 *     @type string $1        Plural form to be localized. No longer used.
535 *     @type string $2        Context information for the translators. No longer used.
536 *     @type string $singular Singular form to be localized.
537 *     @type string $plural   Plural form to be localized.
538 *     @type string $context  Context information for the translators.
539 *     @type string $domain   Text domain.
540 * }
541 */
542function _nx_noop( $singular, $plural, $context, $domain = null ) {
543        return array( 0 => $singular, 1 => $plural, 2 => $context, 'singular' => $singular, 'plural' => $plural, 'context' => $context, 'domain' => $domain );
544}
545
546/**
547 * Translates and retrieves the singular or plural form of a string that's been registered
548 * with _n_noop() or _nx_noop().
549 *
550 * Used when you want to use a translatable plural string once the number is known.
551 *
552 * Example:
553 *
554 *     $message = _n_noop( '%s post', '%s posts', 'text-domain' );
555 *     ...
556 *     printf( translate_nooped_plural( $message, $count, 'text-domain' ), number_format_i18n( $count ) );
557 *
558 * @since 3.1.0
559 *
560 * @param array  $nooped_plural Array with singular, plural, and context keys, usually the result of _n_noop() or _nx_noop().
561 * @param int    $count         Number of objects.
562 * @param string $domain        Optional. Text domain. Unique identifier for retrieving translated strings. If $nooped_plural contains
563 *                              a text domain passed to _n_noop() or _nx_noop(), it will override this value. Default 'default'.
564 * @return string Either $single or $plural translated text.
565 */
566function translate_nooped_plural( $nooped_plural, $count, $domain = 'default' ) {
567        if ( $nooped_plural['domain'] )
568                $domain = $nooped_plural['domain'];
569
570        if ( $nooped_plural['context'] )
571                return _nx( $nooped_plural['singular'], $nooped_plural['plural'], $count, $nooped_plural['context'], $domain );
572        else
573                return _n( $nooped_plural['singular'], $nooped_plural['plural'], $count, $domain );
574}
575
576/**
577 * Load a .mo file into the text domain $domain.
578 *
579 * If the text domain already exists, the translations will be merged. If both
580 * sets have the same string, the translation from the original value will be taken.
581 *
582 * On success, the .mo file will be placed in the $l10n global by $domain
583 * and will be a MO object.
584 *
585 * @since 1.5.0
586 *
587 * @global array $l10n          An array of all currently loaded text domains.
588 * @global array $l10n_unloaded An array of all text domains that have been unloaded again.
589 *
590 * @param string $domain Text domain. Unique identifier for retrieving translated strings.
591 * @param string $mofile Path to the .mo file.
592 * @return bool True on success, false on failure.
593 */
594function load_textdomain( $domain, $mofile ) {
595        global $l10n, $l10n_unloaded;
596
597        $l10n_unloaded = (array) $l10n_unloaded;
598
599        /**
600         * Filters whether to override the .mo file loading.
601         *
602         * @since 2.9.0
603         *
604         * @param bool   $override Whether to override the .mo file loading. Default false.
605         * @param string $domain   Text domain. Unique identifier for retrieving translated strings.
606         * @param string $mofile   Path to the MO file.
607         */
608        $plugin_override = apply_filters( 'override_load_textdomain', false, $domain, $mofile );
609
610        if ( true == $plugin_override ) {
611                unset( $l10n_unloaded[ $domain ] );
612
613                return true;
614        }
615
616        /**
617         * Fires before the MO translation file is loaded.
618         *
619         * @since 2.9.0
620         *
621         * @param string $domain Text domain. Unique identifier for retrieving translated strings.
622         * @param string $mofile Path to the .mo file.
623         */
624        do_action( 'load_textdomain', $domain, $mofile );
625
626        /**
627         * Filters MO file path for loading translations for a specific text domain.
628         *
629         * @since 2.9.0
630         *
631         * @param string $mofile Path to the MO file.
632         * @param string $domain Text domain. Unique identifier for retrieving translated strings.
633         */
634        $mofile = apply_filters( 'load_textdomain_mofile', $mofile, $domain );
635
636        if ( !is_readable( $mofile ) ) return false;
637
638        $mo = new MO();
639        if ( !$mo->import_from_file( $mofile ) ) return false;
640
641        if ( isset( $l10n[$domain] ) )
642                $mo->merge_with( $l10n[$domain] );
643
644        unset( $l10n_unloaded[ $domain ] );
645
646        $l10n[$domain] = &$mo;
647
648        return true;
649}
650
651/**
652 * Unload translations for a text domain.
653 *
654 * @since 3.0.0
655 *
656 * @global array $l10n          An array of all currently loaded text domains.
657 * @global array $l10n_unloaded An array of all text domains that have been unloaded again.
658 *
659 * @param string $domain Text domain. Unique identifier for retrieving translated strings.
660 * @return bool Whether textdomain was unloaded.
661 */
662function unload_textdomain( $domain ) {
663        global $l10n, $l10n_unloaded;
664
665        $l10n_unloaded = (array) $l10n_unloaded;
666
667        /**
668         * Filters whether to override the text domain unloading.
669         *
670         * @since 3.0.0
671         *
672         * @param bool   $override Whether to override the text domain unloading. Default false.
673         * @param string $domain   Text domain. Unique identifier for retrieving translated strings.
674         */
675        $plugin_override = apply_filters( 'override_unload_textdomain', false, $domain );
676
677        if ( $plugin_override ) {
678                $l10n_unloaded[ $domain ] = true;
679
680                return true;
681        }
682
683        /**
684         * Fires before the text domain is unloaded.
685         *
686         * @since 3.0.0
687         *
688         * @param string $domain Text domain. Unique identifier for retrieving translated strings.
689         */
690        do_action( 'unload_textdomain', $domain );
691
692        if ( isset( $l10n[$domain] ) ) {
693                unset( $l10n[$domain] );
694
695                $l10n_unloaded[ $domain ] = true;
696
697                return true;
698        }
699
700        return false;
701}
702
703/**
704 * Load default translated strings based on locale.
705 *
706 * Loads the .mo file in WP_LANG_DIR constant path from WordPress root.
707 * The translated (.mo) file is named based on the locale.
708 *
709 * @see load_textdomain()
710 *
711 * @since 1.5.0
712 *
713 * @param string $locale Optional. Locale to load. Default is the value of get_locale().
714 * @return bool Whether the textdomain was loaded.
715 */
716function load_default_textdomain( $locale = null ) {
717        if ( null === $locale ) {
718                $locale = determine_locale();
719        }
720
721        // Unload previously loaded strings so we can switch translations.
722        unload_textdomain( 'default' );
723
724        $return = load_textdomain( 'default', WP_LANG_DIR . "/$locale.mo" );
725
726        if ( ( is_multisite() || ( defined( 'WP_INSTALLING_NETWORK' ) && WP_INSTALLING_NETWORK ) ) && ! file_exists(  WP_LANG_DIR . "/admin-$locale.mo" ) ) {
727                load_textdomain( 'default', WP_LANG_DIR . "/ms-$locale.mo" );
728                return $return;
729        }
730
731        if ( is_admin() || wp_installing() || ( defined( 'WP_REPAIRING' ) && WP_REPAIRING ) ) {
732                load_textdomain( 'default', WP_LANG_DIR . "/admin-$locale.mo" );
733        }
734
735        if ( is_network_admin() || ( defined( 'WP_INSTALLING_NETWORK' ) && WP_INSTALLING_NETWORK ) )
736                load_textdomain( 'default', WP_LANG_DIR . "/admin-network-$locale.mo" );
737
738        return $return;
739}
740
741/**
742 * Loads a plugin's translated strings.
743 *
744 * If the path is not given then it will be the root of the plugin directory.
745 *
746 * The .mo file should be named based on the text domain with a dash, and then the locale exactly.
747 *
748 * @since 1.5.0
749 * @since 4.6.0 The function now tries to load the .mo file from the languages directory first.
750 *
751 * @param string $domain          Unique identifier for retrieving translated strings
752 * @param string $deprecated      Optional. Use the $plugin_rel_path parameter instead. Default false.
753 * @param string $plugin_rel_path Optional. Relative path to WP_PLUGIN_DIR where the .mo file resides.
754 *                                Default false.
755 * @return bool True when textdomain is successfully loaded, false otherwise.
756 */
757function load_plugin_textdomain( $domain, $deprecated = false, $plugin_rel_path = false ) {
758        /**
759         * Filters a plugin's locale.
760         *
761         * @since 3.0.0
762         *
763         * @param string $locale The plugin's current locale.
764         * @param string $domain Text domain. Unique identifier for retrieving translated strings.
765         */
766        $locale = apply_filters( 'plugin_locale', determine_locale(), $domain );
767
768        $mofile = $domain . '-' . $locale . '.mo';
769
770        // Try to load from the languages directory first.
771        if ( load_textdomain( $domain, WP_LANG_DIR . '/plugins/' . $mofile ) ) {
772                return true;
773        }
774
775        if ( false !== $plugin_rel_path ) {
776                $path = WP_PLUGIN_DIR . '/' . trim( $plugin_rel_path, '/' );
777        } elseif ( false !== $deprecated ) {
778                _deprecated_argument( __FUNCTION__, '2.7.0' );
779                $path = ABSPATH . trim( $deprecated, '/' );
780        } else {
781                $path = WP_PLUGIN_DIR;
782        }
783
784        return load_textdomain( $domain, $path . '/' . $mofile );
785}
786
787/**
788 * Load the translated strings for a plugin residing in the mu-plugins directory.
789 *
790 * @since 3.0.0
791 * @since 4.6.0 The function now tries to load the .mo file from the languages directory first.
792 *
793 * @param string $domain             Text domain. Unique identifier for retrieving translated strings.
794 * @param string $mu_plugin_rel_path Optional. Relative to `WPMU_PLUGIN_DIR` directory in which the .mo
795 *                                   file resides. Default empty string.
796 * @return bool True when textdomain is successfully loaded, false otherwise.
797 */
798function load_muplugin_textdomain( $domain, $mu_plugin_rel_path = '' ) {
799        /** This filter is documented in wp-includes/l10n.php */
800        $locale = apply_filters( 'plugin_locale', determine_locale(), $domain );
801
802        $mofile = $domain . '-' . $locale . '.mo';
803
804        // Try to load from the languages directory first.
805        if ( load_textdomain( $domain, WP_LANG_DIR . '/plugins/' . $mofile ) ) {
806                return true;
807        }
808
809        $path = WPMU_PLUGIN_DIR . '/' . ltrim( $mu_plugin_rel_path, '/' );
810
811        return load_textdomain( $domain, $path . '/' . $mofile );
812}
813
814/**
815 * Load the theme's translated strings.
816 *
817 * If the current locale exists as a .mo file in the theme's root directory, it
818 * will be included in the translated strings by the $domain.
819 *
820 * The .mo files must be named based on the locale exactly.
821 *
822 * @since 1.5.0
823 * @since 4.6.0 The function now tries to load the .mo file from the languages directory first.
824 *
825 * @param string $domain Text domain. Unique identifier for retrieving translated strings.
826 * @param string $path   Optional. Path to the directory containing the .mo file.
827 *                       Default false.
828 * @return bool True when textdomain is successfully loaded, false otherwise.
829 */
830function load_theme_textdomain( $domain, $path = false ) {
831        /**
832         * Filters a theme's locale.
833         *
834         * @since 3.0.0
835         *
836         * @param string $locale The theme's current locale.
837         * @param string $domain Text domain. Unique identifier for retrieving translated strings.
838         */
839        $locale = apply_filters( 'theme_locale', determine_locale(), $domain );
840
841        $mofile = $domain . '-' . $locale . '.mo';
842
843        // Try to load from the languages directory first.
844        if ( load_textdomain( $domain, WP_LANG_DIR . '/themes/' . $mofile ) ) {
845                return true;
846        }
847
848        if ( ! $path ) {
849                $path = get_template_directory();
850        }
851
852        return load_textdomain( $domain, $path . '/' . $locale . '.mo' );
853}
854
855/**
856 * Load the child themes translated strings.
857 *
858 * If the current locale exists as a .mo file in the child themes
859 * root directory, it will be included in the translated strings by the $domain.
860 *
861 * The .mo files must be named based on the locale exactly.
862 *
863 * @since 2.9.0
864 *
865 * @param string $domain Text domain. Unique identifier for retrieving translated strings.
866 * @param string $path   Optional. Path to the directory containing the .mo file.
867 *                       Default false.
868 * @return bool True when the theme textdomain is successfully loaded, false otherwise.
869 */
870function load_child_theme_textdomain( $domain, $path = false ) {
871        if ( ! $path )
872                $path = get_stylesheet_directory();
873        return load_theme_textdomain( $domain, $path );
874}
875
876/**
877 * Load the script translated strings.
878 *
879 * @see WP_Scripts::set_translations()
880 * @link https://core.trac.wordpress.org/ticket/45103
881 * @global WP_Scripts $wp_scripts The WP_Scripts object for printing scripts.
882 *
883 * @since 5.0.0
884 *
885 * @param string $handle Name of the script to register a translation domain to.
886 * @param string $domain The textdomain.
887 * @param string $path   Optional. The full file path to the directory containing translation files.
888 *
889 * @return false|string False if the script textdomain could not be loaded, the translated strings
890 *                      in JSON encoding otherwise.
891 */
892function load_script_textdomain( $handle, $domain, $path = null ) {
893        global $wp_scripts;
894
895        $path   = untrailingslashit( $path );
896        $locale = determine_locale();
897
898        // If a path was given and the handle file exists simply return it.
899        $file_base       = $domain === 'default' ? $locale : $domain . '-' . $locale;
900        $handle_filename = $file_base . '-' . $handle . '.json';
901        if ( $path && file_exists( $path . '/' . $handle_filename ) ) {
902                return file_get_contents( $path . '/' . $handle_filename );
903        }
904
905        $obj = $wp_scripts->registered[ $handle ];
906
907        /** This filter is documented in wp-includes/class.wp-scripts.php */
908        $src = esc_url( apply_filters( 'script_loader_src', $obj->src, $handle ) );
909
910        $relative       = false;
911        $languages_path = WP_LANG_DIR;
912
913        $src_url     = wp_parse_url( $src );
914        $content_url = wp_parse_url( content_url() );
915        $site_url    = wp_parse_url( site_url() );
916
917        // If the host is the same or it's a relative URL.
918        if (
919                strpos( $src_url['path'], $content_url['path'] ) === 0 &&
920                ( ! isset( $src_url['host'] ) || $src_url['host'] !== $content_url['host'] )
921        ) {
922                // Make the src relative the specific plugin or theme.
923                $relative = trim( substr( $src, strlen( $content_url['path'] ) ), '/' );
924                $relative = explode( '/', $relative );
925
926                $languages_path = WP_LANG_DIR . '/' . $relative[0];
927
928                $relative = array_slice( $relative, 2 );
929                $relative = implode( '/', $relative );
930        } elseif ( ! isset( $src_url['host'] ) || $src_url['host'] !== $site_url['host'] ) {
931                if ( ! isset( $site_url['path'] ) ) {
932                        $relative = trim( $src_url['path'], '/' );
933                } elseif ( ( strpos( $src_url['path'], $site_url['path'] ) === 0 ) ) {
934                        // Make the src relative to the WP root.
935                        $relative = substr( $src, strlen( $site_url['path'] ) );
936                        $relative = trim( $relative, '/' );
937                }
938        }
939
940        // If the source is not from WP.
941        if ( false === $relative ) {
942                return false;
943        }
944
945        // Translations are always based on the unminified filename.
946        if ( substr( $relative, -7 ) === '.min.js' ) {
947                $relative = substr( $relative, 0, -7 ) . '.js';
948        }
949
950        $md5_filename = $file_base . '-' . md5( $relative ) . '.json';
951        if ( $path && file_exists( $path . '/' . $md5_filename ) ) {
952                return file_get_contents( $path . '/' . $md5_filename );
953        }
954        if ( file_exists( $languages_path . '/' . $md5_filename ) ) {
955                return file_get_contents( $languages_path . '/' . $md5_filename );
956        }
957
958        return false;
959}
960
961/**
962 * Loads plugin and theme textdomains just-in-time.
963 *
964 * When a textdomain is encountered for the first time, we try to load
965 * the translation file from `wp-content/languages`, removing the need
966 * to call load_plugin_texdomain() or load_theme_texdomain().
967 *
968 * @since 4.6.0
969 * @access private
970 *
971 * @see get_translations_for_domain()
972 * @global array $l10n_unloaded An array of all text domains that have been unloaded again.
973 *
974 * @param string $domain Text domain. Unique identifier for retrieving translated strings.
975 * @return bool True when the textdomain is successfully loaded, false otherwise.
976 */
977function _load_textdomain_just_in_time( $domain ) {
978        global $l10n_unloaded;
979
980        $l10n_unloaded = (array) $l10n_unloaded;
981
982        // Short-circuit if domain is 'default' which is reserved for core.
983        if ( 'default' === $domain || isset( $l10n_unloaded[ $domain ] ) ) {
984                return false;
985        }
986
987        $translation_path = _get_path_to_translation( $domain );
988        if ( false === $translation_path ) {
989                return false;
990        }
991
992        return load_textdomain( $domain, $translation_path );
993}
994
995/**
996 * Gets the path to a translation file for loading a textdomain just in time.
997 *
998 * Caches the retrieved results internally.
999 *
1000 * @since 4.7.0
1001 * @access private
1002 *
1003 * @see _load_textdomain_just_in_time()
1004 *
1005 * @param string $domain Text domain. Unique identifier for retrieving translated strings.
1006 * @param bool   $reset  Whether to reset the internal cache. Used by the switch to locale functionality.
1007 * @return string|false The path to the translation file or false if no translation file was found.
1008 */
1009function _get_path_to_translation( $domain, $reset = false ) {
1010        static $available_translations = array();
1011
1012        if ( true === $reset ) {
1013                $available_translations = array();
1014        }
1015
1016        if ( ! isset( $available_translations[ $domain ] ) ) {
1017                $available_translations[ $domain ] = _get_path_to_translation_from_lang_dir( $domain );
1018        }
1019
1020        return $available_translations[ $domain ];
1021}
1022
1023/**
1024 * Gets the path to a translation file in the languages directory for the current locale.
1025 *
1026 * Holds a cached list of available .mo files to improve performance.
1027 *
1028 * @since 4.7.0
1029 * @access private
1030 *
1031 * @see _get_path_to_translation()
1032 *
1033 * @param string $domain Text domain. Unique identifier for retrieving translated strings.
1034 * @return string|false The path to the translation file or false if no translation file was found.
1035 */
1036function _get_path_to_translation_from_lang_dir( $domain ) {
1037        static $cached_mofiles = null;
1038
1039        if ( null === $cached_mofiles ) {
1040                $cached_mofiles = array();
1041
1042                $locations = array(
1043                        WP_LANG_DIR . '/plugins',
1044                        WP_LANG_DIR . '/themes',
1045                );
1046
1047                foreach ( $locations as $location ) {
1048                        $mofiles = glob( $location . '/*.mo' );
1049                        if ( $mofiles ) {
1050                                $cached_mofiles = array_merge( $cached_mofiles, $mofiles );
1051                        }
1052                }
1053        }
1054
1055        $locale = determine_locale();
1056        $mofile = "{$domain}-{$locale}.mo";
1057
1058        $path = WP_LANG_DIR . '/plugins/' . $mofile;
1059        if ( in_array( $path, $cached_mofiles ) ) {
1060                return $path;
1061        }
1062
1063        $path = WP_LANG_DIR . '/themes/' . $mofile;
1064        if ( in_array( $path, $cached_mofiles ) ) {
1065                return $path;
1066        }
1067
1068        return false;
1069}
1070
1071/**
1072 * Return the Translations instance for a text domain.
1073 *
1074 * If there isn't one, returns empty Translations instance.
1075 *
1076 * @since 2.8.0
1077 *
1078 * @global array $l10n
1079 *
1080 * @param string $domain Text domain. Unique identifier for retrieving translated strings.
1081 * @return Translations|NOOP_Translations A Translations instance.
1082 */
1083function get_translations_for_domain( $domain ) {
1084        global $l10n;
1085        if ( isset( $l10n[ $domain ] ) || ( _load_textdomain_just_in_time( $domain ) && isset( $l10n[ $domain ] ) ) ) {
1086                return $l10n[ $domain ];
1087        }
1088
1089        static $noop_translations = null;
1090        if ( null === $noop_translations ) {
1091                $noop_translations = new NOOP_Translations;
1092        }
1093
1094        return $noop_translations;
1095}
1096
1097/**
1098 * Whether there are translations for the text domain.
1099 *
1100 * @since 3.0.0
1101 *
1102 * @global array $l10n
1103 *
1104 * @param string $domain Text domain. Unique identifier for retrieving translated strings.
1105 * @return bool Whether there are translations.
1106 */
1107function is_textdomain_loaded( $domain ) {
1108        global $l10n;
1109        return isset( $l10n[ $domain ] );
1110}
1111
1112/**
1113 * Translates role name.
1114 *
1115 * Since the role names are in the database and not in the source there
1116 * are dummy gettext calls to get them into the POT file and this function
1117 * properly translates them back.
1118 *
1119 * The before_last_bar() call is needed, because older installations keep the roles
1120 * using the old context format: 'Role name|User role' and just skipping the
1121 * content after the last bar is easier than fixing them in the DB. New installations
1122 * won't suffer from that problem.
1123 *
1124 * @since 2.8.0
1125 *
1126 * @param string $name The role name.
1127 * @return string Translated role name on success, original name on failure.
1128 */
1129function translate_user_role( $name ) {
1130        return translate_with_gettext_context( before_last_bar($name), 'User role' );
1131}
1132
1133/**
1134 * Get all available languages based on the presence of *.mo files in a given directory.
1135 *
1136 * The default directory is WP_LANG_DIR.
1137 *
1138 * @since 3.0.0
1139 * @since 4.7.0 The results are now filterable with the {@see 'get_available_languages'} filter.
1140 *
1141 * @param string $dir A directory to search for language files.
1142 *                    Default WP_LANG_DIR.
1143 * @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.
1144 */
1145function get_available_languages( $dir = null ) {
1146        $languages = array();
1147
1148        $lang_files = glob( ( is_null( $dir ) ? WP_LANG_DIR : $dir ) . '/*.mo' );
1149        if ( $lang_files ) {
1150                foreach ( $lang_files as $lang_file ) {
1151                        $lang_file = basename( $lang_file, '.mo' );
1152                        if ( 0 !== strpos( $lang_file, 'continents-cities' ) && 0 !== strpos( $lang_file, 'ms-' ) &&
1153                                0 !== strpos( $lang_file, 'admin-' ) ) {
1154                                $languages[] = $lang_file;
1155                        }
1156                }
1157        }
1158
1159        /**
1160         * Filters the list of available language codes.
1161         *
1162         * @since 4.7.0
1163         *
1164         * @param array  $languages An array of available language codes.
1165         * @param string $dir       The directory where the language files were found.
1166         */
1167        return apply_filters( 'get_available_languages', $languages, $dir );
1168}
1169
1170/**
1171 * Get installed translations.
1172 *
1173 * Looks in the wp-content/languages directory for translations of
1174 * plugins or themes.
1175 *
1176 * @since 3.7.0
1177 *
1178 * @param string $type What to search for. Accepts 'plugins', 'themes', 'core'.
1179 * @return array Array of language data.
1180 */
1181function wp_get_installed_translations( $type ) {
1182        if ( $type !== 'themes' && $type !== 'plugins' && $type !== 'core' )
1183                return array();
1184
1185        $dir = 'core' === $type ? '' : "/$type";
1186
1187        if ( ! is_dir( WP_LANG_DIR ) )
1188                return array();
1189
1190        if ( $dir && ! is_dir( WP_LANG_DIR . $dir ) )
1191                return array();
1192
1193        $files = scandir( WP_LANG_DIR . $dir );
1194        if ( ! $files )
1195                return array();
1196
1197        $language_data = array();
1198
1199        foreach ( $files as $file ) {
1200                if ( '.' === $file[0] || is_dir( WP_LANG_DIR . "$dir/$file" ) ) {
1201                        continue;
1202                }
1203                if ( substr( $file, -3 ) !== '.po' ) {
1204                        continue;
1205                }
1206                if ( ! preg_match( '/(?:(.+)-)?([a-z]{2,3}(?:_[A-Z]{2})?(?:_[a-z0-9]+)?).po/', $file, $match ) ) {
1207                        continue;
1208                }
1209                if ( ! in_array( substr( $file, 0, -3 ) . '.mo', $files ) )  {
1210                        continue;
1211                }
1212
1213                list( , $textdomain, $language ) = $match;
1214                if ( '' === $textdomain ) {
1215                        $textdomain = 'default';
1216                }
1217                $language_data[ $textdomain ][ $language ] = wp_get_pomo_file_data( WP_LANG_DIR . "$dir/$file" );
1218        }
1219        return $language_data;
1220}
1221
1222/**
1223 * Extract headers from a PO file.
1224 *
1225 * @since 3.7.0
1226 *
1227 * @param string $po_file Path to PO file.
1228 * @return array PO file headers.
1229 */
1230function wp_get_pomo_file_data( $po_file ) {
1231        $headers = get_file_data( $po_file, array(
1232                'POT-Creation-Date'  => '"POT-Creation-Date',
1233                'PO-Revision-Date'   => '"PO-Revision-Date',
1234                'Project-Id-Version' => '"Project-Id-Version',
1235                'X-Generator'        => '"X-Generator',
1236        ) );
1237        foreach ( $headers as $header => $value ) {
1238                // Remove possible contextual '\n' and closing double quote.
1239                $headers[ $header ] = preg_replace( '~(\\\n)?"$~', '', $value );
1240        }
1241        return $headers;
1242}
1243
1244/**
1245 * Language selector.
1246 *
1247 * @since 4.0.0
1248 * @since 4.3.0 Introduced the `echo` argument.
1249 * @since 4.7.0 Introduced the `show_option_site_default` argument.
1250 *
1251 * @see get_available_languages()
1252 * @see wp_get_available_translations()
1253 *
1254 * @param string|array $args {
1255 *     Optional. Array or string of arguments for outputting the language selector.
1256 *
1257 *     @type string   $id                           ID attribute of the select element. Default 'locale'.
1258 *     @type string   $name                         Name attribute of the select element. Default 'locale'.
1259 *     @type array    $languages                    List of installed languages, contain only the locales.
1260 *                                                  Default empty array.
1261 *     @type array    $translations                 List of available translations. Default result of
1262 *                                                  wp_get_available_translations().
1263 *     @type string   $selected                     Language which should be selected. Default empty.
1264 *     @type bool|int $echo                         Whether to echo the generated markup. Accepts 0, 1, or their
1265 *                                                  boolean equivalents. Default 1.
1266 *     @type bool     $show_available_translations  Whether to show available translations. Default true.
1267 *     @type bool     $show_option_site_default     Whether to show an option to fall back to the site's locale. Default false.
1268 * }
1269 * @return string HTML content
1270 */
1271function wp_dropdown_languages( $args = array() ) {
1272
1273        $parsed_args = wp_parse_args( $args, array(
1274                'id'           => 'locale',
1275                'name'         => 'locale',
1276                'languages'    => array(),
1277                'translations' => array(),
1278                'selected'     => '',
1279                'echo'         => 1,
1280                'show_available_translations' => true,
1281                'show_option_site_default'    => false,
1282        ) );
1283
1284        // Bail if no ID or no name.
1285        if ( ! $parsed_args['id'] || ! $parsed_args['name'] ) {
1286                return;
1287        }
1288
1289        // English (United States) uses an empty string for the value attribute.
1290        if ( 'en_US' === $parsed_args['selected'] ) {
1291                $parsed_args['selected'] = '';
1292        }
1293
1294        $translations = $parsed_args['translations'];
1295        if ( empty( $translations ) ) {
1296                require_once( ABSPATH . 'wp-admin/includes/translation-install.php' );
1297                $translations = wp_get_available_translations();
1298        }
1299
1300        /*
1301         * $parsed_args['languages'] should only contain the locales. Find the locale in
1302         * $translations to get the native name. Fall back to locale.
1303         */
1304        $languages = array();
1305        foreach ( $parsed_args['languages'] as $locale ) {
1306                if ( isset( $translations[ $locale ] ) ) {
1307                        $translation = $translations[ $locale ];
1308                        $languages[] = array(
1309                                'language'    => $translation['language'],
1310                                'native_name' => $translation['native_name'],
1311                                'lang'        => current( $translation['iso'] ),
1312                        );
1313
1314                        // Remove installed language from available translations.
1315                        unset( $translations[ $locale ] );
1316                } else {
1317                        $languages[] = array(
1318                                'language'    => $locale,
1319                                'native_name' => $locale,
1320                                'lang'        => '',
1321                        );
1322                }
1323        }
1324
1325        $translations_available = ( ! empty( $translations ) && $parsed_args['show_available_translations'] );
1326
1327        // Holds the HTML markup.
1328        $structure = array();
1329
1330        // List installed languages.
1331        if ( $translations_available ) {
1332                $structure[] = '<optgroup label="' . esc_attr_x( 'Installed', 'translations' ) . '">';
1333        }
1334
1335        // Site default.
1336        if ( $parsed_args['show_option_site_default'] ) {
1337                $structure[] = sprintf(
1338                        '<option value="site-default" data-installed="1"%s>%s</option>',
1339                        selected( 'site-default', $parsed_args['selected'], false ),
1340                        _x( 'Site Default', 'default site language' )
1341                );
1342        }
1343
1344        // Always show English.
1345        $structure[] = sprintf(
1346                '<option value="" lang="en" data-installed="1"%s>English (United States)</option>',
1347                selected( '', $parsed_args['selected'], false )
1348        );
1349
1350        // List installed languages.
1351        foreach ( $languages as $language ) {
1352                $structure[] = sprintf(
1353                        '<option value="%s" lang="%s"%s data-installed="1">%s</option>',
1354                        esc_attr( $language['language'] ),
1355                        esc_attr( $language['lang'] ),
1356                        selected( $language['language'], $parsed_args['selected'], false ),
1357                        esc_html( $language['native_name'] )
1358                );
1359        }
1360        if ( $translations_available ) {
1361                $structure[] = '</optgroup>';
1362        }
1363
1364        // List available translations.
1365        if ( $translations_available ) {
1366                $structure[] = '<optgroup label="' . esc_attr_x( 'Available', 'translations' ) . '">';
1367                foreach ( $translations as $translation ) {
1368                        $structure[] = sprintf(
1369                                '<option value="%s" lang="%s"%s>%s</option>',
1370                                esc_attr( $translation['language'] ),
1371                                esc_attr( current( $translation['iso'] ) ),
1372                                selected( $translation['language'], $parsed_args['selected'], false ),
1373                                esc_html( $translation['native_name'] )
1374                        );
1375                }
1376                $structure[] = '</optgroup>';
1377        }
1378
1379        // Combine the output string.
1380        $output  = sprintf( '<select name="%s" id="%s">', esc_attr( $parsed_args['name'] ), esc_attr( $parsed_args['id'] ) );
1381        $output .= join( "\n", $structure );
1382        $output .= '</select>';
1383
1384        if ( $parsed_args['echo'] ) {
1385                echo $output;
1386        }
1387
1388        return $output;
1389}
1390
1391/**
1392 * Determines whether the current locale is right-to-left (RTL).
1393 *
1394 * For more information on this and similar theme functions, check out
1395 * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/
1396 * Conditional Tags} article in the Theme Developer Handbook.
1397 *
1398 * @since 3.0.0
1399 *
1400 * @global WP_Locale $wp_locale
1401 *
1402 * @return bool Whether locale is RTL.
1403 */
1404function is_rtl() {
1405        global $wp_locale;
1406        if ( ! ( $wp_locale instanceof WP_Locale ) ) {
1407                return false;
1408        }
1409        return $wp_locale->is_rtl();
1410}
1411
1412/**
1413 * Switches the translations according to the given locale.
1414 *
1415 * @since 4.7.0
1416 *
1417 * @global WP_Locale_Switcher $wp_locale_switcher
1418 *
1419 * @param string $locale The locale.
1420 * @return bool True on success, false on failure.
1421 */
1422function switch_to_locale( $locale ) {
1423        /* @var WP_Locale_Switcher $wp_locale_switcher */
1424        global $wp_locale_switcher;
1425
1426        return $wp_locale_switcher->switch_to_locale( $locale );
1427}
1428
1429/**
1430 * Restores the translations according to the previous locale.
1431 *
1432 * @since 4.7.0
1433 *
1434 * @global WP_Locale_Switcher $wp_locale_switcher
1435 *
1436 * @return string|false Locale on success, false on error.
1437 */
1438function restore_previous_locale() {
1439        /* @var WP_Locale_Switcher $wp_locale_switcher */
1440        global $wp_locale_switcher;
1441
1442        return $wp_locale_switcher->restore_previous_locale();
1443}
1444
1445/**
1446 * Restores the translations according to the original locale.
1447 *
1448 * @since 4.7.0
1449 *
1450 * @global WP_Locale_Switcher $wp_locale_switcher
1451 *
1452 * @return string|false Locale on success, false on error.
1453 */
1454function restore_current_locale() {
1455        /* @var WP_Locale_Switcher $wp_locale_switcher */
1456        global $wp_locale_switcher;
1457
1458        return $wp_locale_switcher->restore_current_locale();
1459}
1460
1461/**
1462 * Whether switch_to_locale() is in effect.
1463 *
1464 * @since 4.7.0
1465 *
1466 * @global WP_Locale_Switcher $wp_locale_switcher
1467 *
1468 * @return bool True if the locale has been switched, false otherwise.
1469 */
1470function is_locale_switched() {
1471        /* @var WP_Locale_Switcher $wp_locale_switcher */
1472        global $wp_locale_switcher;
1473
1474        return $wp_locale_switcher->is_switched();
1475}