モジュールの解説[作成]

local errormsg = ('[[Category:不明な通貨を含む記事]]' 	.. '<span class="exchangeinfo" style="display:none;" ' 	.. 'title="不明な為替レート">不明な通貨</span>' 	.. 'レートが見つかりません')  local function countSigificantDigits(number) 	number = string.gsub(number, '%.', '', 1) 	number = mw.text.trim(number, '0') 	return #number end  local function round(num, numSigificantDigits) 	local numDecimalPlaces = numSigificantDigits - math.floor(math.log10(num)) - 1 	local mult = 10^(numDecimalPlaces or 0) 	return math.floor(num * mult + 0.5) / mult end  local function getTabularDataFieldNames(tabularData) 	local fields = {} 	for _,field in pairs(tabularData.schema.fields) do 		table.insert(fields, field.name) 	end 	return fields end  local function getColumnIndices(fields) 	local rowCurrencyIndex, dateIndex 	local targetCurrencyIndices = {} 	local sourceCurrencyIndices = {} 	for i,v in pairs(fields) do 		if v == 'currency' then 			rowCurrencyIndex = i 		elseif v == 'date' then 			dateIndex = i 		elseif string.match(v, '^%u%u%u$') then 			sourceCurrencyIndices[v] = i 		elseif string.match(v, '^_%u%u%u$') then 			targetCurrencyIndices[string.sub(v,2)] = i 		end 	end 	return rowCurrencyIndex, dateIndex, sourceCurrencyIndices, targetCurrencyIndices end  local function getConversionTable(dataPageName) 	local tabularData = mw.ext.data.get(dataPageName) 	if not tabularData then return nil end 	local fields = getTabularDataFieldNames(tabularData) 	local rowCurrencyIndex, dateIndex, sourceCurrencyIndices, targetCurrencyIndices = getColumnIndices(fields) 	 	local conversionTable = {} 	if rowCurrencyIndex then 		for _,row in pairs(tabularData.data) do 			for sourceCurrency,index in pairs(sourceCurrencyIndices) do 				if not conversionTable[sourceCurrency] then 					conversionTable[sourceCurrency] = {} 				end 				conversionTable[sourceCurrency][row[rowCurrencyIndex]] = {rate = row[index], revisionTime = row[dateIndex]} 			end 			for targetCurrency,index in pairs(targetCurrencyIndices) do 				if not conversionTable[row[rowCurrencyIndex]] then 					conversionTable[row[rowCurrencyIndex]] = {} 				end 				conversionTable[row[rowCurrencyIndex]][targetCurrency] = {rate = row[index], revisionTime = row[dateIndex]} 			end 		end 	end 	return conversionTable end  local function getDataFromRateDataPage(dataPageName, source, target) 	local conversionTable = getConversionTable(dataPageName) 	if not conversionTable then return nil end 	local rate, revisionTime 	if conversionTable[source] and conversionTable[source][target] then 		rate = conversionTable[source][target]['rate'] 		rateSignificantDigits = countSigificantDigits(rate) 		revisionTime = conversionTable[source][target]['revisionTime'] 	elseif conversionTable[target] and conversionTable[target][source] then 		local targetToSourceRate = conversionTable[target][source]['rate'] 		rate = targetToSourceRate^-1 		rateSignificantDigits = countSigificantDigits(targetToSourceRate) 		revisionTime = conversionTable[target][source]['revisionTime'] 	end 	return rate, rateSignificantDigits, revisionTime end  local p = {}  function p._rate(source, target, rounded) 	local dataPageNames = { 		'ECB euro foreign exchange reference rates.tab',  		'Xe.com exchange rates.tab'} 	local rate, revisionTime, rateSignificantDigits 	for _,name in pairs(dataPageNames) do 		rate, rateSignificantDigits, revisionTime = getDataFromRateDataPage(name, source, target) 		if not rate or not revisionTime then 			for _,name in pairs(dataPageNames) do 				local USDtoTargetRate, UtoTSigDig, UtoTRevTime = getDataFromRateDataPage(name, 'USD', target) 				local USDtoSourceRate, UtoSSigDig, UtoSRevTime = getDataFromRateDataPage(name, 'USD', source) 				if USDtoTargetRate and USDtoSourceRate then 					rate = USDtoTargetRate/USDtoSourceRate 					revisionTime = UtoTRevTime < UtoSRevTime and UtoTRevTime or UtoSRevTime 					rateSignificantDigits = UtoTSigDig < UtoSSigDig and UtoTSigDig or UtoSSigDig 				end 			end 		end 		if rate and revisionTime then 			break 		end 	end 	if rate and revisionTime then 		if rounded then 			rate = round(rate, rateSignificantDigits) 		end 		return rate, revisionTime 	end end  function p._convert(source, target, amount) 	local rate = p._rate(source, target) 	if rate then 		local amountSigificantDigitsCount = countSigificantDigits(amount) 		return round(amount * rate, amountSigificantDigitsCount + 1) 	end end  function p._convertSingelOrRange(source, target, amounts) 	local amounts = string.gsub(amounts, ',', '') 	local splitOffset = mw.ustring.find(amounts, '-') 	local converted 	if splitOffset then 		local firstAmount = mw.ustring.sub(amounts, 0, splitOffset -1) 		local secondAmount = mw.ustring.sub(amounts, splitOffset + 1) 		local first = p._convert(source, target, firstAmount) 		local second = p._convert(source, target, secondAmount) 		converted = first and second and first .. '&ndash;' .. second 	else 		converted = p._convert(source, target, amounts) 	end 	return converted end   function p.rate(frame) 	local args = frame.args 	local rate = p._rate(args.source, args.target, true) 	local result = rate or args.verbose and errormsg 	return result end  function p.revisionTime(frame) 	local args = frame.args 	local _,revisionTime = p._rate(args.source, args.target) 	local result = revisionTime or args.verbose and errormsg 	return result end  function p.convert(frame) 	local args = frame.args 	local amount = 	string.gsub(args.amount, ',', '') 	local convertedAmount = p._convert(args.source, args.target, amount) 	local result = convertedAmount or args.verbose and errormsg 	return result end  function p.convertSingelOrRange(frame) 	local args = frame.args 	local convertedAmounts = p._convertSingelOrRange( 		args.source, args.target, args.amounts) 	local result = convertedAmounts or args.verbose and errormsg 	return result end  local function currencyWithSymbol(currency, symbolFormat, amount) 	local currencyWithSymbol = ( 		symbolFormat and string.format(symbolFormat, amount) 		or currency .. amount) 	return currencyWithSymbol end  function p.currencyWithConversions(frame) 	local args = frame.args 	local amount = (args.amount and args.amount ~= '') and args.amount or 1 	local i18n = mw.loadData('Module:Exchangerate/i18n') 	local currencySymbols = i18n.symbols[args.currency] 	local shortSymbol = currencySymbols and currencySymbols.shortSymbol 	local currencyWithShortSymbol = currencyWithSymbol( 		args.currency, shortSymbol, amount) 	local uniqueSymbol = currencySymbols and currencySymbols.uniqueSymbol 	local currencyWithUniqueSymbol = currencyWithSymbol( 		args.currency, uniqueSymbol, amount) 	local conversionCurrencies = i18n.defaultConversions or {'USD', 'EUR'} 	local convertedStrings = {} 	for _,convCurrency in ipairs(conversionCurrencies) do 		if args.currency ~= convCurrency then 			local convertedAmount = p._convertSingelOrRange( 				args.currency, convCurrency, amount) 			local convCurrencyUniqueSymbol = (i18n.symbols[convCurrency] 				and i18n.symbols[convCurrency].uniqueSymbol) 			local convCurrencyWithSymbol = convertedAmount and currencyWithSymbol( 				convCurrency, convCurrencyUniqueSymbol, convertedAmount) 			table.insert(convertedStrings, convCurrencyWithSymbol) 		end 	end 	local comma = mw.message.new('comma-separator'):plain() 	local allConvertedStrings = table.concat(convertedStrings, comma) 	local conversions = (allConvertedStrings ~= '') and ' ≈ ' .. allConvertedStrings or '' 	local resultFormat = '<abbr title="%s%s">%s</abbr>' 	local result = string.format(resultFormat, currencyWithUniqueSymbol, 		conversions, currencyWithShortSymbol) 	return result end  return p