| [ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
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 if ( empty( $locale ) || ! is_string( $locale ) ) { 35 $locale = 'en_US'; 36 } 37 38 /** This filter is documented in wp-includes/l10n.php */ 39 $filtered_locale = apply_filters( 'locale', $locale ); 40 41 if ( empty( $filtered_locale ) || ! is_string( $filtered_locale ) ) { 42 return $locale; 43 } 44 45 return $filtered_locale; 46 } 47 48 if ( isset( $wp_local_package ) ) { 49 $locale = $wp_local_package; 50 } 51 52 // WPLANG was defined in wp-config. 53 if ( defined( 'WPLANG' ) ) { 54 $locale = WPLANG; 55 } 56 57 // If multisite, check options. 58 if ( is_multisite() ) { 59 // Don't check blog option when installing. 60 if ( wp_installing() ) { 61 $ms_locale = get_site_option( 'WPLANG' ); 62 } else { 63 $ms_locale = get_option( 'WPLANG' ); 64 if ( false === $ms_locale ) { 65 $ms_locale = get_site_option( 'WPLANG' ); 66 } 67 } 68 69 if ( false !== $ms_locale ) { 70 $locale = $ms_locale; 71 } 72 } else { 73 $db_locale = get_option( 'WPLANG' ); 74 if ( false !== $db_locale ) { 75 $locale = $db_locale; 76 } 77 } 78 79 if ( empty( $locale ) || ! is_string( $locale ) ) { 80 $locale = 'en_US'; 81 } 82 83 /** 84 * Filters the locale ID of the WordPress installation. 85 * 86 * A non-string or empty value is ignored. 87 * 88 * @since 1.5.0 89 * 90 * @param string $locale The locale ID. 91 */ 92 $filtered_locale = apply_filters( 'locale', $locale ); 93 94 if ( empty( $filtered_locale ) || ! is_string( $filtered_locale ) ) { 95 return $locale; 96 } 97 98 return $filtered_locale; 99 } 100 101 /** 102 * Retrieves the locale of a user. 103 * 104 * If the user has a locale set to a non-empty string then it will be 105 * returned. Otherwise it returns the locale of get_locale(). 106 * 107 * @since 4.7.0 108 * 109 * @param int|WP_User $user User's ID or a WP_User object. Defaults to current user. 110 * @return string The locale of the user. 111 */ 112 function get_user_locale( $user = 0 ) { 113 $user_object = false; 114 115 if ( 0 === $user && function_exists( 'wp_get_current_user' ) ) { 116 $user_object = wp_get_current_user(); 117 } elseif ( $user instanceof WP_User ) { 118 $user_object = $user; 119 } elseif ( $user && is_numeric( $user ) ) { 120 $user_object = get_user_by( 'id', $user ); 121 } 122 123 if ( ! $user_object ) { 124 return get_locale(); 125 } 126 127 $locale = $user_object->locale; 128 129 if ( empty( $locale ) || ! is_string( $locale ) ) { 130 return get_locale(); 131 } 132 133 return $locale; 134 } 135 136 /** 137 * Determines the current locale desired for the request. 138 * 139 * @since 5.0.0 140 * 141 * @global string $pagenow The filename of the current screen. 142 * @global string $wp_local_package Locale code of the package. 143 * 144 * @return string The determined locale. 145 */ 146 function determine_locale() { 147 /** 148 * Filters the locale for the current request prior to the default determination process. 149 * 150 * Using this filter allows to override the default logic, effectively short-circuiting the function. 151 * 152 * @since 5.0.0 153 * 154 * @param string|null $locale The locale to return and short-circuit. Default null. 155 */ 156 $determined_locale = apply_filters( 'pre_determine_locale', null ); 157 158 if ( $determined_locale && is_string( $determined_locale ) ) { 159 return $determined_locale; 160 } 161 162 if ( 163 isset( $GLOBALS['pagenow'] ) && 'wp-login.php' === $GLOBALS['pagenow'] && 164 ( ! empty( $_GET['wp_lang'] ) || ! empty( $_COOKIE['wp_lang'] ) ) 165 ) { 166 if ( ! empty( $_GET['wp_lang'] ) ) { 167 $determined_locale = sanitize_locale_name( $_GET['wp_lang'] ); 168 } else { 169 $determined_locale = sanitize_locale_name( $_COOKIE['wp_lang'] ); 170 } 171 } elseif ( 172 is_admin() || 173 ( isset( $_GET['_locale'] ) && 'user' === $_GET['_locale'] && wp_is_json_request() ) 174 ) { 175 $determined_locale = get_user_locale(); 176 } elseif ( 177 ( ! empty( $_REQUEST['language'] ) || isset( $GLOBALS['wp_local_package'] ) ) 178 && wp_installing() 179 ) { 180 if ( ! empty( $_REQUEST['language'] ) ) { 181 $determined_locale = sanitize_locale_name( $_REQUEST['language'] ); 182 } else { 183 $determined_locale = $GLOBALS['wp_local_package']; 184 } 185 } 186 187 if ( empty( $determined_locale ) || ! is_string( $determined_locale ) ) { 188 $determined_locale = get_locale(); 189 } 190 191 /** 192 * Filters the locale for the current request. 193 * 194 * A non-string or empty value is ignored. 195 * 196 * @since 5.0.0 197 * 198 * @param string $determined_locale The locale. 199 */ 200 $filtered_locale = apply_filters( 'determine_locale', $determined_locale ); 201 202 if ( empty( $filtered_locale ) || ! is_string( $filtered_locale ) ) { 203 return $determined_locale; 204 } 205 206 return $filtered_locale; 207 } 208 209 /** 210 * Retrieves the translation of $text. 211 * 212 * If there is no translation, or the text domain isn't loaded, the original text is returned. 213 * 214 * *Note:* Don't use translate() directly, use __() or related functions. 215 * 216 * @since 2.2.0 217 * @since 5.5.0 Introduced `gettext-{$domain}` filter. 218 * 219 * @param string $text Text to translate. 220 * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings. 221 * Default 'default'. 222 * @return string Translated text. 223 */ 224 function translate( $text, $domain = 'default' ) { 225 $translations = get_translations_for_domain( $domain ); 226 $translation = $translations->translate( $text ); 227 228 /** 229 * Filters text with its translation. 230 * 231 * @since 2.0.11 232 * 233 * @param string $translation Translated text. 234 * @param string $text Text to translate. 235 * @param string $domain Text domain. Unique identifier for retrieving translated strings. 236 */ 237 $translation = apply_filters( 'gettext', $translation, $text, $domain ); 238 239 /** 240 * Filters text with its translation for a domain. 241 * 242 * The dynamic portion of the hook name, `$domain`, refers to the text domain. 243 * 244 * @since 5.5.0 245 * 246 * @param string $translation Translated text. 247 * @param string $text Text to translate. 248 * @param string $domain Text domain. Unique identifier for retrieving translated strings. 249 */ 250 $translation = apply_filters( "gettext_{$domain}", $translation, $text, $domain ); 251 252 return $translation; 253 } 254 255 /** 256 * Removes last item on a pipe-delimited string. 257 * 258 * Meant for removing the last item in a string, such as 'Role name|User role'. The original 259 * string will be returned if no pipe '|' characters are found in the string. 260 * 261 * @since 2.8.0 262 * 263 * @param string $text A pipe-delimited string. 264 * @return string Either $text or everything before the last pipe. 265 */ 266 function before_last_bar( $text ) { 267 $last_bar = strrpos( $text, '|' ); 268 if ( false === $last_bar ) { 269 return $text; 270 } else { 271 return substr( $text, 0, $last_bar ); 272 } 273 } 274 275 /** 276 * Retrieves the translation of $text in the context defined in $context. 277 * 278 * If there is no translation, or the text domain isn't loaded, the original text is returned. 279 * 280 * *Note:* Don't use translate_with_gettext_context() directly, use _x() or related functions. 281 * 282 * @since 2.8.0 283 * @since 5.5.0 Introduced `gettext_with_context-{$domain}` filter. 284 * 285 * @param string $text Text to translate. 286 * @param string $context Context information for the translators. 287 * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings. 288 * Default 'default'. 289 * @return string Translated text on success, original text on failure. 290 */ 291 function translate_with_gettext_context( $text, $context, $domain = 'default' ) { 292 $translations = get_translations_for_domain( $domain ); 293 $translation = $translations->translate( $text, $context ); 294 295 /** 296 * Filters text with its translation based on context information. 297 * 298 * @since 2.8.0 299 * 300 * @param string $translation Translated text. 301 * @param string $text Text to translate. 302 * @param string $context Context information for the translators. 303 * @param string $domain Text domain. Unique identifier for retrieving translated strings. 304 */ 305 $translation = apply_filters( 'gettext_with_context', $translation, $text, $context, $domain ); 306 307 /** 308 * Filters text with its translation based on context information for a domain. 309 * 310 * The dynamic portion of the hook name, `$domain`, refers to the text domain. 311 * 312 * @since 5.5.0 313 * 314 * @param string $translation Translated text. 315 * @param string $text Text to translate. 316 * @param string $context Context information for the translators. 317 * @param string $domain Text domain. Unique identifier for retrieving translated strings. 318 */ 319 $translation = apply_filters( "gettext_with_context_{$domain}", $translation, $text, $context, $domain ); 320 321 return $translation; 322 } 323 324 /** 325 * Retrieves the translation of $text. 326 * 327 * If there is no translation, or the text domain isn't loaded, the original text is returned. 328 * 329 * @since 2.1.0 330 * 331 * @param string $text Text to translate. 332 * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings. 333 * Default 'default'. 334 * @return string Translated text. 335 * 336 * @phpstan-param literal-string $text 337 * @phpstan-param literal-string $domain 338 */ 339 function __( $text, $domain = 'default' ) { 340 return translate( $text, $domain ); 341 } 342 343 /** 344 * Retrieves the translation of $text and escapes it for safe use in an attribute. 345 * 346 * If there is no translation, or the text domain isn't loaded, the original text is returned. 347 * 348 * @since 2.8.0 349 * 350 * @param string $text Text to translate. 351 * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings. 352 * Default 'default'. 353 * @return string Translated text on success, original text on failure. 354 * 355 * @phpstan-param literal-string $text 356 * @phpstan-param literal-string $domain 357 */ 358 function esc_attr__( $text, $domain = 'default' ) { 359 return esc_attr( translate( $text, $domain ) ); 360 } 361 362 /** 363 * Retrieves the translation of $text and escapes it for safe use in HTML output. 364 * 365 * If there is no translation, or the text domain isn't loaded, the original text 366 * is escaped and returned. 367 * 368 * @since 2.8.0 369 * 370 * @param string $text Text to translate. 371 * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings. 372 * Default 'default'. 373 * @return string Translated text. 374 * 375 * @phpstan-param literal-string $text 376 * @phpstan-param literal-string $domain 377 */ 378 function esc_html__( $text, $domain = 'default' ) { 379 return esc_html( translate( $text, $domain ) ); 380 } 381 382 /** 383 * Displays translated text. 384 * 385 * @since 1.2.0 386 * 387 * @param string $text Text to translate. 388 * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings. 389 * Default 'default'. 390 * 391 * @phpstan-param literal-string $text 392 * @phpstan-param literal-string $domain 393 */ 394 function _e( $text, $domain = 'default' ) { 395 echo translate( $text, $domain ); 396 } 397 398 /** 399 * Displays translated text that has been escaped for safe use in an attribute. 400 * 401 * Encodes `< > & " '` (less than, greater than, ampersand, double quote, single quote). 402 * Will never double encode entities. 403 * 404 * If you need the value for use in PHP, use esc_attr__(). 405 * 406 * @since 2.8.0 407 * 408 * @param string $text Text to translate. 409 * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings. 410 * Default 'default'. 411 * 412 * @phpstan-param literal-string $text 413 * @phpstan-param literal-string $domain 414 */ 415 function esc_attr_e( $text, $domain = 'default' ) { 416 echo esc_attr( translate( $text, $domain ) ); 417 } 418 419 /** 420 * Displays translated text that has been escaped for safe use in HTML output. 421 * 422 * If there is no translation, or the text domain isn't loaded, the original text 423 * is escaped and displayed. 424 * 425 * If you need the value for use in PHP, use esc_html__(). 426 * 427 * @since 2.8.0 428 * 429 * @param string $text Text to translate. 430 * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings. 431 * Default 'default'. 432 * 433 * @phpstan-param literal-string $text 434 * @phpstan-param literal-string $domain 435 */ 436 function esc_html_e( $text, $domain = 'default' ) { 437 echo esc_html( translate( $text, $domain ) ); 438 } 439 440 /** 441 * Retrieves translated string with gettext context. 442 * 443 * Quite a few times, there will be collisions with similar translatable text 444 * found in more than two places, but with different translated context. 445 * 446 * By including the context in the pot file, translators can translate the two 447 * strings differently. 448 * 449 * @since 2.8.0 450 * 451 * @param string $text Text to translate. 452 * @param string $context Context information for the translators. 453 * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings. 454 * Default 'default'. 455 * @return string Translated context string without pipe. 456 * 457 * @phpstan-param literal-string $text 458 * @phpstan-param literal-string $context 459 * @phpstan-param literal-string $domain 460 */ 461 function _x( $text, $context, $domain = 'default' ) { 462 return translate_with_gettext_context( $text, $context, $domain ); 463 } 464 465 /** 466 * Displays translated string with gettext context. 467 * 468 * @since 3.0.0 469 * 470 * @param string $text Text to translate. 471 * @param string $context Context information for the translators. 472 * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings. 473 * Default 'default'. 474 * 475 * @phpstan-param literal-string $text 476 * @phpstan-param literal-string $context 477 * @phpstan-param literal-string $domain 478 */ 479 function _ex( $text, $context, $domain = 'default' ) { 480 echo _x( $text, $context, $domain ); 481 } 482 483 /** 484 * Translates string with gettext context, and escapes it for safe use in an attribute. 485 * 486 * If there is no translation, or the text domain isn't loaded, the original text 487 * is escaped and returned. 488 * 489 * @since 2.8.0 490 * 491 * @param string $text Text to translate. 492 * @param string $context Context information for the translators. 493 * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings. 494 * Default 'default'. 495 * @return string Translated text. 496 * 497 * @phpstan-param literal-string $text 498 * @phpstan-param literal-string $context 499 * @phpstan-param literal-string $domain 500 */ 501 function esc_attr_x( $text, $context, $domain = 'default' ) { 502 return esc_attr( translate_with_gettext_context( $text, $context, $domain ) ); 503 } 504 505 /** 506 * Translates string with gettext context, and escapes it for safe use in HTML output. 507 * 508 * If there is no translation, or the text domain isn't loaded, the original text 509 * is escaped and returned. 510 * 511 * @since 2.9.0 512 * 513 * @param string $text Text to translate. 514 * @param string $context Context information for the translators. 515 * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings. 516 * Default 'default'. 517 * @return string Translated text. 518 * 519 * @phpstan-param literal-string $text 520 * @phpstan-param literal-string $context 521 * @phpstan-param literal-string $domain 522 */ 523 function esc_html_x( $text, $context, $domain = 'default' ) { 524 return esc_html( translate_with_gettext_context( $text, $context, $domain ) ); 525 } 526 527 /** 528 * Translates and retrieves the singular or plural form based on the supplied number. 529 * 530 * Used when you want to use the appropriate form of a string based on whether a 531 * number is singular or plural. 532 * 533 * Example: 534 * 535 * printf( _n( '%s person', '%s people', $count, 'text-domain' ), number_format_i18n( $count ) ); 536 * 537 * @since 2.8.0 538 * @since 5.5.0 Introduced `ngettext-{$domain}` filter. 539 * 540 * @param string $single The text to be used if the number is singular. 541 * @param string $plural The text to be used if the number is plural. 542 * @param int $number The number to compare against to use either the singular or plural form. 543 * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings. 544 * Default 'default'. 545 * @return string The translated singular or plural form. 546 * 547 * @phpstan-param literal-string $single 548 * @phpstan-param literal-string $plural 549 * @phpstan-param literal-string $domain 550 */ 551 function _n( $single, $plural, $number, $domain = 'default' ) { 552 $translations = get_translations_for_domain( $domain ); 553 $translation = $translations->translate_plural( $single, $plural, $number ); 554 555 /** 556 * Filters the singular or plural form of a string. 557 * 558 * @since 2.2.0 559 * 560 * @param string $translation Translated text. 561 * @param string $single The text to be used if the number is singular. 562 * @param string $plural The text to be used if the number is plural. 563 * @param int $number The number to compare against to use either the singular or plural form. 564 * @param string $domain Text domain. Unique identifier for retrieving translated strings. 565 */ 566 $translation = apply_filters( 'ngettext', $translation, $single, $plural, $number, $domain ); 567 568 /** 569 * Filters the singular or plural form of a string for a domain. 570 * 571 * The dynamic portion of the hook name, `$domain`, refers to the text domain. 572 * 573 * @since 5.5.0 574 * 575 * @param string $translation Translated text. 576 * @param string $single The text to be used if the number is singular. 577 * @param string $plural The text to be used if the number is plural. 578 * @param int $number The number to compare against to use either the singular or plural form. 579 * @param string $domain Text domain. Unique identifier for retrieving translated strings. 580 */ 581 $translation = apply_filters( "ngettext_{$domain}", $translation, $single, $plural, $number, $domain ); 582 583 return $translation; 584 } 585 586 /** 587 * Translates and retrieves the singular or plural form based on the supplied number, with gettext context. 588 * 589 * This is a hybrid of _n() and _x(). It supports context and plurals. 590 * 591 * Used when you want to use the appropriate form of a string with context based on whether a 592 * number is singular or plural. 593 * 594 * Example of a generic phrase which is disambiguated via the context parameter: 595 * 596 * printf( _nx( '%s group', '%s groups', $people, 'group of people', 'text-domain' ), number_format_i18n( $people ) ); 597 * printf( _nx( '%s group', '%s groups', $animals, 'group of animals', 'text-domain' ), number_format_i18n( $animals ) ); 598 * 599 * @since 2.8.0 600 * @since 5.5.0 Introduced `ngettext_with_context-{$domain}` filter. 601 * 602 * @param string $single The text to be used if the number is singular. 603 * @param string $plural The text to be used if the number is plural. 604 * @param int $number The number to compare against to use either the singular or plural form. 605 * @param string $context Context information for the translators. 606 * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings. 607 * Default 'default'. 608 * @return string The translated singular or plural form. 609 * 610 * @phpstan-param literal-string $single 611 * @phpstan-param literal-string $plural 612 * @phpstan-param literal-string $context 613 * @phpstan-param literal-string $domain 614 */ 615 function _nx( $single, $plural, $number, $context, $domain = 'default' ) { 616 $translations = get_translations_for_domain( $domain ); 617 $translation = $translations->translate_plural( $single, $plural, $number, $context ); 618 619 /** 620 * Filters the singular or plural form of a string with gettext context. 621 * 622 * @since 2.8.0 623 * 624 * @param string $translation Translated text. 625 * @param string $single The text to be used if the number is singular. 626 * @param string $plural The text to be used if the number is plural. 627 * @param int $number The number to compare against to use either the singular or plural form. 628 * @param string $context Context information for the translators. 629 * @param string $domain Text domain. Unique identifier for retrieving translated strings. 630 */ 631 $translation = apply_filters( 'ngettext_with_context', $translation, $single, $plural, $number, $context, $domain ); 632 633 /** 634 * Filters the singular or plural form of a string with gettext context for a domain. 635 * 636 * The dynamic portion of the hook name, `$domain`, refers to the text domain. 637 * 638 * @since 5.5.0 639 * 640 * @param string $translation Translated text. 641 * @param string $single The text to be used if the number is singular. 642 * @param string $plural The text to be used if the number is plural. 643 * @param int $number The number to compare against to use either the singular or plural form. 644 * @param string $context Context information for the translators. 645 * @param string $domain Text domain. Unique identifier for retrieving translated strings. 646 */ 647 $translation = apply_filters( "ngettext_with_context_{$domain}", $translation, $single, $plural, $number, $context, $domain ); 648 649 return $translation; 650 } 651 652 /** 653 * Registers plural strings in POT file, but does not translate them. 654 * 655 * Used when you want to keep structures with translatable plural 656 * strings and use them later when the number is known. 657 * 658 * Example: 659 * 660 * $message = _n_noop( '%s post', '%s posts', 'text-domain' ); 661 * ... 662 * printf( translate_nooped_plural( $message, $count, 'text-domain' ), number_format_i18n( $count ) ); 663 * 664 * @since 2.5.0 665 * 666 * @param string $singular Singular form to be localized. 667 * @param string $plural Plural form to be localized. 668 * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings. 669 * Default null. 670 * @return array { 671 * Array of translation information for the strings. 672 * 673 * @type string $0 Singular form to be localized. No longer used. 674 * @type string $1 Plural form to be localized. No longer used. 675 * @type string $singular Singular form to be localized. 676 * @type string $plural Plural form to be localized. 677 * @type null $context Context information for the translators. 678 * @type string|null $domain Text domain. 679 * } 680 * 681 * @phpstan-param literal-string $singular 682 * @phpstan-param literal-string $plural 683 * @phpstan-param literal-string|null $domain 684 * @phpstan-return array{ 685 * 0: literal-string, 686 * 1: literal-string, 687 * singular: literal-string, 688 * plural: literal-string, 689 * context: null, 690 * domain: literal-string|null, 691 * } 692 */ 693 function _n_noop( $singular, $plural, $domain = null ) { 694 return array( 695 0 => $singular, 696 1 => $plural, 697 'singular' => $singular, 698 'plural' => $plural, 699 'context' => null, 700 'domain' => $domain, 701 ); 702 } 703 704 /** 705 * Registers plural strings with gettext context in POT file, but does not translate them. 706 * 707 * Used when you want to keep structures with translatable plural 708 * strings and use them later when the number is known. 709 * 710 * Example of a generic phrase which is disambiguated via the context parameter: 711 * 712 * $messages = array( 713 * 'people' => _nx_noop( '%s group', '%s groups', 'people', 'text-domain' ), 714 * 'animals' => _nx_noop( '%s group', '%s groups', 'animals', 'text-domain' ), 715 * ); 716 * ... 717 * $message = $messages[ $type ]; 718 * printf( translate_nooped_plural( $message, $count, 'text-domain' ), number_format_i18n( $count ) ); 719 * 720 * @since 2.8.0 721 * 722 * @param string $singular Singular form to be localized. 723 * @param string $plural Plural form to be localized. 724 * @param string $context Context information for the translators. 725 * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings. 726 * Default null. 727 * @return array { 728 * Array of translation information for the strings. 729 * 730 * @type string $0 Singular form to be localized. No longer used. 731 * @type string $1 Plural form to be localized. No longer used. 732 * @type string $2 Context information for the translators. No longer used. 733 * @type string $singular Singular form to be localized. 734 * @type string $plural Plural form to be localized. 735 * @type string $context Context information for the translators. 736 * @type string|null $domain Text domain. 737 * } 738 * 739 * @phpstan-param literal-string $singular 740 * @phpstan-param literal-string $plural 741 * @phpstan-param literal-string $context 742 * @phpstan-param literal-string|null $domain 743 * @phpstan-return array{ 744 * 0: literal-string, 745 * 1: literal-string, 746 * 2: literal-string, 747 * singular: literal-string, 748 * plural: literal-string, 749 * context: literal-string, 750 * domain: literal-string|null, 751 * } 752 */ 753 function _nx_noop( $singular, $plural, $context, $domain = null ) { 754 return array( 755 0 => $singular, 756 1 => $plural, 757 2 => $context, 758 'singular' => $singular, 759 'plural' => $plural, 760 'context' => $context, 761 'domain' => $domain, 762 ); 763 } 764 765 /** 766 * Translates and returns the singular or plural form of a string that's been registered 767 * with _n_noop() or _nx_noop(). 768 * 769 * Used when you want to use a translatable plural string once the number is known. 770 * 771 * Example: 772 * 773 * $message = _n_noop( '%s post', '%s posts', 'text-domain' ); 774 * ... 775 * printf( translate_nooped_plural( $message, $count, 'text-domain' ), number_format_i18n( $count ) ); 776 * 777 * @since 3.1.0 778 * 779 * @param array $nooped_plural { 780 * Array that is usually a return value from _n_noop() or _nx_noop(). 781 * 782 * @type string $singular Singular form to be localized. 783 * @type string $plural Plural form to be localized. 784 * @type string|null $context Context information for the translators. 785 * @type string|null $domain Text domain. 786 * } 787 * @param int $count Number of objects. 788 * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings. If $nooped_plural contains 789 * a text domain passed to _n_noop() or _nx_noop(), it will override this value. Default 'default'. 790 * @return string Either $singular or $plural translated text. 791 * 792 * @phpstan-param array{ 793 * singular: literal-string, 794 * plural: literal-string, 795 * context: literal-string|null, 796 * domain: literal-string|null, 797 * ... 798 * } $nooped_plural 799 * @phpstan-param literal-string $domain 800 */ 801 function translate_nooped_plural( $nooped_plural, $count, $domain = 'default' ) { 802 if ( $nooped_plural['domain'] ) { 803 $domain = $nooped_plural['domain']; 804 } 805 806 if ( $nooped_plural['context'] ) { 807 return _nx( $nooped_plural['singular'], $nooped_plural['plural'], $count, $nooped_plural['context'], $domain ); 808 } else { 809 return _n( $nooped_plural['singular'], $nooped_plural['plural'], $count, $domain ); 810 } 811 } 812 813 /** 814 * Loads a .mo file into the text domain $domain. 815 * 816 * If the text domain already exists, the translations will be merged. If both 817 * sets have the same string, the translation from the original value will be taken. 818 * 819 * On success, the .mo file will be placed in the $l10n global by $domain 820 * and will be a MO object. 821 * 822 * @since 1.5.0 823 * @since 6.1.0 Added the `$locale` parameter. 824 * 825 * @global array<string, WP_Translations|NOOP_Translations|MO> $l10n An array of all currently loaded text domains. 826 * @global array<string, true> $l10n_unloaded An array of all text domains that have been unloaded again. 827 * @global WP_Textdomain_Registry $wp_textdomain_registry WordPress Textdomain Registry. 828 * 829 * @param string $domain Text domain. Unique identifier for retrieving translated strings. 830 * @param string $mofile Path to the .mo file. 831 * @param string $locale Optional. Locale. Default is the current locale. 832 * @return bool True on success, false on failure. 833 */ 834 function load_textdomain( $domain, $mofile, $locale = null ) { 835 global $l10n, $l10n_unloaded, $wp_textdomain_registry; 836 837 $l10n_unloaded = (array) $l10n_unloaded; 838 839 if ( ! is_string( $domain ) ) { 840 return false; 841 } 842 843 /** 844 * Filters whether to short-circuit loading .mo file. 845 * 846 * Returning a non-null value from the filter will effectively short-circuit 847 * the loading, returning the passed value instead. 848 * 849 * @since 6.3.0 850 * 851 * @param bool|null $loaded The result of loading a .mo file. Default null. 852 * @param string $domain Text domain. Unique identifier for retrieving translated strings. 853 * @param string $mofile Path to the MO file. 854 * @param string|null $locale Locale. 855 */ 856 $loaded = apply_filters( 'pre_load_textdomain', null, $domain, $mofile, $locale ); 857 if ( null !== $loaded ) { 858 if ( true === $loaded ) { 859 unset( $l10n_unloaded[ $domain ] ); 860 } 861 862 return $loaded; 863 } 864 865 /** 866 * Filters whether to override the .mo file loading. 867 * 868 * @since 2.9.0 869 * @since 6.2.0 Added the `$locale` parameter. 870 * 871 * @param bool $override Whether to override the .mo file loading. Default false. 872 * @param string $domain Text domain. Unique identifier for retrieving translated strings. 873 * @param string $mofile Path to the MO file. 874 * @param string|null $locale Locale. 875 */ 876 $plugin_override = apply_filters( 'override_load_textdomain', false, $domain, $mofile, $locale ); 877 878 if ( true === (bool) $plugin_override ) { 879 unset( $l10n_unloaded[ $domain ] ); 880 881 return true; 882 } 883 884 /** 885 * Fires before the MO translation file is loaded. 886 * 887 * @since 2.9.0 888 * 889 * @param string $domain Text domain. Unique identifier for retrieving translated strings. 890 * @param string $mofile Path to the .mo file. 891 */ 892 do_action( 'load_textdomain', $domain, $mofile ); 893 894 /** 895 * Filters MO file path for loading translations for a specific text domain. 896 * 897 * @since 2.9.0 898 * 899 * @param string $mofile Path to the MO file. 900 * @param string $domain Text domain. Unique identifier for retrieving translated strings. 901 */ 902 $mofile = apply_filters( 'load_textdomain_mofile', $mofile, $domain ); 903 904 if ( ! $locale ) { 905 $locale = determine_locale(); 906 } 907 908 $i18n_controller = WP_Translation_Controller::get_instance(); 909 910 // Ensures the correct locale is set as the current one, in case it was filtered. 911 $i18n_controller->set_locale( $locale ); 912 913 /** 914 * Filters the preferred file format for translation files. 915 * 916 * Can be used to disable the use of PHP files for translations. 917 * 918 * @since 6.5.0 919 * 920 * @param string $preferred_format Preferred file format. Possible values: 'php', 'mo'. Default: 'php'. 921 * @param string $domain The text domain. 922 */ 923 $preferred_format = apply_filters( 'translation_file_format', 'php', $domain ); 924 if ( ! in_array( $preferred_format, array( 'php', 'mo' ), true ) ) { 925 $preferred_format = 'php'; 926 } 927 928 $translation_files = array(); 929 930 if ( 'mo' !== $preferred_format ) { 931 $translation_files[] = substr_replace( $mofile, ".l10n.$preferred_format", - strlen( '.mo' ) ); 932 } 933 934 $translation_files[] = $mofile; 935 936 foreach ( $translation_files as $file ) { 937 /** 938 * Filters the file path for loading translations for the given text domain. 939 * 940 * Similar to the {@see 'load_textdomain_mofile'} filter with the difference that 941 * the file path could be for an MO or PHP file. 942 * 943 * @since 6.5.0 944 * @since 6.6.0 Added the `$locale` parameter. 945 * 946 * @param string $file Path to the translation file to load. 947 * @param string $domain The text domain. 948 * @param string $locale The locale. 949 */ 950 $file = (string) apply_filters( 'load_translation_file', $file, $domain, $locale ); 951 952 $success = $i18n_controller->load_file( $file, $domain, $locale ); 953 954 if ( $success ) { 955 if ( isset( $l10n[ $domain ] ) && $l10n[ $domain ] instanceof MO ) { 956 $i18n_controller->load_file( $l10n[ $domain ]->get_filename(), $domain, $locale ); 957 } 958 959 // Unset NOOP_Translations reference in get_translations_for_domain(). 960 unset( $l10n[ $domain ] ); 961 962 $l10n[ $domain ] = new WP_Translations( $i18n_controller, $domain ); 963 964 $wp_textdomain_registry->set( $domain, $locale, dirname( $file ) ); 965 966 return true; 967 } 968 } 969 970 return false; 971 } 972 973 /** 974 * Unloads translations for a text domain. 975 * 976 * @since 3.0.0 977 * @since 6.1.0 Added the `$reloadable` parameter. 978 * 979 * @global array<string, WP_Translations|NOOP_Translations|MO> $l10n An array of all currently loaded text domains. 980 * @global array<string, true> $l10n_unloaded An array of all text domains that have been unloaded again. 981 * 982 * @param string $domain Text domain. Unique identifier for retrieving translated strings. 983 * @param bool $reloadable Whether the text domain can be loaded just-in-time again. 984 * @return bool Whether textdomain was unloaded. 985 */ 986 function unload_textdomain( $domain, $reloadable = false ) { 987 global $l10n, $l10n_unloaded; 988 989 $l10n_unloaded = (array) $l10n_unloaded; 990 991 /** 992 * Filters whether to override the text domain unloading. 993 * 994 * @since 3.0.0 995 * @since 6.1.0 Added the `$reloadable` parameter. 996 * 997 * @param bool $override Whether to override the text domain unloading. Default false. 998 * @param string $domain Text domain. Unique identifier for retrieving translated strings. 999 * @param bool $reloadable Whether the text domain can be loaded just-in-time again. 1000 */ 1001 $plugin_override = apply_filters( 'override_unload_textdomain', false, $domain, $reloadable ); 1002 1003 if ( $plugin_override ) { 1004 if ( ! $reloadable ) { 1005 $l10n_unloaded[ $domain ] = true; 1006 } 1007 1008 return true; 1009 } 1010 1011 /** 1012 * Fires before the text domain is unloaded. 1013 * 1014 * @since 3.0.0 1015 * @since 6.1.0 Added the `$reloadable` parameter. 1016 * 1017 * @param string $domain Text domain. Unique identifier for retrieving translated strings. 1018 * @param bool $reloadable Whether the text domain can be loaded just-in-time again. 1019 */ 1020 do_action( 'unload_textdomain', $domain, $reloadable ); 1021 1022 // Since multiple locales are supported, reloadable text domains don't actually need to be unloaded. 1023 if ( ! $reloadable ) { 1024 WP_Translation_Controller::get_instance()->unload_textdomain( $domain ); 1025 } 1026 1027 if ( isset( $l10n[ $domain ] ) ) { 1028 if ( $l10n[ $domain ] instanceof NOOP_Translations ) { 1029 unset( $l10n[ $domain ] ); 1030 1031 return false; 1032 } 1033 1034 unset( $l10n[ $domain ] ); 1035 1036 if ( ! $reloadable ) { 1037 $l10n_unloaded[ $domain ] = true; 1038 } 1039 1040 return true; 1041 } 1042 1043 return false; 1044 } 1045 1046 /** 1047 * Loads default translated strings based on locale. 1048 * 1049 * Loads the .mo file in WP_LANG_DIR constant path from WordPress root. 1050 * The translated (.mo) file is named based on the locale. 1051 * 1052 * @see load_textdomain() 1053 * 1054 * @since 1.5.0 1055 * 1056 * @param string $locale Optional. Locale to load. Default is the value of get_locale(). 1057 * @return bool Whether the textdomain was loaded. 1058 */ 1059 function load_default_textdomain( $locale = null ) { 1060 if ( null === $locale ) { 1061 $locale = determine_locale(); 1062 } 1063 1064 // Unload previously loaded strings so we can switch translations. 1065 unload_textdomain( 'default', true ); 1066 1067 $return = load_textdomain( 'default', WP_LANG_DIR . "/$locale.mo", $locale ); 1068 1069 if ( ( is_multisite() || ( defined( 'WP_INSTALLING_NETWORK' ) && WP_INSTALLING_NETWORK ) ) && ! file_exists( WP_LANG_DIR . "/admin-$locale.mo" ) ) { 1070 load_textdomain( 'default', WP_LANG_DIR . "/ms-$locale.mo", $locale ); 1071 return $return; 1072 } 1073 1074 if ( is_admin() || wp_installing() || ( defined( 'WP_REPAIRING' ) && WP_REPAIRING ) || doing_action( 'wp_maybe_auto_update' ) ) { 1075 load_textdomain( 'default', WP_LANG_DIR . "/admin-$locale.mo", $locale ); 1076 } 1077 1078 if ( is_network_admin() || ( defined( 'WP_INSTALLING_NETWORK' ) && WP_INSTALLING_NETWORK ) ) { 1079 load_textdomain( 'default', WP_LANG_DIR . "/admin-network-$locale.mo", $locale ); 1080 } 1081 1082 return $return; 1083 } 1084 1085 /** 1086 * Loads a plugin's translated strings. 1087 * 1088 * If the path is not given then it will be the root of the plugin directory. 1089 * 1090 * The .mo file should be named based on the text domain with a dash, and then the locale exactly. 1091 * 1092 * @since 1.5.0 1093 * @since 4.6.0 The function now tries to load the .mo file from the languages directory first. 1094 * @since 6.7.0 Translations are no longer immediately loaded, but handed off to the just-in-time loading mechanism. 1095 * 1096 * @global WP_Textdomain_Registry $wp_textdomain_registry WordPress Textdomain Registry. 1097 * @global array<string, WP_Translations|NOOP_Translations|MO> $l10n An array of all currently loaded text domains. 1098 * 1099 * @param string $domain Unique identifier for retrieving translated strings 1100 * @param string|false $deprecated Optional. Deprecated. Use the $plugin_rel_path parameter instead. 1101 * Default false. 1102 * @param string|false $plugin_rel_path Optional. Relative path to WP_PLUGIN_DIR where the .mo file resides. 1103 * Default false. 1104 * @return bool True when textdomain is successfully loaded, false otherwise. 1105 * 1106 * @phpstan-param false $deprecated 1107 */ 1108 function load_plugin_textdomain( $domain, $deprecated = false, $plugin_rel_path = false ) { 1109 global $wp_textdomain_registry, $l10n; 1110 1111 if ( ! is_string( $domain ) ) { 1112 return false; 1113 } 1114 1115 if ( false !== $plugin_rel_path ) { 1116 $path = WP_PLUGIN_DIR . '/' . trim( $plugin_rel_path, '/' ); 1117 } elseif ( false !== $deprecated ) { 1118 _deprecated_argument( __FUNCTION__, '2.7.0' ); 1119 $path = ABSPATH . trim( $deprecated, '/' ); 1120 } else { 1121 $path = WP_PLUGIN_DIR; 1122 } 1123 1124 $wp_textdomain_registry->set_custom_path( $domain, $path ); 1125 1126 // If just-in-time loading was triggered before, reset the entry so it can be tried again. 1127 if ( isset( $l10n[ $domain ] ) && $l10n[ $domain ] instanceof NOOP_Translations ) { 1128 unset( $l10n[ $domain ] ); 1129 } 1130 1131 return true; 1132 } 1133 1134 /** 1135 * Loads the translated strings for a plugin residing in the mu-plugins directory. 1136 * 1137 * @since 3.0.0 1138 * @since 4.6.0 The function now tries to load the .mo file from the languages directory first. 1139 * @since 6.7.0 Translations are no longer immediately loaded, but handed off to the just-in-time loading mechanism. 1140 * 1141 * @global WP_Textdomain_Registry $wp_textdomain_registry WordPress Textdomain Registry. 1142 * @global array<string, WP_Translations|NOOP_Translations|MO> $l10n An array of all currently loaded text domains. 1143 * 1144 * @param string $domain Text domain. Unique identifier for retrieving translated strings. 1145 * @param string $mu_plugin_rel_path Optional. Relative to `WPMU_PLUGIN_DIR` directory in which the .mo 1146 * file resides. Default empty string. 1147 * @return bool True when textdomain is successfully loaded, false otherwise. 1148 */ 1149 function load_muplugin_textdomain( $domain, $mu_plugin_rel_path = '' ) { 1150 global $wp_textdomain_registry, $l10n; 1151 1152 if ( ! is_string( $domain ) ) { 1153 return false; 1154 } 1155 1156 $path = WPMU_PLUGIN_DIR . '/' . ltrim( $mu_plugin_rel_path, '/' ); 1157 1158 $wp_textdomain_registry->set_custom_path( $domain, $path ); 1159 1160 // If just-in-time loading was triggered before, reset the entry so it can be tried again. 1161 if ( isset( $l10n[ $domain ] ) && $l10n[ $domain ] instanceof NOOP_Translations ) { 1162 unset( $l10n[ $domain ] ); 1163 } 1164 1165 return true; 1166 } 1167 1168 /** 1169 * Loads the theme's translated strings. 1170 * 1171 * If the current locale exists as a .mo file in the theme's root directory, it 1172 * will be included in the translated strings by the $domain. 1173 * 1174 * The .mo files must be named based on the locale exactly. 1175 * 1176 * @since 1.5.0 1177 * @since 4.6.0 The function now tries to load the .mo file from the languages directory first. 1178 * @since 6.7.0 Translations are no longer immediately loaded, but handed off to the just-in-time loading mechanism. 1179 * 1180 * @global WP_Textdomain_Registry $wp_textdomain_registry WordPress Textdomain Registry. 1181 * @global array<string, WP_Translations|NOOP_Translations|MO> $l10n An array of all currently loaded text domains. 1182 * 1183 * @param string $domain Text domain. Unique identifier for retrieving translated strings. 1184 * @param string|false $path Optional. Path to the directory containing the .mo file. 1185 * Default false. 1186 * @return bool True when textdomain is successfully loaded, false otherwise. 1187 */ 1188 function load_theme_textdomain( $domain, $path = false ) { 1189 global $wp_textdomain_registry, $l10n; 1190 1191 if ( ! is_string( $domain ) ) { 1192 return false; 1193 } 1194 1195 if ( ! $path ) { 1196 $path = get_template_directory(); 1197 } 1198 1199 $wp_textdomain_registry->set_custom_path( $domain, $path ); 1200 1201 // If just-in-time loading was triggered before, reset the entry so it can be tried again. 1202 if ( isset( $l10n[ $domain ] ) && $l10n[ $domain ] instanceof NOOP_Translations ) { 1203 unset( $l10n[ $domain ] ); 1204 } 1205 1206 return true; 1207 } 1208 1209 /** 1210 * Loads the child theme's translated strings. 1211 * 1212 * If the current locale exists as a .mo file in the child theme's 1213 * root directory, it will be included in the translated strings by the $domain. 1214 * 1215 * The .mo files must be named based on the locale exactly. 1216 * 1217 * @since 2.9.0 1218 * 1219 * @param string $domain Text domain. Unique identifier for retrieving translated strings. 1220 * @param string|false $path Optional. Path to the directory containing the .mo file. 1221 * Default false. 1222 * @return bool True when the theme textdomain is successfully loaded, false otherwise. 1223 */ 1224 function load_child_theme_textdomain( $domain, $path = false ) { 1225 if ( ! $path ) { 1226 $path = get_stylesheet_directory(); 1227 } 1228 return load_theme_textdomain( $domain, $path ); 1229 } 1230 1231 /** 1232 * Loads the script translated strings. 1233 * 1234 * @since 5.0.0 1235 * @since 5.0.2 Uses load_script_translations() to load translation data. 1236 * @since 5.1.0 The `$domain` parameter was made optional. 1237 * 1238 * @see WP_Scripts::set_translations() 1239 * 1240 * @param string $handle Name of the script to register a translation domain to. 1241 * @param string $domain Optional. Text domain. Default 'default'. 1242 * @param string $path Optional. The full file path to the directory containing translation files. 1243 * @return string|false The translated strings in JSON encoding on success, 1244 * false if the script textdomain could not be loaded. 1245 */ 1246 function load_script_textdomain( $handle, $domain = 'default', $path = '' ) { 1247 $wp_scripts = wp_scripts(); 1248 1249 if ( ! isset( $wp_scripts->registered[ $handle ] ) ) { 1250 return false; 1251 } 1252 1253 $src = $wp_scripts->registered[ $handle ]->src; 1254 1255 if ( ! preg_match( '|^(https?:)?//|', $src ) && ! ( $wp_scripts->content_url && str_starts_with( $src, $wp_scripts->content_url ) ) ) { 1256 $src = $wp_scripts->base_url . $src; 1257 } 1258 1259 return _load_script_textdomain_from_src( $handle, $src, $domain, $path, false ); 1260 } 1261 1262 /** 1263 * Loads the translation data for a given script module ID and text domain. 1264 * 1265 * Works like {@see load_script_textdomain()} but for script modules registered 1266 * via {@see wp_register_script_module()}. 1267 * 1268 * @since 7.0.0 1269 * 1270 * @param string $id The script module identifier. 1271 * @param string $domain Optional. Text domain. Default 'default'. 1272 * @param string $path Optional. The full file path to the directory containing translation files. 1273 * @return string|false The JSON-encoded translated strings for the given script module and text domain. 1274 * False if there are none. 1275 */ 1276 function load_script_module_textdomain( string $id, string $domain = 'default', string $path = '' ) { 1277 $module = wp_script_modules()->get_registered( $id ); 1278 if ( null === $module ) { 1279 return false; 1280 } 1281 $src = $module['src']; 1282 1283 // Ensure src is an absolute URL for path resolution. 1284 if ( ! preg_match( '|^(https?:)?//|', $src ) ) { 1285 $src = site_url( $src ); 1286 } 1287 1288 return _load_script_textdomain_from_src( $id, $src, $domain, $path, true ); 1289 } 1290 1291 /** 1292 * Resolves and loads the translation JSON file for a given script or script module source URL. 1293 * 1294 * This is a shared implementation used by {@see load_script_textdomain()} and 1295 * {@see load_script_module_textdomain()} to avoid duplicating the path 1296 * resolution and file lookup logic. 1297 * 1298 * @since 7.0.0 1299 * @access private 1300 * 1301 * @global WP_Textdomain_Registry $wp_textdomain_registry WordPress Textdomain Registry. 1302 * 1303 * @param string $handle Name of the script or script module identifier to register a translation domain to. 1304 * @param string $src Absolute source URL of the script or script module. 1305 * @param string $domain Text domain. 1306 * @param string $path The full file path to the directory containing translation files, 1307 * or an empty string to use the default path from the text domain registry. 1308 * @param bool $is_module Whether the source belongs to a script module (true) or a classic script (false). 1309 * @return string|false The JSON-encoded translated strings on success, false otherwise. 1310 */ 1311 function _load_script_textdomain_from_src( string $handle, string $src, string $domain, string $path, bool $is_module ) { 1312 global $wp_textdomain_registry; 1313 1314 $locale = determine_locale(); 1315 1316 if ( ! $path ) { 1317 $path = $wp_textdomain_registry->get( $domain, $locale ); 1318 } 1319 1320 if ( $path ) { 1321 $path = untrailingslashit( $path ); 1322 } 1323 1324 // If a path was given and the handle file exists simply return it. 1325 $file_base = 'default' === $domain ? $locale : $domain . '-' . $locale; 1326 $handle_filename = $file_base . '-' . $handle . '.json'; 1327 1328 if ( $path ) { 1329 $translations = load_script_translations( $path . '/' . $handle_filename, $handle, $domain ); 1330 1331 if ( $translations ) { 1332 return $translations; 1333 } 1334 } 1335 1336 $relative = false; 1337 $languages_path = WP_LANG_DIR; 1338 1339 $src_url = wp_parse_url( $src ); 1340 if ( ! $src_url ) { 1341 return load_script_translations( false, $handle, $domain ); 1342 } 1343 $src_url['path'] ??= ''; 1344 1345 $content_url = wp_parse_url( content_url() ); 1346 if ( ! $content_url ) { 1347 return load_script_translations( false, $handle, $domain ); 1348 } 1349 1350 $plugins_url = wp_parse_url( plugins_url() ); 1351 $site_url = wp_parse_url( site_url() ); 1352 $theme_root = get_theme_root(); 1353 1354 // If the host is the same or it's a relative URL. 1355 if ( 1356 ( ! isset( $content_url['path'] ) || str_starts_with( $src_url['path'], $content_url['path'] ) ) && 1357 ( ! isset( $src_url['host'] ) || ! isset( $content_url['host'] ) || $src_url['host'] === $content_url['host'] ) 1358 ) { 1359 // Make the src relative the specific plugin or theme. 1360 if ( isset( $content_url['path'] ) ) { 1361 $relative = substr( $src_url['path'], strlen( $content_url['path'] ) ); 1362 } else { 1363 $relative = $src_url['path']; 1364 } 1365 $relative = trim( $relative, '/' ); 1366 $relative = explode( '/', $relative ); 1367 1368 /* 1369 * Ensure correct languages path when using a custom `WP_PLUGIN_DIR` / `WP_PLUGIN_URL` configuration, 1370 * a custom theme root, and/or using Multisite with subdirectories. 1371 * See https://core.trac.wordpress.org/ticket/60891 and https://core.trac.wordpress.org/ticket/62016. 1372 */ 1373 1374 $theme_dir = array_slice( explode( '/', $theme_root ), -1 ); 1375 $dirname = $theme_dir[0] === $relative[0] ? 'themes' : 'plugins'; 1376 1377 $languages_path = WP_LANG_DIR . '/' . $dirname; 1378 1379 $relative = array_slice( $relative, 2 ); // Remove plugins/<plugin name> or themes/<theme name>. 1380 $relative = implode( '/', $relative ); 1381 } elseif ( 1382 ( ! isset( $plugins_url['path'] ) || str_starts_with( $src_url['path'], $plugins_url['path'] ) ) && 1383 ( ! isset( $src_url['host'] ) || ! isset( $plugins_url['host'] ) || $src_url['host'] === $plugins_url['host'] ) 1384 ) { 1385 // Make the src relative the specific plugin. 1386 if ( isset( $plugins_url['path'] ) ) { 1387 $relative = substr( $src_url['path'], strlen( $plugins_url['path'] ) ); 1388 } else { 1389 $relative = $src_url['path']; 1390 } 1391 $relative = trim( $relative, '/' ); 1392 $relative = explode( '/', $relative ); 1393 1394 $languages_path = WP_LANG_DIR . '/plugins'; 1395 1396 $relative = array_slice( $relative, 1 ); // Remove <plugin name>. 1397 $relative = implode( '/', $relative ); 1398 } elseif ( ! isset( $src_url['host'] ) || ! isset( $site_url['host'] ) || $src_url['host'] === $site_url['host'] ) { 1399 if ( ! isset( $site_url['path'] ) ) { 1400 $relative = trim( $src_url['path'], '/' ); 1401 } elseif ( str_starts_with( $src_url['path'], trailingslashit( $site_url['path'] ) ) ) { 1402 // Make the src relative to the WP root. 1403 $relative = substr( $src_url['path'], strlen( $site_url['path'] ) ); 1404 $relative = trim( $relative, '/' ); 1405 } 1406 } 1407 1408 /** 1409 * Filters the relative path of scripts used for finding translation files. 1410 * 1411 * @since 5.0.2 1412 * @since 7.0.0 The `$is_module` parameter was added. 1413 * 1414 * @param string|false $relative The relative path of the script. False if it could not be determined. 1415 * @param string $src The full source URL of the script. 1416 * @param bool $is_module Whether the source belongs to a script module (true) or a classic script (false). 1417 */ 1418 $relative = apply_filters( 'load_script_textdomain_relative_path', $relative, $src, $is_module ); 1419 1420 // If the source is not from WP. 1421 if ( ! is_string( $relative ) ) { 1422 return load_script_translations( false, $handle, $domain ); 1423 } 1424 1425 // Translations are always based on the unminified filename. 1426 if ( str_ends_with( $relative, '.min.js' ) ) { 1427 $relative = substr( $relative, 0, -7 ) . '.js'; 1428 } 1429 1430 $md5_filename = $file_base . '-' . md5( $relative ) . '.json'; 1431 1432 if ( $path ) { 1433 $translations = load_script_translations( $path . '/' . $md5_filename, $handle, $domain ); 1434 1435 if ( $translations ) { 1436 return $translations; 1437 } 1438 } 1439 1440 $translations = load_script_translations( $languages_path . '/' . $md5_filename, $handle, $domain ); 1441 1442 if ( $translations ) { 1443 return $translations; 1444 } 1445 1446 return load_script_translations( false, $handle, $domain ); 1447 } 1448 1449 /** 1450 * Loads the translation data for the given script handle and text domain. 1451 * 1452 * @since 5.0.2 1453 * 1454 * @param string|false $file Path to the translation file to load. False if there isn't one. 1455 * @param string $handle Name of the script to register a translation domain to. 1456 * @param string $domain The text domain. 1457 * @return string|false The JSON-encoded translated strings for the given script handle and text domain. 1458 * False if there are none. 1459 */ 1460 function load_script_translations( $file, $handle, $domain ) { 1461 /** 1462 * Pre-filters script translations for the given file, script handle and text domain. 1463 * 1464 * Returning a non-null value allows to override the default logic, effectively short-circuiting the function. 1465 * 1466 * @since 5.0.2 1467 * 1468 * @param string|false|null $translations JSON-encoded translation data. Default null. 1469 * @param string|false $file Path to the translation file to load. False if there isn't one. 1470 * @param string $handle Name of the script to register a translation domain to. 1471 * @param string $domain The text domain. 1472 */ 1473 $translations = apply_filters( 'pre_load_script_translations', null, $file, $handle, $domain ); 1474 1475 if ( null !== $translations ) { 1476 return $translations; 1477 } 1478 1479 /** 1480 * Filters the file path for loading script translations for the given script handle and text domain. 1481 * 1482 * @since 5.0.2 1483 * 1484 * @param string|false $file Path to the translation file to load. False if there isn't one. 1485 * @param string $handle Name of the script to register a translation domain to. 1486 * @param string $domain The text domain. 1487 */ 1488 $file = apply_filters( 'load_script_translation_file', $file, $handle, $domain ); 1489 1490 if ( ! $file || ! is_readable( $file ) ) { 1491 return false; 1492 } 1493 1494 $translations = file_get_contents( $file ); 1495 1496 /** 1497 * Filters script translations for the given file, script handle and text domain. 1498 * 1499 * @since 5.0.2 1500 * 1501 * @param string $translations JSON-encoded translation data. 1502 * @param string $file Path to the translation file that was loaded. 1503 * @param string $handle Name of the script to register a translation domain to. 1504 * @param string $domain The text domain. 1505 */ 1506 return apply_filters( 'load_script_translations', $translations, $file, $handle, $domain ); 1507 } 1508 1509 /** 1510 * Loads plugin and theme text domains just-in-time. 1511 * 1512 * When a textdomain is encountered for the first time, we try to load 1513 * the translation file from `wp-content/languages`, removing the need 1514 * to call load_plugin_textdomain() or load_theme_textdomain(). 1515 * 1516 * @since 4.6.0 1517 * @access private 1518 * 1519 * @global array<string, true> $l10n_unloaded An array of all text domains that have been unloaded again. 1520 * @global WP_Textdomain_Registry $wp_textdomain_registry WordPress Textdomain Registry. 1521 * 1522 * @param string $domain Text domain. Unique identifier for retrieving translated strings. 1523 * @return bool True when the textdomain is successfully loaded, false otherwise. 1524 */ 1525 function _load_textdomain_just_in_time( $domain ) { 1526 global $l10n_unloaded, $wp_textdomain_registry; 1527 1528 $l10n_unloaded = (array) $l10n_unloaded; 1529 1530 // Short-circuit if domain is 'default' which is reserved for core. 1531 if ( 'default' === $domain || isset( $l10n_unloaded[ $domain ] ) ) { 1532 return false; 1533 } 1534 1535 if ( ! $wp_textdomain_registry->has( $domain ) ) { 1536 return false; 1537 } 1538 1539 $locale = determine_locale(); 1540 $path = $wp_textdomain_registry->get( $domain, $locale ); 1541 if ( ! $path ) { 1542 return false; 1543 } 1544 1545 if ( ! doing_action( 'after_setup_theme' ) && ! did_action( 'after_setup_theme' ) ) { 1546 _doing_it_wrong( 1547 __FUNCTION__, 1548 sprintf( 1549 /* translators: 1: The text domain. 2: 'init'. */ 1550 __( 'Translation loading for the %1$s domain was triggered too early. This is usually an indicator for some code in the plugin or theme running too early. Translations should be loaded at the %2$s action or later.' ), 1551 '<code>' . $domain . '</code>', 1552 '<code>init</code>' 1553 ), 1554 '6.7.0' 1555 ); 1556 } 1557 1558 // Themes with their language directory outside of WP_LANG_DIR have a different file name. 1559 $template_directory = trailingslashit( get_template_directory() ); 1560 $stylesheet_directory = trailingslashit( get_stylesheet_directory() ); 1561 if ( str_starts_with( $path, $template_directory ) || str_starts_with( $path, $stylesheet_directory ) ) { 1562 $mofile = "{$path}{$locale}.mo"; 1563 } else { 1564 $mofile = "{$path}{$domain}-{$locale}.mo"; 1565 } 1566 1567 return load_textdomain( $domain, $mofile, $locale ); 1568 } 1569 1570 /** 1571 * Returns the Translations instance for a text domain. 1572 * 1573 * If there isn't one, returns empty Translations instance. 1574 * 1575 * @since 2.8.0 1576 * 1577 * @global array<string, WP_Translations|NOOP_Translations|MO> $l10n An array of all currently loaded text domains. 1578 * 1579 * @param string $domain Text domain. Unique identifier for retrieving translated strings. 1580 * @return WP_Translations|Translations|NOOP_Translations A Translations instance. 1581 */ 1582 function get_translations_for_domain( $domain ) { 1583 global $l10n; 1584 if ( isset( $l10n[ $domain ] ) || ( _load_textdomain_just_in_time( $domain ) && isset( $l10n[ $domain ] ) ) ) { 1585 return $l10n[ $domain ]; 1586 } 1587 1588 static $noop_translations = null; 1589 if ( null === $noop_translations ) { 1590 $noop_translations = new NOOP_Translations(); 1591 } 1592 1593 $l10n[ $domain ] = &$noop_translations; 1594 1595 return $noop_translations; 1596 } 1597 1598 /** 1599 * Determines whether there are translations for the text domain. 1600 * 1601 * @since 3.0.0 1602 * 1603 * @global array<string, WP_Translations|NOOP_Translations|MO> $l10n An array of all currently loaded text domains. 1604 * 1605 * @param string $domain Text domain. Unique identifier for retrieving translated strings. 1606 * @return bool Whether there are translations. 1607 */ 1608 function is_textdomain_loaded( $domain ) { 1609 global $l10n; 1610 return isset( $l10n[ $domain ] ) && ! $l10n[ $domain ] instanceof NOOP_Translations; 1611 } 1612 1613 /** 1614 * Translates role name. 1615 * 1616 * Since the role names are in the database and not in the source there 1617 * are dummy gettext calls to get them into the POT file and this function 1618 * properly translates them back. 1619 * 1620 * The before_last_bar() call is needed, because older installations keep the roles 1621 * using the old context format: 'Role name|User role' and just skipping the 1622 * content after the last bar is easier than fixing them in the DB. New installations 1623 * won't suffer from that problem. 1624 * 1625 * @since 2.8.0 1626 * @since 5.2.0 Added the `$domain` parameter. 1627 * 1628 * @param string $name The role name. 1629 * @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings. 1630 * Default 'default'. 1631 * @return string Translated role name on success, original name on failure. 1632 */ 1633 function translate_user_role( $name, $domain = 'default' ) { 1634 return translate_with_gettext_context( before_last_bar( $name ), 'User role', $domain ); 1635 } 1636 1637 /** 1638 * Gets all available languages based on the presence of *.mo and *.l10n.php files in a given directory. 1639 * 1640 * The default directory is WP_LANG_DIR. 1641 * 1642 * @since 3.0.0 1643 * @since 4.7.0 The results are now filterable with the {@see 'get_available_languages'} filter. 1644 * @since 6.5.0 The initial file list is now cached and also takes into account *.l10n.php files. 1645 * 1646 * @global WP_Textdomain_Registry $wp_textdomain_registry WordPress Textdomain Registry. 1647 * 1648 * @param string $dir A directory to search for language files. 1649 * Default WP_LANG_DIR. 1650 * @return string[] An array of language codes or an empty array if no languages are present. 1651 * Language codes are formed by stripping the file extension from the language file names. 1652 */ 1653 function get_available_languages( $dir = null ) { 1654 global $wp_textdomain_registry; 1655 1656 $languages = array(); 1657 1658 $path = $dir ?? WP_LANG_DIR; 1659 $lang_files = $wp_textdomain_registry->get_language_files_from_path( $path ); 1660 1661 if ( $lang_files ) { 1662 foreach ( $lang_files as $lang_file ) { 1663 $lang_file = basename( $lang_file, '.mo' ); 1664 $lang_file = basename( $lang_file, '.l10n.php' ); 1665 1666 if ( ! str_starts_with( $lang_file, 'continents-cities' ) && ! str_starts_with( $lang_file, 'ms-' ) && 1667 ! str_starts_with( $lang_file, 'admin-' ) ) { 1668 $languages[] = $lang_file; 1669 } 1670 } 1671 } 1672 1673 /** 1674 * Filters the list of available language codes. 1675 * 1676 * @since 4.7.0 1677 * 1678 * @param string[] $languages An array of available language codes. 1679 * @param string $dir The directory where the language files were found. 1680 */ 1681 return apply_filters( 'get_available_languages', array_unique( $languages ), $dir ); 1682 } 1683 1684 /** 1685 * Gets installed translations. 1686 * 1687 * Looks in the wp-content/languages directory for translations of 1688 * plugins or themes. 1689 * 1690 * @since 3.7.0 1691 * 1692 * @global WP_Textdomain_Registry $wp_textdomain_registry WordPress Textdomain Registry. 1693 * 1694 * @param string $type What to search for. Accepts 'plugins', 'themes', 'core'. 1695 * @return array<string, array<string, array<string, string>>> Array of language data, keyed by text domain 1696 * and then by locale, each value being the 1697 * translation file headers. 1698 * 1699 * @phpstan-return ( 1700 * $type is 'plugins'|'themes'|'core' 1701 * ? array<string, array<string, array{ 1702 * 'POT-Creation-Date': string, 1703 * 'PO-Revision-Date': string, 1704 * 'Project-Id-Version': string, 1705 * 'X-Generator': string, 1706 * }>> 1707 * : array{} 1708 * ) 1709 */ 1710 function wp_get_installed_translations( $type ) { 1711 global $wp_textdomain_registry; 1712 1713 if ( 'themes' !== $type && 'plugins' !== $type && 'core' !== $type ) { 1714 return array(); 1715 } 1716 1717 $dir = 'core' === $type ? WP_LANG_DIR : WP_LANG_DIR . "/$type"; 1718 1719 if ( ! is_dir( $dir ) ) { 1720 return array(); 1721 } 1722 1723 $files = $wp_textdomain_registry->get_language_files_from_path( $dir ); 1724 if ( ! $files ) { 1725 return array(); 1726 } 1727 1728 $language_data = array(); 1729 1730 foreach ( $files as $file ) { 1731 if ( ! preg_match( '/(?:(.+)-)?([a-z]{2,3}(?:_[A-Z]{2})?(?:_[a-z0-9]+)?)\.(?:mo|l10n\.php)/', basename( $file ), $match ) ) { 1732 continue; 1733 } 1734 1735 list( , $textdomain, $language ) = $match; 1736 if ( '' === $textdomain ) { 1737 $textdomain = 'default'; 1738 } 1739 1740 if ( str_ends_with( $file, '.mo' ) ) { 1741 $pofile = substr_replace( $file, '.po', - strlen( '.mo' ) ); 1742 1743 if ( ! file_exists( $pofile ) ) { 1744 continue; 1745 } 1746 1747 $language_data[ $textdomain ][ $language ] = wp_get_pomo_file_data( $pofile ); 1748 } else { 1749 $pofile = substr_replace( $file, '.po', - strlen( '.l10n.php' ) ); 1750 1751 // If both a PO and a PHP file exist, prefer the PO file. 1752 if ( file_exists( $pofile ) ) { 1753 continue; 1754 } 1755 1756 $language_data[ $textdomain ][ $language ] = wp_get_l10n_php_file_data( $file ); 1757 } 1758 } 1759 return $language_data; 1760 } 1761 1762 /** 1763 * Extracts headers from a PO file. 1764 * 1765 * @since 3.7.0 1766 * 1767 * @param string $po_file Path to PO file. 1768 * @return array<string, string> Array of PO file header values keyed by header name. 1769 * 1770 * @phpstan-return array{ 1771 * 'POT-Creation-Date': string, 1772 * 'PO-Revision-Date': string, 1773 * 'Project-Id-Version': string, 1774 * 'X-Generator': string, 1775 * } 1776 */ 1777 function wp_get_pomo_file_data( $po_file ) { 1778 $headers = get_file_data( 1779 $po_file, 1780 array( 1781 'POT-Creation-Date' => '"POT-Creation-Date', 1782 'PO-Revision-Date' => '"PO-Revision-Date', 1783 'Project-Id-Version' => '"Project-Id-Version', 1784 'X-Generator' => '"X-Generator', 1785 ) 1786 ); 1787 1788 $result = array( 1789 'POT-Creation-Date' => '', 1790 'PO-Revision-Date' => '', 1791 'Project-Id-Version' => '', 1792 'X-Generator' => '', 1793 ); 1794 1795 foreach ( array_keys( $result ) as $header ) { 1796 $value = $headers[ $header ]; 1797 1798 // Remove possible closing double quote and the contextual '\n' preceding it. 1799 if ( str_ends_with( $value, '"' ) ) { 1800 $value = substr( $value, 0, -1 ); 1801 if ( str_ends_with( $value, '\n' ) ) { 1802 $value = substr( $value, 0, -2 ); 1803 } 1804 } 1805 1806 $result[ $header ] = $value; 1807 } 1808 1809 return $result; 1810 } 1811 1812 /** 1813 * Extracts headers from a PHP translation file. 1814 * 1815 * @since 6.6.0 1816 * 1817 * @param string $php_file Path to a `.l10n.php` file. 1818 * @return array<string, string> Array of file header values keyed by header name. 1819 * 1820 * @phpstan-return array{ 1821 * 'POT-Creation-Date': string, 1822 * 'PO-Revision-Date': string, 1823 * 'Project-Id-Version': string, 1824 * 'X-Generator': string, 1825 * } 1826 */ 1827 function wp_get_l10n_php_file_data( $php_file ) { 1828 $data = (array) include $php_file; 1829 1830 unset( $data['messages'] ); 1831 $headers = array( 1832 'POT-Creation-Date' => 'pot-creation-date', 1833 'PO-Revision-Date' => 'po-revision-date', 1834 'Project-Id-Version' => 'project-id-version', 1835 'X-Generator' => 'x-generator', 1836 ); 1837 1838 $result = array( 1839 'POT-Creation-Date' => '', 1840 'PO-Revision-Date' => '', 1841 'Project-Id-Version' => '', 1842 'X-Generator' => '', 1843 ); 1844 1845 foreach ( $headers as $po_header => $php_header ) { 1846 if ( isset( $data[ $php_header ] ) && is_string( $data[ $php_header ] ) ) { 1847 $result[ $po_header ] = $data[ $php_header ]; 1848 } 1849 } 1850 1851 return $result; 1852 } 1853 1854 /** 1855 * Displays or returns a Language selector. 1856 * 1857 * @since 4.0.0 1858 * @since 4.3.0 Introduced the `echo` argument. 1859 * @since 4.7.0 Introduced the `show_option_site_default` argument. 1860 * @since 5.1.0 Introduced the `show_option_en_us` argument. 1861 * @since 5.9.0 Introduced the `explicit_option_en_us` argument. 1862 * 1863 * @see get_available_languages() 1864 * @see wp_get_available_translations() 1865 * 1866 * @param string|array $args { 1867 * Optional. Array or string of arguments for outputting the language selector. 1868 * 1869 * @type string $id ID attribute of the select element. Default 'locale'. 1870 * @type string $name Name attribute of the select element. Default 'locale'. 1871 * @type string[] $languages List of installed languages, contain only the locales. 1872 * Default empty array. 1873 * @type array $translations List of available translations. Default result of 1874 * wp_get_available_translations(). 1875 * @type string $selected Language which should be selected. Default empty. 1876 * @type bool|int $echo Whether to echo the generated markup. Accepts 0, 1, or their 1877 * boolean equivalents. Default 1. 1878 * @type bool $show_available_translations Whether to show available translations. Default true. 1879 * @type bool $show_option_site_default Whether to show an option to fall back to the site's locale. Default false. 1880 * @type bool $show_option_en_us Whether to show an option for English (United States). Default true. 1881 * @type bool $explicit_option_en_us Whether the English (United States) option uses an explicit value of en_US 1882 * instead of an empty value. Default false. 1883 * } 1884 * @return string|void HTML dropdown list of languages. Always returned, whether or not 1885 * 'echo' is true; nothing is returned when the 'id' or 'name' 1886 * argument is empty. 1887 * @phpstan-return ( 1888 * $args is array{ id: ''|'0', ... } 1889 * ? void 1890 * : ( $args is array{ name: ''|'0', ... } 1891 * ? void 1892 * : ( $args is array ? string : string|void ) ) 1893 * ) 1894 */ 1895 function wp_dropdown_languages( $args = array() ) { 1896 1897 $parsed_args = wp_parse_args( 1898 $args, 1899 array( 1900 'id' => 'locale', 1901 'name' => 'locale', 1902 'languages' => array(), 1903 'translations' => array(), 1904 'selected' => '', 1905 'echo' => 1, 1906 'show_available_translations' => true, 1907 'show_option_site_default' => false, 1908 'show_option_en_us' => true, 1909 'explicit_option_en_us' => false, 1910 ) 1911 ); 1912 1913 // Bail if no ID or no name. 1914 if ( ! $parsed_args['id'] || ! $parsed_args['name'] ) { 1915 return; 1916 } 1917 1918 // English (United States) uses an empty string for the value attribute. 1919 if ( 'en_US' === $parsed_args['selected'] && ! $parsed_args['explicit_option_en_us'] ) { 1920 $parsed_args['selected'] = ''; 1921 } 1922 1923 $translations = $parsed_args['translations']; 1924 if ( empty( $translations ) ) { 1925 require_once ABSPATH . 'wp-admin/includes/translation-install.php'; 1926 $translations = wp_get_available_translations(); 1927 } 1928 1929 /* 1930 * $parsed_args['languages'] should only contain the locales. Find the locale in 1931 * $translations to get the native name. Fall back to locale. 1932 */ 1933 $languages = array(); 1934 foreach ( $parsed_args['languages'] as $locale ) { 1935 if ( isset( $translations[ $locale ] ) ) { 1936 $translation = $translations[ $locale ]; 1937 $languages[] = array( 1938 'language' => $translation['language'], 1939 'native_name' => $translation['native_name'], 1940 'lang' => current( $translation['iso'] ), 1941 ); 1942 1943 // Remove installed language from available translations. 1944 unset( $translations[ $locale ] ); 1945 } else { 1946 $languages[] = array( 1947 'language' => $locale, 1948 'native_name' => $locale, 1949 'lang' => '', 1950 ); 1951 } 1952 } 1953 1954 $translations_available = ( ! empty( $translations ) && $parsed_args['show_available_translations'] ); 1955 1956 // Holds the HTML markup. 1957 $structure = array(); 1958 1959 // List installed languages. 1960 if ( $translations_available ) { 1961 $structure[] = '<optgroup label="' . esc_attr_x( 'Installed', 'translations' ) . '">'; 1962 } 1963 1964 // Site default. 1965 if ( $parsed_args['show_option_site_default'] ) { 1966 $structure[] = sprintf( 1967 '<option value="site-default" data-installed="1"%s>%s</option>', 1968 selected( 'site-default', $parsed_args['selected'], false ), 1969 _x( 'Site Default', 'default site language' ) 1970 ); 1971 } 1972 1973 if ( $parsed_args['show_option_en_us'] ) { 1974 $value = ( $parsed_args['explicit_option_en_us'] ) ? 'en_US' : ''; 1975 $structure[] = sprintf( 1976 '<option value="%s" lang="en" data-installed="1"%s>English (United States)</option>', 1977 esc_attr( $value ), 1978 selected( '', $parsed_args['selected'], false ) 1979 ); 1980 } 1981 1982 // List installed languages. 1983 foreach ( $languages as $language ) { 1984 $structure[] = sprintf( 1985 '<option value="%s" lang="%s"%s data-installed="1">%s</option>', 1986 esc_attr( $language['language'] ), 1987 esc_attr( $language['lang'] ), 1988 selected( $language['language'], $parsed_args['selected'], false ), 1989 esc_html( $language['native_name'] ) 1990 ); 1991 } 1992 if ( $translations_available ) { 1993 $structure[] = '</optgroup>'; 1994 } 1995 1996 // List available translations. 1997 if ( $translations_available ) { 1998 $structure[] = '<optgroup label="' . esc_attr_x( 'Available', 'translations' ) . '">'; 1999 foreach ( $translations as $translation ) { 2000 $structure[] = sprintf( 2001 '<option value="%s" lang="%s"%s>%s</option>', 2002 esc_attr( $translation['language'] ), 2003 esc_attr( current( $translation['iso'] ) ), 2004 selected( $translation['language'], $parsed_args['selected'], false ), 2005 esc_html( $translation['native_name'] ) 2006 ); 2007 } 2008 $structure[] = '</optgroup>'; 2009 } 2010 2011 // Combine the output string. 2012 $output = sprintf( '<select name="%s" id="%s">', esc_attr( $parsed_args['name'] ), esc_attr( $parsed_args['id'] ) ); 2013 $output .= implode( "\n", $structure ); 2014 $output .= '</select>'; 2015 2016 if ( $parsed_args['echo'] ) { 2017 echo $output; 2018 } 2019 2020 return $output; 2021 } 2022 2023 /** 2024 * Determines whether the current locale is right-to-left (RTL). 2025 * 2026 * For more information on this and similar theme functions, check out 2027 * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/ 2028 * Conditional Tags} article in the Theme Developer Handbook. 2029 * 2030 * @since 3.0.0 2031 * 2032 * @global WP_Locale $wp_locale WordPress date and time locale object. 2033 * 2034 * @return bool Whether locale is RTL. 2035 */ 2036 function is_rtl() { 2037 global $wp_locale; 2038 if ( ! ( $wp_locale instanceof WP_Locale ) ) { 2039 return false; 2040 } 2041 return $wp_locale->is_rtl(); 2042 } 2043 2044 /** 2045 * Switches the translations according to the given locale. 2046 * 2047 * @since 4.7.0 2048 * 2049 * @global WP_Locale_Switcher $wp_locale_switcher WordPress locale switcher object. 2050 * 2051 * @param string $locale The locale. 2052 * @return bool True on success, false on failure. 2053 */ 2054 function switch_to_locale( $locale ) { 2055 /* @var WP_Locale_Switcher $wp_locale_switcher */ 2056 global $wp_locale_switcher; 2057 2058 if ( ! $wp_locale_switcher ) { 2059 return false; 2060 } 2061 2062 return $wp_locale_switcher->switch_to_locale( $locale ); 2063 } 2064 2065 /** 2066 * Switches the translations according to the given user's locale. 2067 * 2068 * @since 6.2.0 2069 * 2070 * @global WP_Locale_Switcher $wp_locale_switcher WordPress locale switcher object. 2071 * 2072 * @param int $user_id User ID. 2073 * @return bool True on success, false on failure. 2074 */ 2075 function switch_to_user_locale( $user_id ) { 2076 /* @var WP_Locale_Switcher $wp_locale_switcher */ 2077 global $wp_locale_switcher; 2078 2079 if ( ! $wp_locale_switcher ) { 2080 return false; 2081 } 2082 2083 return $wp_locale_switcher->switch_to_user_locale( $user_id ); 2084 } 2085 2086 /** 2087 * Restores the translations according to the previous locale. 2088 * 2089 * @since 4.7.0 2090 * 2091 * @global WP_Locale_Switcher $wp_locale_switcher WordPress locale switcher object. 2092 * 2093 * @return string|false Locale on success, false on error. 2094 */ 2095 function restore_previous_locale() { 2096 /* @var WP_Locale_Switcher $wp_locale_switcher */ 2097 global $wp_locale_switcher; 2098 2099 if ( ! $wp_locale_switcher ) { 2100 return false; 2101 } 2102 2103 return $wp_locale_switcher->restore_previous_locale(); 2104 } 2105 2106 /** 2107 * Restores the translations according to the original locale. 2108 * 2109 * @since 4.7.0 2110 * 2111 * @global WP_Locale_Switcher $wp_locale_switcher WordPress locale switcher object. 2112 * 2113 * @return string|false Locale on success, false on error. 2114 */ 2115 function restore_current_locale() { 2116 /* @var WP_Locale_Switcher $wp_locale_switcher */ 2117 global $wp_locale_switcher; 2118 2119 if ( ! $wp_locale_switcher ) { 2120 return false; 2121 } 2122 2123 return $wp_locale_switcher->restore_current_locale(); 2124 } 2125 2126 /** 2127 * Determines whether switch_to_locale() is in effect. 2128 * 2129 * @since 4.7.0 2130 * 2131 * @global WP_Locale_Switcher $wp_locale_switcher WordPress locale switcher object. 2132 * 2133 * @return bool True if the locale has been switched, false otherwise. 2134 */ 2135 function is_locale_switched() { 2136 /* @var WP_Locale_Switcher $wp_locale_switcher */ 2137 global $wp_locale_switcher; 2138 2139 return $wp_locale_switcher->is_switched(); 2140 } 2141 2142 /** 2143 * Translates the provided settings value using its i18n schema. 2144 * 2145 * @since 5.9.0 2146 * @access private 2147 * 2148 * @param string|string[]|array[]|object $i18n_schema I18n schema for the setting. 2149 * @param string|string[]|array[] $settings Value for the settings. 2150 * @param string $textdomain Textdomain to use with translations. 2151 * @return string|string[]|array[] Translated settings. 2152 */ 2153 function translate_settings_using_i18n_schema( $i18n_schema, $settings, $textdomain ) { 2154 if ( empty( $i18n_schema ) || empty( $settings ) || empty( $textdomain ) ) { 2155 return $settings; 2156 } 2157 2158 if ( is_string( $i18n_schema ) && is_string( $settings ) ) { 2159 return translate_with_gettext_context( $settings, $i18n_schema, $textdomain ); 2160 } 2161 if ( is_array( $i18n_schema ) && is_array( $settings ) ) { 2162 $translated_settings = array(); 2163 foreach ( $settings as $value ) { 2164 $translated_settings[] = translate_settings_using_i18n_schema( $i18n_schema[0], $value, $textdomain ); 2165 } 2166 return $translated_settings; 2167 } 2168 if ( is_object( $i18n_schema ) && is_array( $settings ) ) { 2169 $group_key = '*'; 2170 $translated_settings = array(); 2171 foreach ( $settings as $key => $value ) { 2172 if ( isset( $i18n_schema->$key ) ) { 2173 $translated_settings[ $key ] = translate_settings_using_i18n_schema( $i18n_schema->$key, $value, $textdomain ); 2174 } elseif ( isset( $i18n_schema->$group_key ) ) { 2175 $translated_settings[ $key ] = translate_settings_using_i18n_schema( $i18n_schema->$group_key, $value, $textdomain ); 2176 } else { 2177 $translated_settings[ $key ] = $value; 2178 } 2179 } 2180 return $translated_settings; 2181 } 2182 return $settings; 2183 } 2184 2185 /** 2186 * Retrieves the list item separator based on the locale. 2187 * 2188 * @since 6.0.0 2189 * 2190 * @global WP_Locale $wp_locale WordPress date and time locale object. 2191 * 2192 * @return string Locale-specific list item separator. 2193 */ 2194 function wp_get_list_item_separator() { 2195 global $wp_locale; 2196 2197 if ( ! ( $wp_locale instanceof WP_Locale ) ) { 2198 // Default value of WP_Locale::get_list_item_separator(). 2199 /* translators: Used between list items, there is a space after the comma. */ 2200 return __( ', ' ); 2201 } 2202 2203 return $wp_locale->get_list_item_separator(); 2204 } 2205 2206 /** 2207 * Retrieves the word count type based on the locale. 2208 * 2209 * @since 6.2.0 2210 * 2211 * @global WP_Locale $wp_locale WordPress date and time locale object. 2212 * 2213 * @return string Locale-specific word count type. Possible values are `characters_excluding_spaces`, 2214 * `characters_including_spaces`, or `words`. Defaults to `words`. 2215 * 2216 * @phpstan-return 'characters_excluding_spaces'|'characters_including_spaces'|'words' 2217 */ 2218 function wp_get_word_count_type() { 2219 global $wp_locale; 2220 2221 if ( ! ( $wp_locale instanceof WP_Locale ) ) { 2222 // Default value of WP_Locale::get_word_count_type(). 2223 return 'words'; 2224 } 2225 2226 return $wp_locale->get_word_count_type(); 2227 } 2228 2229 /** 2230 * Returns a boolean to indicate whether a translation exists for a given string with optional text domain and locale. 2231 * 2232 * @since 6.7.0 2233 * 2234 * @param string $singular Singular translation to check. 2235 * @param string $textdomain Optional. Text domain. Default 'default'. 2236 * @param ?string $locale Optional. Locale. Default current locale. 2237 * @return bool True if the translation exists, false otherwise. 2238 */ 2239 function has_translation( string $singular, string $textdomain = 'default', ?string $locale = null ): bool { 2240 return WP_Translation_Controller::get_instance()->has_translation( $singular, $textdomain, $locale ); 2241 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Wed Oct 14 08:20:33 2026 | Cross-referenced by PHPXref |