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