Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
95.65% |
22 / 23 |
|
100.00% |
4 / 4 |
CRAP | |
100.00% |
1 / 1 |
| LanguageFallback | |
100.00% |
22 / 22 |
|
100.00% |
4 / 4 |
10 | |
100.00% |
1 / 1 |
| __construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| getFirst | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| getAll | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
5 | |||
| getAllIncludingSiteLanguage | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
3 | |||
| 1 | <?php |
| 2 | /** |
| 3 | * @license GPL-2.0-or-later |
| 4 | * @file |
| 5 | */ |
| 6 | |
| 7 | namespace MediaWiki\Language; |
| 8 | |
| 9 | /** |
| 10 | * @since 1.35 |
| 11 | * @ingroup Language |
| 12 | */ |
| 13 | class LanguageFallback { |
| 14 | /** |
| 15 | * Return a fallback chain for messages in getAll |
| 16 | * @since 1.35 |
| 17 | * @deprecated since 1.46; use LanguageFallbackMode::MESSAGES |
| 18 | */ |
| 19 | public const MESSAGES = LanguageFallbackMode::MESSAGES; |
| 20 | |
| 21 | /** |
| 22 | * Return a strict fallback chain in getAll |
| 23 | * @since 1.35 |
| 24 | * @deprecated since 1.46; use LanguageFallbackMode::STRICT |
| 25 | */ |
| 26 | public const STRICT = LanguageFallbackMode::STRICT; |
| 27 | |
| 28 | /** @var array */ |
| 29 | private $fallbackCache = []; |
| 30 | |
| 31 | /** |
| 32 | * Do not call this directly. Use MediaWikiServices. |
| 33 | * |
| 34 | * @since 1.35 |
| 35 | * @param string $siteLangCode Language code of the site, typically $wgLanguageCode |
| 36 | * @param LocalisationCache $localisationCache |
| 37 | * @param LanguageNameUtils $langNameUtils |
| 38 | */ |
| 39 | public function __construct( |
| 40 | private readonly string $siteLangCode, |
| 41 | private readonly LocalisationCache $localisationCache, |
| 42 | private readonly LanguageNameUtils $langNameUtils, |
| 43 | ) { |
| 44 | } |
| 45 | |
| 46 | /** |
| 47 | * Get the first fallback for a given language. |
| 48 | * |
| 49 | * @since 1.35 |
| 50 | * @param string $code |
| 51 | * @return string|null |
| 52 | */ |
| 53 | public function getFirst( $code ) { |
| 54 | return $this->getAll( $code )[0] ?? null; |
| 55 | } |
| 56 | |
| 57 | /** |
| 58 | * Get the ordered list of fallback languages. |
| 59 | * |
| 60 | * @since 1.35 |
| 61 | * @param string $code Language code |
| 62 | * @param int|LanguageFallbackMode $mode Fallback mode, either MESSAGES (which always falls back to 'en'), or STRICT |
| 63 | * (which only falls back to 'en' when explicitly defined) |
| 64 | * @return string[] List of language codes |
| 65 | * @note Using an `int` for $mode was deprecated in MW 1.46 |
| 66 | */ |
| 67 | public function getAll( $code, $mode = LanguageFallbackMode::MESSAGES ) { |
| 68 | // XXX The LanguageNameUtils dependency is just because of this line, is it needed? |
| 69 | // Especially because isValidBuiltInCode() is just a one-line regex anyway, maybe it should |
| 70 | // actually be static? |
| 71 | if ( $code === 'en' || !$this->langNameUtils->isValidBuiltInCode( $code ) ) { |
| 72 | return []; |
| 73 | } |
| 74 | if ( is_int( $mode ) ) { |
| 75 | $mode = LanguageFallbackMode::from( $mode ); |
| 76 | } |
| 77 | return match ( $mode ) { |
| 78 | LanguageFallbackMode::MESSAGES => |
| 79 | // For unknown languages, fallbackSequence returns an empty array. Hardcode fallback |
| 80 | // to 'en' in that case, as English messages are always defined. |
| 81 | $this->localisationCache->getItem( $code, 'fallbackSequence' ) ?: [ 'en' ], |
| 82 | |
| 83 | LanguageFallbackMode::STRICT => |
| 84 | // Use this mode when you don't want to fall back to English unless explicitly |
| 85 | // defined, for example, when you have language-variant icons and an international |
| 86 | // language-independent fallback. |
| 87 | $this->localisationCache->getItem( $code, 'originalFallbackSequence' ), |
| 88 | }; |
| 89 | } |
| 90 | |
| 91 | /** |
| 92 | * Get the ordered list of fallback languages, ending with the fallback language chain for the |
| 93 | * site language. The site fallback list begins with the site language itself. |
| 94 | * |
| 95 | * @since 1.35 |
| 96 | * @param string $code Language code |
| 97 | * @return string[][] [ fallbacks, site fallbacks ] |
| 98 | */ |
| 99 | public function getAllIncludingSiteLanguage( $code ) { |
| 100 | // Usually, we will only store a tiny number of fallback chains, so we cache in a member. |
| 101 | $cacheKey = "{$code}-{$this->siteLangCode}"; |
| 102 | |
| 103 | if ( !array_key_exists( $cacheKey, $this->fallbackCache ) ) { |
| 104 | $fallbacks = $this->getAll( $code ); |
| 105 | |
| 106 | if ( $code === $this->siteLangCode ) { |
| 107 | // Don't bother hitting the localisation cache a second time |
| 108 | $siteFallbacks = [ $code ]; |
| 109 | } else { |
| 110 | // Append the site's fallback chain, including the site language itself |
| 111 | $siteFallbacks = $this->getAll( $this->siteLangCode ); |
| 112 | array_unshift( $siteFallbacks, $this->siteLangCode ); |
| 113 | |
| 114 | // Eliminate any languages already included in the chain |
| 115 | $siteFallbacks = array_diff( $siteFallbacks, $fallbacks ); |
| 116 | } |
| 117 | |
| 118 | $this->fallbackCache[$cacheKey] = [ $fallbacks, $siteFallbacks ]; |
| 119 | } |
| 120 | return $this->fallbackCache[$cacheKey]; |
| 121 | } |
| 122 | } |
| 123 | |
| 124 | /** @deprecated class alias since 1.45 */ |
| 125 | class_alias( LanguageFallback::class, 'MediaWiki\\Languages\\LanguageFallback' ); |