MediaWiki  master
LanguageNameUtils.php
Go to the documentation of this file.
1 <?php
25 namespace MediaWiki\Languages;
26 
27 use HashBagOStuff;
32 use MWException;
33 
46  public const AUTONYMS = null;
47 
51  public const ALL = 'all';
52 
56  public const DEFINED = 'mw';
57 
61  public const SUPPORTED = 'mwfile';
62 
64  private $options;
65 
71 
76  private $validCodeCache = [];
77 
78  public const CONSTRUCTOR_OPTIONS = [
79  'ExtraLanguageNames',
80  'UsePigLatinVariant',
81  ];
82 
84  private $hookRunner;
85 
90  public function __construct( ServiceOptions $options, HookContainer $hookContainer ) {
91  $options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS );
92  $this->options = $options;
93  $this->hookRunner = new HookRunner( $hookContainer );
94  }
95 
103  public function isSupportedLanguage( string $code ) : bool {
104  if ( !$this->isValidBuiltInCode( $code ) ) {
105  return false;
106  }
107 
108  if ( $code === 'qqq' ) {
109  // Special code for internal use, not supported even though there is a qqq.json
110  return false;
111  }
112 
113  return is_readable( $this->getMessagesFileName( $code ) ) ||
114  is_readable( $this->getJsonMessagesFileName( $code ) );
115  }
116 
125  public function isValidCode( string $code ) : bool {
126  if ( !isset( $this->validCodeCache[$code] ) ) {
127  // People think language codes are HTML-safe, so enforce it. Ideally we should only
128  // allow a-zA-Z0-9- but .+ and other chars are often used for {{int:}} hacks. See bugs
129  // T39564, T39587, T38938.
130  $this->validCodeCache[$code] =
131  // Protect against path traversal
132  strcspn( $code, ":/\\\000&<>'\"" ) === strlen( $code ) &&
133  !preg_match( MediaWikiTitleCodec::getTitleInvalidRegex(), $code );
134  }
135  return $this->validCodeCache[$code];
136  }
137 
145  public function isValidBuiltInCode( string $code ) : bool {
146  return (bool)preg_match( '/^[a-z0-9-]{2,}$/', $code );
147  }
148 
156  public function isKnownLanguageTag( string $tag ) : bool {
157  // Quick escape for invalid input to avoid exceptions down the line when code tries to
158  // process tags which are not valid at all.
159  if ( !$this->isValidBuiltInCode( $tag ) ) {
160  return false;
161  }
162 
163  if ( isset( Data\Names::$names[$tag] ) || $this->getLanguageName( $tag, $tag ) !== '' ) {
164  return true;
165  }
166 
167  return false;
168  }
169 
181  public function getLanguageNames( $inLanguage = self::AUTONYMS, $include = self::DEFINED ) {
182  $cacheKey = $inLanguage === self::AUTONYMS ? 'null' : $inLanguage;
183  $cacheKey .= ":$include";
184  if ( !$this->languageNameCache ) {
185  $this->languageNameCache = new HashBagOStuff( [ 'maxKeys' => 20 ] );
186  }
187 
188  $ret = $this->languageNameCache->get( $cacheKey );
189  if ( !$ret ) {
190  $ret = $this->getLanguageNamesUncached( $inLanguage, $include );
191  $this->languageNameCache->set( $cacheKey, $ret );
192  }
193  return $ret;
194  }
195 
202  private function getLanguageNamesUncached( $inLanguage, $include ) {
203  // If passed an invalid language code to use, fallback to en
204  if ( $inLanguage !== self::AUTONYMS && !$this->isValidCode( $inLanguage ) ) {
205  $inLanguage = 'en';
206  }
207 
208  $names = [];
209 
210  if ( $inLanguage !== self::AUTONYMS ) {
211  # TODO: also include for self::AUTONYMS, when this code is more efficient
212  $this->hookRunner->onLanguageGetTranslatedLanguageNames( $names, $inLanguage );
213  }
214 
215  $mwNames = $this->options->get( 'ExtraLanguageNames' ) + Data\Names::$names;
216  if ( $this->options->get( 'UsePigLatinVariant' ) ) {
217  // Pig Latin (for variant development)
218  $mwNames['en-x-piglatin'] = 'Igpay Atinlay';
219  }
220 
221  foreach ( $mwNames as $mwCode => $mwName ) {
222  # - Prefer own MediaWiki native name when not using the hook
223  # - For other names just add if not added through the hook
224  if ( $mwCode === $inLanguage || !isset( $names[$mwCode] ) ) {
225  $names[$mwCode] = $mwName;
226  }
227  }
228 
229  if ( $include === self::ALL ) {
230  ksort( $names );
231  return $names;
232  }
233 
234  $returnMw = [];
235  $coreCodes = array_keys( $mwNames );
236  foreach ( $coreCodes as $coreCode ) {
237  $returnMw[$coreCode] = $names[$coreCode];
238  }
239 
240  if ( $include === self::SUPPORTED ) {
241  $namesMwFile = [];
242  # We do this using a foreach over the codes instead of a directory loop so that messages
243  # files in extensions will work correctly.
244  foreach ( $returnMw as $code => $value ) {
245  if ( is_readable( $this->getMessagesFileName( $code ) ) ||
246  is_readable( $this->getJsonMessagesFileName( $code ) )
247  ) {
248  $namesMwFile[$code] = $names[$code];
249  }
250  }
251 
252  ksort( $namesMwFile );
253  return $namesMwFile;
254  }
255 
256  ksort( $returnMw );
257  # self::DEFINED option; default if it's not one of the other two options
258  # (self::ALL/self::SUPPORTED)
259  return $returnMw;
260  }
261 
271  public function getLanguageName( $code, $inLanguage = self::AUTONYMS, $include = self::ALL ) {
272  $code = strtolower( $code );
273  $array = $this->getLanguageNames( $inLanguage, $include );
274  return $array[$code] ?? '';
275  }
276 
285  public function getFileName( $prefix, $code, $suffix = '.php' ) {
286  if ( !$this->isValidBuiltInCode( $code ) ) {
287  throw new MWException( "Invalid language code \"$code\"" );
288  }
289 
290  return $prefix . str_replace( '-', '_', ucfirst( $code ) ) . $suffix;
291  }
292 
297  public function getMessagesFileName( $code ) {
298  global $IP;
299  $file = $this->getFileName( "$IP/languages/messages/Messages", $code, '.php' );
300  $this->hookRunner->onLanguage__getMessagesFileName( $code, $file );
301  return $file;
302  }
303 
309  public function getJsonMessagesFileName( $code ) {
310  global $IP;
311 
312  if ( !$this->isValidBuiltInCode( $code ) ) {
313  throw new MWException( "Invalid language code \"$code\"" );
314  }
315 
316  return "$IP/languages/i18n/$code.json";
317  }
318 }
MediaWiki\Languages\LanguageNameUtils\$validCodeCache
array $validCodeCache
Cache for validity of language codes.
Definition: LanguageNameUtils.php:76
MediaWikiTitleCodec
A codec for MediaWiki page titles.
Definition: MediaWikiTitleCodec.php:39
HashBagOStuff
Simple store for keeping values in an associative array for the current process.
Definition: HashBagOStuff.php:31
MediaWiki\Languages
MediaWiki\Languages\LanguageNameUtils\__construct
__construct(ServiceOptions $options, HookContainer $hookContainer)
Definition: LanguageNameUtils.php:90
MediaWiki\Languages\LanguageNameUtils\isValidBuiltInCode
isValidBuiltInCode(string $code)
Returns true if a language code is of a valid form for the purposes of internal customisation of Medi...
Definition: LanguageNameUtils.php:145
MediaWiki\Languages\LanguageNameUtils\isSupportedLanguage
isSupportedLanguage(string $code)
Checks whether any localisation is available for that language tag in MediaWiki (MessagesXx....
Definition: LanguageNameUtils.php:103
MediaWiki\Languages\LanguageNameUtils\getFileName
getFileName( $prefix, $code, $suffix='.php')
Get the name of a file for a certain language code.
Definition: LanguageNameUtils.php:285
MediaWikiTitleCodec\getTitleInvalidRegex
static getTitleInvalidRegex()
Returns a simple regex that will match on characters and sequences invalid in titles.
Definition: MediaWikiTitleCodec.php:518
MediaWiki\Languages\LanguageNameUtils\isValidCode
isValidCode(string $code)
Returns true if a language code string is of a valid form, whether or not it exists.
Definition: LanguageNameUtils.php:125
$file
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.
Definition: router.php:42
MediaWiki\Languages\LanguageNameUtils\ALL
const ALL
Return all known languages in getLanguageName(s).
Definition: LanguageNameUtils.php:51
MediaWiki\Languages\LanguageNameUtils\getMessagesFileName
getMessagesFileName( $code)
Definition: LanguageNameUtils.php:297
MediaWiki\Languages\LanguageNameUtils\isKnownLanguageTag
isKnownLanguageTag(string $tag)
Returns true if a language code is an IETF tag known to MediaWiki.
Definition: LanguageNameUtils.php:156
MediaWiki\Languages\LanguageNameUtils
A service that provides utilities to do with language names and codes.
Definition: LanguageNameUtils.php:42
MediaWiki\Languages\LanguageNameUtils\CONSTRUCTOR_OPTIONS
const CONSTRUCTOR_OPTIONS
Definition: LanguageNameUtils.php:78
MWException
MediaWiki exception.
Definition: MWException.php:26
MediaWiki\Config\ServiceOptions
A class for passing options to services.
Definition: ServiceOptions.php:25
MediaWiki\Languages\LanguageNameUtils\$options
ServiceOptions $options
Definition: LanguageNameUtils.php:64
MediaWiki\Languages\LanguageNameUtils\getJsonMessagesFileName
getJsonMessagesFileName( $code)
Definition: LanguageNameUtils.php:309
MediaWiki\Languages\LanguageNameUtils\getLanguageNamesUncached
getLanguageNamesUncached( $inLanguage, $include)
Uncached helper for getLanguageNames.
Definition: LanguageNameUtils.php:202
MediaWiki\Languages\LanguageNameUtils\$hookRunner
HookRunner $hookRunner
Definition: LanguageNameUtils.php:84
MediaWiki\Languages\LanguageNameUtils\SUPPORTED
const SUPPORTED
Return in getLanguageName(s) only the languages for which we have at least some localisation.
Definition: LanguageNameUtils.php:61
MediaWiki\Languages\LanguageNameUtils\DEFINED
const DEFINED
Return in getLanguageName(s) only the languages that are defined by MediaWiki.
Definition: LanguageNameUtils.php:56
MediaWiki\HookContainer\HookContainer
HookContainer class.
Definition: HookContainer.php:44
MediaWiki\HookContainer\HookRunner
This class provides an implementation of the core hook interfaces, forwarding hook calls to HookConta...
Definition: HookRunner.php:571
MediaWiki\Languages\LanguageNameUtils\AUTONYMS
const AUTONYMS
Return autonyms in getLanguageName(s).
Definition: LanguageNameUtils.php:46
MediaWiki\Languages\Data\Names\$names
static $names
Definition: Names.php:48
MediaWiki\Languages\LanguageNameUtils\getLanguageNames
getLanguageNames( $inLanguage=self::AUTONYMS, $include=self::DEFINED)
Get an array of language names, indexed by code.
Definition: LanguageNameUtils.php:181
$IP
$IP
Definition: WebStart.php:49
MediaWiki\Languages\LanguageNameUtils\$languageNameCache
HashBagOStuff null $languageNameCache
Cache for language names.
Definition: LanguageNameUtils.php:70
MediaWiki\Languages\LanguageNameUtils\getLanguageName
getLanguageName( $code, $inLanguage=self::AUTONYMS, $include=self::ALL)
Definition: LanguageNameUtils.php:271
MediaWiki\Config\ServiceOptions\assertRequiredOptions
assertRequiredOptions(array $expectedKeys)
Assert that the list of options provided in this instance exactly match $expectedKeys,...
Definition: ServiceOptions.php:62