Modul:FormatNum: Cudahiya di navbera guhartoyan de
Content deleted Content added
Rûpela nû: "--2013-06-16 FormatNum * format * round FormatNum() local FormatNum = { }; -- Constant for round method "round half to even" (IEEE 754). local ROUND_TO_EVEN = 0; -- Const..." |
Kurteya guhartinê tine |
||
Rêz 1: | Rêz 1: | ||
-- This module is intended to replace the functionality of Template:Formatnum and related templates. |
|||
--[[ 2013-06-16 |
|||
local p = {} |
|||
FormatNum |
|||
* format |
|||
* round |
|||
FormatNum() |
|||
]] |
|||
local FormatNum = { }; |
|||
function p.main(frame) |
|||
local args = frame:getParent().args |
|||
local prec = args.prec or '' |
|||
local sep = args.sep or '' |
|||
local number = args[1] or args.number or '' |
|||
local lang = args[2] or args.lang or '' |
|||
-- validate the language parameter within MediaWiki's caller frame |
|||
if lang == "arabic-indic" then -- only for back-compatibility ("arabic-indic" is not a SupportedLanguage) |
|||
lang = "fa" -- better support than "ks" |
|||
elseif lang == '' or not mw.language.isSupportedLanguage(lang) then |
|||
-- Note that 'SupportedLanguages' are not necessarily 'BuiltinValidCodes', and so they are not necessarily |
|||
-- 'KnownLanguages' (with a language name defined at least in the default localisation of the local wiki). |
|||
-- But they all are ValidLanguageCodes (suitable as Wiki subpages or identifiers: no slash, colon, HTML tags, or entities) |
|||
-- In addition, they do not contain any capital letter in order to be unique in page titles (restriction inexistant in BCP47), |
|||
-- but they may violate the standard format of BCP47 language tags for specific needs in MediaWiki. |
|||
-- Empty/unspecified and unsupported languages are treated here in Commons using the user's language, |
|||
-- instead of the local 'ContentLanguage' of the Wiki. |
|||
lang = frame:callParserFunction( "int", "lang" ) -- get user's chosen language |
|||
end |
|||
return p.formatNum(number, lang, prec, sep ~= '') |
|||
end |
|||
local digit = { -- substitution of decimal digits for languages not supported by mw.language:formatNum() in core Lua libraries for MediaWiki |
|||
["ml-old"] = { '൦', '൧', '൨', '൩', '൪', '൫', '൬', '൭', '൮', '൯' }, |
|||
["mn"] = { '᠐', '᠑', '᠒', '᠓', '᠔', '᠕', '᠖', '᠗', '᠘', '᠙'}, |
|||
["ta"] = { '௦', '௧', '௨', '௩', '௪', '௫', '௬', '௭', '௮', '௯'}, |
|||
["te"] = { '౦', '౧', '౨', '౩', '౪', '౫', '౬', '౭', '౮', '౯'}, |
|||
["th"] = { '๐', '๑', '๒', '๓', '๔', '๕', '๖', '๗', '๘', '๙'} |
|||
} |
|||
function p.formatNum(number, lang, prec, compact) |
|||
-- Constant for round method "round half to even" (IEEE 754). |
|||
local ROUND_TO_EVEN = 0; |
|||
-- Do not alter the specified value when it is not a valid number, return it as is |
|||
-- Constant for round method "round half away from zero" |
|||
local value = tonumber(number) |
|||
-- (German: "kaufmaennisches Runden"), |
|||
if value == nil then |
|||
-- also filters "-0" and converts it to "0". |
|||
return number |
|||
local ROUND_AWAY_FROM_ZERO = 1; |
|||
end |
|||
-- Basic ASCII-only formatting (without paddings) |
|||
number = tostring(value) |
|||
-- Check the presence of an exponent (incorrectly managed in mw.language:FormatNum() and even forgotten due to an internal bug, e.g. in Hindi) |
|||
-- Table storing the format options. |
|||
local exponent |
|||
local FORMAT_TABLE = {}; |
|||
local pos = string.find(number, '[Ee]') |
|||
if pos ~= nil then |
|||
-- Format table for "de". |
|||
exponent = string.sub(number, pos + 1, string.len(number)) |
|||
FORMAT_TABLE.de = {}; |
|||
number = string.sub(number, 1, pos - 1) |
|||
FORMAT_TABLE.de.decimalMark = ","; |
|||
else |
|||
FORMAT_TABLE.de.groupMark = " "; |
|||
exponent = '' |
|||
FORMAT_TABLE.de.groupMinLength = 5; |
|||
end |
|||
FORMAT_TABLE.de.groupOnlyIntegerPart = false; |
|||
-- Format table for "de_currency". |
|||
FORMAT_TABLE.de_currency = {}; |
|||
FORMAT_TABLE.de_currency.decimalMark = ","; |
|||
FORMAT_TABLE.de_currency.groupMark = "."; |
|||
FORMAT_TABLE.de_currency.groupMinLength = 5; |
|||
FORMAT_TABLE.de_currency.groupOnlyIntegerPart = true; |
|||
-- Format table for "ch". |
|||
FORMAT_TABLE.ch = {}; |
|||
FORMAT_TABLE.ch.decimalMark = ","; |
|||
FORMAT_TABLE.ch.groupMark = "'"; |
|||
FORMAT_TABLE.ch.groupMinLength = 5; |
|||
FORMAT_TABLE.ch.groupOnlyIntegerPart = true; |
|||
-- Format table for "en". |
|||
FORMAT_TABLE.en = {}; |
|||
FORMAT_TABLE.en.decimalMark = "."; |
|||
FORMAT_TABLE.en.groupMark = ","; |
|||
FORMAT_TABLE.en.groupMinLength = 4; |
|||
FORMAT_TABLE.en.groupOnlyIntegerPart = true; |
|||
-- Format table for "iso31_0" (ISO 31-0 using comma as decimal mark). |
|||
FORMAT_TABLE.iso31_0 = {}; |
|||
FORMAT_TABLE.iso31_0.decimalMark = ","; |
|||
FORMAT_TABLE.iso31_0.groupMark = " "; |
|||
FORMAT_TABLE.iso31_0.groupMinLength = 4; |
|||
FORMAT_TABLE.iso31_0.groupOnlyIntegerPart = false; |
|||
-- Format table for "iso31_0_point" (ISO 31-0 using point as decimal mark). |
|||
FORMAT_TABLE.iso31_0_point = {}; |
|||
FORMAT_TABLE.iso31_0_point.decimalMark = "."; |
|||
FORMAT_TABLE.iso31_0_point.groupMark = " "; |
|||
FORMAT_TABLE.iso31_0_point.groupMinLength = 4; |
|||
FORMAT_TABLE.iso31_0_point.groupOnlyIntegerPart = false; |
|||
-- Format table for "pc" (simply nil to prevent formatting). |
|||
FORMAT_TABLE.pc = nil; |
|||
-- Format table for "comma" (no grouping - groupMark ""). |
|||
FORMAT_TABLE.comma = {}; |
|||
FORMAT_TABLE.comma.decimalMark = ","; |
|||
FORMAT_TABLE.comma.groupMark = ""; |
|||
FORMAT_TABLE.comma.groupMinLength = 1000; -- (for performance, but also small values wouldn't matter) |
|||
FORMAT_TABLE.comma.groupOnlyIntegerPart = true; |
|||
-- Format table for "at" (only for convenience, same as "iso31_0"). |
|||
FORMAT_TABLE.at = FORMAT_TABLE.iso31_0; |
|||
-- Format table for "ch_currency" (only for convenience, same as "de_currency"). |
|||
FORMAT_TABLE.ch_currency = FORMAT_TABLE.de_currency; |
|||
-- Format table for "dewiki" (only for convenience, same as "de_currency"). |
|||
FORMAT_TABLE.dewiki = FORMAT_TABLE.de_currency; |
|||
-- Constant defining digit group lenghts when digit grouping is used. |
|||
local DIGIT_GROUPING_SIZE = 3; |
|||
--[[ |
|||
Internal used function for rounding. |
|||
@param a_number : Number to be rounded. |
|||
@param a_precision : Number of significant digits of the fractional part. If it |
|||
is negative, the according number of digits of the integer part are also |
|||
rounded. |
|||
@param a_roundMethod : Numeric constant defining the round method to use. |
|||
Supported are ROUND_TO_EVEN and ROUND_AWAY_FROM_ZERO. |
|||
-- Check the minimum precision requested |
|||
@return String of the rounded number like returned by Lua function string.format(). |
|||
prec = tonumber(prec) -- nil if not specified as a true number |
|||
]] |
|||
if prec ~= nil then |
|||
local function numberToString(a_number, a_precision, a_roundMethod) |
|||
prec = math.floor(prec) |
|||
if (a_precision < 0) then |
|||
if prec < 0 then |
|||
prec = nil -- discard an incorrect precision (not a positive integer) |
|||
if (a_roundMethod == ROUND_TO_EVEN) then |
|||
elseif prec > 14 then |
|||
local integerPart = math.floor(math.abs(a_number) / (10 ^ a_precision)); |
|||
prec = 14 -- maximum precision supported by tostring(number) |
|||
-- next even number smaller than a_number / 10^precision |
|||
a_number = a_number - 5 * (10 ^ (a_precision - 1)); |
|||
a_number = math.ceil(a_number / (10 ^ a_precision)) * (10 ^ a_precision); |
|||
else |
|||
-- next even number bigger than a_number / 10^precision |
|||
a_number = a_number + 5 * (10 ^ (a_precision - 1)); |
|||
a_number = math.floor(a_number / (10 ^ a_precision)) * (10 ^ a_precision); |
|||
end |
|||
elseif (a_roundMethod == ROUND_AWAY_FROM_ZERO) then |
|||
if (a_number >= 0) then |
|||
a_number = a_number + 5 * (10 ^ (a_precision - 1)); |
|||
a_number = math.floor(a_number / (10 ^ a_precision)) * (10 ^ a_precision); |
|||
else |
|||
a_number = a_number - 5 * (10 ^ (a_precision - 1)); |
|||
a_number = math.ceil(a_number / (10 ^ a_precision)) * (10 ^ a_precision); |
|||
end |
|||
end |
end |
||
-- handle it as normal integer |
|||
a_precision = 0; |
|||
end |
end |
||
if (a_roundMethod == ROUND_AWAY_FROM_ZERO) then |
|||
-- Preprocess the minimum precision in the ASCII string |
|||
if ((a_number * (10 ^ a_precision)) - math.floor(a_number * (10 ^ a_precision)) == 0.5) then |
|||
local dot |
|||
-- because string.format() uses round to even, we have to add (numbers >0) or |
|||
if (prec or 0) > 0 then |
|||
-- subtract (numbers <0) a little bit to point into the "correct" rounding |
|||
pos = string.find(number, '.', 1, true) -- plain search, no regexp |
|||
-- direction if a_number is exactly in the middle between two rounded numbers |
|||
if pos ~= nil then |
|||
prec = pos + prec - string.len(number) -- effective number of trailing decimals to add or remove |
|||
a_number = a_number + (10 ^ -(a_precision + 1)); |
|||
dot = '' -- already present |
|||
a_number = a_number - (10 ^ -(a_precision + 1)); |
|||
end |
|||
else |
else |
||
dot = '.' -- must be added |
|||
-- filter "-0" and convert it to 0 |
|||
a_number = math.abs(a_number); |
|||
end |
|||
end |
end |
||
else |
|||
dot = '' -- don't add dot |
|||
prec = 0 -- don't alter the precision |
|||
end |
end |
||
return string.format("%." .. tostring(a_precision) .. "f", a_number); |
|||
end -- numberToString() |
|||
if lang ~= nil and mw.language.isKnownLanguageTag(lang) == true then |
|||
-- Convert number to localized digits, decimal separator, and group separators |
|||
local language = mw.getLanguage(lang) |
|||
--[[ |
|||
if compact then |
|||
Internal used function for formatting. |
|||
number = language:formatNum(tonumber(number), { noCommafy = 'y' }) -- caveat: can load localized resources for up to 20 languages |
|||
else |
|||
@param a_number : String of a non-negative number to be formatted. |
|||
number = language:formatNum(tonumber(number)) -- caveat: can load localized resources for up to 20 languages |
|||
@param a_decimalMark : String of the decimal mark to use. |
|||
@param a_groupMark : String of the mark used for digit grouping. |
|||
@param a_groupMinLength : Number defining the minimum length of integer part |
|||
to use digit grouping (normally 4 or 5). However if fractional part is |
|||
longer than DIGIT_GROUPING_SIZE (3 as default) and digit grouping of |
|||
fractional part is not disabled via 'a_groupOnlyIntegerPart', then this |
|||
value is ignored and set to DIGIT_GROUPING_SIZE + 1. |
|||
@param a_groupOnlyIntegerPart : Boolean defining whether activating digit |
|||
grouping only for integer part (true) or for integer and fractional part |
|||
(false). |
|||
@return String of the formatted number according to the parameters. |
|||
]] |
|||
local function formatNumber(a_number, a_decimalMark, a_groupMark, a_groupMinLength, a_groupOnlyIntegerPart) |
|||
-- find the decimal point |
|||
local decimalPosition = mw.ustring.find(a_number, ".", 1, true); |
|||
local needsGrouping = false; |
|||
if (not decimalPosition) then |
|||
-- no decimal point - integer number |
|||
decimalPosition = mw.ustring.len(a_number) + 1; |
|||
if (decimalPosition > a_groupMinLength) then |
|||
needsGrouping = true; |
|||
end |
end |
||
-- Postprocessing the precision |
|||
else |
|||
if prec > 0 then |
|||
local zero = language:formatNum(0) |
|||
if ((decimalPosition > a_groupMinLength) or (((mw.ustring.len(a_number) - decimalPosition) > DIGIT_GROUPING_SIZE) and (not a_groupOnlyIntegerPart))) then |
|||
number = number .. dot .. mw.ustring.rep(zero, prec) |
|||
elseif prec < 0 then |
|||
-- TODO: rounding of last decimal; here only truncate decimals in excess |
|||
number = mw.ustring.sub(number, 1, mw.ustring.len(number) + prec) |
|||
end |
end |
||
-- replace the decimal point |
|||
-- Append the localized base-10 exponent without grouping separators (there's no reliable way to detect a localized leading symbol 'E') |
|||
a_number = mw.ustring.sub(a_number, 1, decimalPosition - 1) .. a_decimalMark .. mw.ustring.sub(a_number, decimalPosition + 1); |
|||
if exponent ~= '' then |
|||
end |
|||
number = number .. 'E' .. language:formatNum(tonumber(exponent),{noCommafy=true}) |
|||
if (needsGrouping and (decimalPosition > DIGIT_GROUPING_SIZE + 1)) then |
|||
-- grouping of integer part necessary |
|||
local i = decimalPosition - DIGIT_GROUPING_SIZE; |
|||
while (i > 1) do |
|||
-- group the integer part |
|||
a_number = mw.ustring.sub(a_number, 1, i - 1) .. a_groupMark .. mw.ustring.sub(a_number, i); |
|||
decimalPosition = decimalPosition + mw.ustring.len(a_groupMark); |
|||
i = i - DIGIT_GROUPING_SIZE; |
|||
end |
end |
||
else -- not localized, ASCII only |
|||
end |
|||
-- Postprocessing the precision |
|||
-- skip to the end of the new decimal mark (in case it is more than one char) |
|||
if prec > 0 then |
|||
decimalPosition = decimalPosition + mw.ustring.len(a_decimalMark) - 1; |
|||
number = number .. dot .. mw.string.rep('0', prec) |
|||
if (a_groupOnlyIntegerPart) then |
|||
elseif prec < 0 then |
|||
-- TODO: rounding of last decimal; here only truncate decimals in excess |
|||
end |
|||
number = mw.string.sub(number, 1, mw.string.len(number) + prec) |
|||
if (needsGrouping and ((mw.ustring.len(a_number) - decimalPosition) > DIGIT_GROUPING_SIZE)) then |
|||
-- grouping of fractional part necessary |
|||
-- using negative numbers (index from the end of the string) |
|||
local i = decimalPosition - mw.ustring.len(a_number) + DIGIT_GROUPING_SIZE; |
|||
while (i <= -1) do |
|||
-- group the fractional part |
|||
a_number = mw.ustring.sub(a_number, 1, i - 1) .. a_groupMark .. mw.ustring.sub(a_number, i); |
|||
i = i + DIGIT_GROUPING_SIZE; |
|||
end |
end |
||
end |
|||
return a_number; |
|||
end -- formatNumber() |
|||
-- Append the base-10 exponent |
|||
if exponent ~= '' then |
|||
number = number .. 'E' .. exponent |
|||
--[[ |
|||
Formatting numbers. |
|||
@param source : String representation |
|||
of an unformatted (but maybe rounded) floating point or integer number. |
|||
@param spec : Formatting option. Currently there are |
|||
"at", "comma", "de", "dewiki", "de_currency", "ch", "ch_currency", "en", "iso31_0", |
|||
"iso31_0_point" and "pc" supported. See the FORMAT_TABLE for details. |
|||
@return String of the formatted number. |
|||
If the argument 'spec' is invalid |
|||
or 'source' is not a valid string representation of a number, |
|||
'source' is returned unmodified. |
|||
]] |
|||
function FormatNum.format(source, spec) |
|||
local number; |
|||
if type(source) == "string" then |
|||
number = mw.text.trim(source); |
|||
end |
|||
if not spec or spec == "" then |
|||
spec = "dewiki" |
|||
end |
|||
if (number and spec) then |
|||
local format = FORMAT_TABLE[spec]; |
|||
if (format) then |
|||
-- format entry found |
|||
local sign = mw.ustring.sub(number, 1, 1); |
|||
if ((sign == "+") or (sign == "-")) then |
|||
-- remove sign from number, add it later again |
|||
number = mw.ustring.sub(number, 2); |
|||
else |
|||
-- was not a sign |
|||
sign = ""; |
|||
end |
|||
if (mw.ustring.sub(number, 1, 1) == ".") then |
|||
-- number begins with "." -> add a 0 to the beginning |
|||
number = "0" .. number; |
|||
else |
|||
if (mw.ustring.sub(number, -1) == ".") then |
|||
-- number ends with "." -> remove it |
|||
number = mw.ustring.sub(number, 1, -2); |
|||
end |
|||
end |
|||
if ((number == mw.ustring.match(number, "^%d+$")) or (number == mw.ustring.match(number, "^%d+%.%d+$"))) then |
|||
-- number has valid format (only digits or digits.digits) -> format it and add sign (if any) again |
|||
number = sign .. formatNumber(number, format.decimalMark, format.groupMark, format.groupMinLength, format.groupOnlyIntegerPart); |
|||
else |
|||
-- number has no valid format -> undo all modifications |
|||
number = source; |
|||
end |
|||
end |
end |
||
end |
end |
||
return number; |
|||
end -- FormatNum.format() |
|||
-- Special cases for substitution of ASCII digits (missing support in Lua core libraries for some languages) |
|||
if digit[lang] then |
|||
for i, v in ipairs(digit[lang]) do |
|||
--[[ |
|||
number = mw.ustring.gsub(number, tostring(i - 1), v) |
|||
Rounding numbers. |
|||
end |
|||
@param number : string with unformatted floating point or integer number. |
|||
@param precision : number of significant fractional part digits. |
|||
If precision is negative, the integer part is rounded as well. |
|||
@param method : number defining the rounding method to use. |
|||
Currently are supported only |
|||
0 for 'IEEE 754' rounding and |
|||
1 for 'round half away from zero'. |
|||
If another number is supplied, the result is undefined. |
|||
@return String of the rounded number as returned by Lua function string.format(). |
|||
If one of the arguments is not a number, 'number' is returned unmodified. |
|||
]] |
|||
function FormatNum.round(source, precision, method) |
|||
local number = tonumber(source); |
|||
if (number and precision and method) then |
|||
return numberToString(number, math.floor(precision), math.floor(method)); |
|||
end |
end |
||
return source; |
|||
end -- FormatNum.round() |
|||
return number |
|||
local p = { }; |
|||
function p.format(frame) |
|||
-- @param 1 : unformatted (but maybe rounded) floating point or integer number. |
|||
-- @param number : same as 1 (DEPRECATED, backward compatibility). |
|||
-- @param format : Formatting option. Currently there are |
|||
-- "at", "comma", "de", "dewiki", "de_currency", "ch", "ch_currency", |
|||
-- "en", "iso31_0", "iso31_0_point" and "pc" supported. |
|||
local source = frame.args[1]; |
|||
if (not source) then |
|||
source = frame.args.number; -- DEPRECATED |
|||
pcall( require, "Module:FormatNumDEPRECATED" ) |
|||
end |
|||
return FormatNum.format(source, frame.args.format) or ""; |
|||
end -- .format() |
|||
function p.round(frame) |
|||
-- @param 1 : string with unformatted floating point or integer number. |
|||
-- @param number : same as 1 (DEPRECATED, backward compatibility). |
|||
-- @param precision : number of significant fractional part digits. |
|||
-- If precision is negative, the integer part is rounded as well. |
|||
-- @param method : number defining the rounding method to be used. |
|||
-- Currently are supported only |
|||
-- 0 for 'IEEE 754' rounding and |
|||
-- 1 for 'round half away from zero'. |
|||
-- If another number is supplied, the result is undefined. |
|||
local precision = tonumber(frame.args.precision); |
|||
local method = tonumber(frame.args.method); |
|||
local source = frame.args[1]; |
|||
if (not source) then |
|||
source = frame.args.number; -- DEPRECATED |
|||
pcall( require, "Module:FormatNumDEPRECATED" ) |
|||
end |
|||
if (source and precision) then |
|||
return FormatNum.round(source, precision, method); |
|||
end |
|||
return source or ""; |
|||
end -- .round() |
|||
-- Export access for Lua modules |
|||
function p.FormatNum() |
|||
return FormatNum; |
|||
end -- .FormatNum() |
|||
-- DEPRECATED |
|||
function p.formatNumber(frame) |
|||
pcall( require, "Module:FormatNumDEPRECATED" ) |
|||
return p.format(frame); |
|||
end |
|||
function p.numberToString(frame) |
|||
pcall( require, "Module:FormatNumDEPRECATED" ) |
|||
return p.round(frame); |
|||
end |
end |
||
return p |
return p |
Guhartoya 20:24, 20 kanûna paşîn 2022
Belgekirina modulê[biafirîne]
-- This module is intended to replace the functionality of Template:Formatnum and related templates.
local p = {}
function p.main(frame)
local args = frame:getParent().args
local prec = args.prec or ''
local sep = args.sep or ''
local number = args[1] or args.number or ''
local lang = args[2] or args.lang or ''
-- validate the language parameter within MediaWiki's caller frame
if lang == "arabic-indic" then -- only for back-compatibility ("arabic-indic" is not a SupportedLanguage)
lang = "fa" -- better support than "ks"
elseif lang == '' or not mw.language.isSupportedLanguage(lang) then
-- Note that 'SupportedLanguages' are not necessarily 'BuiltinValidCodes', and so they are not necessarily
-- 'KnownLanguages' (with a language name defined at least in the default localisation of the local wiki).
-- But they all are ValidLanguageCodes (suitable as Wiki subpages or identifiers: no slash, colon, HTML tags, or entities)
-- In addition, they do not contain any capital letter in order to be unique in page titles (restriction inexistant in BCP47),
-- but they may violate the standard format of BCP47 language tags for specific needs in MediaWiki.
-- Empty/unspecified and unsupported languages are treated here in Commons using the user's language,
-- instead of the local 'ContentLanguage' of the Wiki.
lang = frame:callParserFunction( "int", "lang" ) -- get user's chosen language
end
return p.formatNum(number, lang, prec, sep ~= '')
end
local digit = { -- substitution of decimal digits for languages not supported by mw.language:formatNum() in core Lua libraries for MediaWiki
["ml-old"] = { '൦', '൧', '൨', '൩', '൪', '൫', '൬', '൭', '൮', '൯' },
["mn"] = { '᠐', '᠑', '᠒', '᠓', '᠔', '᠕', '᠖', '᠗', '᠘', '᠙'},
["ta"] = { '௦', '௧', '௨', '௩', '௪', '௫', '௬', '௭', '௮', '௯'},
["te"] = { '౦', '౧', '౨', '౩', '౪', '౫', '౬', '౭', '౮', '౯'},
["th"] = { '๐', '๑', '๒', '๓', '๔', '๕', '๖', '๗', '๘', '๙'}
}
function p.formatNum(number, lang, prec, compact)
-- Do not alter the specified value when it is not a valid number, return it as is
local value = tonumber(number)
if value == nil then
return number
end
-- Basic ASCII-only formatting (without paddings)
number = tostring(value)
-- Check the presence of an exponent (incorrectly managed in mw.language:FormatNum() and even forgotten due to an internal bug, e.g. in Hindi)
local exponent
local pos = string.find(number, '[Ee]')
if pos ~= nil then
exponent = string.sub(number, pos + 1, string.len(number))
number = string.sub(number, 1, pos - 1)
else
exponent = ''
end
-- Check the minimum precision requested
prec = tonumber(prec) -- nil if not specified as a true number
if prec ~= nil then
prec = math.floor(prec)
if prec < 0 then
prec = nil -- discard an incorrect precision (not a positive integer)
elseif prec > 14 then
prec = 14 -- maximum precision supported by tostring(number)
end
end
-- Preprocess the minimum precision in the ASCII string
local dot
if (prec or 0) > 0 then
pos = string.find(number, '.', 1, true) -- plain search, no regexp
if pos ~= nil then
prec = pos + prec - string.len(number) -- effective number of trailing decimals to add or remove
dot = '' -- already present
else
dot = '.' -- must be added
end
else
dot = '' -- don't add dot
prec = 0 -- don't alter the precision
end
if lang ~= nil and mw.language.isKnownLanguageTag(lang) == true then
-- Convert number to localized digits, decimal separator, and group separators
local language = mw.getLanguage(lang)
if compact then
number = language:formatNum(tonumber(number), { noCommafy = 'y' }) -- caveat: can load localized resources for up to 20 languages
else
number = language:formatNum(tonumber(number)) -- caveat: can load localized resources for up to 20 languages
end
-- Postprocessing the precision
if prec > 0 then
local zero = language:formatNum(0)
number = number .. dot .. mw.ustring.rep(zero, prec)
elseif prec < 0 then
-- TODO: rounding of last decimal; here only truncate decimals in excess
number = mw.ustring.sub(number, 1, mw.ustring.len(number) + prec)
end
-- Append the localized base-10 exponent without grouping separators (there's no reliable way to detect a localized leading symbol 'E')
if exponent ~= '' then
number = number .. 'E' .. language:formatNum(tonumber(exponent),{noCommafy=true})
end
else -- not localized, ASCII only
-- Postprocessing the precision
if prec > 0 then
number = number .. dot .. mw.string.rep('0', prec)
elseif prec < 0 then
-- TODO: rounding of last decimal; here only truncate decimals in excess
number = mw.string.sub(number, 1, mw.string.len(number) + prec)
end
-- Append the base-10 exponent
if exponent ~= '' then
number = number .. 'E' .. exponent
end
end
-- Special cases for substitution of ASCII digits (missing support in Lua core libraries for some languages)
if digit[lang] then
for i, v in ipairs(digit[lang]) do
number = mw.ustring.gsub(number, tostring(i - 1), v)
end
end
return number
end
return p