mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/ParserFunctions
synced 2024-11-15 11:59:54 +00:00
(bug 235) parser function for conversion of units of measurement.
[[Template:Convert]] on enwiki is a behemoth of a construction that just about manages to do this sort of conversion, taking {{convert|5|mi|km}} and outputting "5 miles (8 km)", etc. To port this to another wiki requires copying over three and a half thousand subtemplates. The additional load produced by including numerous copies of this template is measurable on large pages on enwiki, and it eats voraciously into the template limits. This revision introduces {{#convert: 5 mi | km }}, outputting "8 km" or thereabouts. See http://www.mediawiki.org/wiki/User:Happy-melon/Convert for more details, or look at the examples in the parser tests. In a very rough profile, comparing 50 calls to {{convert}} verses the same 50 calls to the wrapper template shown at the link above, the parser function implementation reduces page load time by 72%, preprocessor node count by 83%, post-expand include size by 86% and template argument size by 97%. More detailed profiling would probably reveal places where extra caching could improve performance further. The primary reason for putting it in ParserFunctions instead of its own extension is availability: PFs are already available across the cluster, and it's accepted as an essential extension for any wiki wishing to emulate or mirror WMF content. One less separate extension installed on the cluster is one less extension which has to be matched by reusers. It's still missing a lot of units, which I ran out of patience to copy from {{convert}}; I thought I'd get some feedback on the infrastructure first.
This commit is contained in:
parent
e512f7f3cd
commit
65e9e69202
735
Convert.php
Normal file
735
Convert.php
Normal file
|
@ -0,0 +1,735 @@
|
|||
<?php
|
||||
|
||||
if ( !defined( 'MEDIAWIKI' ) ) {
|
||||
die( 'This file is a MediaWiki extension, it is not a valid entry point' );
|
||||
}
|
||||
|
||||
class ConvertError extends Exception {
|
||||
public function __construct( $msg /*...*/ ) {
|
||||
$args = func_get_args();
|
||||
array_shift( $args );
|
||||
$this->message = '<strong class="error">' . wfMsgExt( "pfunc-convert-$msg", 'parseinline', $args ) . '</strong>';
|
||||
}
|
||||
}
|
||||
|
||||
class ConvertParser {
|
||||
|
||||
# A regex which matches the body of the string and the source unit separately
|
||||
const UNITS_REGEX = '/^(.+?)\s*([a-z]+\^?\d?(?:\/\w+\^?\d?)*)$/i';
|
||||
|
||||
# A regex which matches a number
|
||||
const NUM_REGEX = '/\b((?:\+|\-|−|\x2212)?(\d+(?:\.\d+)?)(?:E(?:\+|\-|−|\x2212)?\d+)?)\b/i';
|
||||
|
||||
# ConvertUnit objects
|
||||
protected $sourceUnit;
|
||||
protected $targetUnit;
|
||||
|
||||
# Whether to abbreviate the output unit
|
||||
protected $abbreviate;
|
||||
|
||||
# Whether to link the output unit, if possible
|
||||
protected $link;
|
||||
|
||||
# If set, don't output the unit or format the number
|
||||
protected $raw;
|
||||
|
||||
# What precision to round to.
|
||||
protected $decimalPlaces;
|
||||
protected $significantFigures;
|
||||
|
||||
# The last value converted, which will be used for PLURAL evaluation
|
||||
protected $lastValue;
|
||||
|
||||
public function clearState(){
|
||||
# Make sure we break any references set up in the parameter passing below
|
||||
unset( $this->sourceUnit );
|
||||
unset( $this->targetUnit );
|
||||
$this->sourceUnit = null;
|
||||
$this->targetUnit = null;
|
||||
|
||||
$this->lastValue
|
||||
= $this->link
|
||||
= $this->precision
|
||||
= $this->abbreviate
|
||||
= $this->raw
|
||||
= $this->significantFigures
|
||||
= $this->decimalPlaces
|
||||
= null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate a convert expression
|
||||
* @param $args Array of the parameters passed to the original tag function
|
||||
* @return String
|
||||
* @throws ConvertError
|
||||
*/
|
||||
public function execute( $args ) {
|
||||
$this->clearState();
|
||||
array_shift( $args ); # Dump Parser object
|
||||
|
||||
if( count( $args ) == 0 ){
|
||||
# that was easy
|
||||
return '';
|
||||
}
|
||||
$string = trim( array_shift( $args ) );
|
||||
|
||||
# Process the rest of the args
|
||||
$sourceUnit =& MagicWord::get( 'sourceunit' );
|
||||
$targetUnit =& MagicWord::get( 'targetunit' );
|
||||
$linkUnit =& MagicWord::get( 'linkunit' );
|
||||
$dp =& MagicWord::get( 'decimalplaces' );
|
||||
$sf =& MagicWord::get( 'significantfigures' );
|
||||
$abbr =& MagicWord::get( 'abbreviate' );
|
||||
$raw =& MagicWord::get( 'rawsuffix' );
|
||||
|
||||
$n = 0; # Count of unnamed parameters
|
||||
foreach ( $args as $arg ) {
|
||||
$parts = array_map( 'trim', explode( '=', $arg, 2 ) );
|
||||
if ( count( $parts ) == 2 ) {
|
||||
# Found "="
|
||||
if ( $sourceUnit->matchStartAndRemove( $parts[0] ) ) {
|
||||
if( $targetUnit->matchStartAndRemove( $parts[1] ) ){
|
||||
$this->targetUnit =& $this->sourceUnit;
|
||||
} else {
|
||||
$this->sourceUnit = new ConvertUnit( $parts[1] );
|
||||
}
|
||||
|
||||
} elseif ( $targetUnit->matchStartAndRemove( $parts[0] ) ) {
|
||||
if( $sourceUnit->matchStartAndRemove( $parts[1] ) ){
|
||||
$this->targetUnit =& $this->sourceUnit;
|
||||
} else {
|
||||
$this->targetUnit = new ConvertUnit( $parts[1] );
|
||||
}
|
||||
|
||||
} elseif( $dp->matchStartAndRemove( $parts[0] ) ) {
|
||||
$this->decimalPlaces = intval( $parts[1] );
|
||||
|
||||
} elseif( $sf->matchStartAndRemove( $parts[0] ) ) {
|
||||
# It doesn't make any sense to have negative sig-figs
|
||||
if( intval( $parts[1] ) > 0 ){
|
||||
$this->significantFigures = intval( $parts[1] );
|
||||
}
|
||||
}
|
||||
} elseif( $linkUnit->matchStartAndRemove( $parts[0] ) ) {
|
||||
$this->link = true;
|
||||
} elseif( $abbr->matchStartAndRemove( $parts[0] ) ) {
|
||||
$this->abbreviate = true;
|
||||
} elseif( $raw->matchStartAndRemove( $parts[0] ) ) {
|
||||
$this->raw = true;
|
||||
} elseif( $parts[0] != '' && !$n++ && !$this->targetUnit instanceof ConvertUnit ){
|
||||
# First unnamed parameter = output unit
|
||||
$this->targetUnit = new ConvertUnit( $parts[0] );
|
||||
}
|
||||
}
|
||||
|
||||
# Get the source unit, if not already set. This throws ConvertError on failure
|
||||
if ( !$this->sourceUnit instanceof ConvertUnit ){
|
||||
$this->deduceSourceUnit( $string );
|
||||
}
|
||||
|
||||
# Use the default unit (SI usually)
|
||||
if( !$this->targetUnit instanceof ConvertUnit ){
|
||||
$this->targetUnit = $this->sourceUnit->getDefaultUnit();
|
||||
}
|
||||
|
||||
if( $this->targetUnit->dimension->value != $this->sourceUnit->dimension->value ){
|
||||
throw new ConvertError(
|
||||
'dimensionmismatch',
|
||||
$this->sourceUnit->dimension->getName(true),
|
||||
$this->targetUnit->dimension->getName(true)
|
||||
);
|
||||
}
|
||||
|
||||
return $this->processString( $string );
|
||||
}
|
||||
|
||||
protected function deduceSourceUnit( $string ){
|
||||
# Get the unit from the end of the string
|
||||
$matches = array();
|
||||
preg_match( self::UNITS_REGEX, $string, $matches );
|
||||
|
||||
if( count( $matches ) == 3 ){
|
||||
$this->sourceUnit = new ConvertUnit( $matches[2] );
|
||||
} else {
|
||||
throw new ConvertError( 'nounit' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Identify the values to be converted, and convert them
|
||||
* @param $string String
|
||||
*/
|
||||
protected function processString( $string ){
|
||||
# Replace values
|
||||
$string = preg_replace_callback(
|
||||
self::NUM_REGEX,
|
||||
array( $this, 'convert' ),
|
||||
trim( preg_replace( self::UNITS_REGEX, '$1', $string ) )
|
||||
);
|
||||
if( $this->raw ){
|
||||
return $string;
|
||||
} else {
|
||||
$unit = $this->targetUnit->getText(
|
||||
$this->lastValue,
|
||||
$this->link,
|
||||
$this->abbreviate
|
||||
);
|
||||
return "$string $unit";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Express a value in the $sourceUnit in terms of the $targetUnit, preserving
|
||||
* an appropriate degree of accuracy.
|
||||
* @param $value String
|
||||
* @return void
|
||||
*/
|
||||
public function convert( $value ){
|
||||
global $wgContLang;
|
||||
$valueFloat = floatval( $value[1] );
|
||||
$newValue = $valueFloat
|
||||
* $this->sourceUnit->getConversion()
|
||||
/ $this->targetUnit->getConversion();
|
||||
if( $this->decimalPlaces !== null && $this->significantFigures !== null ){
|
||||
# round to the required number of decimal places, or the required number
|
||||
# of significant figures, whichever is the least precise
|
||||
$dp = floor( $this->significantFigures - log10( abs( $newValue ) ) ); # Convert SF to DP
|
||||
$newValue = round( $newValue, max( $dp, $this->decimalPlaces ) );
|
||||
|
||||
} elseif( $this->decimalPlaces !== null ){
|
||||
$newValue = round( $newValue, $this->decimalPlaces );
|
||||
|
||||
} elseif( $this->significantFigures !== null ){
|
||||
$dp = floor( $this->significantFigures - log10( abs( $newValue ) ) ); # Convert SF to DP
|
||||
$newValue = round( $newValue, $dp );
|
||||
|
||||
} else {
|
||||
# Need to round to a similar accuracy as the original value. To do that we
|
||||
# select the accuracy which will as closely as possible preserve the maximum
|
||||
# percentage error in the value. So 36ft = 36 ± 0.5 ft, so the uncertainty
|
||||
# is ±0.5/36 = ±1.4%. In metres this is 10.9728 ± 1.4%, or 10.9728 ± 0.154
|
||||
# we take the stance of choosing the limit which is *more* precise than the
|
||||
# original value.
|
||||
|
||||
# Strip sign and exponent
|
||||
$num = preg_replace( self::NUM_REGEX, '$2', $value[1] );
|
||||
|
||||
if( strpos( $num, '.' ) !== false ){
|
||||
# If there is a decimal point, this is the number of digits after it.
|
||||
$dpAfter = strlen( $num ) - strpos( $num, '.' ) - 1;
|
||||
$error = pow( 10, -$dpAfter - 1 ) * 5;
|
||||
|
||||
} elseif( $num == 0 ) {
|
||||
# The logarithms below will be unhappy, and it doesn't actually matter
|
||||
# what error we come up with, zero is still zero
|
||||
$error = 1;
|
||||
|
||||
} else {
|
||||
# Number of digits before the point
|
||||
$dpBefore = floor( log10( abs( $num ) ) );
|
||||
|
||||
# Number of digits if we reverse the string = number
|
||||
# of digits excluding trailing zeros
|
||||
$dpAfter = floor( log10( abs( strrev( $num ) ) ) );
|
||||
|
||||
# How many significant figures to consider numbers like "35000" to have
|
||||
# is a tricky question. We say 2 here because if people want to ensure
|
||||
# that the zeros are included, they could write it as 3.500E4
|
||||
$error = pow( 10, $dpBefore - $dpAfter - 1 ) * 5;
|
||||
}
|
||||
|
||||
$errorFraction = $error / $num;
|
||||
|
||||
$i = 10;
|
||||
while( $i > -10 && ( round( $newValue, $i - 1 ) != 0 ) &&
|
||||
# Rounding to 10dp avoids floating point errors in exact conversions,
|
||||
# which are on the order of 1E-16
|
||||
( round( 5 * pow( 10, -$i ) / round( $newValue, $i - 1 ), 10 ) <= round( $errorFraction, 10 ) ) )
|
||||
{
|
||||
$i--;
|
||||
}
|
||||
|
||||
$newValue = round( $newValue, $i );
|
||||
# We may need to stick significant zeros back onto the number
|
||||
if( $i > 0 ){
|
||||
if( strpos( $newValue, '.' ) !== false ){
|
||||
$newValue = str_pad( $newValue, $i + strpos( $newValue, '.' ) + 1, '0' );
|
||||
} else {
|
||||
$newValue .= '.' . str_repeat( '0', $i );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Store the last value for use in PLURAL later
|
||||
$this->lastValue = $newValue;
|
||||
|
||||
return $this->raw
|
||||
? $newValue
|
||||
: $wgContLang->formatNum( $newValue );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A dimension
|
||||
*/
|
||||
class ConvertDimension {
|
||||
|
||||
const MASS = 1; # KILOGRAM
|
||||
const LENGTH = 10; # METRE
|
||||
const TIME = 100; # SECOND
|
||||
const TEMPERATURE = 1E3; # KELVIN
|
||||
const QUANTITY = 1E4; # MOLE
|
||||
const CURRENT = 1E5; # AMPERE
|
||||
const INTENSITY = 1E6; # CANDELA
|
||||
|
||||
# fuel efficiencies are ugly and horrible and dimensionally confused, and have the
|
||||
# same dimensions as LENGTH or 1/LENGTH. But someone wanted to include them... so
|
||||
# we have up to ten dimensions which can be identified by values of this.
|
||||
# 0 = sane unit
|
||||
# 1 = some sort of fuel efficiency
|
||||
const UGLY_HACK_VALUE = 1E7;
|
||||
|
||||
/**
|
||||
* Dimension constants. These are the values you'd get if you added the SI
|
||||
* base units together with the weighting given above, also the output from
|
||||
* getDimensionHash(). Cool thing is, you can add these together to get new
|
||||
* compound dimensions.
|
||||
*/
|
||||
const DIM_DIMENSIONLESS = 0; # Numbers etc
|
||||
const DIM_LENGTH = 10;
|
||||
const DIM_AREA = 20;
|
||||
const DIM_VOLUME = 30;
|
||||
const DIM_TIME = 100;
|
||||
const DIM_TIME_SQ = 200;
|
||||
const DIM_MASS = 1;
|
||||
const DIM_TEMPERATURE = 1000;
|
||||
const DIM_SPEED = -90; # LENGTH / TIME
|
||||
const DIM_ACCELERATION = -190; # LENGTH / TIME_SQ
|
||||
const DIM_FORCE = -189; # MASS * LENGTH / TIME_SQ
|
||||
const DIM_TORQUE = -179; # also MASS * AREA / TIME_SQ, but all units are single
|
||||
const DIM_ENERGY = -179; # MASS * AREA / TIME_SQ, all units are compound
|
||||
const DIM_PRESSURE = -209; # MASS / ( LENGTH * TIME_SQ )
|
||||
const DIM_POWER = -79; # MASS * AREA / TIME
|
||||
const DIM_DENSITY = -29; # MASS / VOLUME
|
||||
const DIM_FUELEFFICIENCY_PVE = 10000020; # fuel efficiency in VOLUME / LENGTH
|
||||
const DIM_FUELEFFICIENCY_NVE = 99999990; # fuel efficiency in LENGTH / VOLUME
|
||||
|
||||
# Map of dimension names to message keys. This also serves as a list of what
|
||||
# dimensions will not throw an error when encountered.
|
||||
public static $legalDimensions = array(
|
||||
self::DIM_LENGTH => 'length',
|
||||
self::DIM_AREA => 'area',
|
||||
self::DIM_VOLUME => 'volume',
|
||||
self::DIM_TIME => 'time',
|
||||
self::DIM_TIME_SQ => 'timesquared',
|
||||
self::DIM_MASS => 'mass',
|
||||
self::DIM_TEMPERATURE => 'temperature',
|
||||
self::DIM_SPEED => 'speed',
|
||||
self::DIM_ACCELERATION => 'acceleration',
|
||||
self::DIM_FORCE => 'force',
|
||||
self::DIM_TORQUE => 'torque',
|
||||
self::DIM_ENERGY => 'energy',
|
||||
self::DIM_PRESSURE => 'pressure',
|
||||
self::DIM_POWER => 'power',
|
||||
self::DIM_DENSITY => 'density',
|
||||
self::DIM_FUELEFFICIENCY_PVE => 'fuelefficiencypositive',
|
||||
self::DIM_FUELEFFICIENCY_NVE => 'fuelefficiencynegative',
|
||||
);
|
||||
|
||||
public $value;
|
||||
protected $name;
|
||||
|
||||
public function __construct( $var, $var2=null ){
|
||||
static $legalDimensionsFlip;
|
||||
|
||||
if( is_string( $var ) ){
|
||||
if( $legalDimensionsFlip === null ){
|
||||
$legalDimensionsFlip = array_flip( self::$legalDimensions );
|
||||
}
|
||||
if( isset( $legalDimensionsFlip[$var] ) ){
|
||||
$dim = $legalDimensionsFlip[$var];
|
||||
} else {
|
||||
# Should be unreachable
|
||||
throw new ConvertError( 'unknowndimension' );
|
||||
}
|
||||
} elseif( $var instanceof self ){
|
||||
$dim = $var->value;
|
||||
} else {
|
||||
$dim = intval( $var );
|
||||
}
|
||||
|
||||
if( $var2 === null ){
|
||||
$this->value = $dim;
|
||||
$this->name = $this->compoundName = self::$legalDimensions[$this->value];
|
||||
|
||||
} else {
|
||||
if( is_string( $var2 ) ){
|
||||
if( $legalDimensionsFlip === null ){
|
||||
$legalDimensionsFlip = array_flip( self::$legalDimensions );
|
||||
}
|
||||
if( isset( $legalDimensionsFlip[$var2] ) ){
|
||||
$dim2 = $legalDimensionsFlip[$var2];
|
||||
} else {
|
||||
# Should be unreachable
|
||||
throw new ConvertError( 'unknowndimension' );
|
||||
}
|
||||
} elseif( $var2 instanceof self ){
|
||||
$dim2 = $var2->value;
|
||||
} else {
|
||||
$dim2 = intval( $var2 );
|
||||
}
|
||||
|
||||
$this->value = $dim - $dim2;
|
||||
if( in_array( $this->value, array_keys( self::$legalDimensions ) ) ){
|
||||
$this->name = self::$legalDimensions[$this->value];
|
||||
$this->compoundName = array(
|
||||
self::$legalDimensions[$var],
|
||||
self::$legalDimensions[$var2],
|
||||
);
|
||||
} else {
|
||||
# Some combinations of units are fine (carats per bushel is a perfectly good,
|
||||
# if somewhat bizarre, measure of density, for instance). But others (like
|
||||
# carats per miles-per-gallon) are definitely not.
|
||||
# TODO: this allows compound units like <gigawatthours>/<pascal> as a unit
|
||||
# of volume; is that a good thing or a bad thing?
|
||||
throw new ConvertError( 'invalidcompoundunit', "$var/$var2" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert to string. Magic in PHP 5.1 and above.
|
||||
* @return String
|
||||
*/
|
||||
public function __toString(){
|
||||
return strval( $this->name );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name, or names, of the dimension
|
||||
* @return String|Array of String
|
||||
*/
|
||||
public function getName( $expandCompound = false ){
|
||||
return $expandCompound
|
||||
? $this->name
|
||||
: $this->compoundName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the localised name of the dimension. Output is unescaped
|
||||
* @return String
|
||||
*/
|
||||
public function getLocalisedName(){
|
||||
return wfMsg( "pfunc-convert-dimension-{$this->name}" );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class ConvertUnit {
|
||||
|
||||
/**
|
||||
* array(
|
||||
* DIMENSION => array(
|
||||
* UNIT => array(
|
||||
* CONVERSION,
|
||||
* REGEX
|
||||
* )
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
protected static $units = array(
|
||||
ConvertDimension::DIM_LENGTH => array(
|
||||
'gigametre' => array( 1000000000, 'Gm' ),
|
||||
'megametre' => array( 1000000, '(?:(?-i)Mm)' ), # Case-sensitivity is forced
|
||||
'kilometre' => array( 1000, 'km' ),
|
||||
'hectometre' => array( 100, 'hm' ),
|
||||
'decametre' => array( 10, 'dam' ),
|
||||
'metre' => array( 1, 'm' ),
|
||||
'decimetre' => array( 0.1, 'dm' ),
|
||||
'centimetre' => array( 0.01, 'cm' ),
|
||||
'millimetre' => array( 0.001, '(?:(?-i)mm)' ), # Case-sensitivity is forced
|
||||
'micrometre' => array( 0.0001, '\x03BCm|\x00B5m|um' ), # There are two similar mu characters
|
||||
'nanometre' => array( 0.0000001, 'nm' ),
|
||||
'angstrom' => array( 0.00000001, '\x00C5' ),
|
||||
|
||||
'mile' => array( 1609.344, 'mi|miles?' ),
|
||||
'furlong' => array( 201.168, 'furlong' ),
|
||||
'chain' => array( 20.1168 , 'chain' ),
|
||||
'rod' => array( 5.0292, 'rod|pole|perch' ),
|
||||
'fathom' => array( 1.8288, 'fathom' ),
|
||||
'yard' => array( 0.9144, 'yards?|yd' ),
|
||||
'foot' => array( 0.3048, 'foot|feet|ft' ),
|
||||
'hand' => array( 0.1016, 'hands?' ),
|
||||
'inch' => array( 0.0254, 'inch|inches|in' ),
|
||||
|
||||
'nauticalmile' => array( 1852, 'nauticalmiles?|nmi' ),
|
||||
'nauticalmileuk' => array( 1853.184, 'oldUKnmi|Brnmi|admi' ),
|
||||
'nauticalmileus' => array( 1853.24496, 'oldUSnmi' ),
|
||||
|
||||
'gigaparsec' => array( 3.0856775813057E25, 'gigaparsecs?|Gpc' ),
|
||||
'megaparsec' => array( 3.0856775813057E22, 'megaparsecs?|Mpc' ),
|
||||
'kiloparsec' => array( 3.0856775813057E19, 'kiloparsecs?|kpc' ),
|
||||
'parsec' => array( 3.0856775813057E16, 'parsecs?|pc' ),
|
||||
'gigalightyear' => array( 9.4607304725808E24, 'gigalightyears?|Gly' ),
|
||||
'mrgalightyear' => array( 9.4607304725808E21, 'megalightyears?|Mly' ),
|
||||
'kilolightyear' => array( 9.4607304725808E18, 'kilolightyears?|kly' ),
|
||||
'lightyear' => array( 9.4607304725808E15, 'lightyears?|ly' ),
|
||||
'astronomicalunit' => array( 149597870700, 'astronomicalunits?|AU' ),
|
||||
),
|
||||
|
||||
ConvertDimension::DIM_AREA => array(
|
||||
'squarekilometre' => array( 1E6, 'km2|km\^2' ),
|
||||
'squaremetre' => array( 1, 'm2|m\^2' ),
|
||||
'squarecentimetre' => array( 1E-4, 'cm2|cm\^2' ),
|
||||
'squaremillimetre' => array( 1E-6, 'mm2|mm\^2' ),
|
||||
'hectare' => array( 1E4, 'hectares?|ha' ),
|
||||
|
||||
'squaremile' => array( 2589988.110336, 'sqmi|mi2|mi\^2' ),
|
||||
'acre' => array( 4046.856422 , 'acres?' ),
|
||||
'squareyard' => array( 0.83612736, 'sqyd|yd2|yd\^2' ),
|
||||
'squarefoot' => array( 0.09290304, 'sqft|ft2|ft\^2' ),
|
||||
'squareinch' => array( 0.00064516, 'sqin|in2|in\^2' ),
|
||||
|
||||
'squarenauticalmile' => array( 3429904, 'sqnmi|nmi2|nmi\^2' ),
|
||||
'dunam' => array( 1000, 'dunam' ),
|
||||
'tsubo' => array( 3.305785, 'tsubo' ),
|
||||
),
|
||||
|
||||
ConvertDimension::DIM_VOLUME => array(
|
||||
'cubicmetre' => array( 1, 'm3|m\^3' ),
|
||||
'cubiccentimetre' => array( 1E-6, 'cm3|cm\^3' ),
|
||||
'cubicmillimetre' => array( 1E-9, 'mm3|mm\^3' ),
|
||||
'kilolitre' => array( 1, 'kl' ),
|
||||
'litre' => array( 1E-3 , 'l' ),
|
||||
'centilitre' => array( 1E-5, 'cl' ),
|
||||
'millilitre' => array( 1E-6, 'ml' ),
|
||||
|
||||
'cubicyard' => array( 0.764554857984, 'cuyd|yd3|yd\^3' ),
|
||||
'cubicfoot' => array( 0.028316846592, 'cuft|ft3|ft\^3' ),
|
||||
'cubicinch' => array( 0.000016387064, 'cuin|in3|in\^3' ),
|
||||
'barrel' => array( 0.16365924, 'bbl|barrels?|impbbl' ),
|
||||
'bushel' => array( 0.03636872, 'bsh|bushels?|impbsh' ),
|
||||
'gallon' => array( 0.00454609, 'gal|gallons?|impgal' ),
|
||||
'quart' => array( 0.0011365225, 'qt|quarts?|impqt' ),
|
||||
'pint' => array( 0.00056826125, 'pt|pints?|imppt' ),
|
||||
'fluidounce' => array( 0.0000284130625, 'floz|impfloz' ),
|
||||
|
||||
'barrelus' => array( 0.119240471196, 'usbbl' ),
|
||||
'barreloil' => array( 0.158987294928, 'oilbbl' ),
|
||||
'barrelbeer' => array( 0.117347765304, 'beerbbl' ),
|
||||
'usgallon' => array( 0.003785411784, 'usgal' ),
|
||||
'usquart' => array( 0.000946352946, 'usqt' ),
|
||||
'uspint' => array( 0.000473176473, 'uspt' ),
|
||||
'usfluidounce' => array( 0.0000295735295625, 'usfloz' ),
|
||||
'usdrybarrel' => array( 0.11562819898508, 'usdrybbl' ),
|
||||
'usbushel' => array( 0.03523907016688, 'usbsh' ),
|
||||
'usdrygallon' => array( 0.00440488377086, 'usdrygal' ),
|
||||
'usdryquart' => array( 0.001101220942715, 'usdryqt' ),
|
||||
'usdrypint' => array( 0.0005506104713575, 'usdrypt' ),
|
||||
),
|
||||
|
||||
ConvertDimension::DIM_TIME => array(
|
||||
'year' => array( 31557600, 'yr' ),
|
||||
'day' => array( 86400, 'days?' ),
|
||||
'hour' => array( 3600, 'hours?|hr|h' ),
|
||||
'minute' => array( 60, 'minutes?|mins?' ),
|
||||
'second' => array( 1, 's' ),
|
||||
),
|
||||
|
||||
ConvertDimension::DIM_SPEED => array(
|
||||
'knot' => array( 0.514444444, 'knot|kn' ),
|
||||
'speedoflight' => array( 2.9979E8, 'c' ),
|
||||
),
|
||||
|
||||
ConvertDimension::DIM_PRESSURE => array(
|
||||
'gigapascal' => array( 1000000000, 'GPa' ),
|
||||
'megapascal' => array( 1000000, '(?:(?-i)M[Pp]a)' ), # Case-sensitivity is forced
|
||||
'kilopascal' => array( 1000, 'kPa' ),
|
||||
'hectopascal' => array( 100, 'hPa' ),
|
||||
'pascal' => array( 1, 'Pa' ),
|
||||
'millipascal' => array( 0.001, '(?:(?-i)m[Pp]a)' ), # Case-sensitivity is forced
|
||||
|
||||
'bar' => array( 100000, 'bar' ),
|
||||
'decibar' => array( 10000, 'dbar' ),
|
||||
'milibar' => array( 100 , 'mbar|mb' ),
|
||||
'kilobarye' => array( 100, 'kba' ),
|
||||
'barye' => array( 0.1, 'ba' ),
|
||||
|
||||
'atmosphere' => array( 101325, 'atm|atmospheres?' ),
|
||||
'torr' => array( 133.32237, 'torr' ),
|
||||
'mmhg' => array( 133.322387415, 'mmHg' ),
|
||||
'inhg' => array( 3386.38864034, 'inHg' ),
|
||||
'psi' => array( 6894.757293, 'psi' ),
|
||||
),
|
||||
# TODO: other dimensions as needed
|
||||
);
|
||||
|
||||
# Default units for each dimension
|
||||
# TODO: this should ideally be localisable
|
||||
protected static $defaultUnit = array(
|
||||
ConvertDimension::DIM_LENGTH => 'metre',
|
||||
ConvertDimension::DIM_AREA => 'squaremetre',
|
||||
ConvertDimension::DIM_VOLUME => 'cubicmetre',
|
||||
ConvertDimension::DIM_TIME => 'second',
|
||||
ConvertDimension::DIM_SPEED => 'metre/second',
|
||||
ConvertDimension::DIM_PRESSURE => 'pascal',
|
||||
);
|
||||
|
||||
# An array of preprocessing conversions to apply to units
|
||||
protected static $unitConversions = array(
|
||||
'/^mph$/ui' => 'mi/h',
|
||||
);
|
||||
|
||||
# Map of UNIT => DIMENSION, created on construct
|
||||
protected static $dimensionMap = false;
|
||||
|
||||
/***************** MEMBER VARIABLES *****************/
|
||||
|
||||
# @var ConvertDimension
|
||||
public $dimension;
|
||||
|
||||
# What number you need to multiply this unit by to get the equivalent
|
||||
# value in SI base units
|
||||
protected $conversion = 1;
|
||||
|
||||
# A regex which matches the unit
|
||||
protected $regex;
|
||||
|
||||
# The name of the unit (key into $units[$dimension] above
|
||||
protected $unitName;
|
||||
|
||||
/***************** MEMBER FUNCTIONS *****************/
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param $rawUnit String
|
||||
*/
|
||||
public function __construct( $rawUnit ){
|
||||
if( self::$dimensionMap === false ){
|
||||
self::$dimensionMap = array();
|
||||
foreach( self::$units as $dimension => $arr ){
|
||||
foreach( $arr as $unit => $val ){
|
||||
self::$dimensionMap[$unit] = $dimension;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->parseUnit( $rawUnit );
|
||||
}
|
||||
|
||||
protected function parseUnit( $rawUnit ){
|
||||
|
||||
# Do mappings like 'mph' --> 'mi/h'
|
||||
$rawUnit = preg_replace(
|
||||
array_keys( self::$unitConversions ),
|
||||
array_values( self::$unitConversions ),
|
||||
$rawUnit
|
||||
);
|
||||
|
||||
$parts = explode( '/', $rawUnit );
|
||||
array_map( 'trim', $parts );
|
||||
if( count( $parts ) == 1 ){
|
||||
# Single unit
|
||||
foreach( self::$units as $dimension => $units ){
|
||||
foreach( $units as $unit => $data ){
|
||||
if( $rawUnit == $unit || preg_match( "/^({$data[1]})$/ui", $parts[0] ) ){
|
||||
$this->dimension = new ConvertDimension( self::$dimensionMap[$unit] );
|
||||
$this->conversion = self::$units[$this->dimension->value][$unit][0];
|
||||
$this->regex = $data[1];
|
||||
$this->unitName = $unit;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Unknown unit
|
||||
throw new ConvertError( 'unknownunit', $rawUnit );
|
||||
|
||||
} elseif( count( $parts ) == 2 ){
|
||||
# Compound unit.
|
||||
$top = new self( $parts[0] );
|
||||
$bottom = new self( $parts[1] );
|
||||
$this->dimension = new ConvertDimension( $top->dimension, $bottom->dimension );
|
||||
$this->conversion = $top->conversion / $bottom->conversion;
|
||||
$this->regex = "(?:{$top->regex})/(?:{$bottom->regex})";
|
||||
$this->unitName = array( $top->unitName, $bottom->unitName );
|
||||
return;
|
||||
|
||||
} else {
|
||||
# Whaaat? Too many parts
|
||||
throw new ConvertError( 'doublecompoundunit', $rawUnit );
|
||||
}
|
||||
}
|
||||
|
||||
public function getConversion(){
|
||||
return $this->conversion;
|
||||
}
|
||||
|
||||
public function getRegex(){
|
||||
return $this->regex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the text of the unit
|
||||
* @param $value String number for PLURAL support
|
||||
* @param $link Bool
|
||||
* @return String
|
||||
*/
|
||||
public function getText( $value, $link=false, $abbreviate=false ){
|
||||
global $wgContLang;
|
||||
$value = $wgContLang->formatNum( $value );
|
||||
$abbr = $abbreviate ? '-abbr' : '';
|
||||
|
||||
if( !is_array( $this->unitName ) ){
|
||||
$msg = "pfunc-convert-unit-{$this->dimension->getName()}-{$this->unitName}";
|
||||
$msgText = wfMsgExt( "$msg$abbr", array( 'parsemag', 'content' ), $value );
|
||||
if( $link && !wfEmptyMsg( "$msg-link" ) ){
|
||||
$title = Title::newFromText( wfMsgForContentNoTrans( "$msg-link" ) );
|
||||
$msgText = "[[{$title->getFullText()}|$msgText]]";
|
||||
}
|
||||
|
||||
} elseif( !wfEmptyMsg( "pfunc-convert-unit-{$this->dimension->getName(true)}-{$this->unitName[0]}-{$this->unitName[1]}" ) ){
|
||||
# A wiki has created, say, [[MediaWiki:pfunc-convert-unit-speed-metres-second]]
|
||||
# so they can have it display "<metres per second>" rather than
|
||||
# "<metres>/<second>"
|
||||
$msg = "pfunc-convert-unit-{$this->dimension->getName(true)}-{$this->unitName[0]}-{$this->unitName[1]}";
|
||||
$msgText = wfMsgExt( "$msg$abbr", array( 'parsemag', 'content' ), $value );
|
||||
if( $link && !wfEmptyMsg( "$msg-link" ) ){
|
||||
$title = Title::newFromText( wfMsgForContentNoTrans( "$msg-link" ) );
|
||||
if( $title instanceof Title ){
|
||||
$msgText = "[[$title|$msgText]]";
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
$dimensionNames = $this->dimension->getName();
|
||||
$msg = "pfunc-convert-unit-{$dimensionNames[0]}-{$this->unitName[0]}";
|
||||
$msgText = wfMsgExt( "$msg$abbr", array( 'parsemag', 'content' ), $value );
|
||||
if( $link && !wfEmptyMsg( "$msg-link" ) ){
|
||||
$title = Title::newFromText( wfMsgForContentNoTrans( "$msg-link" ) );
|
||||
$msgText = "[[{$title->getFullText()}|$msgText]]";
|
||||
}
|
||||
|
||||
$msg2 = "pfunc-convert-unit-{$dimensionNames[1]}-{$this->unitName[1]}";
|
||||
$msg2Text = wfMsgExt( "$msg2$abbr", array( 'parsemag', 'content' ), 1 ); # Singular for denominator
|
||||
if( $link && !wfEmptyMsg( "$msg2-link" ) ){
|
||||
$title = Title::newFromText( wfMsgForContentNoTrans( "$msg2-link" ) );
|
||||
if( $title instanceof Title ){
|
||||
$msg2Text = "[[{$title->getFullText()}|$msg2Text]]";
|
||||
}
|
||||
}
|
||||
$msgText = "$msgText/$msg2Text";
|
||||
}
|
||||
|
||||
return $msgText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default (usually SI) unit associated with this particular dimension
|
||||
* @return ConvertUnit
|
||||
*/
|
||||
public function getDefaultUnit(){
|
||||
return new ConvertUnit( self::$defaultUnit[$this->dimension->value] );
|
||||
}
|
||||
}
|
|
@ -22,6 +22,13 @@ $magicWords['en'] = array(
|
|||
'timel' => array( 0, 'timel' ),
|
||||
'rel2abs' => array( 0, 'rel2abs' ),
|
||||
'titleparts' => array( 0, 'titleparts' ),
|
||||
'convert' => array( 0, 'convert' ),
|
||||
'sourceunit' => array( 0, '#sourceunit' ),
|
||||
'targetunit' => array( 0, '#targetunit' ),
|
||||
'linkunit' => array( 0, '#linkunit' ),
|
||||
'decimalplaces' => array( 0, '#dp' ),
|
||||
'significantfigures' => array( 0, '#sf' ),
|
||||
'abbreviate' => array( 0, '#abbreviate' ),
|
||||
'len' => array( 0, 'len' ),
|
||||
'pos' => array( 0, 'pos' ),
|
||||
'rpos' => array( 0, 'rpos' ),
|
||||
|
|
|
@ -27,7 +27,244 @@ $messages['en'] = array(
|
|||
'pfunc_expr_invalid_argument_ln' => 'Invalid argument for ln: <= 0',
|
||||
'pfunc_expr_unknown_error' => 'Expression error: Unknown error ($1)',
|
||||
'pfunc_expr_not_a_number' => 'In $1: result is not a number',
|
||||
'pfunc_string_too_long' => 'Error: String exceeds $1 character limit',
|
||||
'pfunc_string_too_long' => 'Error: string exceeds $1 character limit',
|
||||
'pfunc-convert-dimensionmismatch' => 'Error: cannot convert between units of "$1" and "$2"',
|
||||
'pfunc-convert-unknownunit' => 'Error: unknown unit "$1"',
|
||||
'pfunc-convert-unknowndimension' => 'Error: unknown dimension "$1"',
|
||||
'pfunc-convert-invalidcompoundunit' => 'Error: invalid compound unit "$1"',
|
||||
'pfunc-convert-nounit' => 'Error: no source unit given',
|
||||
'pfunc-convert-doublecompoundunit' => 'Error: cannot parse double compound units like "$1"',
|
||||
|
||||
# DIMENSION NAMES
|
||||
'pfunc-convert-dimension-length' => 'length',
|
||||
'pfunc-convert-dimension-area' => 'area',
|
||||
'pfunc-convert-dimension-volume' => 'volume',
|
||||
'pfunc-convert-dimension-time' => 'time',
|
||||
'pfunc-convert-dimension-timesquared' => 'time<sup>2</sup>',
|
||||
'pfunc-convert-dimension-mass' => 'mass',
|
||||
'pfunc-convert-dimension-speed' => 'speed',
|
||||
'pfunc-convert-dimension-temperature' => 'temperature',
|
||||
'pfunc-convert-dimension-acceleration' => 'acceleration',
|
||||
'pfunc-convert-dimension-force' => 'force',
|
||||
'pfunc-convert-dimension-torque' => 'torque',
|
||||
'pfunc-convert-dimension-energy' => 'energy',
|
||||
'pfunc-convert-dimension-power' => 'power',
|
||||
'pfunc-convert-dimension-pressure' => 'pressure',
|
||||
'pfunc-convert-dimension-density' => 'density',
|
||||
'pfunc-convert-dimension-fuelefficiencypositive' => 'fuelefficiencypositive',
|
||||
'pfunc-convert-dimension-fuelefficiencynegative' => 'fuelefficiencynegative',
|
||||
|
||||
# LENGTH
|
||||
'pfunc-convert-unit-length-gigametre' => '{{PLURAL:$1|gigametre|gigametres}}',
|
||||
'pfunc-convert-unit-length-megametre' => '{{PLURAL:$1|megametre|megametres}}',
|
||||
'pfunc-convert-unit-length-kilometre' => '{{PLURAL:$1|kilometre|kilometres}}',
|
||||
'pfunc-convert-unit-length-hectometre' => '{{PLURAL:$1|hectometre|hectometres}}',
|
||||
'pfunc-convert-unit-length-decametre' => '{{PLURAL:$1|decametre|decametres}}',
|
||||
'pfunc-convert-unit-length-metre' => '{{PLURAL:$1|metre|metres}}',
|
||||
'pfunc-convert-unit-length-decimetre' => '{{PLURAL:$1|decimetre|decimetres}}',
|
||||
'pfunc-convert-unit-length-centimetre' => '{{PLURAL:$1|centimetre|centimetres}}',
|
||||
'pfunc-convert-unit-length-millimetre' => '{{PLURAL:$1|millimetre|millimetres}}',
|
||||
'pfunc-convert-unit-length-micrometre' => '{{PLURAL:$1|micrometre|micrometres}}',
|
||||
'pfunc-convert-unit-length-nanometre' => '{{PLURAL:$1|nanometre|nanometres}}',
|
||||
'pfunc-convert-unit-length-angstrom' => '{{PLURAL:$1|angstrom|angstroms}}',
|
||||
'pfunc-convert-unit-length-mile' => '{{PLURAL:$1|mile|miles}}',
|
||||
'pfunc-convert-unit-length-furlong' => '{{PLURAL:$1|furlong|furlongs}}',
|
||||
'pfunc-convert-unit-length-chain' => '{{PLURAL:$1|chain|chains}}',
|
||||
'pfunc-convert-unit-length-rod' => '{{PLURAL:$1|rod|rods}}',
|
||||
'pfunc-convert-unit-length-fathom' => '{{PLURAL:$1|fathom|fathoms}}',
|
||||
'pfunc-convert-unit-length-yard' => '{{PLURAL:$1|yard|yards}}',
|
||||
'pfunc-convert-unit-length-foot' => '{{PLURAL:$1|foot|feet}}',
|
||||
'pfunc-convert-unit-length-hand' => '{{PLURAL:$1|hand|hands}}',
|
||||
'pfunc-convert-unit-length-inch' => '{{PLURAL:$1|inch|inches}}',
|
||||
'pfunc-convert-unit-length-nauticalmile' => '{{PLURAL:$1|nautical mile|nautical miles}}',
|
||||
'pfunc-convert-unit-length-nauticalmileuk' => '{{PLURAL:$1|nautical mile (pre-1970 British)|nautical miles (pre-1970 British)}}',
|
||||
'pfunc-convert-unit-length-nauticalmileus' => '{{PLURAL:$1|nautical mile (pre-1954 US)|nautical miles (pre-1954 US)}}',
|
||||
'pfunc-convert-unit-length-gigaparsec' => '{{PLURAL:$1|gigaparsec|gigaparsecs}}',
|
||||
'pfunc-convert-unit-length-megaparsec' => '{{PLURAL:$1|megaparsec|megaparsecs}}',
|
||||
'pfunc-convert-unit-length-kiloparsec' => '{{PLURAL:$1|kiloparsec|kiloparsecs}}',
|
||||
'pfunc-convert-unit-length-parsec' => '{{PLURAL:$1|parsec|parsecs}}',
|
||||
'pfunc-convert-unit-length-gigalightyear' => '{{PLURAL:$1|gigalightyear|gigalightyears}}',
|
||||
'pfunc-convert-unit-length-mrgalightyear' => '{{PLURAL:$1|megalightyear|megalightyears}}',
|
||||
'pfunc-convert-unit-length-kilolightyear' => '{{PLURAL:$1|kilolightyear|kilolightyears}}',
|
||||
'pfunc-convert-unit-length-lightyear' => '{{PLURAL:$1|lightyear|lightyears}}',
|
||||
'pfunc-convert-unit-length-astronomicalunit' => '{{PLURAL:$1|astronomical unit|astronomical units}}',
|
||||
|
||||
'pfunc-convert-unit-length-gigametre-abbr' => 'Gm',
|
||||
'pfunc-convert-unit-length-megametre-abbr' => 'Mm',
|
||||
'pfunc-convert-unit-length-kilometre-abbr' => 'km',
|
||||
'pfunc-convert-unit-length-hectometre-abbr' => 'hm',
|
||||
'pfunc-convert-unit-length-decametre-abbr' => 'dam',
|
||||
'pfunc-convert-unit-length-metre-abbr' => 'm',
|
||||
'pfunc-convert-unit-length-decimetre-abbr' => 'dm',
|
||||
'pfunc-convert-unit-length-centimetre-abbr' => 'cm',
|
||||
'pfunc-convert-unit-length-milimetre-abbr' => 'mm',
|
||||
'pfunc-convert-unit-length-micrometre-abbr' => 'μm',
|
||||
'pfunc-convert-unit-length-nanometre-abbr' => 'nm',
|
||||
'pfunc-convert-unit-length-angstrom-abbr' => 'Å',
|
||||
'pfunc-convert-unit-length-mile-abbr' => 'mi',
|
||||
'pfunc-convert-unit-length-furlong-abbr' => 'furlong',
|
||||
'pfunc-convert-unit-length-chain-abbr' => 'chain',
|
||||
'pfunc-convert-unit-length-rod-abbr' => 'rd',
|
||||
'pfunc-convert-unit-length-fathom-abbr' => 'fathom',
|
||||
'pfunc-convert-unit-length-yard-abbr' => 'yd',
|
||||
'pfunc-convert-unit-length-foot-abbr' => 'ft',
|
||||
'pfunc-convert-unit-length-hand-abbr' => 'h',
|
||||
'pfunc-convert-unit-length-inch-abbr' => 'in',
|
||||
'pfunc-convert-unit-length-nauticalmile-abbr' => 'nmi',
|
||||
'pfunc-convert-unit-length-nauticalmileuk-abbr' => 'nmi (Brit)',
|
||||
'pfunc-convert-unit-length-nauticalmileus-abbr' => 'nmi (pre-1954 US)',
|
||||
'pfunc-convert-unit-length-gigaparsec-abbr' => 'Gpc',
|
||||
'pfunc-convert-unit-length-megaparsec-abbr' => 'Mpc',
|
||||
'pfunc-convert-unit-length-kiloparsec-abbr' => 'kpc',
|
||||
'pfunc-convert-unit-length-parsec-abbr' => 'pc',
|
||||
'pfunc-convert-unit-length-gigalightyear-abbr' => 'Gly',
|
||||
'pfunc-convert-unit-length-mrgalightyear-abbr' => 'Mly',
|
||||
'pfunc-convert-unit-length-kilolightyear-abbr' => 'kly',
|
||||
'pfunc-convert-unit-length-lightyear-abbr' => 'ly',
|
||||
'pfunc-convert-unit-length-astronomicalunit-abbr' => 'AU',
|
||||
|
||||
# AREA #
|
||||
'pfunc-convert-unit-area-squarekilometre' => '{{PLURAL:$1|square kilometre|square kilometres}}',
|
||||
'pfunc-convert-unit-area-squaremetre' => '{{PLURAL:$1|square metre|square metres}}',
|
||||
'pfunc-convert-unit-area-squarecentimetre' => '{{PLURAL:$1|square centimetre|square centimetres}}',
|
||||
'pfunc-convert-unit-area-squaremillimetre' => '{{PLURAL:$1|square millimetre|square millimetres}}',
|
||||
'pfunc-convert-unit-area-hectare' => '{{PLURAL:$1|hectare|hectares}}',
|
||||
'pfunc-convert-unit-area-squaremile' => '{{PLURAL:$1|square mile|square miles}}',
|
||||
'pfunc-convert-unit-area-acre' => '{{PLURAL:$1|acre|acres}}',
|
||||
'pfunc-convert-unit-area-squareyard' => '{{PLURAL:$1|square yard|square yards}}',
|
||||
'pfunc-convert-unit-area-squarefoot' => '{{PLURAL:$1|square foot|square feet}}',
|
||||
'pfunc-convert-unit-area-squareinch' => '{{PLURAL:$1|square inch|square inches}}',
|
||||
'pfunc-convert-unit-area-squarenauticalmile' => '{{PLURAL:$1|square nautical mile|square nautical miles}}',
|
||||
'pfunc-convert-unit-area-dunam' => '{{PLURAL:$1|dunam|dunams}}',
|
||||
'pfunc-convert-unit-area-tsubo' => '{{PLURAL:$1|tsubo|tsubo}}',
|
||||
|
||||
'pfunc-convert-unit-area-squarekilometre-abbr' => 'km<sup>2</sup>',
|
||||
'pfunc-convert-unit-area-squaremetre-abbr' => 'm<sup>2</sup>',
|
||||
'pfunc-convert-unit-area-squarecentimetre-abbr' => 'cm<sup>2</sup>',
|
||||
'pfunc-convert-unit-area-squaremillimetre-abbr' => 'mm<sup>2</sup>',
|
||||
'pfunc-convert-unit-area-hectare-abbr' => 'ha',
|
||||
'pfunc-convert-unit-area-squaremile-abbr' => 'sq mi',
|
||||
'pfunc-convert-unit-area-acre-abbr' => 'acre',
|
||||
'pfunc-convert-unit-area-squareyard-abbr' => 'sq yd',
|
||||
'pfunc-convert-unit-area-squarefoot-abbr' => 'sq ft',
|
||||
'pfunc-convert-unit-area-squareinch-abbr' => 'sq in',
|
||||
'pfunc-convert-unit-area-squarenauticalmile-abbr' => 'sq nmi',
|
||||
'pfunc-convert-unit-area-dunam-abbr' => 'dunam',
|
||||
'pfunc-convert-unit-area-tsubo-abbr' => 'tsubo',
|
||||
|
||||
# TIME #
|
||||
'pfunc-convert-unit-time-second' => '{{PLURAL:$1|second|seconds}}',
|
||||
'pfunc-convert-unit-time-year' => '{{PLURAL:$1|year|years}}',
|
||||
'pfunc-convert-unit-time-day' => '{{PLURAL:$1|day|days}}',
|
||||
'pfunc-convert-unit-time-hour' => '{{PLURAL:$1|hour|hours}}',
|
||||
'pfunc-convert-unit-time-minute' => '{{PLURAL:$1|minute|minutes}}',
|
||||
|
||||
'pfunc-convert-unit-time-second-abbr' => 's',
|
||||
'pfunc-convert-unit-time-year-abbr' => 'yr',
|
||||
'pfunc-convert-unit-time-day-abbr' => 'day',
|
||||
'pfunc-convert-unit-time-hour-abbr' => 'hr',
|
||||
'pfunc-convert-unit-time-minute-abbr' => 'min',
|
||||
|
||||
# VOLUME #
|
||||
'pfunc-convert-unit-volume-cubicmetre' => '{{PLURAL:$1|cubic metre|cubic metres}}',
|
||||
'pfunc-convert-unit-volume-cubiccentimetre' => '{{PLURAL:$1|cubic centimetre|cubic centimetres}}',
|
||||
'pfunc-convert-unit-volume-cubicmillimetre' => '{{PLURAL:$1|cubic millimetre|cubic millimetres}}',
|
||||
'pfunc-convert-unit-volume-kilolitre' => '{{PLURAL:$1|kilolitre|kilolitres}}',
|
||||
'pfunc-convert-unit-volume-litre' => '{{PLURAL:$1|litre|litres}}',
|
||||
'pfunc-convert-unit-volume-centilitre' => '{{PLURAL:$1|centilitre|centilitres}}',
|
||||
'pfunc-convert-unit-volume-millilitre' => '{{PLURAL:$1|millilitre|millilitres}}',
|
||||
'pfunc-convert-unit-volume-cubicyard' => '{{PLURAL:$1|cubic yard|cubic yards}}',
|
||||
'pfunc-convert-unit-volume-cubicfoot' => '{{PLURAL:$1|cubic foot|cubic feet}}',
|
||||
'pfunc-convert-unit-volume-cubicinch' => '{{PLURAL:$1|cubic inch|cubic inches}}',
|
||||
'pfunc-convert-unit-volume-barrel' => '{{PLURAL:$1|barrel|barrels}}',
|
||||
'pfunc-convert-unit-volume-bushel' => '{{PLURAL:$1|bushel|bushels}}',
|
||||
'pfunc-convert-unit-volume-gallon' => '{{PLURAL:$1|gallon|gallons}}',
|
||||
'pfunc-convert-unit-volume-quart' => '{{PLURAL:$1|quart|quarts}}',
|
||||
'pfunc-convert-unit-volume-pint' => '{{PLURAL:$1|pint|pints}}',
|
||||
'pfunc-convert-unit-volume-fluidounce' => '{{PLURAL:$1|fluid ounce|fluid ounces}}',
|
||||
'pfunc-convert-unit-volume-barrelus' => '{{PLURAL:$1|US barrel|US barrels}}',
|
||||
'pfunc-convert-unit-volume-barreloil' => '{{PLURAL:$1|barrel|barrel}}',
|
||||
'pfunc-convert-unit-volume-barrelbeer' => '{{PLURAL:$1|barrel|barrel}}',
|
||||
'pfunc-convert-unit-volume-usgallon' => '{{PLURAL:$1|US gallon|US gallons}}',
|
||||
'pfunc-convert-unit-volume-usquart' => '{{PLURAL:$1|US quart|US quarts}}',
|
||||
'pfunc-convert-unit-volume-uspint' => '{{PLURAL:$1|US pint|US pints}}',
|
||||
'pfunc-convert-unit-volume-usfluidounce' => '{{PLURAL:$1|US fluid ounce|US fluid ounces}}',
|
||||
'pfunc-convert-unit-volume-usdrybarrel' => '{{PLURAL:$1|US dry barrel|US dry barrels}}',
|
||||
'pfunc-convert-unit-volume-usbushel' => '{{PLURAL:$1|US bushel|US bushels}}',
|
||||
'pfunc-convert-unit-volume-usdrygallon' => '{{PLURAL:$1|US dry gallon|US dry gallons}}',
|
||||
'pfunc-convert-unit-volume-usdryquart' => '{{PLURAL:$1|US dry quart|US dry quarts}}',
|
||||
'pfunc-convert-unit-volume-usdrypint' => '{{PLURAL:$1|US dry pint|US dry pints}}',
|
||||
|
||||
'pfunc-convert-unit-volume-cubicmetre-abbr' => 'm<sup>3</sup>',
|
||||
'pfunc-convert-unit-volume-cubiccentimetre-abbr' => 'cm<sup>3</sup>',
|
||||
'pfunc-convert-unit-volume-cubicmillimetre-abbr' => 'mm<sup>3</sup>',
|
||||
'pfunc-convert-unit-volume-kilolitre-abbr' => 'kl',
|
||||
'pfunc-convert-unit-volume-litre-abbr' => 'l',
|
||||
'pfunc-convert-unit-volume-centilitre-abbr' => 'cl',
|
||||
'pfunc-convert-unit-volume-millilitre-abbr' => 'ml',
|
||||
'pfunc-convert-unit-volume-cubicyard-abbr' => 'cu yd',
|
||||
'pfunc-convert-unit-volume-cubicfoot-abbr' => 'cu ft',
|
||||
'pfunc-convert-unit-volume-cubicinch-abbr' => 'cu in',
|
||||
'pfunc-convert-unit-volume-barrel-abbr' => 'bbl',
|
||||
'pfunc-convert-unit-volume-bushel-abbr' => 'bsh',
|
||||
'pfunc-convert-unit-volume-gallon-abbr' => 'gal',
|
||||
'pfunc-convert-unit-volume-quart-abbr' => 'qt',
|
||||
'pfunc-convert-unit-volume-pint-abbr' => 'pt',
|
||||
'pfunc-convert-unit-volume-fluidounce-abbr' => 'fl oz',
|
||||
'pfunc-convert-unit-volume-barrelus-abbr' => 'US bbl',
|
||||
'pfunc-convert-unit-volume-barreloil-abbr' => 'bbl',
|
||||
'pfunc-convert-unit-volume-barrelbeer-abbr' => 'bbl',
|
||||
'pfunc-convert-unit-volume-usgallon-abbr' => 'US gal',
|
||||
'pfunc-convert-unit-volume-usquart-abbr' => 'US qt',
|
||||
'pfunc-convert-unit-volume-uspint-abbr' => 'US pt',
|
||||
'pfunc-convert-unit-volume-usfluidounce-abbr' => 'US fl oz',
|
||||
'pfunc-convert-unit-volume-usdrybarrel-abbr' => 'US bbl',
|
||||
'pfunc-convert-unit-volume-usbushel-abbr' => 'US bsh',
|
||||
'pfunc-convert-unit-volume-usdrygallon-abbr' => 'US dry gal',
|
||||
'pfunc-convert-unit-volume-usdryquart-abbr' => 'US dry qt',
|
||||
'pfunc-convert-unit-volume-usdrypint-abbr' => 'US dry pt',
|
||||
|
||||
# SPEED
|
||||
'pfunc-convert-unit-speed-mile-hour' => 'miles per hour',
|
||||
'pfunc-convert-unit-speed-speedoflight' => 'c',
|
||||
|
||||
'pfunc-convert-unit-speed-mile-hour-abbr' => 'mph',
|
||||
'pfunc-convert-unit-speed-speedoflight-abbr' => 'c',
|
||||
|
||||
# PRESSURE
|
||||
'pfunc-convert-unit-pressure-gigapascal' => '{{PLURAL:$1|gigapascal|gigapascals}}',
|
||||
'pfunc-convert-unit-pressure-megapascal' => '{{PLURAL:$1|megapascal|megapascals}}',
|
||||
'pfunc-convert-unit-pressure-kilopascal' => '{{PLURAL:$1|kilopascal|kilopascals}}',
|
||||
'pfunc-convert-unit-pressure-hectopascal' => '{{PLURAL:$1|hectopascal|hectopascals}}',
|
||||
'pfunc-convert-unit-pressure-pascal' => '{{PLURAL:$1|pascal|pascals}}',
|
||||
'pfunc-convert-unit-pressure-millipascal' => '{{PLURAL:$1|millipascal|millipascals}}',
|
||||
'pfunc-convert-unit-pressure-bar' => 'bar',
|
||||
'pfunc-convert-unit-pressure-decibar' => 'decibar',
|
||||
'pfunc-convert-unit-pressure-millibar' => 'millibar',
|
||||
'pfunc-convert-unit-pressure-kilobarye' => 'kilobarye',
|
||||
'pfunc-convert-unit-pressure-barye' => 'barye',
|
||||
'pfunc-convert-unit-pressure-atmosphere' => '{{PLURAL:$1|atmosphere|atmospheres}}',
|
||||
'pfunc-convert-unit-pressure-torr' => '{{PLURAL:$1|Torr|Torr}}',
|
||||
'pfunc-convert-unit-pressure-mmhg' => '{{PLURAL:$1|milimetre of mercury|milimetres of mercury}}',
|
||||
'pfunc-convert-unit-pressure-inhg' => '{{PLURAL:$1|inch of mercury|inches of mercury}}',
|
||||
'pfunc-convert-unit-pressure-psi' => '{{PLURAL:$1|pound per square-inch|pounds per square-inch}}',
|
||||
|
||||
'pfunc-convert-unit-pressure-gigapascal-abbr' => 'GPa',
|
||||
'pfunc-convert-unit-pressure-megapascal-abbr' => 'MPa',
|
||||
'pfunc-convert-unit-pressure-kilopascal-abbr' => 'kPa',
|
||||
'pfunc-convert-unit-pressure-hectopascal-abbr' => 'hPa',
|
||||
'pfunc-convert-unit-pressure-pascal-abbr' => 'Pa',
|
||||
'pfunc-convert-unit-pressure-millipascal-abbr' => 'mPa',
|
||||
'pfunc-convert-unit-pressure-bar-abbr' => 'bar',
|
||||
'pfunc-convert-unit-pressure-decibar-abbr' => 'dbar',
|
||||
'pfunc-convert-unit-pressure-milibar-abbr' => 'mbar',
|
||||
'pfunc-convert-unit-pressure-kilobarye-abbr' => 'kBa',
|
||||
'pfunc-convert-unit-pressure-barye-abbr' => 'Ba',
|
||||
'pfunc-convert-unit-pressure-atmosphere-abbr' => 'atm',
|
||||
'pfunc-convert-unit-pressure-torr-abbr' => 'Torr',
|
||||
'pfunc-convert-unit-pressure-mmhg-abbr' => 'mmHg',
|
||||
'pfunc-convert-unit-pressure-inhg-abbr' => 'inHg',
|
||||
'pfunc-convert-unit-pressure-psi-abbr' => 'psi',
|
||||
);
|
||||
|
||||
/** Message documentation (Message documentation)
|
||||
|
|
|
@ -46,6 +46,7 @@ $wgExtensionMessagesFiles['ParserFunctionsMagic'] = dirname( __FILE__ ) . '/Pars
|
|||
|
||||
$wgParserTestFiles[] = dirname( __FILE__ ) . "/funcsParserTests.txt";
|
||||
$wgParserTestFiles[] = dirname( __FILE__ ) . "/stringFunctionTests.txt";
|
||||
$wgParserTestFiles[] = dirname( __FILE__ ) . "/convertTests.txt";
|
||||
|
||||
function wfSetupParserFunctions() {
|
||||
global $wgPFHookStub, $wgHooks;
|
||||
|
@ -89,6 +90,7 @@ class ParserFunctions_HookStub {
|
|||
$parser->setFunctionHook( 'timel', array( &$this, 'localTime' ) );
|
||||
$parser->setFunctionHook( 'rel2abs', array( &$this, 'rel2abs' ) );
|
||||
$parser->setFunctionHook( 'titleparts', array( &$this, 'titleparts' ) );
|
||||
$parser->setFunctionHook( 'convert', array( &$this, 'convert' ) );
|
||||
|
||||
// String Functions
|
||||
if ( $wgPFEnableStringFunctions ) {
|
||||
|
|
|
@ -512,6 +512,29 @@ class ExtParserFunctions {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a ConvertParser object
|
||||
* @return ConvertParser
|
||||
*/
|
||||
protected function &getConvertParser() {
|
||||
if ( !isset( $this->mConvertParser ) ) {
|
||||
if ( !class_exists( 'ConvertParser' ) ) {
|
||||
require( dirname( __FILE__ ) . '/Convert.php' );
|
||||
}
|
||||
$this->mConvertParser = new ConvertParser;
|
||||
}
|
||||
return $this->mConvertParser;
|
||||
}
|
||||
|
||||
public function convert( /*...*/ ) {
|
||||
try {
|
||||
$args = func_get_args();
|
||||
return $this->getConvertParser()->execute( $args );
|
||||
} catch ( ConvertError $e ) {
|
||||
return $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
// Verifies parameter is less than max string length.
|
||||
private function checkLength( $text ) {
|
||||
global $wgPFStringLengthLimit;
|
||||
|
|
193
convertTests.txt
Normal file
193
convertTests.txt
Normal file
|
@ -0,0 +1,193 @@
|
|||
!! test
|
||||
Simple conversion
|
||||
!! input
|
||||
{{#convert: 10 m | km }}
|
||||
!!result
|
||||
<p>0.01 kilometres
|
||||
</p>
|
||||
!! end
|
||||
|
||||
!! test
|
||||
Position and formatting of numbers and units
|
||||
!! input
|
||||
*{{#convert: 10 m | km }}
|
||||
*{{#convert: 10m | km }}
|
||||
*{{#convert: 10 km | m }}
|
||||
*{{#convert: 10-km | m }}
|
||||
*{{#convert: 10E2 km | m }}
|
||||
*{{#convert: 10E-2 km | m }}
|
||||
*{{#convert: 10.0E2 km | m }}
|
||||
*{{#convert: 10.0E2.5 km | m }}
|
||||
!! result
|
||||
<ul><li>0.01 kilometres
|
||||
</li><li>0.01 kilometres
|
||||
</li><li>10,000 metres
|
||||
</li><li>10,000- metres
|
||||
</li><li>1,000,000 metres
|
||||
</li><li>100 metres
|
||||
</li><li>1,000,000 metres
|
||||
</li><li>1,000,000.5,000 metres
|
||||
</li></ul>
|
||||
|
||||
!! end
|
||||
|
||||
!! test
|
||||
Precision 1
|
||||
!! input
|
||||
*{{#convert: 10 m | km }}
|
||||
*{{#convert: 11 m | km }}
|
||||
*{{#convert: 12 m | km }}
|
||||
*{{#convert: 13 m | km }}
|
||||
*{{#convert: 14 m | km }}
|
||||
*{{#convert: 15 m | km }}
|
||||
*{{#convert: 16 m | km }}
|
||||
*{{#convert: 17 m | km }}
|
||||
*{{#convert: 18 m | km }}
|
||||
*{{#convert: 19 m | km }}
|
||||
*{{#convert: 20 m | km }}
|
||||
!! result
|
||||
<ul><li>0.01 kilometres
|
||||
</li><li>0.011 kilometres
|
||||
</li><li>0.012 kilometres
|
||||
</li><li>0.013 kilometres
|
||||
</li><li>0.014 kilometres
|
||||
</li><li>0.015 kilometres
|
||||
</li><li>0.016 kilometres
|
||||
</li><li>0.017 kilometres
|
||||
</li><li>0.018 kilometres
|
||||
</li><li>0.019 kilometres
|
||||
</li><li>0.02 kilometres
|
||||
</li></ul>
|
||||
|
||||
!! end
|
||||
|
||||
!! test
|
||||
Precision 2
|
||||
!! input
|
||||
*{{#convert: 10.0 m | km }}
|
||||
*{{#convert: 10.1 m | km }}
|
||||
*{{#convert: 10.2 m | km }}
|
||||
*{{#convert: 10.3 m | km }}
|
||||
*{{#convert: 10.4 m | km }}
|
||||
*{{#convert: 10.5 m | km }}
|
||||
*{{#convert: 10.6 m | km }}
|
||||
*{{#convert: 10.7 m | km }}
|
||||
!! result
|
||||
<ul><li>0.0100 kilometres
|
||||
</li><li>0.0101 kilometres
|
||||
</li><li>0.0102 kilometres
|
||||
</li><li>0.0103 kilometres
|
||||
</li><li>0.0104 kilometres
|
||||
</li><li>0.0105 kilometres
|
||||
</li><li>0.0106 kilometres
|
||||
</li><li>0.0107 kilometres
|
||||
</li></ul>
|
||||
|
||||
!! end
|
||||
|
||||
!! test
|
||||
String interpolation
|
||||
!! input
|
||||
{{#convert: 25, 26, 27, 28, 29, and 30 km }}
|
||||
!! result
|
||||
<p>25,000, 26,000, 27,000, 28,000, 29,000, and 30,000 metres
|
||||
</p>
|
||||
!! end
|
||||
|
||||
!! test
|
||||
Precision 3
|
||||
!! input
|
||||
{{#convert: 25, 26, 27, 28, 29, and 30 miles }}
|
||||
!! result
|
||||
<p>40,000, 42,000, 43,000, 45,000, 47,000, and 50,000 metres
|
||||
</p>
|
||||
!! end
|
||||
|
||||
!! test
|
||||
Precision 4
|
||||
!! input
|
||||
{{#convert:35000, 35E3, 35.0E3, 350E2, 3.500E4, 35000E0, 350000E-1 m | km }}
|
||||
!! result
|
||||
<p>35, 35, 35.0, 35, 35.00, 35, 35 kilometres
|
||||
</p>
|
||||
!! end
|
||||
|
||||
!! test
|
||||
#sourceunit
|
||||
!!input
|
||||
*{{#convert: 25 | #sourceunit = km }}
|
||||
*{{#convert: 25 | #sourceunit=km }}
|
||||
*{{#convert: 25 | #sourceunit = km | #sourceunit = mm }}
|
||||
*{{#convert: 25 | #sourceunit = km | cm }}
|
||||
!! result
|
||||
<ul><li>25,000 metres
|
||||
</li><li>25,000 metres
|
||||
</li><li>0.025 metres
|
||||
</li><li>2,500,000 centimetres
|
||||
</li></ul>
|
||||
|
||||
!! end
|
||||
|
||||
!! test
|
||||
Precision overrides
|
||||
!!input
|
||||
*{{#convert: 1 mi | #dp = 0 }}
|
||||
*{{#convert: 1 mi | #dp=1 }}
|
||||
*{{#convert: 1 mi | #dp = -2 }}
|
||||
*{{#convert: 1 mi | #dp = 5 }}
|
||||
*{{#convert: 1 mi | #dp = -8 }}
|
||||
*{{#convert: 1 mi | #sf = 0 }}
|
||||
*{{#convert: 1 mi | #sf=1 }}
|
||||
*{{#convert: 1 mi | #sf = 3 }}
|
||||
*{{#convert: 1 mi | #sf = 5 }}
|
||||
*{{#convert: 1 mi | #sf = -8 }}
|
||||
!! result
|
||||
<ul><li>1,609 metres
|
||||
</li><li>1,609.3 metres
|
||||
</li><li>1,600 metres
|
||||
</li><li>1,609.344 metres
|
||||
</li><li>0 metres
|
||||
</li><li>2,000 metres
|
||||
</li><li>2,000 metres
|
||||
</li><li>1,610 metres
|
||||
</li><li>1,609.3 metres
|
||||
</li><li>2,000 metres
|
||||
</li></ul>
|
||||
|
||||
!! end
|
||||
|
||||
|
||||
!! test
|
||||
Errors
|
||||
!! input
|
||||
*{{#convert: 25 | km }}
|
||||
*{{#convert: 25 foobars | mi }}
|
||||
*{{#convert: 25 mi | #sourceunit = foobar }}
|
||||
*{{#convert: 25 km | s }}
|
||||
*{{#convert: 25 km/Pa | m/Pa }}
|
||||
*{{#convert: 25 km/s/l }}
|
||||
*{{#convert: 25 km/m3 }}
|
||||
!! result
|
||||
<ul><li><strong class="error">Error: no source unit given</strong>
|
||||
</li><li><strong class="error">Error: unknown unit "foobars"</strong>
|
||||
</li><li><strong class="error">Error: unknown unit "foobar"</strong>
|
||||
</li><li><strong class="error">Error: cannot convert between units of "length" and "time"</strong>
|
||||
</li><li><strong class="error">Error: invalid compound unit "length/pressure"</strong>
|
||||
</li><li><strong class="error">Error: cannot parse double compound units like "km/s/l"</strong>
|
||||
</li><li><strong class="error">Error: invalid compound unit "length/volume"</strong>
|
||||
</li></ul>
|
||||
|
||||
!! end
|
||||
|
||||
|
||||
!! test
|
||||
#sourceunit = #targetunit
|
||||
!! input
|
||||
*{{#convert: 25 km | #targetunit = #sourceunit }}
|
||||
*{{#convert: 25 km | #sourceunit = #targetunit }}
|
||||
!! result
|
||||
<ul><li>25 kilometres
|
||||
</li><li>25 kilometres
|
||||
</li></ul>
|
||||
|
||||
!! end
|
Loading…
Reference in a new issue