Module:Formatnum/sandbox: Difference between revisions
Appearance
Content deleted Content added
Simplify |
Wordify moved to Module:Wordify |
||
Line 5: | Line 5: | ||
Helper functions used to avoid redundant code. |
Helper functions used to avoid redundant code. |
||
]] |
]] |
||
local function err(msg) |
|||
-- Generates wikitext error messages. |
|||
return mw.ustring.format('<strong class="error">Formatting error: %s</strong>', msg) |
|||
end |
|||
local function getArgs(frame) |
local function getArgs(frame) |
||
Line 21: | Line 16: | ||
return args |
return args |
||
end |
end |
||
local function _round(value, precision) |
|||
local rescale = math.pow(10, precision or 0); |
|||
return math.floor(value * rescale + 0.5) / rescale; |
|||
end |
|||
--[[ |
|||
------------------------------------------------------------------------------------ |
|||
-- isPositiveInteger |
|||
-- |
|||
-- This function returns true if the given value is a positive integer, and false |
|||
-- if not. Although it doesn't operate on tables, it is included here as it is |
|||
-- useful for determining whether a given table key is in the array part or the |
|||
-- hash part of a table. |
|||
------------------------------------------------------------------------------------ |
|||
--]] |
|||
function isPositiveInteger(v) |
|||
return type(v) == 'number' and v >= 1 and math.floor(v) == v and v < math.huge |
|||
end |
|||
--[[ |
|||
------------------------------------------------------------------------------------ |
|||
-- reverseNumKeys |
|||
-- |
|||
-- This takes a table and returns an array containing the numbers of any numerical |
|||
-- keys that have non-nil values, sorted in reverse numerical order. |
|||
------------------------------------------------------------------------------------ |
|||
--]] |
|||
local function reverseNumKeys(t) |
|||
local nums = {} |
|||
for k, v in pairs(t) do |
|||
if isPositiveInteger(k) then |
|||
nums[#nums + 1] = k |
|||
end |
|||
end |
|||
table.sort(nums, function(a, b) return a > b end) |
|||
return nums |
|||
end |
|||
--[[ |
|||
------------------------------------------------------------------------------------ |
|||
-- reverseSparseIpairs |
|||
-- |
|||
-- This is a reverse iterator for sparse arrays. It can be used like a resersed ipairs, but can |
|||
-- handle nil values. |
|||
------------------------------------------------------------------------------------ |
|||
--]] |
|||
local function reverseSparseIpairs(t) |
|||
local nums = reverseNumKeys(t) |
|||
local i = 0 |
|||
local lim = #nums |
|||
return function () |
|||
i = i + 1 |
|||
if i <= lim then |
|||
local key = nums[i] |
|||
return key, t[key] |
|||
else |
|||
return nil, nil |
|||
end |
|||
end |
|||
end |
|||
function p.main(frame) |
function p.main(frame) |
||
Line 207: | Line 139: | ||
return number |
return number |
||
end |
|||
--[[ |
|||
wordify |
|||
Usage: |
|||
{{#invoke:Formatnum | wordify | x | y | round= | precision= }} |
|||
--]] |
|||
local usa = { |
|||
[6] = 'million', |
|||
[9] = 'billion', |
|||
[12] = 'trillion', |
|||
[15] = 'quadrillion', |
|||
[18] = 'quintillion', |
|||
[21] = 'sextillion', |
|||
[24] = 'septillion', |
|||
[27] = 'octillion', |
|||
[30] = 'nonillion', |
|||
[33] = 'decillion', |
|||
[36] = 'undecillion', |
|||
[39] = 'duodecillion', |
|||
[42] = 'tredecillion', |
|||
[45] = 'quattuordecillion', |
|||
[48] = 'quindecillion', |
|||
[51] = 'sexdecillion', |
|||
[54] = 'septendecillion', |
|||
[57] = 'octodecillion', |
|||
[60] = 'novemdecillion', |
|||
[63] = 'vigintillion', |
|||
[100] = 'googol', |
|||
[303] = 'centillion' |
|||
} |
|||
local ind = { |
|||
[5] = 'lakh', |
|||
[7] = 'crore', |
|||
[12] = 'lakh crore', |
|||
[14] = 'crore crore' |
|||
} |
|||
local buildOne |
|||
local unit |
|||
local declension = { |
|||
['ca'] = { '', function (u) |
|||
local n = mw.ustring.len(u) |
|||
if mw.ustring.sub(u, n, n) == "d" then return u .. "s" else return mw.ustring.sub(u, 1, -2) .. "ons" end |
|||
end }, |
|||
['da'] = { '', 'er' }, |
|||
['de'] = { '', function (u) |
|||
local n = mw.ustring.len(u) |
|||
if mw.ustring.sub(u, n, n) == "e" then return u .. "n" else return u .. "en" end |
|||
end }, |
|||
['eo'] = { '', 'j' }, |
|||
['es'] = { function (arr, i, lang) |
|||
if i < 15 then |
|||
return buildOne(arr, i, lang) |
|||
end |
|||
local j = math.floor(i / 6) |
|||
if (j*6)+3 == i then |
|||
return "mil " .. unit(arr, i-3, lang, 1000), true |
|||
else |
|||
return buildOne(arr, i, lang) |
|||
end |
|||
end, |
|||
function (u) |
|||
if mw.ustring.sub(u, 1, 4) == "mil " then |
|||
return u |
|||
else |
|||
local n = mw.ustring.len(u) |
|||
if mw.ustring.sub(u, n, n) == "o" then return u .. "s" else return mw.ustring.sub(u, 1, -3) .. "ones" end |
|||
end |
|||
end }, |
|||
['fr'] = { '', 's' }, |
|||
['it'] = { '', function (u) |
|||
return mw.ustring.sub(u, 1, -2) .. "i" |
|||
end }, |
|||
['la'] = { '', function (u) |
|||
local n = mw.ustring.len(u) |
|||
if mw.ustring.sub(u, n, n) == "o" then return u .. "nibus" else return mw.ustring.sub(u, 1, -3) .. "a" end |
|||
end }, |
|||
['no'] = { '', 'er' }, |
|||
['pl'] = { '', 'y', [5] = 'ów', ["fraction"] = 'a' }, |
|||
['pt'] = { function (arr, i, lang) |
|||
if i < 9 then |
|||
return buildOne(arr, i, lang) |
|||
end |
|||
local j = math.floor(i / 6) |
|||
if (j*6)+3 == i then |
|||
return "mil " .. unit(arr, i-3, lang, 1000), true |
|||
else |
|||
return buildOne(arr, i, lang) |
|||
end |
|||
end, |
|||
function (u) |
|||
if mw.ustring.sub(u, 1, 4) == "mil " then |
|||
return u |
|||
else |
|||
return mw.ustring.sub(u, 1, -3) .. "ões" |
|||
end |
|||
end }, |
|||
['sk'] = { '', function (u) |
|||
local n = mw.ustring.len(u) |
|||
if mw.ustring.sub(u, n, n) == "n" then return u .. 'y' else return mw.ustring.sub(u, 1, -2) .. "y" end |
|||
end, |
|||
[5] = function (u) |
|||
local n = mw.ustring.len(u) |
|||
if mw.ustring.sub(u, n, n) == "n" then return u .. 'ov' else return mw.ustring.sub(u, 1, -5) .. "árd" end |
|||
end, |
|||
["fraction"] = function (u) |
|||
local n = mw.ustring.len(u) |
|||
if mw.ustring.sub(u, n, n) == "n" then return u .. 'a' else return mw.ustring.sub(u, 1, -5) .. "árd" end |
|||
end |
|||
}, |
|||
['sl'] = { '', function (u) |
|||
local n = mw.ustring.len(u) |
|||
if mw.ustring.sub(u, n, n) == "a" then return mw.ustring.sub(u, 1, -2) .. 'i' else return u .. "a" end |
|||
end, |
|||
function (u) |
|||
local n = mw.ustring.len(u) |
|||
if mw.ustring.sub(u, n, n) == "a" then return mw.ustring.sub(u, 1, -2) .. 'e' else return u .. "e" end |
|||
end, |
|||
[5] = function (u) |
|||
local n = mw.ustring.len(u) |
|||
if mw.ustring.sub(u, n, n) == "a" then return mw.ustring.sub(u, 1, -2) else return u .. "ov" end |
|||
end, |
|||
["fraction"] = function (u) |
|||
local n = mw.ustring.len(u) |
|||
if mw.ustring.sub(u, n, n) == "a" then return mw.ustring.sub(u, 1, -2) .. 'e' else return u .. "a" end |
|||
end |
|||
}, |
|||
['sv'] = { '', 'er' } |
|||
} |
|||
local latin = { |
|||
['language_top'] = { |
|||
[''] = 306, |
|||
['ca'] = 60, |
|||
['da'] = 60, |
|||
['de'] = 60, |
|||
['en'] = 306, |
|||
['eo'] = 117, |
|||
['es'] = 120, |
|||
['fr'] = 123, |
|||
['it'] = 60, |
|||
['la'] = 306, |
|||
['nl'] = 60, |
|||
['no'] = 60, |
|||
['pl'] = 60, |
|||
['pt'] = 60, |
|||
['sk'] = 60, |
|||
['sl'] = 60, |
|||
['sv'] = 60 |
|||
}, |
|||
['exception'] = { |
|||
[6] = { |
|||
['pt'] = { 'milhão' } |
|||
}, |
|||
[36] = { |
|||
['it'] = 'none' |
|||
}, |
|||
[39] = { |
|||
['it'] = 'none' |
|||
}, |
|||
[42] = { |
|||
['it'] = 'none' |
|||
}, |
|||
[45] = { |
|||
['it'] = 'none' |
|||
}, |
|||
[48] = { |
|||
['it'] = 'none' |
|||
}, |
|||
[51] = { |
|||
['it'] = 'none', |
|||
}, |
|||
[54] = { |
|||
['it'] = 'none' |
|||
}, |
|||
[57] = { |
|||
['it'] = 'none' |
|||
} |
|||
}, |
|||
['base'] = { |
|||
[''] = { 'lli' }, |
|||
['ca'] = { 'li' }, |
|||
['da'] = { 'lli' }, |
|||
['de'] = { 'lli' }, |
|||
['en'] = { 'lli' }, |
|||
['eo'] = { 'li' }, |
|||
['es'] = { 'll' }, |
|||
['fr'] = { 'lli' }, |
|||
['it'] = { 'li' }, |
|||
['la'] = { 'lli' }, |
|||
['nl'] = { 'lj' }, |
|||
['no'] = { 'lli' }, |
|||
['pl'] = { 'li' }, |
|||
['pt'] = { 'li' }, |
|||
['sk'] = { 'li' }, |
|||
['sl'] = { 'lij' }, |
|||
['sv'] = { 'lj' } |
|||
}, |
|||
['suffix'] = { |
|||
[''] = { 'on', 'ard' }, |
|||
['ca'] = { 'ó', 'ard' }, |
|||
['da'] = { 'on', 'ard' }, |
|||
['de'] = { 'on', 'arde' }, |
|||
['en'] = { 'on', 'ard' }, |
|||
['eo'] = { 'ono', 'ardo' }, |
|||
['es'] = { 'ón', 'ardo' }, |
|||
['fr'] = { 'on', 'ard' }, |
|||
['it'] = { 'one', 'ardo' }, |
|||
['la'] = { 'o', 'ardum' }, |
|||
['nl'] = { 'oen', 'ard' }, |
|||
['no'] = { 'on', 'ard' }, |
|||
['pl'] = { 'on', 'ard' }, |
|||
['pt'] = { 'ão' }, |
|||
['sk'] = { 'ón', 'arda' }, |
|||
['sl'] = { 'on', 'arda' }, |
|||
['sv'] = { 'on', 'ard' } |
|||
}, |
|||
['capitalize'] = { |
|||
['de'] = true |
|||
}, |
|||
['prefix'] = { 'mi', 'bi', 'tri', 'quadri', 'quinti', 'sexti', 'septi', 'octi', 'noni', 'deci', |
|||
[16] = 'sedeci', |
|||
[20] = 'viginti', |
|||
[30] = 'triginti', |
|||
[40] = 'quadraginti', |
|||
[50] = 'quinquaginti', |
|||
[60] = 'sexaginti', |
|||
[70] = 'septuaginti', |
|||
[80] = 'octoginti', |
|||
[90] = 'nonaginti', |
|||
[100] = 'centi' |
|||
}, |
|||
['unit_prefix'] = { 'un', 'duo', 'tre', 'quattuor', 'quin', 'sex', 'septen', 'octo', 'novem' }, |
|||
['prefix_exception'] = { |
|||
[1] = {}, |
|||
[2] = { |
|||
['eo'] = { 'dui' } |
|||
}, |
|||
[3] = { |
|||
['pl'] = { 'try' } |
|||
}, |
|||
[4] = { |
|||
['da'] = { 'kvadri' }, |
|||
['eo'] = { 'kvari' }, |
|||
['es'] = { 'cuatri' }, |
|||
['no'] = { 'kvadri' }, |
|||
['pl'] = { 'kwadry' }, |
|||
['sk'] = { 'kvadri' }, |
|||
['sl'] = { 'kvadri' }, |
|||
['sv'] = { 'kvadri' } |
|||
}, |
|||
[5] = { |
|||
['da'] = { 'kvinti' }, |
|||
['eo'] = { 'kvini' }, |
|||
['no'] = { 'kvinti' }, |
|||
['pl'] = { 'kwinty' }, |
|||
['sk'] = { 'kvinti' }, |
|||
['sl'] = { 'kvinti' }, |
|||
['sv'] = { 'kvinti' } |
|||
}, |
|||
[6] = { |
|||
['da'] = { 'seksti' }, |
|||
['eo'] = { 'sesi' }, |
|||
['no'] = { 'seksti' }, |
|||
['pl'] = { 'seksty' }, |
|||
['sl'] = { 'seksti' } |
|||
}, |
|||
[7] = { |
|||
['eo'] = { 'sepi' }, |
|||
['pl'] = { 'septy' }, |
|||
}, |
|||
[8] = { |
|||
['da'] = { 'okti' }, |
|||
['de'] = { 'Okti' }, |
|||
['eo'] = { 'oki' }, |
|||
['no'] = { 'okti' }, |
|||
['pl'] = { 'okty' }, |
|||
['sk'] = { 'okti' }, |
|||
['sl'] = { 'okti' }, |
|||
['sv'] = { 'okti' } |
|||
}, |
|||
[9] = { |
|||
['eo'] = { 'naŭi' } |
|||
}, |
|||
[10] = { |
|||
['de'] = { 'Dezi' }, |
|||
['eo'] = { 'deki' }, |
|||
['fr'] = { 'déci' }, |
|||
['no'] = { 'desi' }, |
|||
['pl'] = { 'decy' } |
|||
}, |
|||
[11] = { |
|||
}, |
|||
[12] = { |
|||
}, |
|||
[13] = { |
|||
}, |
|||
[14] = { |
|||
}, |
|||
[15] = { |
|||
}, |
|||
[16] = { |
|||
}, |
|||
[17] = { |
|||
}, |
|||
[18] = { |
|||
}, |
|||
[19] = { |
|||
}, |
|||
[20] = { |
|||
} |
|||
}, |
|||
['unit_prefix_exception'] = { |
|||
[1] = {}, |
|||
[2] = { |
|||
}, |
|||
[3] = { |
|||
['fr'] = { 'tré' } |
|||
}, |
|||
[4] = { |
|||
['eo'] = { 'kvatuor' }, |
|||
['es'] = { 'cuatro' } |
|||
}, |
|||
[5] = { |
|||
['eo'] = { 'kvin' } |
|||
}, |
|||
[6] = { |
|||
['eo'] = { 'seks' } |
|||
}, |
|||
[7] = { |
|||
}, |
|||
[8] = { |
|||
['eo'] = { 'okto' } |
|||
}, |
|||
[9] = { |
|||
['es'] = { 'noven' }, |
|||
['fr'] = { 'noni' } |
|||
} |
|||
} |
|||
} |
|||
local function makeprefix(arr, j, lang) |
|||
local entry = arr['prefix_exception'][j][(lang or "")] |
|||
local p |
|||
if entry then |
|||
p = entry[1] |
|||
else |
|||
p = arr['prefix'][j] |
|||
if not p then |
|||
if j < 11 then |
|||
return nil |
|||
elseif p < 100 then |
|||
local k = math.floor(j / 10) |
|||
local d = k * 10 |
|||
local l = j - d |
|||
local unitentry = arr['unit_prefix_exception'][l][(lang or "")] |
|||
local u |
|||
if unitentry then |
|||
u = unitentry[1] |
|||
else |
|||
u = arr['unit_prefix'][l] |
|||
end |
|||
local decentry = arr['prefix_exception'][d][(lang or "")] |
|||
local x |
|||
if decentry then |
|||
x = deceentry[1] |
|||
else |
|||
x = arr['prefix'][d] |
|||
end |
|||
p = u .. x |
|||
else |
|||
return nil |
|||
end |
|||
end |
|||
end |
|||
if arr['capitalize'][(lang or "")] then |
|||
return mw.ustring.upper(mw.ustring.sub(p, 1, 1)) .. mw.ustring.sub(p, 2) |
|||
else |
|||
return p |
|||
end |
|||
end |
|||
buildOne = function(arr, i, lang) |
|||
local j = math.floor(i / 6) |
|||
if j < 1 then |
|||
return nil |
|||
else |
|||
local pref = makeprefix(arr, j, lang) |
|||
local suf = (j * 6) == i and arr['suffix'][(lang or "")][1] or arr['suffix'][(lang or "")][2] |
|||
return pref .. arr['base'][(lang or "")][1] .. suf |
|||
end |
|||
end |
|||
local function makeOne(arr, i, lang) |
|||
local top = arr['exception'][i] |
|||
local entry = top and top[(lang or "")] |
|||
local one = nil |
|||
local simplify = nil |
|||
if entry then |
|||
one, simplify = entry[1], entry['simplify'] |
|||
end |
|||
if one then |
|||
return one, simplify |
|||
else |
|||
if declension[lang] and declension[lang][1] then |
|||
local d = declension[lang][1] |
|||
if type(d) == 'function' then |
|||
return d(arr, i, lang) |
|||
end |
|||
end |
|||
return buildOne(arr, i, lang) |
|||
end |
|||
end |
|||
unit = function(arr, i, lang, r) |
|||
local one, simplify = makeOne(arr, i, lang) |
|||
local top = arr['exception'][i] |
|||
local entry = top and top[(lang or "")] |
|||
if r == 1 then |
|||
return one, simplify |
|||
elseif r == 2 then |
|||
if entry and entry[2] then |
|||
return entry and entry[2] |
|||
elseif declension[lang] and declension[lang][2] then |
|||
local d = declension[lang][2] |
|||
if type(d) == 'function' then |
|||
return d(one) |
|||
else |
|||
return one .. d |
|||
end |
|||
else |
|||
return one |
|||
end |
|||
elseif r == 3 or r == 4 then |
|||
if entry and entry[3] then |
|||
return entry and entry[3] |
|||
elseif declension[lang] and declension[lang][3] then |
|||
local d = declension[lang][3] |
|||
if type(d) == 'function' then |
|||
return d(one) |
|||
else |
|||
return one .. d |
|||
end |
|||
elseif entry and entry[2] then |
|||
return entry and entry[2] |
|||
elseif declension[lang] and declension[lang][2] then |
|||
local d = declension[lang][2] |
|||
if type(d) == 'function' then |
|||
return d(one) |
|||
else |
|||
return one .. d |
|||
end |
|||
else |
|||
return one |
|||
end |
|||
elseif r == _round(r, 0) then |
|||
if entry and entry[5] then |
|||
return entry and entry[5] |
|||
elseif declension[lang] and declension[lang][5] then |
|||
local d = declension[lang][5] |
|||
if type(d) == 'function' then |
|||
return d(one) |
|||
else |
|||
return one .. d |
|||
end |
|||
elseif entry and entry[3] then |
|||
return entry and entry[3] |
|||
elseif declension[lang] and declension[lang][3] then |
|||
local d = declension[lang][3] |
|||
if type(d) == 'function' then |
|||
return d(one) |
|||
else |
|||
return one .. d |
|||
end |
|||
elseif entry and entry[2] then |
|||
return entry and entry[2] |
|||
elseif declension[lang] and declension[lang][2] then |
|||
local d = declension[lang][2] |
|||
if type(d) == 'function' then |
|||
return d(one) |
|||
else |
|||
return one .. d |
|||
end |
|||
else |
|||
return one |
|||
end |
|||
else |
|||
if entry and entry["fraction"] then |
|||
return entry and entry["fraction"] |
|||
elseif declension[lang] and declension[lang]["fraction"] then |
|||
local d = declension[lang]["fraction"] |
|||
if type(d) == 'function' then |
|||
return d(one) |
|||
else |
|||
return one .. d |
|||
end |
|||
elseif entry and entry[5] then |
|||
return entry and entry[5] |
|||
elseif declension[lang] and declension[lang][5] then |
|||
local d = declension[lang][5] |
|||
if type(d) == 'function' then |
|||
return d(one) |
|||
else |
|||
return one .. d |
|||
end |
|||
elseif entry and entry[3] then |
|||
return entry and entry[3] |
|||
elseif declension[lang] and declension[lang][3] then |
|||
local d = declension[lang][3] |
|||
if type(d) == 'function' then |
|||
return d(one) |
|||
else |
|||
return one .. d |
|||
end |
|||
elseif entry and entry[2] then |
|||
return (r < 2 and one or entry and entry[2]) |
|||
elseif declension[lang] and declension[lang][2] then |
|||
local d = declension[lang][2] |
|||
if type(d) == 'function' then |
|||
return (r < 2 and one or d(one)) |
|||
else |
|||
return (r < 2 and one or (one .. d)) |
|||
end |
|||
else |
|||
return one |
|||
end |
|||
end |
|||
end |
|||
local function linkend(u) |
|||
return "|" .. u .. "]]" |
|||
end |
|||
local function unitlink(lk, i, u, lang) |
|||
if lk then |
|||
if lang == "ca" then |
|||
return "[[ca:" .. "Escales curta i llarga" .. linkend(u) |
|||
elseif lang == "da" then |
|||
return "[[da:" .. "Store tal" .. linkend(u) |
|||
elseif lang == "de" then |
|||
return "[[de:" .. "Zahlennamen" .. linkend(u) |
|||
elseif lang == "eo" then |
|||
return "[[eo:" .. "Vortoj por grandegaj nombroj" .. linkend(u) |
|||
elseif lang == "fr" then |
|||
return "[[fr:" .. (i > 36 and "Noms des grands nombres" or ("Ordres de grandeur de nombres#10" .. i)) .. linkend(u) |
|||
elseif lang == "it" then |
|||
return "[[it:" .. ("Ordini di grandezza (numeri)#10" .. i) .. linkend(u) |
|||
elseif lang == "la" then |
|||
return "[[la:" .. "Nomina permagnorum numerorum" .. linkend(u) |
|||
elseif lang == "nl" then |
|||
return "[[nl:" .. "Lijst van machten van tien" .. linkend(u) |
|||
elseif lang == "no" then |
|||
return "[[no:" .. "Navn på store tall" .. linkend(u) |
|||
elseif lang == "pl" then |
|||
return "[[pl:" .. "Liczebniki główne potęg tysiąca" .. linkend(u) |
|||
elseif lang == "sk" then |
|||
return "[[sk:" .. "Veľké čísla" .. linkend(u) |
|||
elseif lang == "sl" then |
|||
return "[[sl:" .. "Imena velikih števil" .. linkend(u) |
|||
elseif lang == "sv" then |
|||
return "[[sv:" .. "Namn på stora tal" .. linkend(u) |
|||
else |
|||
return "[[en:" .. (i > 39 and "Names of large numbers" or ("Orders of magnitude (numbers)#10" .. i)) .. linkend(u) |
|||
end |
|||
else |
|||
return u |
|||
end |
|||
end |
|||
function p.wordify(frame) |
|||
local args = getArgs(frame) |
|||
local x = args[1] |
|||
local numsys = args.numsys |
|||
local prec = args.prec |
|||
local lk = args.lk |
|||
local lang = args.lang |
|||
local simplify = args.simplify |
|||
return p._wordify(x, numsys, prec, (lk == "on" and true), lang, (simplify == "yes" and true)) |
|||
end |
|||
function p._wordify(x, numsys, prec, lk, lang, simplify) |
|||
if tonumber(x) then |
|||
if numsys == nil or numsys == "" or numsys:lower() == "usa" then |
|||
for i, v in reverseSparseIpairs(usa) do |
|||
local y = x / math.pow(10,i) |
|||
local r = _round(y, prec) |
|||
if r >= 1 then |
|||
if r == 1 and simplify then |
|||
return unitlink(lk, i, v, (lang or "en")) |
|||
else |
|||
return p.formatNum(r, (lang or "en"), prec) .. " " .. unitlink(lk, i, v, (lang or "en")) |
|||
end |
|||
end |
|||
end |
|||
return p.formatNum(_round(x, prec), (lang or "en"), prec) |
|||
elseif numsys:lower() == "fra" then |
|||
if latin['language_top'][(lang or "")] then |
|||
for i = latin['language_top'][(lang or "")], 6, -3 do |
|||
local entry = latin['exception'][i] |
|||
if not entry or entry[(lang or "")] ~= "none" then |
|||
local y = x / math.pow(10,i) |
|||
local r = _round(y, prec) |
|||
if r >= 1 then |
|||
local u, simp = unit(latin, i, lang, r) |
|||
if r == 1 and (simplify or simp) then |
|||
return unitlink(lk, i, u, (lang or "")) |
|||
else |
|||
return p.formatNum(r, (lang or "en"), prec) .. " " .. unitlink(lk, i, u, (lang or "en")) |
|||
end |
|||
end |
|||
end |
|||
end |
|||
end |
|||
return p.formatNum(_round(x, prec), (lang or "en"), prec) |
|||
elseif numsys:lower() == "ind" then |
|||
local y = x / 1E14 |
|||
local r = _round(y, prec) |
|||
if r >= 1 then |
|||
if r == 1 and simplify then |
|||
return (lk and "[[crore]] crore" or ind[14]) |
|||
else |
|||
return p.formatNum(r, (lang or "en"), prec) .. " " .. (lk and "[[crore]] crore" or ind[14]) |
|||
end |
|||
else |
|||
local y = x / 1E12 |
|||
local r = _round(y, prec) |
|||
if r >= 1 then |
|||
if r == 1 and simplify then |
|||
return (lk and "[[lakh]] [[crore]]" or ind[12]) |
|||
else |
|||
return p.formatNum(r, (lang or "en"), prec) .. " " .. (lk and "[[lakh]] [[crore]]" or ind[12]) |
|||
end |
|||
else |
|||
local y = x / 1E7 |
|||
local r = _round(y, prec) |
|||
if r >= 1 then |
|||
local v = ind[7] |
|||
if r == 1 and simplify then |
|||
return (lk and "[[" .. v .. "]]" or v) |
|||
else |
|||
return p.formatNum(r, (lang or "en"), prec) .. " " .. (lk and "[[" .. v .. "]]" or v) |
|||
end |
|||
else |
|||
local y = x / 1E5 |
|||
local r = _round(y, prec) |
|||
if r >= 1 then |
|||
local v = ind[5] |
|||
if r == 1 and simplify then |
|||
return (lk and "[[" .. v .. "]]" or v) |
|||
else |
|||
return p.formatNum(r, (lang or "en"), prec) .. " " .. (lk and "[[" .. v .. "]]" or v) |
|||
end |
|||
else |
|||
return p.formatNum(_round(x, prec), (lang or "en"), prec) |
|||
end |
|||
end |
|||
end |
|||
end |
|||
else |
|||
return err("number system not supported") |
|||
end |
|||
else |
|||
return err("Not a number: " .. x) |
|||
end |
|||
end |
|||
--[[ |
|||
Helper function that interprets the input numerically. If the |
|||
input does not appear to be a number, attempts evaluating it as |
|||
a parser functions expression. |
|||
]] |
|||
function p._cleanNumber(number_string) |
|||
if type(number_string) == 'number' then |
|||
-- We were passed a number, so we don't need to do any processing. |
|||
return number_string, tostring(number_string) |
|||
elseif type(number_string) ~= 'string' or not number_string:find('%S') then |
|||
-- We were passed a non-string or a blank string, so exit. |
|||
return nil, nil; |
|||
end |
|||
-- Attempt basic conversion |
|||
local number = tonumber(number_string) |
|||
-- If failed, attempt to evaluate input as an expression |
|||
if number == nil then |
|||
local success, result = pcall(mw.ext.ParserFunctions.expr, number_string) |
|||
if success then |
|||
number = tonumber(result) |
|||
number_string = tostring(number) |
|||
else |
|||
number = nil |
|||
number_string = nil |
|||
end |
|||
else |
|||
number_string = number_string:match("^%s*(.-)%s*$") -- String is valid but may contain padding, clean it. |
|||
number_string = number_string:match("^%+(.*)$") or number_string -- Trim any leading + signs. |
|||
if number_string:find('^%-?0[xX]') then |
|||
-- Number is using 0xnnn notation to indicate base 16; use the number that Lua detected instead. |
|||
number_string = tostring(number) |
|||
end |
|||
end |
|||
return number, number_string |
|||
end |
end |
||
Latest revision as of 13:54, 1 March 2021
![]() | This is the module sandbox page for Module:Formatnum (diff). See also the companion subpage for test cases (run). |
This module provides a number formatting function. This function can be used from #invoke or from other Lua modules.
This module is used by Module:Complex date
Use from other Lua modules
[edit]To use the module from normal wiki pages, no special preparation is needed. If you are using the module from another Lua module, first you need to load it, like this:
local mf = require('Module:Formatnum')
(The mf
variable stands for Module Formatnum; you can choose something more descriptive if you prefer.)
Most functions in the module have a version for Lua and a version for #invoke. It is possible to use the #invoke functions from other Lua modules, but using the Lua functions has the advantage that you do not need to access a Lua frame object. Lua functions are preceded by _
, whereas #invoke functions are not.
main
[edit]{{#invoke:Formatnum|main|x|lang=|prec=|sep=}}
mf.formatNum(x, lang, prec, sep)
See also
[edit]-- This module is intended to replace the functionality of Template:Formatnum and related templates.
local p = {} -- Holds functions to be returned from #invoke, and functions to make available to other Lua modules.
--[[
Helper functions used to avoid redundant code.
]]
local function getArgs(frame)
local args = {}
for key, value in pairs(frame:getParent().args) do
args[key] = value
end
for key, value in pairs(frame.args) do
args[key] = value
end
return args
end
function p.main(frame)
local args = getArgs(frame)
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:lower() == "none" then
-- no language, so do nothing
elseif 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
if not mw.language.isSupportedLanguage(lang) then
lang = mw.language.getContentLanguage().code
end
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