[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/l10n/ -> class-wp-translation-controller.php (source)

   1  <?php
   2  /**
   3   * I18N: WP_Translation_Controller class.
   4   *
   5   * @package WordPress
   6   * @subpackage I18N
   7   * @since 6.5.0
   8   */
   9  
  10  /**
  11   * Class WP_Translation_Controller.
  12   *
  13   * @since 6.5.0
  14   */
  15  final class WP_Translation_Controller {
  16      /**
  17       * Current locale.
  18       *
  19       * @since 6.5.0
  20       * @var string
  21       */
  22      protected $current_locale = 'en_US';
  23  
  24      /**
  25       * Map of loaded translations per locale and text domain.
  26       *
  27       * [ Locale => [ Textdomain => [ ..., ... ] ] ]
  28       *
  29       * @since 6.5.0
  30       * @var array<string, array<string, WP_Translation_File[]>>
  31       */
  32      protected $loaded_translations = array();
  33  
  34      /**
  35       * List of loaded translation files.
  36       *
  37       * [ Filename => [ Locale => [ Textdomain => WP_Translation_File ] ] ]
  38       *
  39       * @since 6.5.0
  40       * @var array<string, array<string, array<string, WP_Translation_File|false>>>
  41       */
  42      protected $loaded_files = array();
  43  
  44      /**
  45       * Container for the main instance of the class.
  46       *
  47       * @since 6.5.0
  48       * @var WP_Translation_Controller|null
  49       */
  50      private static $instance = null;
  51  
  52      /**
  53       * Utility method to retrieve the main instance of the class.
  54       *
  55       * The instance will be created if it does not exist yet.
  56       *
  57       * @since 6.5.0
  58       *
  59       * @return WP_Translation_Controller
  60       */
  61  	public static function get_instance(): WP_Translation_Controller {
  62          self::$instance ??= new self();
  63  
  64          return self::$instance;
  65      }
  66  
  67      /**
  68       * Returns the current locale.
  69       *
  70       * @since 6.5.0
  71       *
  72       * @return string Locale.
  73       */
  74  	public function get_locale(): string {
  75          return $this->current_locale;
  76      }
  77  
  78      /**
  79       * Sets the current locale.
  80       *
  81       * @since 6.5.0
  82       *
  83       * @param string $locale Locale.
  84       */
  85  	public function set_locale( string $locale ) {
  86          $this->current_locale = $locale;
  87      }
  88  
  89      /**
  90       * Loads a translation file for a given text domain.
  91       *
  92       * @since 6.5.0
  93       *
  94       * @param string $translation_file Translation file.
  95       * @param string $textdomain       Optional. Text domain. Default 'default'.
  96       * @param string $locale           Optional. Locale. Default current locale.
  97       * @return bool True on success, false otherwise.
  98       */
  99  	public function load_file( string $translation_file, string $textdomain = 'default', ?string $locale = null ): bool {
 100          $locale ??= $this->current_locale;
 101  
 102          $translation_file = realpath( $translation_file );
 103  
 104          if ( false === $translation_file ) {
 105              return false;
 106          }
 107  
 108          if (
 109              isset( $this->loaded_files[ $translation_file ][ $locale ][ $textdomain ] ) &&
 110              false !== $this->loaded_files[ $translation_file ][ $locale ][ $textdomain ]
 111          ) {
 112              return null === $this->loaded_files[ $translation_file ][ $locale ][ $textdomain ]->error();
 113          }
 114  
 115          if (
 116              isset( $this->loaded_files[ $translation_file ][ $locale ] ) &&
 117              array() !== $this->loaded_files[ $translation_file ][ $locale ]
 118          ) {
 119              $moe = reset( $this->loaded_files[ $translation_file ][ $locale ] );
 120          } else {
 121              $moe = WP_Translation_File::create( $translation_file );
 122              if ( false === $moe || null !== $moe->error() ) {
 123                  $moe = false;
 124              }
 125          }
 126  
 127          $this->loaded_files[ $translation_file ][ $locale ][ $textdomain ] = $moe;
 128  
 129          if ( ! $moe instanceof WP_Translation_File ) {
 130              return false;
 131          }
 132  
 133          if ( ! isset( $this->loaded_translations[ $locale ][ $textdomain ] ) ) {
 134              $this->loaded_translations[ $locale ][ $textdomain ] = array();
 135          }
 136  
 137          $this->loaded_translations[ $locale ][ $textdomain ][] = $moe;
 138  
 139          return true;
 140      }
 141  
 142      /**
 143       * Unloads a translation file for a given text domain.
 144       *
 145       * @since 6.5.0
 146       *
 147       * @param WP_Translation_File|string $file       Translation file instance or file name.
 148       * @param string                     $textdomain Optional. Text domain. Default 'default'.
 149       * @param string                     $locale     Optional. Locale. Defaults to all locales.
 150       * @return bool True on success, false otherwise.
 151       */
 152  	public function unload_file( $file, string $textdomain = 'default', ?string $locale = null ): bool {
 153          if ( is_string( $file ) ) {
 154              $file = realpath( $file );
 155          }
 156  
 157          if ( null !== $locale ) {
 158              if ( isset( $this->loaded_translations[ $locale ][ $textdomain ] ) ) {
 159                  foreach ( $this->loaded_translations[ $locale ][ $textdomain ] as $i => $moe ) {
 160                      if ( $file === $moe || $file === $moe->get_file() ) {
 161                          unset( $this->loaded_translations[ $locale ][ $textdomain ][ $i ] );
 162                          unset( $this->loaded_files[ $moe->get_file() ][ $locale ][ $textdomain ] );
 163                          return true;
 164                      }
 165                  }
 166              }
 167  
 168              return true;
 169          }
 170  
 171          foreach ( $this->loaded_translations as $l => $domains ) {
 172              if ( ! isset( $domains[ $textdomain ] ) ) {
 173                  continue;
 174              }
 175  
 176              foreach ( $domains[ $textdomain ] as $i => $moe ) {
 177                  if ( $file === $moe || $file === $moe->get_file() ) {
 178                      unset( $this->loaded_translations[ $l ][ $textdomain ][ $i ] );
 179                      unset( $this->loaded_files[ $moe->get_file() ][ $l ][ $textdomain ] );
 180                      return true;
 181                  }
 182              }
 183          }
 184  
 185          return false;
 186      }
 187  
 188      /**
 189       * Unloads all translation files for a given text domain.
 190       *
 191       * @since 6.5.0
 192       *
 193       * @param string $textdomain Optional. Text domain. Default 'default'.
 194       * @param string $locale     Optional. Locale. Defaults to all locales.
 195       * @return bool True on success, false otherwise.
 196       */
 197  	public function unload_textdomain( string $textdomain = 'default', ?string $locale = null ): bool {
 198          $unloaded = false;
 199  
 200          if ( null !== $locale ) {
 201              if ( isset( $this->loaded_translations[ $locale ][ $textdomain ] ) ) {
 202                  $unloaded = true;
 203                  foreach ( $this->loaded_translations[ $locale ][ $textdomain ] as $moe ) {
 204                      unset( $this->loaded_files[ $moe->get_file() ][ $locale ][ $textdomain ] );
 205                  }
 206              }
 207  
 208              unset( $this->loaded_translations[ $locale ][ $textdomain ] );
 209  
 210              return $unloaded;
 211          }
 212  
 213          foreach ( $this->loaded_translations as $l => $domains ) {
 214              if ( ! isset( $domains[ $textdomain ] ) ) {
 215                  continue;
 216              }
 217  
 218              $unloaded = true;
 219  
 220              foreach ( $domains[ $textdomain ] as $moe ) {
 221                  unset( $this->loaded_files[ $moe->get_file() ][ $l ][ $textdomain ] );
 222              }
 223  
 224              unset( $this->loaded_translations[ $l ][ $textdomain ] );
 225          }
 226  
 227          return $unloaded;
 228      }
 229  
 230      /**
 231       * Determines whether translations are loaded for a given text domain.
 232       *
 233       * @since 6.5.0
 234       *
 235       * @param string $textdomain Optional. Text domain. Default 'default'.
 236       * @param string $locale     Optional. Locale. Default current locale.
 237       * @return bool True if there are any loaded translations, false otherwise.
 238       */
 239  	public function is_textdomain_loaded( string $textdomain = 'default', ?string $locale = null ): bool {
 240          $locale ??= $this->current_locale;
 241  
 242          return isset( $this->loaded_translations[ $locale ][ $textdomain ] ) &&
 243              array() !== $this->loaded_translations[ $locale ][ $textdomain ];
 244      }
 245  
 246      /**
 247       * Translates a singular string.
 248       *
 249       * @since 6.5.0
 250       *
 251       * @param string $text       Text to translate.
 252       * @param string $context    Optional. Context for the string. Default empty string.
 253       * @param string $textdomain Optional. Text domain. Default 'default'.
 254       * @param string $locale     Optional. Locale. Default current locale.
 255       * @return string|false Translation on success, false otherwise.
 256       */
 257  	public function translate( string $text, string $context = '', string $textdomain = 'default', ?string $locale = null ) {
 258          if ( '' !== $context ) {
 259              $context .= "\4";
 260          }
 261  
 262          $translation = $this->locate_translation( "{$context}{$text}", $textdomain, $locale );
 263  
 264          if ( false === $translation ) {
 265              return false;
 266          }
 267  
 268          return $translation['entries'][0];
 269      }
 270  
 271      /**
 272       * Translates plurals.
 273       *
 274       * Checks both singular+plural combinations as well as just singulars,
 275       * in case the translation file does not store the plural.
 276       *
 277       * @since 6.5.0
 278       *
 279       * @param array       $plurals {
 280       *     Pair of singular and plural translations.
 281       *
 282       *     @type string $0 Singular translation.
 283       *     @type string $1 Plural translation.
 284       * }
 285       * @param int         $number     Number of items.
 286       * @param string      $context    Optional. Context for the string. Default empty string.
 287       * @param string      $textdomain Optional. Text domain. Default 'default'.
 288       * @param string|null $locale     Optional. Locale. Default current locale.
 289       * @return string|false Translation on success, false otherwise.
 290       */
 291  	public function translate_plural( array $plurals, int $number, string $context = '', string $textdomain = 'default', ?string $locale = null ) {
 292          if ( '' !== $context ) {
 293              $context .= "\4";
 294          }
 295  
 296          $text        = implode( "\0", $plurals );
 297          $translation = $this->locate_translation( "{$context}{$text}", $textdomain, $locale );
 298  
 299          if ( false === $translation ) {
 300              $text        = $plurals[0];
 301              $translation = $this->locate_translation( "{$context}{$text}", $textdomain, $locale );
 302  
 303              if ( false === $translation ) {
 304                  return false;
 305              }
 306          }
 307  
 308          /** @var WP_Translation_File $source */
 309          $source = $translation['source'];
 310          $num    = $source->get_plural_form( $number );
 311  
 312          // See \Translations::translate_plural().
 313          return $translation['entries'][ $num ] ?? $translation['entries'][0];
 314      }
 315  
 316      /**
 317       * Returns all existing headers for a given text domain.
 318       *
 319       * @since 6.5.0
 320       *
 321       * @param string $textdomain Optional. Text domain. Default 'default'.
 322       * @return array<string, string> Headers.
 323       */
 324  	public function get_headers( string $textdomain = 'default' ): array {
 325          if ( array() === $this->loaded_translations ) {
 326              return array();
 327          }
 328  
 329          $headers = array();
 330  
 331          foreach ( $this->get_files( $textdomain ) as $moe ) {
 332              foreach ( $moe->headers() as $header => $value ) {
 333                  $headers[ $this->normalize_header( $header ) ] = $value;
 334              }
 335          }
 336  
 337          return $headers;
 338      }
 339  
 340      /**
 341       * Normalizes header names to be capitalized.
 342       *
 343       * @since 6.5.0
 344       *
 345       * @param string $header Header name.
 346       * @return string Normalized header name.
 347       */
 348  	protected function normalize_header( string $header ): string {
 349          $parts = explode( '-', $header );
 350          $parts = array_map( 'ucfirst', $parts );
 351          return implode( '-', $parts );
 352      }
 353  
 354      /**
 355       * Returns all entries for a given text domain.
 356       *
 357       * @since 6.5.0
 358       *
 359       * @param string $textdomain Optional. Text domain. Default 'default'.
 360       * @return array<string, string> Entries.
 361       */
 362  	public function get_entries( string $textdomain = 'default' ): array {
 363          if ( array() === $this->loaded_translations ) {
 364              return array();
 365          }
 366  
 367          $entries = array();
 368  
 369          foreach ( $this->get_files( $textdomain ) as $moe ) {
 370              $entries = array_merge( $entries, $moe->entries() );
 371          }
 372  
 373          return $entries;
 374      }
 375  
 376      /**
 377       * Locates translation for a given string and text domain.
 378       *
 379       * @since 6.5.0
 380       *
 381       * @param string $singular   Singular translation.
 382       * @param string $textdomain Optional. Text domain. Default 'default'.
 383       * @param string $locale     Optional. Locale. Default current locale.
 384       * @return array{source: WP_Translation_File, entries: string[]}|false {
 385       *     Translations on success, false otherwise.
 386       *
 387       *     @type WP_Translation_File $source Translation file instance.
 388       *     @type string[]            $entries Array of translation entries.
 389       * }
 390       */
 391  	protected function locate_translation( string $singular, string $textdomain = 'default', ?string $locale = null ) {
 392          if ( array() === $this->loaded_translations ) {
 393              return false;
 394          }
 395  
 396          // Find the translation in all loaded files for this text domain.
 397          foreach ( $this->get_files( $textdomain, $locale ) as $moe ) {
 398              $translation = $moe->translate( $singular );
 399              if ( false !== $translation ) {
 400                  return array(
 401                      'entries' => explode( "\0", $translation ),
 402                      'source'  => $moe,
 403                  );
 404              }
 405              if ( null !== $moe->error() ) {
 406                  // Unload this file, something is wrong.
 407                  $this->unload_file( $moe, $textdomain, $locale );
 408              }
 409          }
 410  
 411          // Nothing could be found.
 412          return false;
 413      }
 414  
 415      /**
 416       * Returns all translation files for a given text domain.
 417       *
 418       * @since 6.5.0
 419       *
 420       * @param string $textdomain Optional. Text domain. Default 'default'.
 421       * @param string $locale     Optional. Locale. Default current locale.
 422       * @return WP_Translation_File[] List of translation files.
 423       */
 424  	protected function get_files( string $textdomain = 'default', ?string $locale = null ): array {
 425          $locale ??= $this->current_locale;
 426  
 427          return $this->loaded_translations[ $locale ][ $textdomain ] ?? array();
 428      }
 429  
 430      /**
 431       * Returns a boolean to indicate whether a translation exists for a given string with optional text domain and locale.
 432       *
 433       * @since 6.7.0
 434       *
 435       * @param string  $singular   Singular translation to check.
 436       * @param string  $textdomain Optional. Text domain. Default 'default'.
 437       * @param ?string $locale     Optional. Locale. Default current locale.
 438       * @return bool  True if the translation exists, false otherwise.
 439       */
 440  	public function has_translation( string $singular, string $textdomain = 'default', ?string $locale = null ): bool {
 441          $locale ??= $this->current_locale;
 442  
 443          return false !== $this->locate_translation( $singular, $textdomain, $locale );
 444      }
 445  }


Generated : Wed Sep 9 08:20:27 2026 Cross-referenced by PHPXref