Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
76.56% |
49 / 64 |
|
0.00% |
0 / 3 |
CRAP | |
0.00% |
0 / 1 |
TimeUnits | |
76.56% |
49 / 64 |
|
0.00% |
0 / 3 |
25.15 | |
0.00% |
0 / 1 |
getUnits | |
33.33% |
3 / 9 |
|
0.00% |
0 / 1 |
8.74 | |||
loadLanguage | |
85.71% |
18 / 21 |
|
0.00% |
0 / 1 |
7.14 | |||
onGetHumanTimestamp | |
82.35% |
28 / 34 |
|
0.00% |
0 / 1 |
9.45 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Extension\CLDR; |
4 | |
5 | use Language; |
6 | use MediaWiki\MediaWikiServices; |
7 | use MWTimestamp; |
8 | use User; |
9 | |
10 | /** |
11 | * A class for querying translated time units from CLDR data. |
12 | * |
13 | * @author Niklas Laxström |
14 | * @author Ryan Kaldari |
15 | * @copyright Copyright © 2007-2013 |
16 | * @license GPL-2.0-or-later |
17 | */ |
18 | class TimeUnits { |
19 | |
20 | private static $cache = []; |
21 | |
22 | /** |
23 | * Get localized time units for a particular language, using fallback languages for missing |
24 | * items. The time units are returned as an associative array. The keys are of the form: |
25 | * <unit>-<tense>-<ordinality> (for example, 'hour-future-two'). The values include a placeholder |
26 | * for the number (for example, '{0} months ago'). |
27 | * |
28 | * @param string $code The language to return the list in |
29 | * @return array an associative array of time unit codes and localized time units |
30 | */ |
31 | public static function getUnits( $code ) { |
32 | // Load time units localized for the requested language |
33 | $units = self::loadLanguage( $code ); |
34 | |
35 | if ( $units ) { |
36 | return $units; |
37 | } |
38 | // Load missing time units from fallback languages |
39 | $fallbacks = MediaWikiServices::getInstance()->getLanguageFallback()->getAll( $code ); |
40 | foreach ( $fallbacks as $fallback ) { |
41 | if ( $units ) { |
42 | break; |
43 | } |
44 | // Get time units from a fallback language |
45 | $units = self::loadLanguage( $fallback ); |
46 | } |
47 | |
48 | return $units; |
49 | } |
50 | |
51 | /** |
52 | * Load time units localized for a particular language. Helper function for getUnits. |
53 | * |
54 | * @param string $code The language to return the list in |
55 | * @return array an associative array of time unit codes and localized time units |
56 | */ |
57 | private static function loadLanguage( $code ) { |
58 | if ( !isset( self::$cache[$code] ) ) { |
59 | self::$cache[$code] = []; |
60 | |
61 | $langNameUtils = MediaWikiServices::getInstance()->getLanguageNameUtils(); |
62 | |
63 | if ( !$langNameUtils->isValidBuiltInCode( $code ) ) { |
64 | return []; |
65 | } |
66 | |
67 | /* Load override for wrong or missing entries in cldr */ |
68 | $override = __DIR__ . '/../LocalNames/' . |
69 | $langNameUtils->getFileName( 'LocalNames', $code, '.php' ); |
70 | if ( file_exists( $override ) ) { |
71 | $timeUnits = false; |
72 | |
73 | require $override; |
74 | |
75 | // @phan-suppress-next-line PhanImpossibleCondition |
76 | if ( is_array( $timeUnits ) ) { |
77 | self::$cache[$code] = $timeUnits; |
78 | } |
79 | } |
80 | |
81 | $filename = __DIR__ . '/../CldrNames/' . |
82 | $langNameUtils->getFileName( 'CldrNames', $code, '.php' ); |
83 | if ( file_exists( $filename ) ) { |
84 | $timeUnits = false; |
85 | require $filename; |
86 | // @phan-suppress-next-line PhanImpossibleCondition |
87 | if ( is_array( $timeUnits ) ) { |
88 | self::$cache[$code] += $timeUnits; |
89 | } |
90 | } else { |
91 | wfDebug( __METHOD__ . ": Unable to load time units for $filename\n" ); |
92 | } |
93 | } |
94 | |
95 | return self::$cache[$code]; |
96 | } |
97 | |
98 | /** |
99 | * Handler for GetHumanTimestamp hook. |
100 | * Converts the given time into a human-friendly relative format, for |
101 | * example, '6 days ago', 'In 10 months'. |
102 | * |
103 | * @param string &$output The output timestamp |
104 | * @param MWTimestamp $timestamp The current (user-adjusted) timestamp |
105 | * @param MWTimestamp $relativeTo The relative (user-adjusted) timestamp |
106 | * @param User $user User whose preferences are being used to make timestamp |
107 | * @param Language $lang Language that will be used to render the timestamp |
108 | * @return bool False means the timestamp was overridden so stop further |
109 | * processing. True means the timestamp was not overridden. |
110 | */ |
111 | public static function onGetHumanTimestamp( &$output, $timestamp, $relativeTo, $user, $lang ) { |
112 | // Map PHP's DateInterval property codes to CLDR unit names. |
113 | $units = [ |
114 | 's' => 'second', |
115 | 'i' => 'minute', |
116 | 'h' => 'hour', |
117 | 'd' => 'day', |
118 | 'm' => 'month', |
119 | 'y' => 'year', |
120 | ]; |
121 | |
122 | // Get the difference between the two timestamps (as a DateInterval object). |
123 | $timeDifference = $timestamp->diff( $relativeTo ); |
124 | |
125 | // Figure out if the timestamp is in the future or the past. |
126 | if ( $timeDifference->invert ) { |
127 | $tense = 'future'; |
128 | } else { |
129 | $tense = 'past'; |
130 | } |
131 | |
132 | // Figure out which unit (days, months, etc.) it makes sense to display |
133 | // the timestamp in, and get the number of that unit to use. |
134 | $unit = null; |
135 | $number = 0; |
136 | foreach ( $units as $code => $testUnit ) { |
137 | $testNumber = (int)$timeDifference->format( '%' . $code ); |
138 | if ( $testNumber > 0 ) { |
139 | $unit = $testUnit; |
140 | $number = $testNumber; |
141 | } |
142 | } |
143 | |
144 | // If it occurred less than 1 second ago, output 'just now' message. |
145 | if ( !$unit || !$number ) { |
146 | $output = wfMessage( 'just-now' )->inLanguage( $lang )->text(); |
147 | return false; |
148 | } |
149 | |
150 | // Get the CLDR time unit strings for the user's language. |
151 | // If no strings are returned, abandon the timestamp override. |
152 | $timeUnits = self::getUnits( $lang->getCode() ); |
153 | if ( !$timeUnits ) { |
154 | return true; |
155 | } |
156 | |
157 | // Figure out which grammatical number to use. |
158 | // If the template doesn't exist, fall back to 'other' as the default. |
159 | $grammaticalNumber = $lang->getPluralRuleType( $number ); |
160 | $timeUnitKey = "{$unit}-{$tense}-{$grammaticalNumber}"; |
161 | if ( !isset( $timeUnits[$timeUnitKey] ) ) { |
162 | $timeUnitKey = "{$unit}-{$tense}-other"; |
163 | } |
164 | |
165 | // Not all languages have translations for everything |
166 | if ( !isset( $timeUnits[$timeUnitKey] ) ) { |
167 | return true; |
168 | } |
169 | |
170 | // Select the appropriate template for the timestamp. |
171 | $timeUnit = $timeUnits[$timeUnitKey]; |
172 | // Replace the placeholder with the number. |
173 | $output = str_replace( '{0}', $lang->formatNum( $number ), $timeUnit ); |
174 | |
175 | return false; |
176 | } |
177 | } |