Localisation updates from https://translatewiki.net.
[mediawiki.git] / includes / api / ApiQueryLanguageinfo.php
blob10fe2eaea3f8418ea4bd82d9a15200d64bb05ddc
1 <?php
2 /**
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 * http://www.gnu.org/copyleft/gpl.html
18 * @file
21 namespace MediaWiki\Api;
23 use MediaWiki\Language\LanguageCode;
24 use MediaWiki\Languages\LanguageConverterFactory;
25 use MediaWiki\Languages\LanguageFactory;
26 use MediaWiki\Languages\LanguageFallback;
27 use MediaWiki\Languages\LanguageNameUtils;
28 use MediaWiki\Message\Message;
29 use Wikimedia\ParamValidator\ParamValidator;
30 use Wikimedia\Timestamp\ConvertibleTimestamp;
32 /**
33 * API module to enumerate language information.
35 * @ingroup API
37 class ApiQueryLanguageinfo extends ApiQueryBase {
39 /**
40 * The maximum time for {@link execute()};
41 * if execution takes longer than this, apply continuation.
43 * If the localization cache is used, this time is not expected to ever be
44 * exceeded; on the other hand, if it is not used, a typical request will
45 * not yield more than a handful of languages before the time is exceeded
46 * and continuation is applied, if one of the expensive props is requested.
48 private const MAX_EXECUTE_SECONDS = 3;
50 private LanguageFactory $languageFactory;
51 private LanguageNameUtils $languageNameUtils;
52 private LanguageFallback $languageFallback;
53 private LanguageConverterFactory $languageConverterFactory;
55 public function __construct(
56 ApiQuery $queryModule,
57 string $moduleName,
58 LanguageFactory $languageFactory,
59 LanguageNameUtils $languageNameUtils,
60 LanguageFallback $languageFallback,
61 LanguageConverterFactory $languageConverterFactory
62 ) {
63 parent::__construct( $queryModule, $moduleName, 'li' );
64 $this->languageFactory = $languageFactory;
65 $this->languageNameUtils = $languageNameUtils;
66 $this->languageFallback = $languageFallback;
67 $this->languageConverterFactory = $languageConverterFactory;
70 public function execute() {
71 // ConvertibleTimestamp::time() used so we can fake the current time in tests
72 $endTime = ConvertibleTimestamp::time() + self::MAX_EXECUTE_SECONDS;
74 $props = array_fill_keys( $this->getParameter( 'prop' ), true );
75 $includeCode = isset( $props['code'] );
76 $includeBcp47 = isset( $props['bcp47'] );
77 $includeDir = isset( $props['dir'] );
78 $includeAutonym = isset( $props['autonym'] );
79 $includeName = isset( $props['name'] );
80 $includeVariantnames = isset( $props['variantnames'] );
81 $includeFallbacks = isset( $props['fallbacks'] );
82 $includeVariants = isset( $props['variants'] );
84 $targetLanguageCode = $this->getLanguage()->getCode();
85 $include = LanguageNameUtils::ALL;
87 $availableLanguageCodes = array_keys( $this->languageNameUtils->getLanguageNames(
88 // MediaWiki and extensions may return different sets of language codes
89 // when asked for language names in different languages;
90 // asking for English language names is most likely to give us the full set,
91 // even though we may not need those at all
92 'en',
93 $include
94 ) );
95 $selectedLanguageCodes = $this->getParameter( 'code' );
96 if ( $selectedLanguageCodes === [ '*' ] ) {
97 $languageCodes = $availableLanguageCodes;
98 } else {
99 $languageCodes = array_values( array_intersect(
100 $availableLanguageCodes,
101 $selectedLanguageCodes
102 ) );
103 $unrecognizedCodes = array_values( array_diff(
104 $selectedLanguageCodes,
105 $availableLanguageCodes
106 ) );
107 if ( $unrecognizedCodes !== [] ) {
108 $this->addWarning( [
109 'apiwarn-unrecognizedvalues',
110 $this->encodeParamName( 'code' ),
111 Message::listParam( $unrecognizedCodes, 'comma' ),
112 count( $unrecognizedCodes ),
113 ] );
116 // order of $languageCodes is guaranteed by LanguageNameUtils::getLanguageNames()
117 // and preserved by array_values() + array_intersect()
119 $continue = $this->getParameter( 'continue' ) ?? reset( $languageCodes );
121 $result = $this->getResult();
122 $rootPath = [
123 $this->getQuery()->getModuleName(),
124 $this->getModuleName(),
126 $result->addArrayType( $rootPath, 'assoc' );
128 foreach ( $languageCodes as $languageCode ) {
129 if ( $languageCode < $continue ) {
130 continue;
133 $now = ConvertibleTimestamp::time();
134 if ( $now >= $endTime ) {
135 $this->setContinueEnumParameter( 'continue', $languageCode );
136 break;
139 $info = [];
140 ApiResult::setArrayType( $info, 'assoc' );
142 if ( $includeCode ) {
143 $info['code'] = $languageCode;
146 if ( $includeBcp47 ) {
147 $bcp47 = LanguageCode::bcp47( $languageCode );
148 $info['bcp47'] = $bcp47;
151 if ( $includeDir ) {
152 $dir = $this->languageFactory->getLanguage( $languageCode )->getDir();
153 $info['dir'] = $dir;
156 if ( $includeAutonym ) {
157 $autonym = $this->languageNameUtils->getLanguageName(
158 $languageCode,
159 LanguageNameUtils::AUTONYMS,
160 $include
162 $info['autonym'] = $autonym;
165 if ( $includeName ) {
166 $name = $this->languageNameUtils->getLanguageName(
167 $languageCode,
168 $targetLanguageCode,
169 $include
171 $info['name'] = $name;
174 if ( $includeFallbacks ) {
175 $fallbacks = $this->languageFallback->getAll(
176 $languageCode,
177 // allow users to distinguish between implicit and explicit 'en' fallbacks
178 LanguageFallback::STRICT
180 ApiResult::setIndexedTagName( $fallbacks, 'fb' );
181 $info['fallbacks'] = $fallbacks;
184 if ( $includeVariants || $includeVariantnames ) {
185 $language = $this->languageFactory->getLanguage( $languageCode );
186 $converter = $this->languageConverterFactory->getLanguageConverter( $language );
187 $variants = $converter->getVariants();
189 if ( $includeVariants ) {
190 $info['variants'] = $variants;
191 ApiResult::setIndexedTagName( $info['variants'], 'var' );
193 if ( $includeVariantnames ) {
194 $info['variantnames'] = [];
195 foreach ( $variants as $variantCode ) {
196 $info['variantnames'][$variantCode] = $language->getVariantname( $variantCode );
201 $fit = $result->addValue( $rootPath, $languageCode, $info );
202 if ( !$fit ) {
203 $this->setContinueEnumParameter( 'continue', $languageCode );
204 break;
209 public function getCacheMode( $params ) {
210 return 'public';
213 public function getAllowedParams() {
214 return [
215 'prop' => [
216 ParamValidator::PARAM_DEFAULT => 'code',
217 ParamValidator::PARAM_ISMULTI => true,
218 ParamValidator::PARAM_TYPE => [
219 'code',
220 'bcp47',
221 'dir',
222 'autonym',
223 'name',
224 'variantnames',
225 'fallbacks',
226 'variants',
228 self::PARAM_HELP_MSG_PER_VALUE => [],
230 'code' => [
231 ParamValidator::PARAM_DEFAULT => '*',
232 ParamValidator::PARAM_ISMULTI => true,
234 'continue' => [
235 self::PARAM_HELP_MSG => 'api-help-param-continue',
240 protected function getExamplesMessages() {
241 $pathUrl = 'action=' . $this->getQuery()->getModuleName() .
242 '&meta=' . $this->getModuleName();
243 $pathMsg = $this->getModulePath();
244 $prefix = $this->getModulePrefix();
246 return [
247 "$pathUrl"
248 => "apihelp-$pathMsg-example-simple",
249 "$pathUrl&{$prefix}prop=autonym|name&uselang=de"
250 => "apihelp-$pathMsg-example-autonym-name-de",
251 "$pathUrl&{$prefix}prop=fallbacks|variants&{$prefix}code=oc"
252 => "apihelp-$pathMsg-example-fallbacks-variants-oc",
253 "$pathUrl&{$prefix}prop=bcp47|dir"
254 => "apihelp-$pathMsg-example-bcp47-dir",
260 /** @deprecated class alias since 1.43 */
261 class_alias( ApiQueryLanguageinfo::class, 'ApiQueryLanguageinfo' );