Modul:Exchange rate
Hinweis: Seit dem 24. Februar 2023 werden die Wechselkurse auf Wikimedia Commons nicht mehr aktualisiert.
Versionsbezeichnung auf Wikidata: 2024-02-24
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 )
- Die obige Dokumentation wurde aus der Seite Modul:Exchange rate/Doku eingefügt. (bearbeiten | Versionsgeschichte) Die Kategorien für dieses Modul sollten in der Dokumentation eingetragen werden. Die Interwiki-Links sollten auf Wikidata eingepflegt werden.
- Liste der Unterseiten
--[[
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