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
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
;
33 * API module to enumerate language information.
37 class ApiQueryLanguageinfo
extends ApiQueryBase
{
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,
58 LanguageFactory
$languageFactory,
59 LanguageNameUtils
$languageNameUtils,
60 LanguageFallback
$languageFallback,
61 LanguageConverterFactory
$languageConverterFactory
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
95 $selectedLanguageCodes = $this->getParameter( 'code' );
96 if ( $selectedLanguageCodes === [ '*' ] ) {
97 $languageCodes = $availableLanguageCodes;
99 $languageCodes = array_values( array_intersect(
100 $availableLanguageCodes,
101 $selectedLanguageCodes
103 $unrecognizedCodes = array_values( array_diff(
104 $selectedLanguageCodes,
105 $availableLanguageCodes
107 if ( $unrecognizedCodes !== [] ) {
109 'apiwarn-unrecognizedvalues',
110 $this->encodeParamName( 'code' ),
111 Message
::listParam( $unrecognizedCodes, 'comma' ),
112 count( $unrecognizedCodes ),
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();
123 $this->getQuery()->getModuleName(),
124 $this->getModuleName(),
126 $result->addArrayType( $rootPath, 'assoc' );
128 foreach ( $languageCodes as $languageCode ) {
129 if ( $languageCode < $continue ) {
133 $now = ConvertibleTimestamp
::time();
134 if ( $now >= $endTime ) {
135 $this->setContinueEnumParameter( 'continue', $languageCode );
140 ApiResult
::setArrayType( $info, 'assoc' );
142 if ( $includeCode ) {
143 $info['code'] = $languageCode;
146 if ( $includeBcp47 ) {
147 $bcp47 = LanguageCode
::bcp47( $languageCode );
148 $info['bcp47'] = $bcp47;
152 $dir = $this->languageFactory
->getLanguage( $languageCode )->getDir();
156 if ( $includeAutonym ) {
157 $autonym = $this->languageNameUtils
->getLanguageName(
159 LanguageNameUtils
::AUTONYMS
,
162 $info['autonym'] = $autonym;
165 if ( $includeName ) {
166 $name = $this->languageNameUtils
->getLanguageName(
171 $info['name'] = $name;
174 if ( $includeFallbacks ) {
175 $fallbacks = $this->languageFallback
->getAll(
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 );
203 $this->setContinueEnumParameter( 'continue', $languageCode );
209 public function getCacheMode( $params ) {
213 public function getAllowedParams() {
216 ParamValidator
::PARAM_DEFAULT
=> 'code',
217 ParamValidator
::PARAM_ISMULTI
=> true,
218 ParamValidator
::PARAM_TYPE
=> [
228 self
::PARAM_HELP_MSG_PER_VALUE
=> [],
231 ParamValidator
::PARAM_DEFAULT
=> '*',
232 ParamValidator
::PARAM_ISMULTI
=> true,
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();
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' );