Modul:Exchange rate
Lua-Fehler in Modul:Failsafe, Zeile 62: attempt to index field 'wikibase' (a nil value)
Benötigte weitere Module
Dieses Modul benötigt folgende weitere Module: CountryData/Currencies
Verwendung in anderen Modulen
Dieses Modul ist notwendig für die Ausführung folgender Module. Bei Anpassungen sollte die Funktionstüchtigkeit der folgenden Module geprüft werden. Benutze dazu auch diese Tracking-Kategorie um Fehler zu finden, die sich dann auf Artikel auswirken:
- vCard
- Modul benötigt das Modul Exchange rate – Wartungskategorie, in der nochmals alle Module gelistet sind, die von diesem Modul abhängig sind.
Häufig genutzte Variablen
amount
:string
Wert oder Wertebereich eines Geldbetrages,source
:string
dreistelliger ISO-4217-Code der Ursprungswährung,target
:string
dreistelliger ISO-4217-Code der Zielwährung,frame
:table
Parametertabelle, die durch einen#invoke
-Aufruf übergeben wird.
Extern nutzbare Funktionen
function er.getRate( source, target, toRound )
Die Funktion liefert drei Werte zurück: rate
, asOf
, digitCount
.
- Vorgabe:
toRound
:boolean
. Fallstrue
wirdrate
nur mit maximal signifikanten Stellen ausgegeben.
- Ergebnis:
rate
:number
. Wechselkurs für die Umrechnung aus Ursprungs- in die Zielwährung.asOf
:string
. Datumsangabe für den Wechselkurs in der Form YYYY-MM-DD.digitCount
:number
: Anzahl signifikanter Stellen für den Wechselkurs.
function er.getWrapper( amount, source, target, digits, externalFormatter )
Die Funktion liefert eine Formatierungszeichenkette für ein span
-Tag, wobei öffnendes und schließendes Tag den %s
-Platzhalter umschließen. Das title
-Attribut des Tags enthält den umgerechneten Betrag in mehreren Währungen (üblicherweise EUR, CHF und USD), das class
-Attribut zwei Werte, nämlich voy-currency
und voy-currency-xxx
, wobei xxx den ISO-4217-Code der Ursprungswährung in Kleinbuchstaben darstellen.
- Vorgabe:
digits
:number
. Anzahl der Nachkommastellen der umgerechneten Beträge. Standard ist 2.externalFormatter
:function
. Externe Funktion, die eine Formatierungszeichenkette für das Einfügen eines Betrages zurückliefert. Sie stellt einen Ersatz für die lokale FunktiongetFormatter
dar, um den Zugriff auf das externe Modul mit den Währungscodes zu vermeiden.
- Ergebnis:
string
: Formatierungszeichenkette.
function er.rate( frame )
Die Funktion liefert den Wechselkurs für die Umrechnung aus Ursprungs- in die Zielwährung in wählbaren Formaten zur Verfügung.
- Vorgabe:
args.source
,args.target
,args.show
undargs.digits
.args.show
:date
: nur Datum des Wechselkurses,all
: Wechselkurs mit Datum in Klammern.args.digits
: maximale Anzahl der Nachkommastellen.
- Ergebnis:
string
: Formatierungszeichenkette.
function er.convert( frame )
function er.currencyWithConversions( frame )
Lokale Funktionen
local function getFormatter( isoCode, externalFormatter )
local function getDigitCount( num )
local function round( num, digitCount )
local function getFields( tabularData )
local function getRateTable( tableName )
local function getCurrencyData( rateTable, source, target )
local function getDate( aDate, formatStr )
local function insertThousandsSep( amount )
local function formatNumber( num )
local function addUnit( amount, isoCode, externalFormatter )
local function formatRate( rate, asOf, show, digits, target )
local function convertSingle( source, target, amount, digits )
function er._convert( source, targets, amount, withUnit, digits, externalFormatter )
--[[ 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 = '2022-10-22', 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 ) 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 .. messages.wrongParams 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 ), args.amount:gsub( '-', '–' ) ) end return er