ru-se.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

l10n.php (56738B)


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