| [ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Wed Sep 9 08:20:27 2026 | Cross-referenced by PHPXref |