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