mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/Scribunto
synced 2024-11-27 17:50:06 +00:00
Scribunto language library
Added a language library. Introduced functions which are easy and safe, most of them already have parser function interfaces. Change-Id: I4465150f3e16493a15a056f7ddb7787bdf3b0373
This commit is contained in:
parent
799440eb4f
commit
2d36fbc492
|
@ -103,6 +103,7 @@ $wgAutoloadClasses['Scribunto_LuaDataProvider'] = $dir.'tests/engines/LuaCommon/
|
|||
$wgAutoloadClasses['Scribunto_LuaSiteLibrary'] = $dir.'engines/LuaCommon/SiteLibrary.php';
|
||||
$wgAutoloadClasses['Scribunto_LuaUriLibrary'] = $dir.'engines/LuaCommon/UriLibrary.php';
|
||||
$wgAutoloadClasses['Scribunto_LuaUstringLibrary'] = $dir.'engines/LuaCommon/UstringLibrary.php';
|
||||
$wgAutoloadClasses['Scribunto_LuaLanguageLibrary'] = $dir.'engines/LuaCommon/LanguageLibrary.php';
|
||||
|
||||
/***** Configuration *****/
|
||||
|
||||
|
|
269
engines/LuaCommon/LanguageLibrary.php
Normal file
269
engines/LuaCommon/LanguageLibrary.php
Normal file
|
@ -0,0 +1,269 @@
|
|||
<?php
|
||||
|
||||
class Scribunto_LuaLanguageLibrary extends Scribunto_LuaLibraryBase {
|
||||
const MAX_LANG_CACHE_SIZE = 20;
|
||||
const MAX_TIME_CHARS = 6000;
|
||||
|
||||
var $langCache = array();
|
||||
var $timeCache = array();
|
||||
var $timeChars = 0;
|
||||
|
||||
function register() {
|
||||
// Pre-populate the language cache
|
||||
global $wgContLang;
|
||||
$this->langCache[$wgContLang->getCode()] = $wgContLang;
|
||||
|
||||
$statics = array(
|
||||
'getContLangCode',
|
||||
'isSupportedLanguage',
|
||||
'isKnownLanguageTag',
|
||||
'isValidCode',
|
||||
'isValidBuiltInCode',
|
||||
'fetchLanguageName',
|
||||
);
|
||||
$methods = array(
|
||||
'lcfirst',
|
||||
'ucfirst',
|
||||
'lc',
|
||||
'uc',
|
||||
'caseFold',
|
||||
'formatNum',
|
||||
'formatDate',
|
||||
'parseFormattedNumber',
|
||||
'convertPlural',
|
||||
'convertGrammar',
|
||||
'gender',
|
||||
'isRTL',
|
||||
);
|
||||
$lib = array();
|
||||
foreach ( $statics as $name ) {
|
||||
$lib[$name] = array( $this, $name );
|
||||
}
|
||||
$ths = $this;
|
||||
foreach ( $methods as $name ) {
|
||||
$lib[$name] = function () use ( $ths, $name ) {
|
||||
$args = func_get_args();
|
||||
return $ths->languageMethod( $name, $args );
|
||||
};
|
||||
}
|
||||
$this->getEngine()->registerInterface( 'mw.language.lua', $lib );
|
||||
}
|
||||
|
||||
function getContLangCode() {
|
||||
global $wgContLang;
|
||||
return array( $wgContLang->getCode() );
|
||||
}
|
||||
|
||||
function isSupportedLanguage( $code ) {
|
||||
$this->checkType( 'isSupportedLanguage', 1, $code, 'string' );
|
||||
return array( Language::isSupportedLanguage( $code ) );
|
||||
}
|
||||
|
||||
function isKnownLanguageTag( $code ) {
|
||||
$this->checkType( 'isKnownLanguageTag', 1, $code, 'string' );
|
||||
return array( Language::isKnownLanguageTag( $code ) );
|
||||
}
|
||||
|
||||
function isValidCode( $code ) {
|
||||
$this->checkType( 'isValidCode', 1, $code, 'string' );
|
||||
return array( Language::isValidCode( $code ) );
|
||||
}
|
||||
|
||||
function isValidBuiltInCode( $code ) {
|
||||
$this->checkType( 'isValidBuiltInCode', 1, $code, 'string' );
|
||||
return array( (bool)Language::isValidBuiltInCode( $code ) );
|
||||
}
|
||||
|
||||
function fetchLanguageName( $code, $inLanguage ) {
|
||||
$this->checkType( 'fetchLanguageName', 1, $code, 'string' );
|
||||
$this->checkTypeOptional( 'fetchLanguageName', 2, $inLanguage, 'string', null );
|
||||
return array( Language::fetchLanguageName( $code, $inLanguage ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Language object method handler
|
||||
*/
|
||||
function languageMethod( $name, $args ) {
|
||||
$name = strval( $name );
|
||||
$code = array_shift( $args );
|
||||
if ( !isset( $this->langCache[$code] ) ) {
|
||||
if ( count( $this->langCache ) > self::MAX_LANG_CACHE_SIZE ) {
|
||||
throw new Scribunto_LuaError( 'too many language codes requested' );
|
||||
}
|
||||
$this->langCache[$code] = Language::factory( $code );
|
||||
}
|
||||
$lang = $this->langCache[$code];
|
||||
switch ( $name ) {
|
||||
// Zero arguments
|
||||
case 'isRTL':
|
||||
return array( $lang->$name() );
|
||||
|
||||
// One string argument passed straight through
|
||||
case 'lcfirst':
|
||||
case 'ucfirst':
|
||||
case 'lc':
|
||||
case 'uc':
|
||||
case 'caseFold':
|
||||
$this->checkType( $name, 1, $args[0], 'string' );
|
||||
return array( $lang->$name( $args[0] ) );
|
||||
|
||||
case 'parseFormattedNumber':
|
||||
if ( is_numeric( $args[0] ) ) {
|
||||
$args[0] = strval( $args[0] );
|
||||
}
|
||||
$this->checkType( $name, 1, $args[0], 'string' );
|
||||
return array( $lang->$name( $args[0] ) );
|
||||
|
||||
// Custom handling
|
||||
default:
|
||||
return $this->$name( $lang, $args );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* convertPlural handler
|
||||
*/
|
||||
function convertPlural( $lang, $args ) {
|
||||
$number = array_shift( $args );
|
||||
$this->checkType( 'convertPlural', 1, $number, 'number' );
|
||||
if ( is_array( $args[0] ) ) {
|
||||
$args = $args[0];
|
||||
}
|
||||
$forms = array_values( array_map( 'strval', $args ) );
|
||||
return array( $lang->convertPlural( $number, $forms ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* convertGrammar handler
|
||||
*/
|
||||
function convertGrammar( $lang, $args ) {
|
||||
$this->checkType( 'convertGrammar', 1, $args[0], 'string' );
|
||||
$this->checkType( 'convertGrammar', 2, $args[1], 'string' );
|
||||
return array( $lang->convertGrammar( $args[0], $args[1] ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* gender handler
|
||||
*/
|
||||
function gender( $lang, $args ) {
|
||||
$this->checkType( 'gender', 1, $args[0], 'string' );
|
||||
$username = trim( array_shift( $args[0] ) );
|
||||
|
||||
if ( is_array( $args[0] ) ) {
|
||||
$args = $args[0];
|
||||
}
|
||||
$forms = array_values( array_map( 'strval', $args ) );
|
||||
|
||||
// Shortcuts
|
||||
if ( count( $forms ) === 0 ) {
|
||||
return '';
|
||||
} elseif ( count( $forms ) === 1 ) {
|
||||
return $forms[0];
|
||||
}
|
||||
|
||||
if ( $username === 'male' || $username === 'female' ) {
|
||||
$gender = $username;
|
||||
} else {
|
||||
// default
|
||||
$gender = User::getDefaultOption( 'gender' );
|
||||
|
||||
// Check for "User:" prefix
|
||||
$title = Title::newFromText( $username );
|
||||
if ( $title && $title->getNamespace() == NS_USER ) {
|
||||
$username = $title->getText();
|
||||
}
|
||||
|
||||
// check parameter, or use the ParserOptions if in interface message
|
||||
$user = User::newFromName( $username );
|
||||
if ( $user ) {
|
||||
$gender = GenderCache::singleton()->getGenderOf( $user, __METHOD__ );
|
||||
} elseif ( $username === '' ) {
|
||||
$parserOptions = $this->getParserOptions();
|
||||
if ( $parserOptions->getInterfaceMessage() ) {
|
||||
$gender = GenderCache::singleton()->getGenderOf( $parserOptions->getUser(), __METHOD__ );
|
||||
}
|
||||
}
|
||||
}
|
||||
return array( $lang->gender( $gender, $forms ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* formatNum handler
|
||||
*/
|
||||
function formatNum( $lang, $args ) {
|
||||
$num = $args[0];
|
||||
$this->checkType( 'formatNum', 1, $num, 'number' );
|
||||
|
||||
$noCommafy = false;
|
||||
if ( isset( $args[1] ) ) {
|
||||
$this->checkType( 'formatNum', 2, $args[1], 'table' );
|
||||
$options = $args[1];
|
||||
$noCommafy = !empty( $options['noCommafy'] );
|
||||
}
|
||||
return array( $lang->formatNum( $num, $noCommafy ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* formatDate handler
|
||||
*/
|
||||
function formatDate( $lang, $args ) {
|
||||
$this->checkType( 'formatDate', 1, $args[0], 'string' );
|
||||
$this->checkTypeOptional( 'formatDate', 2, $args[1], 'string', '' );
|
||||
$this->checkTypeOptional( 'formatDate', 3, $args[2], 'boolean', false );
|
||||
|
||||
list( $format, $date, $local ) = $args;
|
||||
$langcode = $lang->getCode();
|
||||
|
||||
if ( $date === '' ) {
|
||||
$cacheKey = $this->getParserOptions()->getTimestamp();
|
||||
$timestamp = new MWTimestamp( $cacheKey );
|
||||
$date = $timestamp->getTimestamp( TS_ISO_8601 );
|
||||
} else {
|
||||
# Correct for DateTime interpreting 'XXXX' as XX:XX o'clock
|
||||
if ( preg_match( '/^[0-9]{4}$/', $date ) ) {
|
||||
$date = '00:00 '.$date;
|
||||
}
|
||||
|
||||
$cacheKey = $date;
|
||||
}
|
||||
|
||||
if ( isset( $this->timeCache[$format][$cacheKey][$langcode][$local] ) ) {
|
||||
return array( $this->timeCache[$format][$cacheKey][$langcode][$local] );
|
||||
}
|
||||
|
||||
$this->timeChars += strlen( $format );
|
||||
if ( $this->timeChars > self::MAX_TIME_CHARS ) {
|
||||
throw new Scribunto_LuaError( "Too many calls to mw.language:formatDate()" );
|
||||
}
|
||||
|
||||
# Default input timezone is UTC.
|
||||
try {
|
||||
$utc = new DateTimeZone( 'UTC' );
|
||||
$dateObject = new DateTime( $date, $utc );
|
||||
} catch ( Exception $ex ) {
|
||||
throw new Scribunto_LuaError( "bad argument #2 to 'formatDate' (not a valid timestamp)" );
|
||||
}
|
||||
|
||||
# Set output timezone.
|
||||
if ( $local ) {
|
||||
if ( isset( $wgLocaltimezone ) ) {
|
||||
$tz = new DateTimeZone( $wgLocaltimezone );
|
||||
} else {
|
||||
$tz = new DateTimeZone( date_default_timezone_get() );
|
||||
}
|
||||
$dateObject->setTimezone( $tz );
|
||||
} else {
|
||||
$dateObject->setTimezone( $utc );
|
||||
}
|
||||
# Generate timestamp
|
||||
$ts = $dateObject->format( 'YmdHis' );
|
||||
|
||||
if ( $ts >= 100000000000000 ) {
|
||||
throw new Scribunto_LuaError( "mw.language:formatDate() only supports years up to 9999" );
|
||||
}
|
||||
|
||||
$ret = $lang->sprintfDate( $format, $ts );
|
||||
$this->timeCache[$format][$cacheKey][$langcode][$local] = $ret;
|
||||
return array( $ret );
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@ abstract class Scribunto_LuaEngine extends ScribuntoEngineBase {
|
|||
'mw.site' => 'Scribunto_LuaSiteLibrary',
|
||||
'mw.uri' => 'Scribunto_LuaUriLibrary',
|
||||
'mw.ustring' => 'Scribunto_LuaUstringLibrary',
|
||||
'mw.language' => 'Scribunto_LuaLanguageLibrary',
|
||||
);
|
||||
|
||||
/**
|
||||
|
|
114
engines/LuaCommon/lualib/mw.language.lua
Normal file
114
engines/LuaCommon/lualib/mw.language.lua
Normal file
|
@ -0,0 +1,114 @@
|
|||
local language = {}
|
||||
local php
|
||||
local util = require 'libraryUtil'
|
||||
|
||||
function language.setupInterface()
|
||||
-- Boilerplate
|
||||
language.setupInterface = nil
|
||||
php = mw_interface
|
||||
mw_interface = nil
|
||||
|
||||
-- Register this library in the "mw" global
|
||||
mw = mw or {}
|
||||
mw.language = language
|
||||
mw.getContentLanguage = language.getContentLanguage
|
||||
mw.getLanguage = mw.language.new
|
||||
|
||||
local lang = mw.getContentLanguage();
|
||||
|
||||
-- Extend ustring
|
||||
if mw.ustring then
|
||||
mw.ustring.upper = function ( s )
|
||||
return lang:uc( s )
|
||||
end
|
||||
mw.ustring.lower = function ( s )
|
||||
return lang:lc( s )
|
||||
end
|
||||
string.uupper = mw.ustring.upper
|
||||
string.ulower = mw.ustring.lower
|
||||
end
|
||||
|
||||
package.loaded['mw.language'] = language
|
||||
end
|
||||
|
||||
function language.isSupportedLanguage( code )
|
||||
return php.isSupportedLanguage( code )
|
||||
end
|
||||
|
||||
function language.isKnownLanguageTag( code )
|
||||
return php.isKnownLanguageTag( code )
|
||||
end
|
||||
|
||||
function language.isValidCode( code )
|
||||
return php.isValidCode( code )
|
||||
end
|
||||
|
||||
function language.isValidBuiltInCode( code )
|
||||
return php.isValidBuiltInCode( code )
|
||||
end
|
||||
|
||||
function language.fetchLanguageName( code, inLanguage )
|
||||
return php.fetchLanguageName( code, inLanguage )
|
||||
end
|
||||
|
||||
function language.new( code )
|
||||
if code == nil then
|
||||
error( "too few arguments to mw.language.new()", 2 )
|
||||
end
|
||||
|
||||
local lang = { code = code }
|
||||
|
||||
local checkSelf = util.makeCheckSelfFunction( 'mw.language', 'lang', lang, 'language object' )
|
||||
|
||||
local wrappers = {
|
||||
isRTL = 0,
|
||||
lcfirst = 1,
|
||||
ucfirst = 1,
|
||||
lc = 1,
|
||||
uc = 1,
|
||||
caseFold = 1,
|
||||
formatNum = 1,
|
||||
formatDate = 1,
|
||||
parseFormattedNumber = 1,
|
||||
convertPlural = 2,
|
||||
convertGrammar = 2,
|
||||
gender = 2,
|
||||
}
|
||||
|
||||
for name, numArgs in pairs( wrappers ) do
|
||||
lang[name] = function ( self, ... )
|
||||
checkSelf( self, name )
|
||||
if select( '#', ... ) < numArgs then
|
||||
error( "too few arguments to mw.language:" .. name, 2 )
|
||||
end
|
||||
return php[name]( self.code, ... )
|
||||
end
|
||||
end
|
||||
|
||||
-- Alias
|
||||
lang.plural = lang.convertPlural
|
||||
|
||||
-- Parser function compat
|
||||
function lang:grammar( case, word )
|
||||
checkSelf( self, name )
|
||||
return self:convertGrammar( word, case )
|
||||
end
|
||||
|
||||
function lang:getCode()
|
||||
checkSelf( self, 'getCode' )
|
||||
return self.code
|
||||
end
|
||||
|
||||
return lang
|
||||
end
|
||||
|
||||
local contLangCode
|
||||
|
||||
function language.getContentLanguage()
|
||||
if contLangCode == nil then
|
||||
contLangCode = php.getContLangCode()
|
||||
end
|
||||
return language.new( contLangCode )
|
||||
end
|
||||
|
||||
return language
|
Loading…
Reference in a new issue