Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
95.65% covered (success)
95.65%
22 / 23
100.00% covered (success)
100.00%
4 / 4
CRAP
100.00% covered (success)
100.00%
1 / 1
LanguageFallback
100.00% covered (success)
100.00%
22 / 22
100.00% covered (success)
100.00%
4 / 4
10
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getFirst
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getAll
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
5
 getAllIncludingSiteLanguage
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2/**
3 * @license GPL-2.0-or-later
4 * @file
5 */
6
7namespace MediaWiki\Language;
8
9/**
10 * @since 1.35
11 * @ingroup Language
12 */
13class 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 */
125class_alias( LanguageFallback::class, 'MediaWiki\\Languages\\LanguageFallback' );