wp/wp-includes/l10n.php
changeset 21 48c4eec2b7e6
parent 19 3d72ae0968f4
child 22 8c2e4d02f4ef
equal deleted inserted replaced
20:7b1b88e27a20 21:48c4eec2b7e6
    86  * If the user has a locale set to a non-empty string then it will be
    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().
    87  * returned. Otherwise it returns the locale of get_locale().
    88  *
    88  *
    89  * @since 4.7.0
    89  * @since 4.7.0
    90  *
    90  *
    91  * @param int|WP_User $user_id User's ID or a WP_User object. Defaults to current user.
    91  * @param int|WP_User $user User's ID or a WP_User object. Defaults to current user.
    92  * @return string The locale of the user.
    92  * @return string The locale of the user.
    93  */
    93  */
    94 function get_user_locale( $user_id = 0 ) {
    94 function get_user_locale( $user = 0 ) {
    95 	$user = false;
    95 	$user_object = false;
    96 	if ( 0 === $user_id && function_exists( 'wp_get_current_user' ) ) {
    96 
    97 		$user = wp_get_current_user();
    97 	if ( 0 === $user && function_exists( 'wp_get_current_user' ) ) {
    98 	} elseif ( $user_id instanceof WP_User ) {
    98 		$user_object = wp_get_current_user();
    99 		$user = $user_id;
    99 	} elseif ( $user instanceof WP_User ) {
   100 	} elseif ( $user_id && is_numeric( $user_id ) ) {
   100 		$user_object = $user;
   101 		$user = get_user_by( 'id', $user_id );
   101 	} elseif ( $user && is_numeric( $user ) ) {
   102 	}
   102 		$user_object = get_user_by( 'id', $user );
   103 
   103 	}
   104 	if ( ! $user ) {
   104 
       
   105 	if ( ! $user_object ) {
   105 		return get_locale();
   106 		return get_locale();
   106 	}
   107 	}
   107 
   108 
   108 	$locale = $user->locale;
   109 	$locale = $user_object->locale;
       
   110 
   109 	return $locale ? $locale : get_locale();
   111 	return $locale ? $locale : get_locale();
   110 }
   112 }
   111 
   113 
   112 /**
   114 /**
   113  * Determine the current locale desired for the request.
   115  * Determines the current locale desired for the request.
   114  *
   116  *
   115  * @since 5.0.0
   117  * @since 5.0.0
   116  *
   118  *
   117  * @global string $pagenow The filename of the current screen.
   119  * @global string $pagenow The filename of the current screen.
   118  *
   120  *
   128 	 *
   130 	 *
   129 	 * @param string|null $locale The locale to return and short-circuit. Default null.
   131 	 * @param string|null $locale The locale to return and short-circuit. Default null.
   130 	 */
   132 	 */
   131 	$determined_locale = apply_filters( 'pre_determine_locale', null );
   133 	$determined_locale = apply_filters( 'pre_determine_locale', null );
   132 
   134 
   133 	if ( ! empty( $determined_locale ) && is_string( $determined_locale ) ) {
   135 	if ( $determined_locale && is_string( $determined_locale ) ) {
   134 		return $determined_locale;
   136 		return $determined_locale;
   135 	}
   137 	}
   136 
   138 
   137 	$determined_locale = get_locale();
   139 	if (
   138 
   140 		isset( $GLOBALS['pagenow'] ) && 'wp-login.php' === $GLOBALS['pagenow'] &&
   139 	if ( is_admin() ) {
   141 		( ! empty( $_GET['wp_lang'] ) || ! empty( $_COOKIE['wp_lang'] ) )
       
   142 	) {
       
   143 		if ( ! empty( $_GET['wp_lang'] ) ) {
       
   144 			$determined_locale = sanitize_locale_name( $_GET['wp_lang'] );
       
   145 		} else {
       
   146 			$determined_locale = sanitize_locale_name( $_COOKIE['wp_lang'] );
       
   147 		}
       
   148 	} elseif (
       
   149 		is_admin() ||
       
   150 		( isset( $_GET['_locale'] ) && 'user' === $_GET['_locale'] && wp_is_json_request() )
       
   151 	) {
   140 		$determined_locale = get_user_locale();
   152 		$determined_locale = get_user_locale();
   141 	}
   153 	} elseif (
   142 
   154 		( ! empty( $_REQUEST['language'] ) || isset( $GLOBALS['wp_local_package'] ) )
   143 	if ( isset( $_GET['_locale'] ) && 'user' === $_GET['_locale'] && wp_is_json_request() ) {
   155 		&& wp_installing()
   144 		$determined_locale = get_user_locale();
   156 	) {
   145 	}
   157 		if ( ! empty( $_REQUEST['language'] ) ) {
   146 
   158 			$determined_locale = sanitize_locale_name( $_REQUEST['language'] );
   147 	$wp_lang = '';
   159 		} else {
   148 
   160 			$determined_locale = $GLOBALS['wp_local_package'];
   149 	if ( ! empty( $_GET['wp_lang'] ) ) {
   161 		}
   150 		$wp_lang = sanitize_text_field( $_GET['wp_lang'] );
   162 	}
   151 	} elseif ( ! empty( $_COOKIE['wp_lang'] ) ) {
   163 
   152 		$wp_lang = sanitize_text_field( $_COOKIE['wp_lang'] );
   164 	if ( ! $determined_locale ) {
   153 	}
   165 		$determined_locale = get_locale();
   154 
       
   155 	if ( ! empty( $wp_lang ) && ! empty( $GLOBALS['pagenow'] ) && 'wp-login.php' === $GLOBALS['pagenow'] ) {
       
   156 		$determined_locale = $wp_lang;
       
   157 	}
   166 	}
   158 
   167 
   159 	/**
   168 	/**
   160 	 * Filters the locale for the current request.
   169 	 * Filters the locale for the current request.
   161 	 *
   170 	 *
   162 	 * @since 5.0.0
   171 	 * @since 5.0.0
   163 	 *
   172 	 *
   164 	 * @param string $locale The locale.
   173 	 * @param string $determined_locale The locale.
   165 	 */
   174 	 */
   166 	return apply_filters( 'determine_locale', $determined_locale );
   175 	return apply_filters( 'determine_locale', $determined_locale );
   167 }
   176 }
   168 
   177 
   169 /**
   178 /**
   170  * Retrieve the translation of $text.
   179  * Retrieves the translation of $text.
   171  *
   180  *
   172  * If there is no translation, or the text domain isn't loaded, the original text is returned.
   181  * If there is no translation, or the text domain isn't loaded, the original text is returned.
   173  *
   182  *
   174  * *Note:* Don't use translate() directly, use __() or related functions.
   183  * *Note:* Don't use translate() directly, use __() or related functions.
   175  *
   184  *
   176  * @since 2.2.0
   185  * @since 2.2.0
   177  * @since 5.5.0 Introduced gettext-{$domain} filter.
   186  * @since 5.5.0 Introduced `gettext-{$domain}` filter.
   178  *
   187  *
   179  * @param string $text   Text to translate.
   188  * @param string $text   Text to translate.
   180  * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings.
   189  * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings.
   181  *                       Default 'default'.
   190  *                       Default 'default'.
   182  * @return string Translated text.
   191  * @return string Translated text.
   211 
   220 
   212 	return $translation;
   221 	return $translation;
   213 }
   222 }
   214 
   223 
   215 /**
   224 /**
   216  * Remove last item on a pipe-delimited string.
   225  * Removes last item on a pipe-delimited string.
   217  *
   226  *
   218  * Meant for removing the last item in a string, such as 'Role name|User role'. The original
   227  * Meant for removing the last item in a string, such as 'Role name|User role'. The original
   219  * string will be returned if no pipe '|' characters are found in the string.
   228  * string will be returned if no pipe '|' characters are found in the string.
   220  *
   229  *
   221  * @since 2.8.0
   230  * @since 2.8.0
   222  *
   231  *
   223  * @param string $string A pipe-delimited string.
   232  * @param string $text A pipe-delimited string.
   224  * @return string Either $string or everything before the last pipe.
   233  * @return string Either $text or everything before the last pipe.
   225  */
   234  */
   226 function before_last_bar( $string ) {
   235 function before_last_bar( $text ) {
   227 	$last_bar = strrpos( $string, '|' );
   236 	$last_bar = strrpos( $text, '|' );
   228 	if ( false === $last_bar ) {
   237 	if ( false === $last_bar ) {
   229 		return $string;
   238 		return $text;
   230 	} else {
   239 	} else {
   231 		return substr( $string, 0, $last_bar );
   240 		return substr( $text, 0, $last_bar );
   232 	}
   241 	}
   233 }
   242 }
   234 
   243 
   235 /**
   244 /**
   236  * Retrieve the translation of $text in the context defined in $context.
   245  * Retrieves the translation of $text in the context defined in $context.
   237  *
   246  *
   238  * If there is no translation, or the text domain isn't loaded, the original text is returned.
   247  * If there is no translation, or the text domain isn't loaded, the original text is returned.
   239  *
   248  *
   240  * *Note:* Don't use translate_with_gettext_context() directly, use _x() or related functions.
   249  * *Note:* Don't use translate_with_gettext_context() directly, use _x() or related functions.
   241  *
   250  *
   242  * @since 2.8.0
   251  * @since 2.8.0
   243  * @since 5.5.0 Introduced gettext_with_context-{$domain} filter.
   252  * @since 5.5.0 Introduced `gettext_with_context-{$domain}` filter.
   244  *
   253  *
   245  * @param string $text    Text to translate.
   254  * @param string $text    Text to translate.
   246  * @param string $context Context information for the translators.
   255  * @param string $context Context information for the translators.
   247  * @param string $domain  Optional. Text domain. Unique identifier for retrieving translated strings.
   256  * @param string $domain  Optional. Text domain. Unique identifier for retrieving translated strings.
   248  *                        Default 'default'.
   257  *                        Default 'default'.
   280 
   289 
   281 	return $translation;
   290 	return $translation;
   282 }
   291 }
   283 
   292 
   284 /**
   293 /**
   285  * Retrieve the translation of $text.
   294  * Retrieves the translation of $text.
   286  *
   295  *
   287  * If there is no translation, or the text domain isn't loaded, the original text is returned.
   296  * If there is no translation, or the text domain isn't loaded, the original text is returned.
   288  *
   297  *
   289  * @since 2.1.0
   298  * @since 2.1.0
   290  *
   299  *
   296 function __( $text, $domain = 'default' ) {
   305 function __( $text, $domain = 'default' ) {
   297 	return translate( $text, $domain );
   306 	return translate( $text, $domain );
   298 }
   307 }
   299 
   308 
   300 /**
   309 /**
   301  * Retrieve the translation of $text and escapes it for safe use in an attribute.
   310  * Retrieves the translation of $text and escapes it for safe use in an attribute.
   302  *
   311  *
   303  * If there is no translation, or the text domain isn't loaded, the original text is returned.
   312  * If there is no translation, or the text domain isn't loaded, the original text is returned.
   304  *
   313  *
   305  * @since 2.8.0
   314  * @since 2.8.0
   306  *
   315  *
   312 function esc_attr__( $text, $domain = 'default' ) {
   321 function esc_attr__( $text, $domain = 'default' ) {
   313 	return esc_attr( translate( $text, $domain ) );
   322 	return esc_attr( translate( $text, $domain ) );
   314 }
   323 }
   315 
   324 
   316 /**
   325 /**
   317  * Retrieve the translation of $text and escapes it for safe use in HTML output.
   326  * Retrieves the translation of $text and escapes it for safe use in HTML output.
   318  *
   327  *
   319  * If there is no translation, or the text domain isn't loaded, the original text
   328  * If there is no translation, or the text domain isn't loaded, the original text
   320  * is escaped and returned.
   329  * is escaped and returned.
   321  *
   330  *
   322  * @since 2.8.0
   331  * @since 2.8.0
   329 function esc_html__( $text, $domain = 'default' ) {
   338 function esc_html__( $text, $domain = 'default' ) {
   330 	return esc_html( translate( $text, $domain ) );
   339 	return esc_html( translate( $text, $domain ) );
   331 }
   340 }
   332 
   341 
   333 /**
   342 /**
   334  * Display translated text.
   343  * Displays translated text.
   335  *
   344  *
   336  * @since 1.2.0
   345  * @since 1.2.0
   337  *
   346  *
   338  * @param string $text   Text to translate.
   347  * @param string $text   Text to translate.
   339  * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings.
   348  * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings.
   342 function _e( $text, $domain = 'default' ) {
   351 function _e( $text, $domain = 'default' ) {
   343 	echo translate( $text, $domain );
   352 	echo translate( $text, $domain );
   344 }
   353 }
   345 
   354 
   346 /**
   355 /**
   347  * Display translated text that has been escaped for safe use in an attribute.
   356  * Displays translated text that has been escaped for safe use in an attribute.
   348  *
   357  *
   349  * Encodes `< > & " '` (less than, greater than, ampersand, double quote, single quote).
   358  * Encodes `< > & " '` (less than, greater than, ampersand, double quote, single quote).
   350  * Will never double encode entities.
   359  * Will never double encode entities.
   351  *
   360  *
   352  * If you need the value for use in PHP, use esc_attr__().
   361  * If you need the value for use in PHP, use esc_attr__().
   360 function esc_attr_e( $text, $domain = 'default' ) {
   369 function esc_attr_e( $text, $domain = 'default' ) {
   361 	echo esc_attr( translate( $text, $domain ) );
   370 	echo esc_attr( translate( $text, $domain ) );
   362 }
   371 }
   363 
   372 
   364 /**
   373 /**
   365  * Display translated text that has been escaped for safe use in HTML output.
   374  * Displays translated text that has been escaped for safe use in HTML output.
   366  *
   375  *
   367  * If there is no translation, or the text domain isn't loaded, the original text
   376  * If there is no translation, or the text domain isn't loaded, the original text
   368  * is escaped and displayed.
   377  * is escaped and displayed.
   369  *
   378  *
   370  * If you need the value for use in PHP, use esc_html__().
   379  * If you need the value for use in PHP, use esc_html__().
   378 function esc_html_e( $text, $domain = 'default' ) {
   387 function esc_html_e( $text, $domain = 'default' ) {
   379 	echo esc_html( translate( $text, $domain ) );
   388 	echo esc_html( translate( $text, $domain ) );
   380 }
   389 }
   381 
   390 
   382 /**
   391 /**
   383  * Retrieve translated string with gettext context.
   392  * Retrieves translated string with gettext context.
   384  *
   393  *
   385  * Quite a few times, there will be collisions with similar translatable text
   394  * Quite a few times, there will be collisions with similar translatable text
   386  * found in more than two places, but with different translated context.
   395  * found in more than two places, but with different translated context.
   387  *
   396  *
   388  * By including the context in the pot file, translators can translate the two
   397  * By including the context in the pot file, translators can translate the two
   399 function _x( $text, $context, $domain = 'default' ) {
   408 function _x( $text, $context, $domain = 'default' ) {
   400 	return translate_with_gettext_context( $text, $context, $domain );
   409 	return translate_with_gettext_context( $text, $context, $domain );
   401 }
   410 }
   402 
   411 
   403 /**
   412 /**
   404  * Display translated string with gettext context.
   413  * Displays translated string with gettext context.
   405  *
   414  *
   406  * @since 3.0.0
   415  * @since 3.0.0
   407  *
   416  *
   408  * @param string $text    Text to translate.
   417  * @param string $text    Text to translate.
   409  * @param string $context Context information for the translators.
   418  * @param string $context Context information for the translators.
   413 function _ex( $text, $context, $domain = 'default' ) {
   422 function _ex( $text, $context, $domain = 'default' ) {
   414 	echo _x( $text, $context, $domain );
   423 	echo _x( $text, $context, $domain );
   415 }
   424 }
   416 
   425 
   417 /**
   426 /**
   418  * Translate string with gettext context, and escapes it for safe use in an attribute.
   427  * Translates string with gettext context, and escapes it for safe use in an attribute.
   419  *
   428  *
   420  * If there is no translation, or the text domain isn't loaded, the original text
   429  * If there is no translation, or the text domain isn't loaded, the original text
   421  * is escaped and returned.
   430  * is escaped and returned.
   422  *
   431  *
   423  * @since 2.8.0
   432  * @since 2.8.0
   431 function esc_attr_x( $text, $context, $domain = 'default' ) {
   440 function esc_attr_x( $text, $context, $domain = 'default' ) {
   432 	return esc_attr( translate_with_gettext_context( $text, $context, $domain ) );
   441 	return esc_attr( translate_with_gettext_context( $text, $context, $domain ) );
   433 }
   442 }
   434 
   443 
   435 /**
   444 /**
   436  * Translate string with gettext context, and escapes it for safe use in HTML output.
   445  * Translates string with gettext context, and escapes it for safe use in HTML output.
   437  *
   446  *
   438  * If there is no translation, or the text domain isn't loaded, the original text
   447  * If there is no translation, or the text domain isn't loaded, the original text
   439  * is escaped and returned.
   448  * is escaped and returned.
   440  *
   449  *
   441  * @since 2.9.0
   450  * @since 2.9.0
   459  * Example:
   468  * Example:
   460  *
   469  *
   461  *     printf( _n( '%s person', '%s people', $count, 'text-domain' ), number_format_i18n( $count ) );
   470  *     printf( _n( '%s person', '%s people', $count, 'text-domain' ), number_format_i18n( $count ) );
   462  *
   471  *
   463  * @since 2.8.0
   472  * @since 2.8.0
   464  * @since 5.5.0 Introduced ngettext-{$domain} filter.
   473  * @since 5.5.0 Introduced `ngettext-{$domain}` filter.
   465  *
   474  *
   466  * @param string $single The text to be used if the number is singular.
   475  * @param string $single The text to be used if the number is singular.
   467  * @param string $plural The text to be used if the number is plural.
   476  * @param string $plural The text to be used if the number is plural.
   468  * @param int    $number The number to compare against to use either the singular or plural form.
   477  * @param int    $number The number to compare against to use either the singular or plural form.
   469  * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings.
   478  * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings.
   480 	 * @since 2.2.0
   489 	 * @since 2.2.0
   481 	 *
   490 	 *
   482 	 * @param string $translation Translated text.
   491 	 * @param string $translation Translated text.
   483 	 * @param string $single      The text to be used if the number is singular.
   492 	 * @param string $single      The text to be used if the number is singular.
   484 	 * @param string $plural      The text to be used if the number is plural.
   493 	 * @param string $plural      The text to be used if the number is plural.
   485 	 * @param string $number      The number to compare against to use either the singular or plural form.
   494 	 * @param int    $number      The number to compare against to use either the singular or plural form.
   486 	 * @param string $domain      Text domain. Unique identifier for retrieving translated strings.
   495 	 * @param string $domain      Text domain. Unique identifier for retrieving translated strings.
   487 	 */
   496 	 */
   488 	$translation = apply_filters( 'ngettext', $translation, $single, $plural, $number, $domain );
   497 	$translation = apply_filters( 'ngettext', $translation, $single, $plural, $number, $domain );
   489 
   498 
   490 	/**
   499 	/**
   495 	 * @since 5.5.0
   504 	 * @since 5.5.0
   496 	 *
   505 	 *
   497 	 * @param string $translation Translated text.
   506 	 * @param string $translation Translated text.
   498 	 * @param string $single      The text to be used if the number is singular.
   507 	 * @param string $single      The text to be used if the number is singular.
   499 	 * @param string $plural      The text to be used if the number is plural.
   508 	 * @param string $plural      The text to be used if the number is plural.
   500 	 * @param string $number      The number to compare against to use either the singular or plural form.
   509 	 * @param int    $number      The number to compare against to use either the singular or plural form.
   501 	 * @param string $domain      Text domain. Unique identifier for retrieving translated strings.
   510 	 * @param string $domain      Text domain. Unique identifier for retrieving translated strings.
   502 	 */
   511 	 */
   503 	$translation = apply_filters( "ngettext_{$domain}", $translation, $single, $plural, $number, $domain );
   512 	$translation = apply_filters( "ngettext_{$domain}", $translation, $single, $plural, $number, $domain );
   504 
   513 
   505 	return $translation;
   514 	return $translation;
   517  *
   526  *
   518  *     printf( _nx( '%s group', '%s groups', $people, 'group of people', 'text-domain' ), number_format_i18n( $people ) );
   527  *     printf( _nx( '%s group', '%s groups', $people, 'group of people', 'text-domain' ), number_format_i18n( $people ) );
   519  *     printf( _nx( '%s group', '%s groups', $animals, 'group of animals', 'text-domain' ), number_format_i18n( $animals ) );
   528  *     printf( _nx( '%s group', '%s groups', $animals, 'group of animals', 'text-domain' ), number_format_i18n( $animals ) );
   520  *
   529  *
   521  * @since 2.8.0
   530  * @since 2.8.0
   522  * @since 5.5.0 Introduced ngettext_with_context-{$domain} filter.
   531  * @since 5.5.0 Introduced `ngettext_with_context-{$domain}` filter.
   523  *
   532  *
   524  * @param string $single  The text to be used if the number is singular.
   533  * @param string $single  The text to be used if the number is singular.
   525  * @param string $plural  The text to be used if the number is plural.
   534  * @param string $plural  The text to be used if the number is plural.
   526  * @param int    $number  The number to compare against to use either the singular or plural form.
   535  * @param int    $number  The number to compare against to use either the singular or plural form.
   527  * @param string $context Context information for the translators.
   536  * @param string $context Context information for the translators.
   539 	 * @since 2.8.0
   548 	 * @since 2.8.0
   540 	 *
   549 	 *
   541 	 * @param string $translation Translated text.
   550 	 * @param string $translation Translated text.
   542 	 * @param string $single      The text to be used if the number is singular.
   551 	 * @param string $single      The text to be used if the number is singular.
   543 	 * @param string $plural      The text to be used if the number is plural.
   552 	 * @param string $plural      The text to be used if the number is plural.
   544 	 * @param string $number      The number to compare against to use either the singular or plural form.
   553 	 * @param int    $number      The number to compare against to use either the singular or plural form.
   545 	 * @param string $context     Context information for the translators.
   554 	 * @param string $context     Context information for the translators.
   546 	 * @param string $domain      Text domain. Unique identifier for retrieving translated strings.
   555 	 * @param string $domain      Text domain. Unique identifier for retrieving translated strings.
   547 	 */
   556 	 */
   548 	$translation = apply_filters( 'ngettext_with_context', $translation, $single, $plural, $number, $context, $domain );
   557 	$translation = apply_filters( 'ngettext_with_context', $translation, $single, $plural, $number, $context, $domain );
   549 
   558 
   555 	 * @since 5.5.0
   564 	 * @since 5.5.0
   556 	 *
   565 	 *
   557 	 * @param string $translation Translated text.
   566 	 * @param string $translation Translated text.
   558 	 * @param string $single      The text to be used if the number is singular.
   567 	 * @param string $single      The text to be used if the number is singular.
   559 	 * @param string $plural      The text to be used if the number is plural.
   568 	 * @param string $plural      The text to be used if the number is plural.
   560 	 * @param string $number      The number to compare against to use either the singular or plural form.
   569 	 * @param int    $number      The number to compare against to use either the singular or plural form.
   561 	 * @param string $context     Context information for the translators.
   570 	 * @param string $context     Context information for the translators.
   562 	 * @param string $domain      Text domain. Unique identifier for retrieving translated strings.
   571 	 * @param string $domain      Text domain. Unique identifier for retrieving translated strings.
   563 	 */
   572 	 */
   564 	$translation = apply_filters( "ngettext_with_context_{$domain}", $translation, $single, $plural, $number, $context, $domain );
   573 	$translation = apply_filters( "ngettext_with_context_{$domain}", $translation, $single, $plural, $number, $context, $domain );
   565 
   574 
   585  * @param string $domain   Optional. Text domain. Unique identifier for retrieving translated strings.
   594  * @param string $domain   Optional. Text domain. Unique identifier for retrieving translated strings.
   586  *                         Default null.
   595  *                         Default null.
   587  * @return array {
   596  * @return array {
   588  *     Array of translation information for the strings.
   597  *     Array of translation information for the strings.
   589  *
   598  *
   590  *     @type string $0        Singular form to be localized. No longer used.
   599  *     @type string      $0        Singular form to be localized. No longer used.
   591  *     @type string $1        Plural form to be localized. No longer used.
   600  *     @type string      $1        Plural form to be localized. No longer used.
   592  *     @type string $singular Singular form to be localized.
   601  *     @type string      $singular Singular form to be localized.
   593  *     @type string $plural   Plural form to be localized.
   602  *     @type string      $plural   Plural form to be localized.
   594  *     @type null   $context  Context information for the translators.
   603  *     @type null        $context  Context information for the translators.
   595  *     @type string $domain   Text domain.
   604  *     @type string|null $domain   Text domain.
   596  * }
   605  * }
   597  */
   606  */
   598 function _n_noop( $singular, $plural, $domain = null ) {
   607 function _n_noop( $singular, $plural, $domain = null ) {
   599 	return array(
   608 	return array(
   600 		0          => $singular,
   609 		0          => $singular,
   652 		'domain'   => $domain,
   661 		'domain'   => $domain,
   653 	);
   662 	);
   654 }
   663 }
   655 
   664 
   656 /**
   665 /**
   657  * Translates and retrieves the singular or plural form of a string that's been registered
   666  * Translates and returns the singular or plural form of a string that's been registered
   658  * with _n_noop() or _nx_noop().
   667  * with _n_noop() or _nx_noop().
   659  *
   668  *
   660  * Used when you want to use a translatable plural string once the number is known.
   669  * Used when you want to use a translatable plural string once the number is known.
   661  *
   670  *
   662  * Example:
   671  * Example:
   665  *     ...
   674  *     ...
   666  *     printf( translate_nooped_plural( $message, $count, 'text-domain' ), number_format_i18n( $count ) );
   675  *     printf( translate_nooped_plural( $message, $count, 'text-domain' ), number_format_i18n( $count ) );
   667  *
   676  *
   668  * @since 3.1.0
   677  * @since 3.1.0
   669  *
   678  *
   670  * @param array  $nooped_plural Array with singular, plural, and context keys, usually the result of _n_noop() or _nx_noop().
   679  * @param array  $nooped_plural {
       
   680  *     Array that is usually a return value from _n_noop() or _nx_noop().
       
   681  *
       
   682  *     @type string      $singular Singular form to be localized.
       
   683  *     @type string      $plural   Plural form to be localized.
       
   684  *     @type string|null $context  Context information for the translators.
       
   685  *     @type string|null $domain   Text domain.
       
   686  * }
   671  * @param int    $count         Number of objects.
   687  * @param int    $count         Number of objects.
   672  * @param string $domain        Optional. Text domain. Unique identifier for retrieving translated strings. If $nooped_plural contains
   688  * @param string $domain        Optional. Text domain. Unique identifier for retrieving translated strings. If $nooped_plural contains
   673  *                              a text domain passed to _n_noop() or _nx_noop(), it will override this value. Default 'default'.
   689  *                              a text domain passed to _n_noop() or _nx_noop(), it will override this value. Default 'default'.
   674  * @return string Either $single or $plural translated text.
   690  * @return string Either $singular or $plural translated text.
   675  */
   691  */
   676 function translate_nooped_plural( $nooped_plural, $count, $domain = 'default' ) {
   692 function translate_nooped_plural( $nooped_plural, $count, $domain = 'default' ) {
   677 	if ( $nooped_plural['domain'] ) {
   693 	if ( $nooped_plural['domain'] ) {
   678 		$domain = $nooped_plural['domain'];
   694 		$domain = $nooped_plural['domain'];
   679 	}
   695 	}
   684 		return _n( $nooped_plural['singular'], $nooped_plural['plural'], $count, $domain );
   700 		return _n( $nooped_plural['singular'], $nooped_plural['plural'], $count, $domain );
   685 	}
   701 	}
   686 }
   702 }
   687 
   703 
   688 /**
   704 /**
   689  * Load a .mo file into the text domain $domain.
   705  * Loads a .mo file into the text domain $domain.
   690  *
   706  *
   691  * If the text domain already exists, the translations will be merged. If both
   707  * If the text domain already exists, the translations will be merged. If both
   692  * sets have the same string, the translation from the original value will be taken.
   708  * sets have the same string, the translation from the original value will be taken.
   693  *
   709  *
   694  * On success, the .mo file will be placed in the $l10n global by $domain
   710  * On success, the .mo file will be placed in the $l10n global by $domain
   695  * and will be a MO object.
   711  * and will be a MO object.
   696  *
   712  *
   697  * @since 1.5.0
   713  * @since 1.5.0
       
   714  * @since 6.1.0 Added the `$locale` parameter.
       
   715  *
       
   716  * @global MO[]                   $l10n                   An array of all currently loaded text domains.
       
   717  * @global MO[]                   $l10n_unloaded          An array of all text domains that have been unloaded again.
       
   718  * @global WP_Textdomain_Registry $wp_textdomain_registry WordPress Textdomain Registry.
       
   719  *
       
   720  * @param string $domain Text domain. Unique identifier for retrieving translated strings.
       
   721  * @param string $mofile Path to the .mo file.
       
   722  * @param string $locale Optional. Locale. Default is the current locale.
       
   723  * @return bool True on success, false on failure.
       
   724  */
       
   725 function load_textdomain( $domain, $mofile, $locale = null ) {
       
   726 	/** @var WP_Textdomain_Registry $wp_textdomain_registry */
       
   727 	global $l10n, $l10n_unloaded, $wp_textdomain_registry;
       
   728 
       
   729 	$l10n_unloaded = (array) $l10n_unloaded;
       
   730 
       
   731 	if ( ! is_string( $domain ) ) {
       
   732 		return false;
       
   733 	}
       
   734 
       
   735 	/**
       
   736 	 * Filters whether to short-circuit loading .mo file.
       
   737 	 *
       
   738 	 * Returning a non-null value from the filter will effectively short-circuit
       
   739 	 * the loading, returning the passed value instead.
       
   740 	 *
       
   741 	 * @since 6.3.0
       
   742 	 *
       
   743 	 * @param bool|null   $loaded The result of loading a .mo file. Default null.
       
   744 	 * @param string      $domain Text domain. Unique identifier for retrieving translated strings.
       
   745 	 * @param string      $mofile Path to the MO file.
       
   746 	 * @param string|null $locale Locale.
       
   747 	 */
       
   748 	$loaded = apply_filters( 'pre_load_textdomain', null, $domain, $mofile, $locale );
       
   749 	if ( null !== $loaded ) {
       
   750 		if ( true === $loaded ) {
       
   751 			unset( $l10n_unloaded[ $domain ] );
       
   752 		}
       
   753 
       
   754 		return $loaded;
       
   755 	}
       
   756 
       
   757 	/**
       
   758 	 * Filters whether to override the .mo file loading.
       
   759 	 *
       
   760 	 * @since 2.9.0
       
   761 	 * @since 6.2.0 Added the `$locale` parameter.
       
   762 	 *
       
   763 	 * @param bool        $override Whether to override the .mo file loading. Default false.
       
   764 	 * @param string      $domain   Text domain. Unique identifier for retrieving translated strings.
       
   765 	 * @param string      $mofile   Path to the MO file.
       
   766 	 * @param string|null $locale   Locale.
       
   767 	 */
       
   768 	$plugin_override = apply_filters( 'override_load_textdomain', false, $domain, $mofile, $locale );
       
   769 
       
   770 	if ( true === (bool) $plugin_override ) {
       
   771 		unset( $l10n_unloaded[ $domain ] );
       
   772 
       
   773 		return true;
       
   774 	}
       
   775 
       
   776 	/**
       
   777 	 * Fires before the MO translation file is loaded.
       
   778 	 *
       
   779 	 * @since 2.9.0
       
   780 	 *
       
   781 	 * @param string $domain Text domain. Unique identifier for retrieving translated strings.
       
   782 	 * @param string $mofile Path to the .mo file.
       
   783 	 */
       
   784 	do_action( 'load_textdomain', $domain, $mofile );
       
   785 
       
   786 	/**
       
   787 	 * Filters MO file path for loading translations for a specific text domain.
       
   788 	 *
       
   789 	 * @since 2.9.0
       
   790 	 *
       
   791 	 * @param string $mofile Path to the MO file.
       
   792 	 * @param string $domain Text domain. Unique identifier for retrieving translated strings.
       
   793 	 */
       
   794 	$mofile = apply_filters( 'load_textdomain_mofile', $mofile, $domain );
       
   795 
       
   796 	if ( ! $locale ) {
       
   797 		$locale = determine_locale();
       
   798 	}
       
   799 
       
   800 	$i18n_controller = WP_Translation_Controller::get_instance();
       
   801 
       
   802 	// Ensures the correct locale is set as the current one, in case it was filtered.
       
   803 	$i18n_controller->set_locale( $locale );
       
   804 
       
   805 	/**
       
   806 	 * Filters the preferred file format for translation files.
       
   807 	 *
       
   808 	 * Can be used to disable the use of PHP files for translations.
       
   809 	 *
       
   810 	 * @since 6.5.0
       
   811 	 *
       
   812 	 * @param string $preferred_format Preferred file format. Possible values: 'php', 'mo'. Default: 'php'.
       
   813 	 * @param string $domain           The text domain.
       
   814 	 */
       
   815 	$preferred_format = apply_filters( 'translation_file_format', 'php', $domain );
       
   816 	if ( ! in_array( $preferred_format, array( 'php', 'mo' ), true ) ) {
       
   817 		$preferred_format = 'php';
       
   818 	}
       
   819 
       
   820 	$translation_files = array();
       
   821 
       
   822 	if ( 'mo' !== $preferred_format ) {
       
   823 		$translation_files[] = substr_replace( $mofile, ".l10n.$preferred_format", - strlen( '.mo' ) );
       
   824 	}
       
   825 
       
   826 	$translation_files[] = $mofile;
       
   827 
       
   828 	foreach ( $translation_files as $file ) {
       
   829 		/**
       
   830 		 * Filters the file path for loading translations for the given text domain.
       
   831 		 *
       
   832 		 * Similar to the {@see 'load_textdomain_mofile'} filter with the difference that
       
   833 		 * the file path could be for an MO or PHP file.
       
   834 		 *
       
   835 		 * @since 6.5.0
       
   836 		 * @since 6.6.0 Added the `$locale` parameter.
       
   837 		 *
       
   838 		 * @param string $file   Path to the translation file to load.
       
   839 		 * @param string $domain The text domain.
       
   840 		 * @param string $locale The locale.
       
   841 		 */
       
   842 		$file = (string) apply_filters( 'load_translation_file', $file, $domain, $locale );
       
   843 
       
   844 		$success = $i18n_controller->load_file( $file, $domain, $locale );
       
   845 
       
   846 		if ( $success ) {
       
   847 			if ( isset( $l10n[ $domain ] ) && $l10n[ $domain ] instanceof MO ) {
       
   848 				$i18n_controller->load_file( $l10n[ $domain ]->get_filename(), $domain, $locale );
       
   849 			}
       
   850 
       
   851 			// Unset NOOP_Translations reference in get_translations_for_domain().
       
   852 			unset( $l10n[ $domain ] );
       
   853 
       
   854 			$l10n[ $domain ] = new WP_Translations( $i18n_controller, $domain );
       
   855 
       
   856 			$wp_textdomain_registry->set( $domain, $locale, dirname( $file ) );
       
   857 
       
   858 			return true;
       
   859 		}
       
   860 	}
       
   861 
       
   862 	return false;
       
   863 }
       
   864 
       
   865 /**
       
   866  * Unloads translations for a text domain.
       
   867  *
       
   868  * @since 3.0.0
       
   869  * @since 6.1.0 Added the `$reloadable` parameter.
   698  *
   870  *
   699  * @global MO[] $l10n          An array of all currently loaded text domains.
   871  * @global MO[] $l10n          An array of all currently loaded text domains.
   700  * @global MO[] $l10n_unloaded An array of all text domains that have been unloaded again.
   872  * @global MO[] $l10n_unloaded An array of all text domains that have been unloaded again.
   701  *
   873  *
   702  * @param string $domain Text domain. Unique identifier for retrieving translated strings.
   874  * @param string $domain     Text domain. Unique identifier for retrieving translated strings.
   703  * @param string $mofile Path to the .mo file.
   875  * @param bool   $reloadable Whether the text domain can be loaded just-in-time again.
   704  * @return bool True on success, false on failure.
   876  * @return bool Whether textdomain was unloaded.
   705  */
   877  */
   706 function load_textdomain( $domain, $mofile ) {
   878 function unload_textdomain( $domain, $reloadable = false ) {
   707 	global $l10n, $l10n_unloaded;
   879 	global $l10n, $l10n_unloaded;
   708 
   880 
   709 	$l10n_unloaded = (array) $l10n_unloaded;
   881 	$l10n_unloaded = (array) $l10n_unloaded;
   710 
   882 
   711 	/**
   883 	/**
   712 	 * Filters whether to override the .mo file loading.
   884 	 * Filters whether to override the text domain unloading.
   713 	 *
   885 	 *
   714 	 * @since 2.9.0
   886 	 * @since 3.0.0
   715 	 *
   887 	 * @since 6.1.0 Added the `$reloadable` parameter.
   716 	 * @param bool   $override Whether to override the .mo file loading. Default false.
   888 	 *
   717 	 * @param string $domain   Text domain. Unique identifier for retrieving translated strings.
   889 	 * @param bool   $override   Whether to override the text domain unloading. Default false.
   718 	 * @param string $mofile   Path to the MO file.
   890 	 * @param string $domain     Text domain. Unique identifier for retrieving translated strings.
   719 	 */
   891 	 * @param bool   $reloadable Whether the text domain can be loaded just-in-time again.
   720 	$plugin_override = apply_filters( 'override_load_textdomain', false, $domain, $mofile );
   892 	 */
   721 
   893 	$plugin_override = apply_filters( 'override_unload_textdomain', false, $domain, $reloadable );
   722 	if ( true === (bool) $plugin_override ) {
   894 
   723 		unset( $l10n_unloaded[ $domain ] );
   895 	if ( $plugin_override ) {
       
   896 		if ( ! $reloadable ) {
       
   897 			$l10n_unloaded[ $domain ] = true;
       
   898 		}
   724 
   899 
   725 		return true;
   900 		return true;
   726 	}
   901 	}
   727 
   902 
   728 	/**
   903 	/**
   729 	 * Fires before the MO translation file is loaded.
   904 	 * Fires before the text domain is unloaded.
   730 	 *
   905 	 *
   731 	 * @since 2.9.0
   906 	 * @since 3.0.0
   732 	 *
   907 	 * @since 6.1.0 Added the `$reloadable` parameter.
   733 	 * @param string $domain Text domain. Unique identifier for retrieving translated strings.
   908 	 *
   734 	 * @param string $mofile Path to the .mo file.
   909 	 * @param string $domain     Text domain. Unique identifier for retrieving translated strings.
   735 	 */
   910 	 * @param bool   $reloadable Whether the text domain can be loaded just-in-time again.
   736 	do_action( 'load_textdomain', $domain, $mofile );
   911 	 */
   737 
   912 	do_action( 'unload_textdomain', $domain, $reloadable );
   738 	/**
   913 
   739 	 * Filters MO file path for loading translations for a specific text domain.
   914 	// Since multiple locales are supported, reloadable text domains don't actually need to be unloaded.
   740 	 *
   915 	if ( ! $reloadable ) {
   741 	 * @since 2.9.0
   916 		WP_Translation_Controller::get_instance()->unload_textdomain( $domain );
   742 	 *
       
   743 	 * @param string $mofile Path to the MO file.
       
   744 	 * @param string $domain Text domain. Unique identifier for retrieving translated strings.
       
   745 	 */
       
   746 	$mofile = apply_filters( 'load_textdomain_mofile', $mofile, $domain );
       
   747 
       
   748 	if ( ! is_readable( $mofile ) ) {
       
   749 		return false;
       
   750 	}
       
   751 
       
   752 	$mo = new MO();
       
   753 	if ( ! $mo->import_from_file( $mofile ) ) {
       
   754 		return false;
       
   755 	}
   917 	}
   756 
   918 
   757 	if ( isset( $l10n[ $domain ] ) ) {
   919 	if ( isset( $l10n[ $domain ] ) ) {
   758 		$mo->merge_with( $l10n[ $domain ] );
   920 		if ( $l10n[ $domain ] instanceof NOOP_Translations ) {
   759 	}
   921 			unset( $l10n[ $domain ] );
   760 
   922 
   761 	unset( $l10n_unloaded[ $domain ] );
   923 			return false;
   762 
   924 		}
   763 	$l10n[ $domain ] = &$mo;
   925 
   764 
   926 		unset( $l10n[ $domain ] );
   765 	return true;
   927 
   766 }
   928 		if ( ! $reloadable ) {
   767 
   929 			$l10n_unloaded[ $domain ] = true;
   768 /**
   930 		}
   769  * Unload translations for a text domain.
       
   770  *
       
   771  * @since 3.0.0
       
   772  *
       
   773  * @global MO[] $l10n          An array of all currently loaded text domains.
       
   774  * @global MO[] $l10n_unloaded An array of all text domains that have been unloaded again.
       
   775  *
       
   776  * @param string $domain Text domain. Unique identifier for retrieving translated strings.
       
   777  * @return bool Whether textdomain was unloaded.
       
   778  */
       
   779 function unload_textdomain( $domain ) {
       
   780 	global $l10n, $l10n_unloaded;
       
   781 
       
   782 	$l10n_unloaded = (array) $l10n_unloaded;
       
   783 
       
   784 	/**
       
   785 	 * Filters whether to override the text domain unloading.
       
   786 	 *
       
   787 	 * @since 3.0.0
       
   788 	 *
       
   789 	 * @param bool   $override Whether to override the text domain unloading. Default false.
       
   790 	 * @param string $domain   Text domain. Unique identifier for retrieving translated strings.
       
   791 	 */
       
   792 	$plugin_override = apply_filters( 'override_unload_textdomain', false, $domain );
       
   793 
       
   794 	if ( $plugin_override ) {
       
   795 		$l10n_unloaded[ $domain ] = true;
       
   796 
   931 
   797 		return true;
   932 		return true;
   798 	}
   933 	}
   799 
   934 
   800 	/**
       
   801 	 * Fires before the text domain is unloaded.
       
   802 	 *
       
   803 	 * @since 3.0.0
       
   804 	 *
       
   805 	 * @param string $domain Text domain. Unique identifier for retrieving translated strings.
       
   806 	 */
       
   807 	do_action( 'unload_textdomain', $domain );
       
   808 
       
   809 	if ( isset( $l10n[ $domain ] ) ) {
       
   810 		unset( $l10n[ $domain ] );
       
   811 
       
   812 		$l10n_unloaded[ $domain ] = true;
       
   813 
       
   814 		return true;
       
   815 	}
       
   816 
       
   817 	return false;
   935 	return false;
   818 }
   936 }
   819 
   937 
   820 /**
   938 /**
   821  * Load default translated strings based on locale.
   939  * Loads default translated strings based on locale.
   822  *
   940  *
   823  * Loads the .mo file in WP_LANG_DIR constant path from WordPress root.
   941  * Loads the .mo file in WP_LANG_DIR constant path from WordPress root.
   824  * The translated (.mo) file is named based on the locale.
   942  * The translated (.mo) file is named based on the locale.
   825  *
   943  *
   826  * @see load_textdomain()
   944  * @see load_textdomain()
   834 	if ( null === $locale ) {
   952 	if ( null === $locale ) {
   835 		$locale = determine_locale();
   953 		$locale = determine_locale();
   836 	}
   954 	}
   837 
   955 
   838 	// Unload previously loaded strings so we can switch translations.
   956 	// Unload previously loaded strings so we can switch translations.
   839 	unload_textdomain( 'default' );
   957 	unload_textdomain( 'default', true );
   840 
   958 
   841 	$return = load_textdomain( 'default', WP_LANG_DIR . "/$locale.mo" );
   959 	$return = load_textdomain( 'default', WP_LANG_DIR . "/$locale.mo", $locale );
   842 
   960 
   843 	if ( ( is_multisite() || ( defined( 'WP_INSTALLING_NETWORK' ) && WP_INSTALLING_NETWORK ) ) && ! file_exists( WP_LANG_DIR . "/admin-$locale.mo" ) ) {
   961 	if ( ( is_multisite() || ( defined( 'WP_INSTALLING_NETWORK' ) && WP_INSTALLING_NETWORK ) ) && ! file_exists( WP_LANG_DIR . "/admin-$locale.mo" ) ) {
   844 		load_textdomain( 'default', WP_LANG_DIR . "/ms-$locale.mo" );
   962 		load_textdomain( 'default', WP_LANG_DIR . "/ms-$locale.mo", $locale );
   845 		return $return;
   963 		return $return;
   846 	}
   964 	}
   847 
   965 
   848 	if ( is_admin() || wp_installing() || ( defined( 'WP_REPAIRING' ) && WP_REPAIRING ) ) {
   966 	if ( is_admin() || wp_installing() || ( defined( 'WP_REPAIRING' ) && WP_REPAIRING ) ) {
   849 		load_textdomain( 'default', WP_LANG_DIR . "/admin-$locale.mo" );
   967 		load_textdomain( 'default', WP_LANG_DIR . "/admin-$locale.mo", $locale );
   850 	}
   968 	}
   851 
   969 
   852 	if ( is_network_admin() || ( defined( 'WP_INSTALLING_NETWORK' ) && WP_INSTALLING_NETWORK ) ) {
   970 	if ( is_network_admin() || ( defined( 'WP_INSTALLING_NETWORK' ) && WP_INSTALLING_NETWORK ) ) {
   853 		load_textdomain( 'default', WP_LANG_DIR . "/admin-network-$locale.mo" );
   971 		load_textdomain( 'default', WP_LANG_DIR . "/admin-network-$locale.mo", $locale );
   854 	}
   972 	}
   855 
   973 
   856 	return $return;
   974 	return $return;
   857 }
   975 }
   858 
   976 
   872  * @param string|false $plugin_rel_path Optional. Relative path to WP_PLUGIN_DIR where the .mo file resides.
   990  * @param string|false $plugin_rel_path Optional. Relative path to WP_PLUGIN_DIR where the .mo file resides.
   873  *                                      Default false.
   991  *                                      Default false.
   874  * @return bool True when textdomain is successfully loaded, false otherwise.
   992  * @return bool True when textdomain is successfully loaded, false otherwise.
   875  */
   993  */
   876 function load_plugin_textdomain( $domain, $deprecated = false, $plugin_rel_path = false ) {
   994 function load_plugin_textdomain( $domain, $deprecated = false, $plugin_rel_path = false ) {
       
   995 	/** @var WP_Textdomain_Registry $wp_textdomain_registry */
       
   996 	global $wp_textdomain_registry;
       
   997 
       
   998 	if ( ! is_string( $domain ) ) {
       
   999 		return false;
       
  1000 	}
       
  1001 
   877 	/**
  1002 	/**
   878 	 * Filters a plugin's locale.
  1003 	 * Filters a plugin's locale.
   879 	 *
  1004 	 *
   880 	 * @since 3.0.0
  1005 	 * @since 3.0.0
   881 	 *
  1006 	 *
   885 	$locale = apply_filters( 'plugin_locale', determine_locale(), $domain );
  1010 	$locale = apply_filters( 'plugin_locale', determine_locale(), $domain );
   886 
  1011 
   887 	$mofile = $domain . '-' . $locale . '.mo';
  1012 	$mofile = $domain . '-' . $locale . '.mo';
   888 
  1013 
   889 	// Try to load from the languages directory first.
  1014 	// Try to load from the languages directory first.
   890 	if ( load_textdomain( $domain, WP_LANG_DIR . '/plugins/' . $mofile ) ) {
  1015 	if ( load_textdomain( $domain, WP_LANG_DIR . '/plugins/' . $mofile, $locale ) ) {
   891 		return true;
  1016 		return true;
   892 	}
  1017 	}
   893 
  1018 
   894 	if ( false !== $plugin_rel_path ) {
  1019 	if ( false !== $plugin_rel_path ) {
   895 		$path = WP_PLUGIN_DIR . '/' . trim( $plugin_rel_path, '/' );
  1020 		$path = WP_PLUGIN_DIR . '/' . trim( $plugin_rel_path, '/' );
   898 		$path = ABSPATH . trim( $deprecated, '/' );
  1023 		$path = ABSPATH . trim( $deprecated, '/' );
   899 	} else {
  1024 	} else {
   900 		$path = WP_PLUGIN_DIR;
  1025 		$path = WP_PLUGIN_DIR;
   901 	}
  1026 	}
   902 
  1027 
   903 	return load_textdomain( $domain, $path . '/' . $mofile );
  1028 	$wp_textdomain_registry->set_custom_path( $domain, $path );
   904 }
  1029 
   905 
  1030 	return load_textdomain( $domain, $path . '/' . $mofile, $locale );
   906 /**
  1031 }
   907  * Load the translated strings for a plugin residing in the mu-plugins directory.
  1032 
       
  1033 /**
       
  1034  * Loads the translated strings for a plugin residing in the mu-plugins directory.
   908  *
  1035  *
   909  * @since 3.0.0
  1036  * @since 3.0.0
   910  * @since 4.6.0 The function now tries to load the .mo file from the languages directory first.
  1037  * @since 4.6.0 The function now tries to load the .mo file from the languages directory first.
       
  1038  *
       
  1039  * @global WP_Textdomain_Registry $wp_textdomain_registry WordPress Textdomain Registry.
   911  *
  1040  *
   912  * @param string $domain             Text domain. Unique identifier for retrieving translated strings.
  1041  * @param string $domain             Text domain. Unique identifier for retrieving translated strings.
   913  * @param string $mu_plugin_rel_path Optional. Relative to `WPMU_PLUGIN_DIR` directory in which the .mo
  1042  * @param string $mu_plugin_rel_path Optional. Relative to `WPMU_PLUGIN_DIR` directory in which the .mo
   914  *                                   file resides. Default empty string.
  1043  *                                   file resides. Default empty string.
   915  * @return bool True when textdomain is successfully loaded, false otherwise.
  1044  * @return bool True when textdomain is successfully loaded, false otherwise.
   916  */
  1045  */
   917 function load_muplugin_textdomain( $domain, $mu_plugin_rel_path = '' ) {
  1046 function load_muplugin_textdomain( $domain, $mu_plugin_rel_path = '' ) {
       
  1047 	/** @var WP_Textdomain_Registry $wp_textdomain_registry */
       
  1048 	global $wp_textdomain_registry;
       
  1049 
       
  1050 	if ( ! is_string( $domain ) ) {
       
  1051 		return false;
       
  1052 	}
       
  1053 
   918 	/** This filter is documented in wp-includes/l10n.php */
  1054 	/** This filter is documented in wp-includes/l10n.php */
   919 	$locale = apply_filters( 'plugin_locale', determine_locale(), $domain );
  1055 	$locale = apply_filters( 'plugin_locale', determine_locale(), $domain );
   920 
  1056 
   921 	$mofile = $domain . '-' . $locale . '.mo';
  1057 	$mofile = $domain . '-' . $locale . '.mo';
   922 
  1058 
   923 	// Try to load from the languages directory first.
  1059 	// Try to load from the languages directory first.
   924 	if ( load_textdomain( $domain, WP_LANG_DIR . '/plugins/' . $mofile ) ) {
  1060 	if ( load_textdomain( $domain, WP_LANG_DIR . '/plugins/' . $mofile, $locale ) ) {
   925 		return true;
  1061 		return true;
   926 	}
  1062 	}
   927 
  1063 
   928 	$path = WPMU_PLUGIN_DIR . '/' . ltrim( $mu_plugin_rel_path, '/' );
  1064 	$path = WPMU_PLUGIN_DIR . '/' . ltrim( $mu_plugin_rel_path, '/' );
   929 
  1065 
   930 	return load_textdomain( $domain, $path . '/' . $mofile );
  1066 	$wp_textdomain_registry->set_custom_path( $domain, $path );
   931 }
  1067 
   932 
  1068 	return load_textdomain( $domain, $path . '/' . $mofile, $locale );
   933 /**
  1069 }
   934  * Load the theme's translated strings.
  1070 
       
  1071 /**
       
  1072  * Loads the theme's translated strings.
   935  *
  1073  *
   936  * If the current locale exists as a .mo file in the theme's root directory, it
  1074  * If the current locale exists as a .mo file in the theme's root directory, it
   937  * will be included in the translated strings by the $domain.
  1075  * will be included in the translated strings by the $domain.
   938  *
  1076  *
   939  * The .mo files must be named based on the locale exactly.
  1077  * The .mo files must be named based on the locale exactly.
   940  *
  1078  *
   941  * @since 1.5.0
  1079  * @since 1.5.0
   942  * @since 4.6.0 The function now tries to load the .mo file from the languages directory first.
  1080  * @since 4.6.0 The function now tries to load the .mo file from the languages directory first.
       
  1081  *
       
  1082  * @global WP_Textdomain_Registry $wp_textdomain_registry WordPress Textdomain Registry.
   943  *
  1083  *
   944  * @param string       $domain Text domain. Unique identifier for retrieving translated strings.
  1084  * @param string       $domain Text domain. Unique identifier for retrieving translated strings.
   945  * @param string|false $path   Optional. Path to the directory containing the .mo file.
  1085  * @param string|false $path   Optional. Path to the directory containing the .mo file.
   946  *                             Default false.
  1086  *                             Default false.
   947  * @return bool True when textdomain is successfully loaded, false otherwise.
  1087  * @return bool True when textdomain is successfully loaded, false otherwise.
   948  */
  1088  */
   949 function load_theme_textdomain( $domain, $path = false ) {
  1089 function load_theme_textdomain( $domain, $path = false ) {
       
  1090 	/** @var WP_Textdomain_Registry $wp_textdomain_registry */
       
  1091 	global $wp_textdomain_registry;
       
  1092 
       
  1093 	if ( ! is_string( $domain ) ) {
       
  1094 		return false;
       
  1095 	}
       
  1096 
   950 	/**
  1097 	/**
   951 	 * Filters a theme's locale.
  1098 	 * Filters a theme's locale.
   952 	 *
  1099 	 *
   953 	 * @since 3.0.0
  1100 	 * @since 3.0.0
   954 	 *
  1101 	 *
   958 	$locale = apply_filters( 'theme_locale', determine_locale(), $domain );
  1105 	$locale = apply_filters( 'theme_locale', determine_locale(), $domain );
   959 
  1106 
   960 	$mofile = $domain . '-' . $locale . '.mo';
  1107 	$mofile = $domain . '-' . $locale . '.mo';
   961 
  1108 
   962 	// Try to load from the languages directory first.
  1109 	// Try to load from the languages directory first.
   963 	if ( load_textdomain( $domain, WP_LANG_DIR . '/themes/' . $mofile ) ) {
  1110 	if ( load_textdomain( $domain, WP_LANG_DIR . '/themes/' . $mofile, $locale ) ) {
   964 		return true;
  1111 		return true;
   965 	}
  1112 	}
   966 
  1113 
   967 	if ( ! $path ) {
  1114 	if ( ! $path ) {
   968 		$path = get_template_directory();
  1115 		$path = get_template_directory();
   969 	}
  1116 	}
   970 
  1117 
   971 	return load_textdomain( $domain, $path . '/' . $locale . '.mo' );
  1118 	$wp_textdomain_registry->set_custom_path( $domain, $path );
   972 }
  1119 
   973 
  1120 	return load_textdomain( $domain, $path . '/' . $locale . '.mo', $locale );
   974 /**
  1121 }
   975  * Load the child themes translated strings.
  1122 
   976  *
  1123 /**
   977  * If the current locale exists as a .mo file in the child themes
  1124  * Loads the child theme's translated strings.
       
  1125  *
       
  1126  * If the current locale exists as a .mo file in the child theme's
   978  * root directory, it will be included in the translated strings by the $domain.
  1127  * root directory, it will be included in the translated strings by the $domain.
   979  *
  1128  *
   980  * The .mo files must be named based on the locale exactly.
  1129  * The .mo files must be named based on the locale exactly.
   981  *
  1130  *
   982  * @since 2.9.0
  1131  * @since 2.9.0
  1006  * @param string $domain Optional. Text domain. Default 'default'.
  1155  * @param string $domain Optional. Text domain. Default 'default'.
  1007  * @param string $path   Optional. The full file path to the directory containing translation files.
  1156  * @param string $path   Optional. The full file path to the directory containing translation files.
  1008  * @return string|false The translated strings in JSON encoding on success,
  1157  * @return string|false The translated strings in JSON encoding on success,
  1009  *                      false if the script textdomain could not be loaded.
  1158  *                      false if the script textdomain could not be loaded.
  1010  */
  1159  */
  1011 function load_script_textdomain( $handle, $domain = 'default', $path = null ) {
  1160 function load_script_textdomain( $handle, $domain = 'default', $path = '' ) {
  1012 	$wp_scripts = wp_scripts();
  1161 	$wp_scripts = wp_scripts();
  1013 
  1162 
  1014 	if ( ! isset( $wp_scripts->registered[ $handle ] ) ) {
  1163 	if ( ! isset( $wp_scripts->registered[ $handle ] ) ) {
  1015 		return false;
  1164 		return false;
  1016 	}
  1165 	}
  1030 		}
  1179 		}
  1031 	}
  1180 	}
  1032 
  1181 
  1033 	$src = $wp_scripts->registered[ $handle ]->src;
  1182 	$src = $wp_scripts->registered[ $handle ]->src;
  1034 
  1183 
  1035 	if ( ! preg_match( '|^(https?:)?//|', $src ) && ! ( $wp_scripts->content_url && 0 === strpos( $src, $wp_scripts->content_url ) ) ) {
  1184 	if ( ! preg_match( '|^(https?:)?//|', $src ) && ! ( $wp_scripts->content_url && str_starts_with( $src, $wp_scripts->content_url ) ) ) {
  1036 		$src = $wp_scripts->base_url . $src;
  1185 		$src = $wp_scripts->base_url . $src;
  1037 	}
  1186 	}
  1038 
  1187 
  1039 	$relative       = false;
  1188 	$relative       = false;
  1040 	$languages_path = WP_LANG_DIR;
  1189 	$languages_path = WP_LANG_DIR;
  1044 	$plugins_url = wp_parse_url( plugins_url() );
  1193 	$plugins_url = wp_parse_url( plugins_url() );
  1045 	$site_url    = wp_parse_url( site_url() );
  1194 	$site_url    = wp_parse_url( site_url() );
  1046 
  1195 
  1047 	// If the host is the same or it's a relative URL.
  1196 	// If the host is the same or it's a relative URL.
  1048 	if (
  1197 	if (
  1049 		( ! isset( $content_url['path'] ) || strpos( $src_url['path'], $content_url['path'] ) === 0 ) &&
  1198 		( ! isset( $content_url['path'] ) || str_starts_with( $src_url['path'], $content_url['path'] ) ) &&
  1050 		( ! isset( $src_url['host'] ) || ! isset( $content_url['host'] ) || $src_url['host'] === $content_url['host'] )
  1199 		( ! isset( $src_url['host'] ) || ! isset( $content_url['host'] ) || $src_url['host'] === $content_url['host'] )
  1051 	) {
  1200 	) {
  1052 		// Make the src relative the specific plugin or theme.
  1201 		// Make the src relative the specific plugin or theme.
  1053 		if ( isset( $content_url['path'] ) ) {
  1202 		if ( isset( $content_url['path'] ) ) {
  1054 			$relative = substr( $src_url['path'], strlen( $content_url['path'] ) );
  1203 			$relative = substr( $src_url['path'], strlen( $content_url['path'] ) );
  1056 			$relative = $src_url['path'];
  1205 			$relative = $src_url['path'];
  1057 		}
  1206 		}
  1058 		$relative = trim( $relative, '/' );
  1207 		$relative = trim( $relative, '/' );
  1059 		$relative = explode( '/', $relative );
  1208 		$relative = explode( '/', $relative );
  1060 
  1209 
  1061 		$languages_path = WP_LANG_DIR . '/' . $relative[0];
  1210 		$languages_path = WP_LANG_DIR . '/plugins';
  1062 
  1211 
  1063 		$relative = array_slice( $relative, 2 ); // Remove plugins/<plugin name> or themes/<theme name>.
  1212 		$relative = array_slice( $relative, 2 ); // Remove plugins/<plugin name> or themes/<theme name>.
  1064 		$relative = implode( '/', $relative );
  1213 		$relative = implode( '/', $relative );
  1065 	} elseif (
  1214 	} elseif (
  1066 		( ! isset( $plugins_url['path'] ) || strpos( $src_url['path'], $plugins_url['path'] ) === 0 ) &&
  1215 		( ! isset( $plugins_url['path'] ) || str_starts_with( $src_url['path'], $plugins_url['path'] ) ) &&
  1067 		( ! isset( $src_url['host'] ) || ! isset( $plugins_url['host'] ) || $src_url['host'] === $plugins_url['host'] )
  1216 		( ! isset( $src_url['host'] ) || ! isset( $plugins_url['host'] ) || $src_url['host'] === $plugins_url['host'] )
  1068 	) {
  1217 	) {
  1069 		// Make the src relative the specific plugin.
  1218 		// Make the src relative the specific plugin.
  1070 		if ( isset( $plugins_url['path'] ) ) {
  1219 		if ( isset( $plugins_url['path'] ) ) {
  1071 			$relative = substr( $src_url['path'], strlen( $plugins_url['path'] ) );
  1220 			$relative = substr( $src_url['path'], strlen( $plugins_url['path'] ) );
  1080 		$relative = array_slice( $relative, 1 ); // Remove <plugin name>.
  1229 		$relative = array_slice( $relative, 1 ); // Remove <plugin name>.
  1081 		$relative = implode( '/', $relative );
  1230 		$relative = implode( '/', $relative );
  1082 	} elseif ( ! isset( $src_url['host'] ) || ! isset( $site_url['host'] ) || $src_url['host'] === $site_url['host'] ) {
  1231 	} elseif ( ! isset( $src_url['host'] ) || ! isset( $site_url['host'] ) || $src_url['host'] === $site_url['host'] ) {
  1083 		if ( ! isset( $site_url['path'] ) ) {
  1232 		if ( ! isset( $site_url['path'] ) ) {
  1084 			$relative = trim( $src_url['path'], '/' );
  1233 			$relative = trim( $src_url['path'], '/' );
  1085 		} elseif ( ( strpos( $src_url['path'], trailingslashit( $site_url['path'] ) ) === 0 ) ) {
  1234 		} elseif ( str_starts_with( $src_url['path'], trailingslashit( $site_url['path'] ) ) ) {
  1086 			// Make the src relative to the WP root.
  1235 			// Make the src relative to the WP root.
  1087 			$relative = substr( $src_url['path'], strlen( $site_url['path'] ) );
  1236 			$relative = substr( $src_url['path'], strlen( $site_url['path'] ) );
  1088 			$relative = trim( $relative, '/' );
  1237 			$relative = trim( $relative, '/' );
  1089 		}
  1238 		}
  1090 	}
  1239 	}
  1103 	if ( false === $relative ) {
  1252 	if ( false === $relative ) {
  1104 		return load_script_translations( false, $handle, $domain );
  1253 		return load_script_translations( false, $handle, $domain );
  1105 	}
  1254 	}
  1106 
  1255 
  1107 	// Translations are always based on the unminified filename.
  1256 	// Translations are always based on the unminified filename.
  1108 	if ( substr( $relative, -7 ) === '.min.js' ) {
  1257 	if ( str_ends_with( $relative, '.min.js' ) ) {
  1109 		$relative = substr( $relative, 0, -7 ) . '.js';
  1258 		$relative = substr( $relative, 0, -7 ) . '.js';
  1110 	}
  1259 	}
  1111 
  1260 
  1112 	$md5_filename = $file_base . '-' . md5( $relative ) . '.json';
  1261 	$md5_filename = $file_base . '-' . md5( $relative ) . '.json';
  1113 
  1262 
  1187 	 */
  1336 	 */
  1188 	return apply_filters( 'load_script_translations', $translations, $file, $handle, $domain );
  1337 	return apply_filters( 'load_script_translations', $translations, $file, $handle, $domain );
  1189 }
  1338 }
  1190 
  1339 
  1191 /**
  1340 /**
  1192  * Loads plugin and theme textdomains just-in-time.
  1341  * Loads plugin and theme text domains just-in-time.
  1193  *
  1342  *
  1194  * When a textdomain is encountered for the first time, we try to load
  1343  * When a textdomain is encountered for the first time, we try to load
  1195  * the translation file from `wp-content/languages`, removing the need
  1344  * the translation file from `wp-content/languages`, removing the need
  1196  * to call load_plugin_texdomain() or load_theme_texdomain().
  1345  * to call load_plugin_textdomain() or load_theme_textdomain().
  1197  *
  1346  *
  1198  * @since 4.6.0
  1347  * @since 4.6.0
  1199  * @access private
  1348  * @access private
  1200  *
  1349  *
  1201  * @see get_translations_for_domain()
  1350  * @global MO[]                   $l10n_unloaded          An array of all text domains that have been unloaded again.
  1202  * @global MO[] $l10n_unloaded An array of all text domains that have been unloaded again.
  1351  * @global WP_Textdomain_Registry $wp_textdomain_registry WordPress Textdomain Registry.
  1203  *
  1352  *
  1204  * @param string $domain Text domain. Unique identifier for retrieving translated strings.
  1353  * @param string $domain Text domain. Unique identifier for retrieving translated strings.
  1205  * @return bool True when the textdomain is successfully loaded, false otherwise.
  1354  * @return bool True when the textdomain is successfully loaded, false otherwise.
  1206  */
  1355  */
  1207 function _load_textdomain_just_in_time( $domain ) {
  1356 function _load_textdomain_just_in_time( $domain ) {
  1208 	global $l10n_unloaded;
  1357 	/** @var WP_Textdomain_Registry $wp_textdomain_registry */
       
  1358 	global $l10n_unloaded, $wp_textdomain_registry;
  1209 
  1359 
  1210 	$l10n_unloaded = (array) $l10n_unloaded;
  1360 	$l10n_unloaded = (array) $l10n_unloaded;
  1211 
  1361 
  1212 	// Short-circuit if domain is 'default' which is reserved for core.
  1362 	// Short-circuit if domain is 'default' which is reserved for core.
  1213 	if ( 'default' === $domain || isset( $l10n_unloaded[ $domain ] ) ) {
  1363 	if ( 'default' === $domain || isset( $l10n_unloaded[ $domain ] ) ) {
  1214 		return false;
  1364 		return false;
  1215 	}
  1365 	}
  1216 
  1366 
  1217 	$translation_path = _get_path_to_translation( $domain );
  1367 	if ( ! $wp_textdomain_registry->has( $domain ) ) {
  1218 	if ( false === $translation_path ) {
       
  1219 		return false;
  1368 		return false;
  1220 	}
  1369 	}
  1221 
  1370 
  1222 	return load_textdomain( $domain, $translation_path );
       
  1223 }
       
  1224 
       
  1225 /**
       
  1226  * Gets the path to a translation file for loading a textdomain just in time.
       
  1227  *
       
  1228  * Caches the retrieved results internally.
       
  1229  *
       
  1230  * @since 4.7.0
       
  1231  * @access private
       
  1232  *
       
  1233  * @see _load_textdomain_just_in_time()
       
  1234  *
       
  1235  * @param string $domain Text domain. Unique identifier for retrieving translated strings.
       
  1236  * @param bool   $reset  Whether to reset the internal cache. Used by the switch to locale functionality.
       
  1237  * @return string|false The path to the translation file or false if no translation file was found.
       
  1238  */
       
  1239 function _get_path_to_translation( $domain, $reset = false ) {
       
  1240 	static $available_translations = array();
       
  1241 
       
  1242 	if ( true === $reset ) {
       
  1243 		$available_translations = array();
       
  1244 	}
       
  1245 
       
  1246 	if ( ! isset( $available_translations[ $domain ] ) ) {
       
  1247 		$available_translations[ $domain ] = _get_path_to_translation_from_lang_dir( $domain );
       
  1248 	}
       
  1249 
       
  1250 	return $available_translations[ $domain ];
       
  1251 }
       
  1252 
       
  1253 /**
       
  1254  * Gets the path to a translation file in the languages directory for the current locale.
       
  1255  *
       
  1256  * Holds a cached list of available .mo files to improve performance.
       
  1257  *
       
  1258  * @since 4.7.0
       
  1259  * @access private
       
  1260  *
       
  1261  * @see _get_path_to_translation()
       
  1262  *
       
  1263  * @param string $domain Text domain. Unique identifier for retrieving translated strings.
       
  1264  * @return string|false The path to the translation file or false if no translation file was found.
       
  1265  */
       
  1266 function _get_path_to_translation_from_lang_dir( $domain ) {
       
  1267 	static $cached_mofiles = null;
       
  1268 
       
  1269 	if ( null === $cached_mofiles ) {
       
  1270 		$cached_mofiles = array();
       
  1271 
       
  1272 		$locations = array(
       
  1273 			WP_LANG_DIR . '/plugins',
       
  1274 			WP_LANG_DIR . '/themes',
       
  1275 		);
       
  1276 
       
  1277 		foreach ( $locations as $location ) {
       
  1278 			$mofiles = glob( $location . '/*.mo' );
       
  1279 			if ( $mofiles ) {
       
  1280 				$cached_mofiles = array_merge( $cached_mofiles, $mofiles );
       
  1281 			}
       
  1282 		}
       
  1283 	}
       
  1284 
       
  1285 	$locale = determine_locale();
  1371 	$locale = determine_locale();
  1286 	$mofile = "{$domain}-{$locale}.mo";
  1372 	$path   = $wp_textdomain_registry->get( $domain, $locale );
  1287 
  1373 	if ( ! $path ) {
  1288 	$path = WP_LANG_DIR . '/plugins/' . $mofile;
  1374 		return false;
  1289 	if ( in_array( $path, $cached_mofiles, true ) ) {
  1375 	}
  1290 		return $path;
  1376 	// Themes with their language directory outside of WP_LANG_DIR have a different file name.
  1291 	}
  1377 	$template_directory   = trailingslashit( get_template_directory() );
  1292 
  1378 	$stylesheet_directory = trailingslashit( get_stylesheet_directory() );
  1293 	$path = WP_LANG_DIR . '/themes/' . $mofile;
  1379 	if ( str_starts_with( $path, $template_directory ) || str_starts_with( $path, $stylesheet_directory ) ) {
  1294 	if ( in_array( $path, $cached_mofiles, true ) ) {
  1380 		$mofile = "{$path}{$locale}.mo";
  1295 		return $path;
  1381 	} else {
  1296 	}
  1382 		$mofile = "{$path}{$domain}-{$locale}.mo";
  1297 
  1383 	}
  1298 	return false;
  1384 
  1299 }
  1385 	return load_textdomain( $domain, $mofile, $locale );
  1300 
  1386 }
  1301 /**
  1387 
  1302  * Return the Translations instance for a text domain.
  1388 /**
       
  1389  * Returns the Translations instance for a text domain.
  1303  *
  1390  *
  1304  * If there isn't one, returns empty Translations instance.
  1391  * If there isn't one, returns empty Translations instance.
  1305  *
  1392  *
  1306  * @since 2.8.0
  1393  * @since 2.8.0
  1307  *
  1394  *
  1308  * @global MO[] $l10n
  1395  * @global MO[] $l10n An array of all currently loaded text domains.
  1309  *
  1396  *
  1310  * @param string $domain Text domain. Unique identifier for retrieving translated strings.
  1397  * @param string $domain Text domain. Unique identifier for retrieving translated strings.
  1311  * @return Translations|NOOP_Translations A Translations instance.
  1398  * @return Translations|NOOP_Translations A Translations instance.
  1312  */
  1399  */
  1313 function get_translations_for_domain( $domain ) {
  1400 function get_translations_for_domain( $domain ) {
  1316 		return $l10n[ $domain ];
  1403 		return $l10n[ $domain ];
  1317 	}
  1404 	}
  1318 
  1405 
  1319 	static $noop_translations = null;
  1406 	static $noop_translations = null;
  1320 	if ( null === $noop_translations ) {
  1407 	if ( null === $noop_translations ) {
  1321 		$noop_translations = new NOOP_Translations;
  1408 		$noop_translations = new NOOP_Translations();
  1322 	}
  1409 	}
       
  1410 
       
  1411 	$l10n[ $domain ] = &$noop_translations;
  1323 
  1412 
  1324 	return $noop_translations;
  1413 	return $noop_translations;
  1325 }
  1414 }
  1326 
  1415 
  1327 /**
  1416 /**
  1328  * Whether there are translations for the text domain.
  1417  * Determines whether there are translations for the text domain.
  1329  *
  1418  *
  1330  * @since 3.0.0
  1419  * @since 3.0.0
  1331  *
  1420  *
  1332  * @global MO[] $l10n
  1421  * @global MO[] $l10n An array of all currently loaded text domains.
  1333  *
  1422  *
  1334  * @param string $domain Text domain. Unique identifier for retrieving translated strings.
  1423  * @param string $domain Text domain. Unique identifier for retrieving translated strings.
  1335  * @return bool Whether there are translations.
  1424  * @return bool Whether there are translations.
  1336  */
  1425  */
  1337 function is_textdomain_loaded( $domain ) {
  1426 function is_textdomain_loaded( $domain ) {
  1338 	global $l10n;
  1427 	global $l10n;
  1339 	return isset( $l10n[ $domain ] );
  1428 	return isset( $l10n[ $domain ] ) && ! $l10n[ $domain ] instanceof NOOP_Translations;
  1340 }
  1429 }
  1341 
  1430 
  1342 /**
  1431 /**
  1343  * Translates role name.
  1432  * Translates role name.
  1344  *
  1433  *
  1362 function translate_user_role( $name, $domain = 'default' ) {
  1451 function translate_user_role( $name, $domain = 'default' ) {
  1363 	return translate_with_gettext_context( before_last_bar( $name ), 'User role', $domain );
  1452 	return translate_with_gettext_context( before_last_bar( $name ), 'User role', $domain );
  1364 }
  1453 }
  1365 
  1454 
  1366 /**
  1455 /**
  1367  * Get all available languages based on the presence of *.mo files in a given directory.
  1456  * Gets all available languages based on the presence of *.mo and *.l10n.php files in a given directory.
  1368  *
  1457  *
  1369  * The default directory is WP_LANG_DIR.
  1458  * The default directory is WP_LANG_DIR.
  1370  *
  1459  *
  1371  * @since 3.0.0
  1460  * @since 3.0.0
  1372  * @since 4.7.0 The results are now filterable with the {@see 'get_available_languages'} filter.
  1461  * @since 4.7.0 The results are now filterable with the {@see 'get_available_languages'} filter.
       
  1462  * @since 6.5.0 The initial file list is now cached and also takes into account *.l10n.php files.
       
  1463  *
       
  1464  * @global WP_Textdomain_Registry $wp_textdomain_registry WordPress Textdomain Registry.
  1373  *
  1465  *
  1374  * @param string $dir A directory to search for language files.
  1466  * @param string $dir A directory to search for language files.
  1375  *                    Default WP_LANG_DIR.
  1467  *                    Default WP_LANG_DIR.
  1376  * @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.
  1468  * @return string[] An array of language codes or an empty array if no languages are present.
       
  1469  *                  Language codes are formed by stripping the file extension from the language file names.
  1377  */
  1470  */
  1378 function get_available_languages( $dir = null ) {
  1471 function get_available_languages( $dir = null ) {
       
  1472 	global $wp_textdomain_registry;
       
  1473 
  1379 	$languages = array();
  1474 	$languages = array();
  1380 
  1475 
  1381 	$lang_files = glob( ( is_null( $dir ) ? WP_LANG_DIR : $dir ) . '/*.mo' );
  1476 	$path       = is_null( $dir ) ? WP_LANG_DIR : $dir;
       
  1477 	$lang_files = $wp_textdomain_registry->get_language_files_from_path( $path );
       
  1478 
  1382 	if ( $lang_files ) {
  1479 	if ( $lang_files ) {
  1383 		foreach ( $lang_files as $lang_file ) {
  1480 		foreach ( $lang_files as $lang_file ) {
  1384 			$lang_file = basename( $lang_file, '.mo' );
  1481 			$lang_file = basename( $lang_file, '.mo' );
  1385 			if ( 0 !== strpos( $lang_file, 'continents-cities' ) && 0 !== strpos( $lang_file, 'ms-' ) &&
  1482 			$lang_file = basename( $lang_file, '.l10n.php' );
  1386 				0 !== strpos( $lang_file, 'admin-' ) ) {
  1483 
       
  1484 			if ( ! str_starts_with( $lang_file, 'continents-cities' ) && ! str_starts_with( $lang_file, 'ms-' ) &&
       
  1485 				! str_starts_with( $lang_file, 'admin-' ) ) {
  1387 				$languages[] = $lang_file;
  1486 				$languages[] = $lang_file;
  1388 			}
  1487 			}
  1389 		}
  1488 		}
  1390 	}
  1489 	}
  1391 
  1490 
  1395 	 * @since 4.7.0
  1494 	 * @since 4.7.0
  1396 	 *
  1495 	 *
  1397 	 * @param string[] $languages An array of available language codes.
  1496 	 * @param string[] $languages An array of available language codes.
  1398 	 * @param string   $dir       The directory where the language files were found.
  1497 	 * @param string   $dir       The directory where the language files were found.
  1399 	 */
  1498 	 */
  1400 	return apply_filters( 'get_available_languages', $languages, $dir );
  1499 	return apply_filters( 'get_available_languages', array_unique( $languages ), $dir );
  1401 }
  1500 }
  1402 
  1501 
  1403 /**
  1502 /**
  1404  * Get installed translations.
  1503  * Gets installed translations.
  1405  *
  1504  *
  1406  * Looks in the wp-content/languages directory for translations of
  1505  * Looks in the wp-content/languages directory for translations of
  1407  * plugins or themes.
  1506  * plugins or themes.
  1408  *
  1507  *
  1409  * @since 3.7.0
  1508  * @since 3.7.0
  1410  *
  1509  *
       
  1510  * @global WP_Textdomain_Registry $wp_textdomain_registry WordPress Textdomain Registry.
       
  1511  *
  1411  * @param string $type What to search for. Accepts 'plugins', 'themes', 'core'.
  1512  * @param string $type What to search for. Accepts 'plugins', 'themes', 'core'.
  1412  * @return array Array of language data.
  1513  * @return array Array of language data.
  1413  */
  1514  */
  1414 function wp_get_installed_translations( $type ) {
  1515 function wp_get_installed_translations( $type ) {
       
  1516 	global $wp_textdomain_registry;
       
  1517 
  1415 	if ( 'themes' !== $type && 'plugins' !== $type && 'core' !== $type ) {
  1518 	if ( 'themes' !== $type && 'plugins' !== $type && 'core' !== $type ) {
  1416 		return array();
  1519 		return array();
  1417 	}
  1520 	}
  1418 
  1521 
  1419 	$dir = 'core' === $type ? '' : "/$type";
  1522 	$dir = 'core' === $type ? WP_LANG_DIR : WP_LANG_DIR . "/$type";
  1420 
  1523 
  1421 	if ( ! is_dir( WP_LANG_DIR ) ) {
  1524 	if ( ! is_dir( $dir ) ) {
  1422 		return array();
  1525 		return array();
  1423 	}
  1526 	}
  1424 
  1527 
  1425 	if ( $dir && ! is_dir( WP_LANG_DIR . $dir ) ) {
  1528 	$files = $wp_textdomain_registry->get_language_files_from_path( $dir );
  1426 		return array();
       
  1427 	}
       
  1428 
       
  1429 	$files = scandir( WP_LANG_DIR . $dir );
       
  1430 	if ( ! $files ) {
  1529 	if ( ! $files ) {
  1431 		return array();
  1530 		return array();
  1432 	}
  1531 	}
  1433 
  1532 
  1434 	$language_data = array();
  1533 	$language_data = array();
  1435 
  1534 
  1436 	foreach ( $files as $file ) {
  1535 	foreach ( $files as $file ) {
  1437 		if ( '.' === $file[0] || is_dir( WP_LANG_DIR . "$dir/$file" ) ) {
  1536 		if ( ! preg_match( '/(?:(.+)-)?([a-z]{2,3}(?:_[A-Z]{2})?(?:_[a-z0-9]+)?)\.(?:mo|l10n\.php)/', basename( $file ), $match ) ) {
  1438 			continue;
       
  1439 		}
       
  1440 		if ( substr( $file, -3 ) !== '.po' ) {
       
  1441 			continue;
       
  1442 		}
       
  1443 		if ( ! preg_match( '/(?:(.+)-)?([a-z]{2,3}(?:_[A-Z]{2})?(?:_[a-z0-9]+)?).po/', $file, $match ) ) {
       
  1444 			continue;
       
  1445 		}
       
  1446 		if ( ! in_array( substr( $file, 0, -3 ) . '.mo', $files, true ) ) {
       
  1447 			continue;
  1537 			continue;
  1448 		}
  1538 		}
  1449 
  1539 
  1450 		list( , $textdomain, $language ) = $match;
  1540 		list( , $textdomain, $language ) = $match;
  1451 		if ( '' === $textdomain ) {
  1541 		if ( '' === $textdomain ) {
  1452 			$textdomain = 'default';
  1542 			$textdomain = 'default';
  1453 		}
  1543 		}
  1454 		$language_data[ $textdomain ][ $language ] = wp_get_pomo_file_data( WP_LANG_DIR . "$dir/$file" );
  1544 
       
  1545 		if ( str_ends_with( $file, '.mo' ) ) {
       
  1546 			$pofile = substr_replace( $file, '.po', - strlen( '.mo' ) );
       
  1547 
       
  1548 			if ( ! file_exists( $pofile ) ) {
       
  1549 				continue;
       
  1550 			}
       
  1551 
       
  1552 			$language_data[ $textdomain ][ $language ] = wp_get_pomo_file_data( $pofile );
       
  1553 		} else {
       
  1554 			$pofile = substr_replace( $file, '.po', - strlen( '.l10n.php' ) );
       
  1555 
       
  1556 			// If both a PO and a PHP file exist, prefer the PO file.
       
  1557 			if ( file_exists( $pofile ) ) {
       
  1558 				continue;
       
  1559 			}
       
  1560 
       
  1561 			$language_data[ $textdomain ][ $language ] = wp_get_l10n_php_file_data( $file );
       
  1562 		}
  1455 	}
  1563 	}
  1456 	return $language_data;
  1564 	return $language_data;
  1457 }
  1565 }
  1458 
  1566 
  1459 /**
  1567 /**
  1460  * Extract headers from a PO file.
  1568  * Extracts headers from a PO file.
  1461  *
  1569  *
  1462  * @since 3.7.0
  1570  * @since 3.7.0
  1463  *
  1571  *
  1464  * @param string $po_file Path to PO file.
  1572  * @param string $po_file Path to PO file.
  1465  * @return string[] Array of PO file header values keyed by header name.
  1573  * @return string[] Array of PO file header values keyed by header name.
  1480 	}
  1588 	}
  1481 	return $headers;
  1589 	return $headers;
  1482 }
  1590 }
  1483 
  1591 
  1484 /**
  1592 /**
  1485  * Language selector.
  1593  * Extracts headers from a PHP translation file.
       
  1594  *
       
  1595  * @since 6.6.0
       
  1596  *
       
  1597  * @param string $php_file Path to a `.l10n.php` file.
       
  1598  * @return string[] Array of file header values keyed by header name.
       
  1599  */
       
  1600 function wp_get_l10n_php_file_data( $php_file ) {
       
  1601 	$data = (array) include $php_file;
       
  1602 
       
  1603 	unset( $data['messages'] );
       
  1604 	$headers = array(
       
  1605 		'POT-Creation-Date'  => 'pot-creation-date',
       
  1606 		'PO-Revision-Date'   => 'po-revision-date',
       
  1607 		'Project-Id-Version' => 'project-id-version',
       
  1608 		'X-Generator'        => 'x-generator',
       
  1609 	);
       
  1610 
       
  1611 	$result = array(
       
  1612 		'POT-Creation-Date'  => '',
       
  1613 		'PO-Revision-Date'   => '',
       
  1614 		'Project-Id-Version' => '',
       
  1615 		'X-Generator'        => '',
       
  1616 	);
       
  1617 
       
  1618 	foreach ( $headers as $po_header => $php_header ) {
       
  1619 		if ( isset( $data[ $php_header ] ) ) {
       
  1620 			$result[ $po_header ] = $data[ $php_header ];
       
  1621 		}
       
  1622 	}
       
  1623 
       
  1624 	return $result;
       
  1625 }
       
  1626 
       
  1627 /**
       
  1628  * Displays or returns a Language selector.
  1486  *
  1629  *
  1487  * @since 4.0.0
  1630  * @since 4.0.0
  1488  * @since 4.3.0 Introduced the `echo` argument.
  1631  * @since 4.3.0 Introduced the `echo` argument.
  1489  * @since 4.7.0 Introduced the `show_option_site_default` argument.
  1632  * @since 4.7.0 Introduced the `show_option_site_default` argument.
  1490  * @since 5.1.0 Introduced the `show_option_en_us` argument.
  1633  * @since 5.1.0 Introduced the `show_option_en_us` argument.
  1496  * @param string|array $args {
  1639  * @param string|array $args {
  1497  *     Optional. Array or string of arguments for outputting the language selector.
  1640  *     Optional. Array or string of arguments for outputting the language selector.
  1498  *
  1641  *
  1499  *     @type string   $id                           ID attribute of the select element. Default 'locale'.
  1642  *     @type string   $id                           ID attribute of the select element. Default 'locale'.
  1500  *     @type string   $name                         Name attribute of the select element. Default 'locale'.
  1643  *     @type string   $name                         Name attribute of the select element. Default 'locale'.
  1501  *     @type array    $languages                    List of installed languages, contain only the locales.
  1644  *     @type string[] $languages                    List of installed languages, contain only the locales.
  1502  *                                                  Default empty array.
  1645  *                                                  Default empty array.
  1503  *     @type array    $translations                 List of available translations. Default result of
  1646  *     @type array    $translations                 List of available translations. Default result of
  1504  *                                                  wp_get_available_translations().
  1647  *                                                  wp_get_available_translations().
  1505  *     @type string   $selected                     Language which should be selected. Default empty.
  1648  *     @type string   $selected                     Language which should be selected. Default empty.
  1506  *     @type bool|int $echo                         Whether to echo the generated markup. Accepts 0, 1, or their
  1649  *     @type bool|int $echo                         Whether to echo the generated markup. Accepts 0, 1, or their
  1674  */
  1817  */
  1675 function switch_to_locale( $locale ) {
  1818 function switch_to_locale( $locale ) {
  1676 	/* @var WP_Locale_Switcher $wp_locale_switcher */
  1819 	/* @var WP_Locale_Switcher $wp_locale_switcher */
  1677 	global $wp_locale_switcher;
  1820 	global $wp_locale_switcher;
  1678 
  1821 
       
  1822 	if ( ! $wp_locale_switcher ) {
       
  1823 		return false;
       
  1824 	}
       
  1825 
  1679 	return $wp_locale_switcher->switch_to_locale( $locale );
  1826 	return $wp_locale_switcher->switch_to_locale( $locale );
       
  1827 }
       
  1828 
       
  1829 /**
       
  1830  * Switches the translations according to the given user's locale.
       
  1831  *
       
  1832  * @since 6.2.0
       
  1833  *
       
  1834  * @global WP_Locale_Switcher $wp_locale_switcher WordPress locale switcher object.
       
  1835  *
       
  1836  * @param int $user_id User ID.
       
  1837  * @return bool True on success, false on failure.
       
  1838  */
       
  1839 function switch_to_user_locale( $user_id ) {
       
  1840 	/* @var WP_Locale_Switcher $wp_locale_switcher */
       
  1841 	global $wp_locale_switcher;
       
  1842 
       
  1843 	if ( ! $wp_locale_switcher ) {
       
  1844 		return false;
       
  1845 	}
       
  1846 
       
  1847 	return $wp_locale_switcher->switch_to_user_locale( $user_id );
  1680 }
  1848 }
  1681 
  1849 
  1682 /**
  1850 /**
  1683  * Restores the translations according to the previous locale.
  1851  * Restores the translations according to the previous locale.
  1684  *
  1852  *
  1690  */
  1858  */
  1691 function restore_previous_locale() {
  1859 function restore_previous_locale() {
  1692 	/* @var WP_Locale_Switcher $wp_locale_switcher */
  1860 	/* @var WP_Locale_Switcher $wp_locale_switcher */
  1693 	global $wp_locale_switcher;
  1861 	global $wp_locale_switcher;
  1694 
  1862 
       
  1863 	if ( ! $wp_locale_switcher ) {
       
  1864 		return false;
       
  1865 	}
       
  1866 
  1695 	return $wp_locale_switcher->restore_previous_locale();
  1867 	return $wp_locale_switcher->restore_previous_locale();
  1696 }
  1868 }
  1697 
  1869 
  1698 /**
  1870 /**
  1699  * Restores the translations according to the original locale.
  1871  * Restores the translations according to the original locale.
  1706  */
  1878  */
  1707 function restore_current_locale() {
  1879 function restore_current_locale() {
  1708 	/* @var WP_Locale_Switcher $wp_locale_switcher */
  1880 	/* @var WP_Locale_Switcher $wp_locale_switcher */
  1709 	global $wp_locale_switcher;
  1881 	global $wp_locale_switcher;
  1710 
  1882 
       
  1883 	if ( ! $wp_locale_switcher ) {
       
  1884 		return false;
       
  1885 	}
       
  1886 
  1711 	return $wp_locale_switcher->restore_current_locale();
  1887 	return $wp_locale_switcher->restore_current_locale();
  1712 }
  1888 }
  1713 
  1889 
  1714 /**
  1890 /**
  1715  * Whether switch_to_locale() is in effect.
  1891  * Determines whether switch_to_locale() is in effect.
  1716  *
  1892  *
  1717  * @since 4.7.0
  1893  * @since 4.7.0
  1718  *
  1894  *
  1719  * @global WP_Locale_Switcher $wp_locale_switcher WordPress locale switcher object.
  1895  * @global WP_Locale_Switcher $wp_locale_switcher WordPress locale switcher object.
  1720  *
  1896  *
  1781  * @return string Locale-specific list item separator.
  1957  * @return string Locale-specific list item separator.
  1782  */
  1958  */
  1783 function wp_get_list_item_separator() {
  1959 function wp_get_list_item_separator() {
  1784 	global $wp_locale;
  1960 	global $wp_locale;
  1785 
  1961 
       
  1962 	if ( ! ( $wp_locale instanceof WP_Locale ) ) {
       
  1963 		// Default value of WP_Locale::get_list_item_separator().
       
  1964 		/* translators: Used between list items, there is a space after the comma. */
       
  1965 		return __( ', ' );
       
  1966 	}
       
  1967 
  1786 	return $wp_locale->get_list_item_separator();
  1968 	return $wp_locale->get_list_item_separator();
  1787 }
  1969 }
       
  1970 
       
  1971 /**
       
  1972  * Retrieves the word count type based on the locale.
       
  1973  *
       
  1974  * @since 6.2.0
       
  1975  *
       
  1976  * @global WP_Locale $wp_locale WordPress date and time locale object.
       
  1977  *
       
  1978  * @return string Locale-specific word count type. Possible values are `characters_excluding_spaces`,
       
  1979  *                `characters_including_spaces`, or `words`. Defaults to `words`.
       
  1980  */
       
  1981 function wp_get_word_count_type() {
       
  1982 	global $wp_locale;
       
  1983 
       
  1984 	if ( ! ( $wp_locale instanceof WP_Locale ) ) {
       
  1985 		// Default value of WP_Locale::get_word_count_type().
       
  1986 		return 'words';
       
  1987 	}
       
  1988 
       
  1989 	return $wp_locale->get_word_count_type();
       
  1990 }