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


Generated : Sat Nov 23 08:20:01 2024 Cross-referenced by PHPXref