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 
81  public const CONSTRUCTOR_OPTIONS = [
82  'ExtraLanguageNames',
83  'UsePigLatinVariant',
84  ];
85 
87  private $hookRunner;
88 
93  public function __construct( ServiceOptions $options, HookContainer $hookContainer ) {
94  $options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS );
95  $this->options = $options;
96  $this->hookRunner = new HookRunner( $hookContainer );
97  }
98 
106  public function isSupportedLanguage( string $code ) : bool {
107  if ( !$this->isValidBuiltInCode( $code ) ) {
108  return false;
109  }
110 
111  if ( $code === 'qqq' ) {
112  // Special code for internal use, not supported even though there is a qqq.json
113  return false;
114  }
115 
116  return is_readable( $this->getMessagesFileName( $code ) ) ||
117  is_readable( $this->getJsonMessagesFileName( $code ) );
118  }
119 
129  public function isValidCode( string $code ) : bool {
130  if ( !isset( $this->validCodeCache[$code] ) ) {
131  // People think language codes are HTML-safe, so enforce it. Ideally we should only
132  // allow a-zA-Z0-9- but .+ and other chars are often used for {{int:}} hacks. See bugs
133  // T39564, T39587, T38938.
134  $this->validCodeCache[$code] =
135  // Protect against path traversal
136  strcspn( $code, ":/\\\000&<>'\"" ) === strlen( $code ) &&
137  !preg_match( MediaWikiTitleCodec::getTitleInvalidRegex(), $code );
138  }
139  return $this->validCodeCache[$code];
140  }
141 
149  public function isValidBuiltInCode( string $code ) : bool {
150  return (bool)preg_match( '/^[a-z0-9-]{2,}$/', $code );
151  }
152 
160  public function isKnownLanguageTag( string $tag ) : bool {
161  // Quick escape for invalid input to avoid exceptions down the line when code tries to
162  // process tags which are not valid at all.
163  if ( !$this->isValidBuiltInCode( $tag ) ) {
164  return false;
165  }
166 
167  if ( isset( Data\Names::$names[$tag] ) || $this->getLanguageName( $tag, $tag ) !== '' ) {
168  return true;
169  }
170 
171  return false;
172  }
173 
185  public function getLanguageNames( $inLanguage = self::AUTONYMS, $include = self::DEFINED ) {
186  $cacheKey = $inLanguage === self::AUTONYMS ? 'null' : $inLanguage;
187  $cacheKey .= ":$include";
188  if ( !$this->languageNameCache ) {
189  $this->languageNameCache = new HashBagOStuff( [ 'maxKeys' => 20 ] );
190  }
191 
192  $ret = $this->languageNameCache->get( $cacheKey );
193  if ( !$ret ) {
194  $ret = $this->getLanguageNamesUncached( $inLanguage, $include );
195  $this->languageNameCache->set( $cacheKey, $ret );
196  }
197  return $ret;
198  }
199 
206  private function getLanguageNamesUncached( $inLanguage, $include ) {
207  // If passed an invalid language code to use, fallback to en
208  if ( $inLanguage !== self::AUTONYMS && !$this->isValidCode( $inLanguage ) ) {
209  $inLanguage = 'en';
210  }
211 
212  $names = [];
213 
214  if ( $inLanguage !== self::AUTONYMS ) {
215  # TODO: also include for self::AUTONYMS, when this code is more efficient
216  $this->hookRunner->onLanguageGetTranslatedLanguageNames( $names, $inLanguage );
217  }
218 
219  $mwNames = $this->options->get( 'ExtraLanguageNames' ) + Data\Names::$names;
220  if ( $this->options->get( 'UsePigLatinVariant' ) ) {
221  // Pig Latin (for variant development)
222  $mwNames['en-x-piglatin'] = 'Igpay Atinlay';
223  }
224 
225  foreach ( $mwNames as $mwCode => $mwName ) {
226  # - Prefer own MediaWiki native name when not using the hook
227  # - For other names just add if not added through the hook
228  if ( $mwCode === $inLanguage || !isset( $names[$mwCode] ) ) {
229  $names[$mwCode] = $mwName;
230  }
231  }
232 
233  if ( $include === self::ALL ) {
234  ksort( $names );
235  return $names;
236  }
237 
238  $returnMw = [];
239  $coreCodes = array_keys( $mwNames );
240  foreach ( $coreCodes as $coreCode ) {
241  $returnMw[$coreCode] = $names[$coreCode];
242  }
243 
244  if ( $include === self::SUPPORTED ) {
245  $namesMwFile = [];
246  # We do this using a foreach over the codes instead of a directory loop so that messages
247  # files in extensions will work correctly.
248  foreach ( $returnMw as $code => $value ) {
249  if ( is_readable( $this->getMessagesFileName( $code ) ) ||
250  is_readable( $this->getJsonMessagesFileName( $code ) )
251  ) {
252  $namesMwFile[$code] = $names[$code];
253  }
254  }
255 
256  ksort( $namesMwFile );
257  return $namesMwFile;
258  }
259 
260  ksort( $returnMw );
261  # self::DEFINED option; default if it's not one of the other two options
262  # (self::ALL/self::SUPPORTED)
263  return $returnMw;
264  }
265 
275  public function getLanguageName( $code, $inLanguage = self::AUTONYMS, $include = self::ALL ) {
276  $code = strtolower( $code );
277  $array = $this->getLanguageNames( $inLanguage, $include );
278  return $array[$code] ?? '';
279  }
280 
289  public function getFileName( $prefix, $code, $suffix = '.php' ) {
290  if ( !$this->isValidBuiltInCode( $code ) ) {
291  throw new MWException( "Invalid language code \"$code\"" );
292  }
293 
294  return $prefix . str_replace( '-', '_', ucfirst( $code ) ) . $suffix;
295  }
296 
301  public function getMessagesFileName( $code ) {
302  global $IP;
303  $file = $this->getFileName( "$IP/languages/messages/Messages", $code, '.php' );
304  $this->hookRunner->onLanguage__getMessagesFileName( $code, $file );
305  return $file;
306  }
307 
313  public function getJsonMessagesFileName( $code ) {
314  global $IP;
315 
316  if ( !$this->isValidBuiltInCode( $code ) ) {
317  throw new MWException( "Invalid language code \"$code\"" );
318  }
319 
320  return "$IP/languages/i18n/$code.json";
321  }
322 }
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:32
MediaWiki\Languages
MediaWiki\Languages\LanguageNameUtils\__construct
__construct(ServiceOptions $options, HookContainer $hookContainer)
Definition: LanguageNameUtils.php:93
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:149
MediaWiki\Languages\LanguageNameUtils\isSupportedLanguage
isSupportedLanguage(string $code)
Checks whether any localisation is available for that language tag in MediaWiki (MessagesXx....
Definition: LanguageNameUtils.php:106
MediaWiki\Languages\LanguageNameUtils\getFileName
getFileName( $prefix, $code, $suffix='.php')
Get the name of a file for a certain language code.
Definition: LanguageNameUtils.php:289
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:129
$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:301
MediaWiki\Languages\LanguageNameUtils\isKnownLanguageTag
isKnownLanguageTag(string $tag)
Returns true if a language code is an IETF tag known to MediaWiki.
Definition: LanguageNameUtils.php:160
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:81
MWException
MediaWiki exception.
Definition: MWException.php:29
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:313
MediaWiki\Languages\LanguageNameUtils\getLanguageNamesUncached
getLanguageNamesUncached( $inLanguage, $include)
Uncached helper for getLanguageNames.
Definition: LanguageNameUtils.php:206
MediaWiki\Languages\LanguageNameUtils\$hookRunner
HookRunner $hookRunner
Definition: LanguageNameUtils.php:87
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:45
MediaWiki\HookContainer\HookRunner
This class provides an implementation of the core hook interfaces, forwarding hook calls to HookConta...
Definition: HookRunner.php:562
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:185
$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:275
MediaWiki\Config\ServiceOptions\assertRequiredOptions
assertRequiredOptions(array $expectedKeys)
Assert that the list of options provided in this instance exactly match $expectedKeys,...
Definition: ServiceOptions.php:62