[editar] [

Version check
Designación de la versión en Wikidata: 2024-02-24
Uso
Esta documentación es transcluida desde Módulo:Exchange rate/doc.
Los editores pueden experimentar en la zona de pruebas de la plantilla.
Por favor, añade las categorías a la subpágina de documentación. Subpáginas de esta plantilla.
Los editores pueden experimentar en la zona de pruebas de la plantilla.
Por favor, añade las categorías a la subpágina de documentación. Subpáginas de esta plantilla.
--[[ Thanks to GiftBot who is uploading/updating currency exchange rates to Wikimedia Commons. This service is available since March of 2022. ]]-- -- module variable and administration local er = { moduleInterface = { suite = 'Exchange rate', serial = '2024-02-24', item = 112066294 } } -- require( 'strict' ) -- Exchange-rate tables stored on Wikimedia Commons local tableNames = { 'ECB euro foreign exchange reference rates.tab', 'Xe.com exchange rates.tab' } -- language-dependent error messages local messages = { unknownIsoCode = '[[Category:Währung: Seiten mit unbekanntem Währungscode]] <span class="error">Unbekannter Währungscode</span>', wrongParams = '[[Category:Währung: Fehlerhafte Parameter]] <span class="error">Fehlerhafte(r) Parameter</span>' } -- language-dependent constants local language = { defaultUnits = { 'EUR', 'CHF', 'USD' }, decimalSep = ',', -- decimal separator thousandsSep = '.', commaSep = mw.message.new( 'comma-separator' ):plain(), dateFormat = 'j. M Y', convertFormatter = '≈ %s', defaultFormatter = '%s unit', wrapperClass = 'voy-currency', conversionVia = 'EUR', -- EUR or USD all = 'alle', -- lowercase letters date = 'datum' } -- variables for internal use local cu -- for currencies-table module local rateTables = {} -- to prevent multiple fetching -- check if arg is set local function isSet( arg ) return arg and arg ~= '' end -- returns a currency formatter string for isoCode -- the following function must be localized local function getFormatter( isoCode, externalFormatter ) isoCode = isSet( isoCode ) and isoCode:upper() or 'XXX' if externalFormatter then return externalFormatter( isoCode ) elseif not cu then cu = mw.loadData( 'Module:CountryData/Currencies' ) end local tab = cu.isoToQid[ isoCode ] and cu.currencies[ cu.isoToQid[ isoCode ] ] local default = cu.currencies.default or language.defaultFormatter if tab then if tab.f then return tab.f else local unit = tab.add and tab.add:gsub( ',.*', '' ) or tab.iso return default:gsub( 'unit', unit ) end end return default:gsub( 'unit', isoCode ) end -- returns count of significant digits -- zeros after decimal separator are significant local function getDigitCount( num ) num = num:gsub( '%.', '' ):gsub( '^0+', '' ) return #num end -- rounds mantissa/significand of number num to digit count digitCount local function round( num, digitCount ) return tonumber( string.format( '%.' .. digitCount .. 'g', num ) ) end -- returns tabularData fields schema as associative table local function getFields( tabularData ) local fields = {} local tFields = tabularData.schema.fields for i = 1, #tFields do fields[ tFields[ i ].name ] = i end return fields end -- returns currency-rates table as associative table -- this is an expensive function: the rateTables should be established only once local function getRateTable( tableName ) local rows = {} local colNo, fields, row, tData if not rateTables[ tableName ] then local tabularData = mw.ext.data.get( tableName ) if not tabularData then return nil end fields = getFields( tabularData ) colNo = fields[ 'currency' ] tData = tabularData.data for i = 1, #tData do row = tData[ i ] rows[ row[ colNo ] ] = row end rateTables[ tableName ] = { fields = fields, rows = rows } end return rateTables[ tableName ] end -- returns exchange-rate properties for source -> target iso codes local function getCurrencyData( rateTable, source, target ) local rate, digitCount, asOf local fields = rateTable.fields local row = rateTable.rows[ source ] if row then rate = row[ fields[ target ] ]:gsub( ',', '' ) -- remove English thousands separator digitCount = getDigitCount( rate ) rate = tonumber( rate ) asOf = row[ fields[ 'date' ] ] end return rate, digitCount, asOf end -- returns exchange rate for source -> target iso codes -- toRound: Boolean function er.getRate( source, target, toRound ) -- source, target are three-letter ISO 4217 codes if not source:match( '^%a%a%a$' ) or not target:match( '^%a%a%a$' ) then return nil end local rateTable, fields, rate, rows, digitCount, asOf source = source:upper() target = target:upper() for i = 1, #tableNames do rateTable = getRateTable( tableNames[ i ] ) if rateTable then fields = rateTable.fields if fields[ target ] then rate, digitCount, asOf = getCurrencyData( rateTable, source, target ) if rate then rate = 1/rate end elseif fields[ source ] then rate, digitCount, asOf = getCurrencyData( rateTable, target, source ) elseif fields[ language.conversionVia ] then local rate1, digitCount1, asOf1 = getCurrencyData( rateTable, source, language.conversionVia ) local rate2, digitCount2, asOf2 = getCurrencyData( rateTable, target, language.conversionVia ) if rate1 and rate2 then rate = rate2/rate1 digitCount = digitCount1 < digitCount2 and digitCount1 or digitCount2 asOf = asOf1 < asOf2 and asOf1 or asOf2 end end end if rate then break end end if rate and toRound then rate = round( rate, digitCount ) end return rate, asOf, digitCount end -- returns a converted date for aDate due to formatStr local function getDate( aDate, formatStr ) local function formatDate( aDate, formatStr ) return mw.getContentLanguage():formatDate( formatStr, aDate, true ) end if isSet( aDate ) then local success, t = pcall( formatDate, aDate, formatStr ) return success and t or '' else return '' end end -- inserts thousands separators in amount string local function insertThousandsSep( amount ) local k local sep = '%1' .. language.thousandsSep .. '%2' while true do amount, k = amount:gsub( '^(-?%d+)(%d%d%d)', sep ) if k == 0 then break end end return amount end -- localizes a number string local function formatNumber( num ) if language.decimalSep ~= '.' then num = num:gsub( '%.', language.decimalSep ) end return insertThousandsSep( num ) end -- adds the currency unit of isoCode to amount string local function addUnit( amount, isoCode, externalFormatter ) local formatStr = getFormatter( isoCode, externalFormatter ) return mw.ustring.format( mw.text.decode( formatStr ), amount ) end local function outputFormat( digits ) digits = math.floor( tonumber( digits ) or 2 ) if digits < 0 or digits > 6 then digits = 2 end return '%.'.. digits .. 'f' end -- selects different rate outputs due to show local function formatRate( rate, asOf, show, digits, target ) show = ( show or '' ):lower() rate = formatNumber( isSet( digits ) and outputFormat( digits ):format( rate ) or tostring( rate ) ) if isSet( digits ) or show == 'all' or show == language.all then rate = addUnit( rate, target ) end if show == 'all' or show == language.all then return rate .. ' (' .. getDate( asOf, language.dateFormat ) .. ')' elseif show == 'date' or show == language.date then return getDate( asOf, language.dateFormat ) else return rate end end -- converts a single currency amount without adding the currency unit local function convertSingle( source, target, amount, digits ) local rate, asOf, digitCount = er.getRate( source, target ) if rate then return formatNumber( outputFormat( digits ):format( round( amount * rate, digitCount ) ):gsub( '%.0*$', '' ) ) else return nil end end -- converts a single currency amount or an amount range and adding the currency unit function er._convert( source, targets, amount, withUnit, digits, externalFormatter ) local amount1, amount2, pos, result local results = {} if not isSet( targets ) then targets = language.defaultUnits withUnit = true elseif type( targets ) == 'string' then targets = { targets } end amount = amount:gsub( '[ %a%' .. language.thousandsSep .. ']+', '' ):gsub( '-', '–' ) if language.decimalSep ~= '.' then amount = amount:gsub( language.decimalSep, '.' ) end for i, target in ipairs( targets ) do if target ~= source then pos = mw.ustring.find( amount, '[^,%.%d]' ) if pos then amount1 = mw.ustring.sub( amount, 1, pos - 1 ) amount2 = tonumber( mw.ustring.sub( amount, pos + 1 ) ) else amount1 = amount end amount1 = tonumber( amount1 ) or 1 result = convertSingle( source, target, amount1, digits ) if pos and result and amount2 then amount2 = convertSingle( source, target, amount2, digits ) result = amount2 and ( result .. mw.ustring.sub( amount, pos, pos ) .. amount2 ) end if result then if withUnit then result = addUnit( result, target, externalFormatter ) end table.insert( results, result ) end end end result = table.concat( results, language.commaSep ) return result ~= '' and result end -- returns a wrapper format string with tooltip title function er.getWrapper( amount, source, target, digits, externalFormatter, withMaintenance) local formatStr = getFormatter( source, externalFormatter ) local title = er._convert( source, target, amount, true, digits ) if title then return tostring( mw.html.create( 'abbr' ) :attr( 'title', mw.ustring.format( language.convertFormatter, title ) ) :addClass( language.wrapperClass ) :addClass( language.wrapperClass .. '-' .. source:lower() ) :wikitext( formatStr ) ) else return formatStr .. ( withMaintenance and messages.wrongParams or '' ) end end -- #invoke function returning the exchange rate function er.rate( frame ) local args = frame.args local rate, asOf, digitCount = er.getRate( args.source, args.target, true ) return rate and formatRate( rate, asOf, args.show, args.digits, args.target ) or messages.unknownIsoCode end -- #invoke function returning the converted amount or amount range function er.convert( frame ) local args = frame.args if isSet( args.show ) then return er.rate( frame ) else return er._convert( args.source, args.target, isSet( args.amount ) and args.amount or '1', ( args.plain or '' ) ~= '1', args.digits ) or messages.wrongParams end end -- #invoke function returning exchange-rate information -- returns the formatted amount or amount range with a tooltip containing -- converted values function er.currencyWithConversions( frame ) local args = frame.args if not isSet( args.amount ) then args.amount = '1' end return mw.ustring.format( er.getWrapper( args.amount, args.source, args.target, args.digits, nil, true ), args.amount:gsub( '-', '–' ) ) end return er