Module:number list: Difference between revisions
Jump to navigation
Jump to search
(Blanked the page) Tag: Blanking |
No edit summary |
||
Line 1: | Line 1: | ||
local export = {} | |||
local m_links = require("Module:links") | |||
local form_types = { | |||
{key = "cardinal", display = "[[cardinal number|Cardinal]]"}, | |||
{key = "ordinal", display = "[[ordinal number|Ordinal]]"}, | |||
{key = "adverbial", display = "[[adverbial number|Adverbial]]"}, | |||
{key = "multiplier", display = "[[multiplier|Multiplier]]"}, | |||
{key = "distributive", display = "[[distributive number|Distributive]]"}, | |||
{key = "collective", display = "[[collective number|Collective]]"}, | |||
{key = "fractional", display = "[[fractional|Fractional]]"}, | |||
} | |||
local function index_of_number_type(t, type) | |||
for i, subtable in ipairs(t) do | |||
if subtable.key == type then | |||
return i | |||
end | |||
end | |||
end | |||
-- additional_types is an array of tables like form_types, | |||
-- but each table can contain the keys "before" or "after", which specify | |||
-- the numeral type that the form should appear before or after. | |||
-- The transformations are applied in order. | |||
local function add_form_types(additional_types) | |||
local types = require "Module:table".deepcopy(form_types) | |||
for _, type in ipairs(additional_types) do | |||
type = require "Module:table".shallowcopy(type) | |||
local i | |||
if type.before or type.after then | |||
i = index_of_number_type(types, type.before or type.after) | |||
end | |||
-- For now, simply log an error message | |||
-- if the "before" or "after" number type was not found, | |||
-- and insert the number type at the end. | |||
if i then | |||
if type.before then | |||
table.insert(types, i - 1, type) | |||
else | |||
table.insert(types, i + 1, type) | |||
end | |||
else | |||
table.insert(types, type) | |||
if type.before or type.after then | |||
mw.log("Number type " | |||
.. (type.before or type.after) | |||
.. " was not found.") | |||
end | |||
end | |||
type.before, type.after = nil, nil | |||
end | |||
return types | |||
end | |||
function export.get_number_types(language_code) | |||
local m_data = require("Module:number list/data/" .. language_code) | |||
local final_form_types = form_types | |||
if m_data.additional_number_types then | |||
final_form_types = add_form_types(m_data.additional_number_types) | |||
end | |||
return final_form_types | |||
end | |||
function export.display_number_type(number_type) | |||
if number_type.display then | |||
return number_type.display | |||
else | |||
return (number_type.key:gsub("^.", string.upper):gsub("_", " ")) | |||
end | |||
end | |||
local a = ("a"):byte() | |||
local function multiple_num_links(terms, lang) | |||
local links = {} | |||
for i, term in ipairs(terms) do | |||
links[i] = m_links.language_link{lang = lang, term = term, alt = "[" .. string.char(a + i - 1) .. "]", tr = "-"} | |||
end | |||
return "<sup>" .. table.concat(links, ", ") .. "</sup>" | |||
end | |||
function map(func, array) | |||
local new_array = {} | |||
for i,v in ipairs(array) do | |||
new_array[i] = func(v) | |||
end | |||
return new_array | |||
end | |||
local function unsuffix(term) | |||
if type(term) == "table" then | |||
return map(unsuffix, term) | |||
end | |||
if term:find("^-a ") ~= nil then | |||
return term:sub(4) | |||
end | |||
if term:sub(1, 1) == "-" then | |||
return term:sub(2) | |||
end | |||
return term | |||
end | |||
-- See [[w:Digit grouping]]. | |||
local function add_separator(number, separator, group, start) | |||
number = tostring(number) | |||
start = start or group | |||
if start >= #number then | |||
return number | |||
end | |||
local parts = { number:sub(-start) } | |||
for i = start + 1, #number, group do | |||
table.insert(parts, 1, number:sub(-(i + group - 1), -i)) | |||
end | |||
return table.concat(parts, separator) | |||
end | |||
local function add_thousands_separator(number, separator) | |||
return add_separator(number, separator, 3) | |||
end | |||
local function add_Indic_separator(number, separator) | |||
return add_separator(number, separator, 2, 3) | |||
end | |||
function export.generate_decimal_numeral(numeral_config, number) | |||
if type(number) ~= "number" then | |||
return nil | |||
end | |||
if numeral_config.module and numeral_config.func then | |||
return require("Module:" .. numeral_config.module)[numeral_config.func](number) | |||
end | |||
local thousands_separator, Indic_separator, zero_codepoint = | |||
numeral_config.thousands_separator, | |||
numeral_config.Indic_separator, | |||
numeral_config.zero_codepoint | |||
if not zero_codepoint then | |||
return nil | |||
end | |||
local number_string = tostring(number) | |||
if thousands_separator then | |||
number_string = add_thousands_separator(number_string, thousands_separator) | |||
elseif Indic_separator then | |||
number_string = add_Indic_separator(number_string, Indic_separator) | |||
end | |||
return number_string:gsub("[0-9]", function (digit) | |||
return mw.ustring.char(zero_codepoint + tonumber(digit)) | |||
end) | |||
end | |||
local function remove_duplicate_entry_names(lang, terms) | |||
local entries = require "Module:fun".map( | |||
function(term) return lang:makeEntryName(term) end, | |||
terms) | |||
local entry_set = {} | |||
return require "Module:fun".filter( | |||
function(entry) | |||
local already_seen = entry_set[entry] | |||
entry_set[entry] = true | |||
return not already_seen | |||
end, | |||
entries) | |||
end | |||
function export.show_box(frame) | |||
local full_link = m_links.full_link | |||
local params = { | |||
[1] = {required = true}, | |||
[2] = {required = true}, | |||
[3] = {}, | |||
[4] = {}, | |||
["type"] = {}, | |||
} | |||
local args = require("Module:parameters").process(frame:getParent().args, params) | |||
local lang = args[1] or "und" | |||
local cur_num = args[2] or 2 | |||
local cur_type = args.type | |||
if not (type(cur_num) == "number" or cur_num:find "^%d+$") then | |||
error("Extraneous characters in parameter 2: should be decimal number (integer).") | |||
end | |||
local alt_pagename = args[3] or false | |||
local remove_suffix = args[4] or false | |||
lang = require("Module:languages").getByCode(lang) or error("The language code \"" .. lang .. "\" is not valid.") | |||
-- Get the data from the data module. | |||
-- [[Module:number list/data/en]] has to be loaded with require because its | |||
-- exported numbers table has a metatable. | |||
local module_name = "Module:number list/data/" .. lang:getCode() | |||
local m_data = require(module_name) | |||
-- Numbers that can't be represented exactly as a Lua number | |||
-- must be stored as a string. The first power of 10 that cannot be | |||
-- represented exactly is 10^22 (ten sextillion in short scale, | |||
-- ten thousand trillion in long scale), but the first power of ten whose | |||
-- neighboring numbers cannot be represented exactly | |||
-- is 10^16 (ten quadrillion or ten thousand billion). | |||
-- The number data must be looked up using the string, but after that | |||
-- the number string must be converted to a number, because some code | |||
-- after this point requires a number. This might cause bugs! | |||
-- Ideally we would use a big integer library of some kind. | |||
local cur_data = m_data.numbers[cur_num] or m_data.numbers[tonumber(cur_num)] | |||
if not cur_data then | |||
error('The number "' .. cur_num .. '" is not found in the "numbers" table in [[' .. module_name .. ']].') | |||
end | |||
-- Save original cur_num if it is a string, for use below. | |||
orig_cur_num = cur_num | |||
cur_num = tonumber(cur_num) | |||
-- Go over each number and make links | |||
local forms = {} | |||
local full_pagename = (mw.title.getCurrentTitle().nsText=="Reconstruction" and "*" or "") .. mw.title.getCurrentTitle().subpageText | |||
if alt_pagename then full_pagename = alt_pagename end | |||
if cur_type and not cur_data[cur_type] then | |||
error("The numeral type " .. cur_type .. " for " .. orig_cur_num | |||
.. " is not found in [[" .. module_name .. "]].") | |||
end | |||
for _, form_type in ipairs(export.get_number_types(lang:getCode())) do | |||
local numeral = cur_data[form_type.key] | |||
if numeral then | |||
local form = {} | |||
local numerals | |||
if type(numeral) == "string" then | |||
numerals = {numeral} | |||
elseif type(numeral) == "table" then | |||
numerals = numeral | |||
end | |||
for _, numeral in ipairs(numerals) do | |||
-- If this number is the current page, then store the key for later use | |||
if not cur_type and lang:makeEntryName(numeral) == full_pagename then | |||
cur_type = form_type.key | |||
end | |||
table.insert(form, full_link({lang = lang, term = remove_suffix and unsuffix(numeral) or numeral, alt = numeral })) | |||
end | |||
local displayed_number_type = export.display_number_type(form_type) | |||
if form_type.key == cur_type then | |||
displayed_number_type = "'''" .. displayed_number_type .. "'''" | |||
end | |||
table.insert(forms, " ''" .. displayed_number_type .. "'': " .. table.concat(form, ", ")) | |||
end | |||
end | |||
if not cur_type and mw.title.getCurrentTitle().nsText ~= "Template" then | |||
error("The current page name does not match any of the numbers listed in [[" .. module_name .. "]] for " .. cur_num .. ". Check the data module or the spelling of the page.") | |||
end | |||
-- Current number in header | |||
-- Prevent large numbers, such as 100 trillion ([[རབ་བཀྲམ་ཆེན་པོ]]) from being | |||
-- displayed in scientific notation (1+e14). | |||
local cur_display = ("%i"):format(cur_num) | |||
---[[ | |||
if cur_num >= 1000 then | |||
cur_display = add_thousands_separator(cur_display, ",") | |||
end | |||
--]] | |||
local numeral | |||
if m_data.numeral_config then | |||
numeral = export.generate_decimal_numeral(m_data.numeral_config, cur_num) | |||
elseif cur_data["numeral"] then | |||
numeral = tostring(cur_data["numeral"]) | |||
end | |||
if numeral then | |||
cur_display = full_link({lang = lang, alt = numeral, tr = "-"}) .. "<br/><span style=\"font-size: smaller;\">" .. cur_display .. "</span>" | |||
end | |||
-- Link to previous number | |||
local prev_data = m_data.numbers[cur_num - 1] | |||
local prev_display = "" | |||
-- Current format: | |||
-- if multiple entries: | |||
-- <sup>[a], [b], ...</sup> ← <numeral> | |||
-- else | |||
-- ← <numeral> | |||
local prev_num = prev_data and prev_data[cur_type] | |||
if prev_num then | |||
local entries | |||
if type(prev_num) == "table" then | |||
entries = remove_duplicate_entry_names(lang, prev_num) | |||
else | |||
entries = { prev_num } | |||
end | |||
mw.logObject(prev_num, 'prev_num') | |||
mw.logObject(entries, 'entries') | |||
if #entries > 1 then | |||
prev_display = multiple_num_links(remove_suffix and unsuffix(entries) or entries, lang) | |||
.. " ← " .. (cur_num - 1) | |||
else | |||
prev_display = m_links.language_link { | |||
lang = lang, | |||
term = remove_suffix and unsuffix(entries[1]) or entries[1], | |||
alt = " ← " .. (cur_num - 1), | |||
tr = "-", | |||
} | |||
end | |||
end | |||
-- Link to next number | |||
local next_data = m_data.numbers[cur_num + 1] | |||
local next_display = "" | |||
-- Current format: | |||
-- if multiple entries: | |||
-- <numeral> → <sup>[a], [b], ...</sup> | |||
-- else | |||
-- <numeral> → | |||
local next_num = next_data and next_data[cur_type] | |||
if next_num then | |||
local entries | |||
if type(next_num) == "table" then | |||
entries = remove_duplicate_entry_names(lang, next_num) | |||
else | |||
entries = { next_num } | |||
end | |||
if #entries > 1 then | |||
next_display = (cur_num + 1) .. " → " | |||
.. multiple_num_links(remove_suffix and unsuffix(entries) or entries, lang) | |||
else | |||
next_display = m_links.language_link { | |||
lang = lang, | |||
term = remove_suffix and unsuffix(entries[1]) or entries[1], | |||
alt = (cur_num + 1) .. " → ", | |||
tr = "-", | |||
} | |||
end | |||
end | |||
-- Link to number times ten and divided by ten | |||
-- Show this only if the number is a power of ten times a number 1-9 (that is, of the form x000...) | |||
local up_display | |||
local down_display | |||
-- This test *could* be done numerically, but this is nice and simple and it works. | |||
if tostring(cur_num):find("^[1-9]0*$") then | |||
up_num = cur_num * 10 | |||
local up_data = m_data.numbers[up_num] | |||
if up_data and up_data[cur_type] then | |||
if type(up_data[cur_type]) == "table" then | |||
up_display = up_num .. multiple_num_links(remove_suffix and unsuffix(up_data[cur_type]) or up_data[cur_type], lang) | |||
else | |||
up_display = full_link({lang = lang, term = remove_suffix and unsuffix(up_data[cur_type]) or up_data[cur_type], alt = up_num, tr = "-"}) | |||
end | |||
end | |||
-- Only divide by 10 if the number is at least 10 | |||
if cur_num >= 10 then | |||
local down_num = cur_num / 10 | |||
local down_data = m_data.numbers[down_num] | |||
if down_data and down_data[cur_type] then | |||
if type(down_data[cur_type]) == "table" then | |||
down_display = down_num .. multiple_num_links(remove_suffix and unsuffix(down_data[cur_type]) or down_data[cur_type], lang) | |||
else | |||
down_display = full_link({lang = lang, term = remove_suffix and unsuffix(down_data[cur_type]) or down_data[cur_type], alt = down_num, tr = "-"}) | |||
end | |||
end | |||
end | |||
end | |||
local canonical_name = lang:getCanonicalName() | |||
local appendix1 = canonical_name .. ' numerals' | |||
local appendix2 = canonical_name .. ' numbers' | |||
local appendix | |||
local title | |||
if mw.title.new(appendix1, "Appendix").exists then | |||
appendix = appendix1 | |||
elseif mw.title.new(appendix2, "Appendix").exists then | |||
appendix = appendix2 | |||
end | |||
if appendix then | |||
title = '[[Appendix:' .. appendix .. '|' .. appendix2 .. ']]' | |||
else | |||
title = appendix2 | |||
end | |||
local edit_link = ' <sup>(<span class="plainlinks">[' .. | |||
tostring(mw.uri.fullUrl(module_name, { action = "edit" })) .. | |||
" edit]</span>)</sup>" | |||
return [=[{| class="floatright" cellpadding="5" cellspacing="0" style="background: #ffffff; border: 1px #aaa solid; border-collapse: collapse; margin-top: .5em;" rules="all" | |||
|+ ''']=] .. title .. edit_link .. "'''" .. | |||
(up_display and [=[ | |||
|- style="text-align: center; background:#dddddd;" | |||
| | |||
| style="font-size:smaller;" | ]=] .. up_display .. [=[ | |||
| | |||
]=] or "\n") .. [=[|- style="text-align: center;" | |||
| style="min-width: 6em; font-size:smaller; background:#dddddd;" | ]=] .. prev_display .. [=[ | |||
! style="min-width: 6em; font-size:larger;" | ]=] .. cur_display .. [=[ | |||
| style="min-width: 6em; font-size:smaller; background:#dddddd;" | ]=] .. next_display .. [=[ | |||
]=] .. (down_display and [=[|- style="text-align: center; background:#dddddd;" | |||
| | |||
| style="font-size:smaller;" | ]=] .. down_display .. [=[ | |||
| | |||
]=] or "") .. [=[|- | |||
| colspan="3" | ]=] .. table.concat(forms, "<br/>") .. [=[ | |||
|}]=] | |||
end | |||
local trim = mw.text.trim | |||
-- Assumes string or nil (or false), the types that can be found in an args table. | |||
local function if_not_empty(val) | |||
if val and trim(val) == "" then | |||
return nil | |||
else | |||
return val | |||
end | |||
end | |||
function export.show_box_manual(frame) | |||
local m_links = require("Module:links") | |||
local num_type = frame.args["type"] | |||
local args = {} | |||
--cloning parent's args while also assigning nil to empty strings | |||
for pname, param in pairs(frame:getParent().args) do | |||
args[pname] = if_not_empty(param) | |||
end | |||
local lang = args[1] or (mw.title.getCurrentTitle().nsText == "Template" and "und") or error("Language code has not been specified. Please pass parameter 1 to the template.") | |||
local sc = args["sc"]; | |||
local headlink = args["headlink"] | |||
local wplink = args["wplink"] | |||
local alt = args["alt"] | |||
local tr = args["tr"] | |||
local prev_symbol = if_not_empty(args[2]) | |||
local cur_symbol = if_not_empty(args[3]); | |||
local next_symbol = if_not_empty(args[4]) | |||
local prev_term = if_not_empty(args[5]) | |||
local next_term = if_not_empty(args[6]) | |||
local cardinal_term = args["card"]; local cardinal_alt = args["cardalt"]; local cardinal_tr = args["cardtr"] | |||
local ordinal_term = args["ord"]; local ordinal_alt = args["ordalt"]; local ordinal_tr = args["ordtr"] | |||
local adverbial_term = args["adv"]; local adverbial_alt = args["advalt"]; local adverbial_tr = args["advtr"] | |||
local multiplier_term = args["mult"]; local multiplier_alt = args["multalt"]; local multiplier_tr = args["multtr"] | |||
local distributive_term = args["dis"]; local distributive_alt = args["disalt"]; local distributive_tr = args["distr"] | |||
local collective_term = args["coll"]; local collective_alt = args["collalt"]; local collective_tr = args["colltr"] | |||
local fractional_term = args["frac"]; local fractional_alt = args["fracalt"]; local fractional_tr = args["fractr"] | |||
local optional1_title = args["opt"] | |||
local optional1_term = args["optx"]; local optional1_alt = args["optxalt"]; local optional1_tr = args["optxtr"] | |||
local optional2_title = args["opt2"] | |||
local optional2_term = args["opt2x"]; local optional2_alt = args["opt2xalt"]; local optional2_tr = args["opt2xtr"] | |||
lang = require("Module:languages").getByCode(lang) or error("The language code \"" .. lang .. "\" is not valid.") | |||
sc = (sc and (require("Module:scripts").getByCode(sc) or error("The script code \"" .. sc .. "\" is not valid.")) or nil) | |||
require("Module:debug").track("number list/" .. lang:getCode()) | |||
if sc then | |||
require("Module:debug").track("number list/sc") | |||
end | |||
if headlink then | |||
require("Module:debug").track("number list/headlink") | |||
end | |||
if wplink then | |||
require("Module:debug").track("number list/wplink") | |||
end | |||
if alt then | |||
require("Module:debug").track("number list/alt") | |||
end | |||
if cardinal_alt or ordinal_alt or adverbial_alt or multiplier_alt or distributive_alt or collective_alt or fractional_alt or optional1_alt or optional2_alt then | |||
require("Module:debug").track("number list/xalt") | |||
end | |||
local lang_type = lang:getType() | |||
local subpage = mw.title.getCurrentTitle().subpageText | |||
local is_reconstructed = lang_type == "reconstructed" or mw.title.getCurrentTitle().nsText == "Reconstruction" | |||
alt = alt or (is_reconstructed and "*" or "") .. subpage | |||
if num_type == "cardinal" then | |||
cardinal_term = (is_reconstructed and "*" or "") .. subpage | |||
cardinal_alt = alt | |||
cardinal_tr = tr | |||
elseif num_type == "ordinal" then | |||
ordinal_term = (is_reconstructed and "*" or "") .. subpage | |||
ordinal_alt = alt | |||
ordinal_tr = tr | |||
end | |||
local header = lang:getCanonicalName() .. " " .. num_type .. " numbers" | |||
if headlink then | |||
header = "[[" .. headlink .. "|" .. header .. "]]" | |||
end | |||
local previous = "" | |||
if prev_term or prev_symbol then | |||
previous = m_links.full_link({lang = lang, sc = sc, term = prev_term, alt = " < " .. prev_symbol, tr = "-"}) | |||
end | |||
local current = m_links.full_link({lang = lang, sc = sc, alt = cur_symbol, tr = "-"}) | |||
local next = "" | |||
if next_term or next_symbol then | |||
next = m_links.full_link({lang = lang, sc = sc, term = next_term, alt = next_symbol .. " > ", tr = "-"}) | |||
end | |||
local forms = {} | |||
if cardinal_term then | |||
table.insert(forms, " ''[[cardinal number|Cardinal]]'' : " .. m_links.full_link({lang = lang, sc = sc, term = cardinal_term, alt = cardinal_alt, tr = cardinal_tr})) | |||
end | |||
if ordinal_term then | |||
table.insert(forms, " ''[[ordinal number|Ordinal]]'' : " .. m_links.full_link({lang = lang, sc = sc, term = ordinal_term, alt = ordinal_alt, tr = ordinal_tr})) | |||
end | |||
if adverbial_term then | |||
table.insert(forms, " ''[[adverbial number|Adverbial]]'' : " .. m_links.full_link({lang = lang, sc = sc, term = adverbial_term, alt = adverbial_alt, tr = adverbial_tr})) | |||
end | |||
if multiplier_term then | |||
table.insert(forms, " ''[[multiplier|Multiplier]]'' : " .. m_links.full_link({lang = lang, sc = sc, term = multiplier_term, alt = multiplier_alt, tr = multiplier_tr})) | |||
end | |||
if distributive_term then | |||
table.insert(forms, " ''[[distributive number|Distributive]]'' : " .. m_links.full_link({lang = lang, sc = sc, term = distributive_term, alt = distributive_alt, tr = distributive_tr})) | |||
end | |||
if collective_term then | |||
table.insert(forms, " ''[[collective number|Collective]]'' : " .. m_links.full_link({lang = lang, sc = sc, term = collective_term, alt = collective_alt, tr = collective_tr})) | |||
end | |||
if fractional_term then | |||
table.insert(forms, " ''[[fractional|Fractional]]'' : " .. m_links.full_link({lang = lang, sc = sc, term = fractional_term, alt = fractional_alt, tr = fractional_tr})) | |||
end | |||
if optional1_title then | |||
table.insert(forms, " ''" .. optional1_title .. "'' : " .. m_links.full_link({lang = lang, sc = sc, term = optional1_term, alt = optional1_alt, tr = optional1_tr})) | |||
end | |||
if optional2_title then | |||
table.insert(forms, " ''" .. optional2_title .. "'' : " .. m_links.full_link({lang = lang, sc = sc, term = optional2_term, alt = optional2_alt, tr = optional2_tr})) | |||
end | |||
local footer = "" | |||
if wplink then | |||
footer = | |||
"[[w:" .. lang:getCode() .. ":Main Page|" .. lang:getCanonicalName() .. " Wikipedia]] article on " .. | |||
m_links.full_link({lang = lang, sc = sc, term = "w:" .. lang:getCode() .. ":" .. wplink, alt = alt, tr = tr}) | |||
end | |||
return [=[{| class="floatright" cellpadding="5" cellspacing="0" style="background: #ffffff; border: 1px #aaa solid; border-collapse: collapse; margin-top: .5em;" rules="all" | |||
|+ ''']=] .. header .. [=[''' | |||
|- | |||
| style="width: 64px; background:#dddddd; text-align: center; font-size:smaller;" | ]=] .. previous .. [=[ | |||
! style="width: 98px; text-align: center; font-size:larger;" | ]=] .. current .. [=[ | |||
| style="width: 64px; text-align: center; background:#dddddd; font-size:smaller;" | ]=] .. next .. [=[ | |||
|- | |||
| colspan="3" | ]=] .. table.concat(forms, "<br/>") .. [=[ | |||
|- | |||
| colspan="3" style="text-align: center; background: #dddddd;" | ]=] .. footer .. [=[ | |||
|}]=] | |||
end | |||
return export |
Revision as of 17:36, 7 August 2020
- The following documentation is located at Module:number list/doc.[edit]
- Useful links: subpage list • links • transclusions • testcases • sandbox
local export = {}
local m_links = require("Module:links")
local form_types = {
{key = "cardinal", display = "[[cardinal number|Cardinal]]"},
{key = "ordinal", display = "[[ordinal number|Ordinal]]"},
{key = "adverbial", display = "[[adverbial number|Adverbial]]"},
{key = "multiplier", display = "[[multiplier|Multiplier]]"},
{key = "distributive", display = "[[distributive number|Distributive]]"},
{key = "collective", display = "[[collective number|Collective]]"},
{key = "fractional", display = "[[fractional|Fractional]]"},
}
local function index_of_number_type(t, type)
for i, subtable in ipairs(t) do
if subtable.key == type then
return i
end
end
end
-- additional_types is an array of tables like form_types,
-- but each table can contain the keys "before" or "after", which specify
-- the numeral type that the form should appear before or after.
-- The transformations are applied in order.
local function add_form_types(additional_types)
local types = require "Module:table".deepcopy(form_types)
for _, type in ipairs(additional_types) do
type = require "Module:table".shallowcopy(type)
local i
if type.before or type.after then
i = index_of_number_type(types, type.before or type.after)
end
-- For now, simply log an error message
-- if the "before" or "after" number type was not found,
-- and insert the number type at the end.
if i then
if type.before then
table.insert(types, i - 1, type)
else
table.insert(types, i + 1, type)
end
else
table.insert(types, type)
if type.before or type.after then
mw.log("Number type "
.. (type.before or type.after)
.. " was not found.")
end
end
type.before, type.after = nil, nil
end
return types
end
function export.get_number_types(language_code)
local m_data = require("Module:number list/data/" .. language_code)
local final_form_types = form_types
if m_data.additional_number_types then
final_form_types = add_form_types(m_data.additional_number_types)
end
return final_form_types
end
function export.display_number_type(number_type)
if number_type.display then
return number_type.display
else
return (number_type.key:gsub("^.", string.upper):gsub("_", " "))
end
end
local a = ("a"):byte()
local function multiple_num_links(terms, lang)
local links = {}
for i, term in ipairs(terms) do
links[i] = m_links.language_link{lang = lang, term = term, alt = "[" .. string.char(a + i - 1) .. "]", tr = "-"}
end
return "<sup>" .. table.concat(links, ", ") .. "</sup>"
end
function map(func, array)
local new_array = {}
for i,v in ipairs(array) do
new_array[i] = func(v)
end
return new_array
end
local function unsuffix(term)
if type(term) == "table" then
return map(unsuffix, term)
end
if term:find("^-a ") ~= nil then
return term:sub(4)
end
if term:sub(1, 1) == "-" then
return term:sub(2)
end
return term
end
-- See [[w:Digit grouping]].
local function add_separator(number, separator, group, start)
number = tostring(number)
start = start or group
if start >= #number then
return number
end
local parts = { number:sub(-start) }
for i = start + 1, #number, group do
table.insert(parts, 1, number:sub(-(i + group - 1), -i))
end
return table.concat(parts, separator)
end
local function add_thousands_separator(number, separator)
return add_separator(number, separator, 3)
end
local function add_Indic_separator(number, separator)
return add_separator(number, separator, 2, 3)
end
function export.generate_decimal_numeral(numeral_config, number)
if type(number) ~= "number" then
return nil
end
if numeral_config.module and numeral_config.func then
return require("Module:" .. numeral_config.module)[numeral_config.func](number)
end
local thousands_separator, Indic_separator, zero_codepoint =
numeral_config.thousands_separator,
numeral_config.Indic_separator,
numeral_config.zero_codepoint
if not zero_codepoint then
return nil
end
local number_string = tostring(number)
if thousands_separator then
number_string = add_thousands_separator(number_string, thousands_separator)
elseif Indic_separator then
number_string = add_Indic_separator(number_string, Indic_separator)
end
return number_string:gsub("[0-9]", function (digit)
return mw.ustring.char(zero_codepoint + tonumber(digit))
end)
end
local function remove_duplicate_entry_names(lang, terms)
local entries = require "Module:fun".map(
function(term) return lang:makeEntryName(term) end,
terms)
local entry_set = {}
return require "Module:fun".filter(
function(entry)
local already_seen = entry_set[entry]
entry_set[entry] = true
return not already_seen
end,
entries)
end
function export.show_box(frame)
local full_link = m_links.full_link
local params = {
[1] = {required = true},
[2] = {required = true},
[3] = {},
[4] = {},
["type"] = {},
}
local args = require("Module:parameters").process(frame:getParent().args, params)
local lang = args[1] or "und"
local cur_num = args[2] or 2
local cur_type = args.type
if not (type(cur_num) == "number" or cur_num:find "^%d+$") then
error("Extraneous characters in parameter 2: should be decimal number (integer).")
end
local alt_pagename = args[3] or false
local remove_suffix = args[4] or false
lang = require("Module:languages").getByCode(lang) or error("The language code \"" .. lang .. "\" is not valid.")
-- Get the data from the data module.
-- [[Module:number list/data/en]] has to be loaded with require because its
-- exported numbers table has a metatable.
local module_name = "Module:number list/data/" .. lang:getCode()
local m_data = require(module_name)
-- Numbers that can't be represented exactly as a Lua number
-- must be stored as a string. The first power of 10 that cannot be
-- represented exactly is 10^22 (ten sextillion in short scale,
-- ten thousand trillion in long scale), but the first power of ten whose
-- neighboring numbers cannot be represented exactly
-- is 10^16 (ten quadrillion or ten thousand billion).
-- The number data must be looked up using the string, but after that
-- the number string must be converted to a number, because some code
-- after this point requires a number. This might cause bugs!
-- Ideally we would use a big integer library of some kind.
local cur_data = m_data.numbers[cur_num] or m_data.numbers[tonumber(cur_num)]
if not cur_data then
error('The number "' .. cur_num .. '" is not found in the "numbers" table in [[' .. module_name .. ']].')
end
-- Save original cur_num if it is a string, for use below.
orig_cur_num = cur_num
cur_num = tonumber(cur_num)
-- Go over each number and make links
local forms = {}
local full_pagename = (mw.title.getCurrentTitle().nsText=="Reconstruction" and "*" or "") .. mw.title.getCurrentTitle().subpageText
if alt_pagename then full_pagename = alt_pagename end
if cur_type and not cur_data[cur_type] then
error("The numeral type " .. cur_type .. " for " .. orig_cur_num
.. " is not found in [[" .. module_name .. "]].")
end
for _, form_type in ipairs(export.get_number_types(lang:getCode())) do
local numeral = cur_data[form_type.key]
if numeral then
local form = {}
local numerals
if type(numeral) == "string" then
numerals = {numeral}
elseif type(numeral) == "table" then
numerals = numeral
end
for _, numeral in ipairs(numerals) do
-- If this number is the current page, then store the key for later use
if not cur_type and lang:makeEntryName(numeral) == full_pagename then
cur_type = form_type.key
end
table.insert(form, full_link({lang = lang, term = remove_suffix and unsuffix(numeral) or numeral, alt = numeral }))
end
local displayed_number_type = export.display_number_type(form_type)
if form_type.key == cur_type then
displayed_number_type = "'''" .. displayed_number_type .. "'''"
end
table.insert(forms, " ''" .. displayed_number_type .. "'': " .. table.concat(form, ", "))
end
end
if not cur_type and mw.title.getCurrentTitle().nsText ~= "Template" then
error("The current page name does not match any of the numbers listed in [[" .. module_name .. "]] for " .. cur_num .. ". Check the data module or the spelling of the page.")
end
-- Current number in header
-- Prevent large numbers, such as 100 trillion ([[རབ་བཀྲམ་ཆེན་པོ]]) from being
-- displayed in scientific notation (1+e14).
local cur_display = ("%i"):format(cur_num)
---[[
if cur_num >= 1000 then
cur_display = add_thousands_separator(cur_display, ",")
end
--]]
local numeral
if m_data.numeral_config then
numeral = export.generate_decimal_numeral(m_data.numeral_config, cur_num)
elseif cur_data["numeral"] then
numeral = tostring(cur_data["numeral"])
end
if numeral then
cur_display = full_link({lang = lang, alt = numeral, tr = "-"}) .. "<br/><span style=\"font-size: smaller;\">" .. cur_display .. "</span>"
end
-- Link to previous number
local prev_data = m_data.numbers[cur_num - 1]
local prev_display = ""
-- Current format:
-- if multiple entries:
-- <sup>[a], [b], ...</sup> ← <numeral>
-- else
-- ← <numeral>
local prev_num = prev_data and prev_data[cur_type]
if prev_num then
local entries
if type(prev_num) == "table" then
entries = remove_duplicate_entry_names(lang, prev_num)
else
entries = { prev_num }
end
mw.logObject(prev_num, 'prev_num')
mw.logObject(entries, 'entries')
if #entries > 1 then
prev_display = multiple_num_links(remove_suffix and unsuffix(entries) or entries, lang)
.. " ← " .. (cur_num - 1)
else
prev_display = m_links.language_link {
lang = lang,
term = remove_suffix and unsuffix(entries[1]) or entries[1],
alt = " ← " .. (cur_num - 1),
tr = "-",
}
end
end
-- Link to next number
local next_data = m_data.numbers[cur_num + 1]
local next_display = ""
-- Current format:
-- if multiple entries:
-- <numeral> → <sup>[a], [b], ...</sup>
-- else
-- <numeral> →
local next_num = next_data and next_data[cur_type]
if next_num then
local entries
if type(next_num) == "table" then
entries = remove_duplicate_entry_names(lang, next_num)
else
entries = { next_num }
end
if #entries > 1 then
next_display = (cur_num + 1) .. " → "
.. multiple_num_links(remove_suffix and unsuffix(entries) or entries, lang)
else
next_display = m_links.language_link {
lang = lang,
term = remove_suffix and unsuffix(entries[1]) or entries[1],
alt = (cur_num + 1) .. " → ",
tr = "-",
}
end
end
-- Link to number times ten and divided by ten
-- Show this only if the number is a power of ten times a number 1-9 (that is, of the form x000...)
local up_display
local down_display
-- This test *could* be done numerically, but this is nice and simple and it works.
if tostring(cur_num):find("^[1-9]0*$") then
up_num = cur_num * 10
local up_data = m_data.numbers[up_num]
if up_data and up_data[cur_type] then
if type(up_data[cur_type]) == "table" then
up_display = up_num .. multiple_num_links(remove_suffix and unsuffix(up_data[cur_type]) or up_data[cur_type], lang)
else
up_display = full_link({lang = lang, term = remove_suffix and unsuffix(up_data[cur_type]) or up_data[cur_type], alt = up_num, tr = "-"})
end
end
-- Only divide by 10 if the number is at least 10
if cur_num >= 10 then
local down_num = cur_num / 10
local down_data = m_data.numbers[down_num]
if down_data and down_data[cur_type] then
if type(down_data[cur_type]) == "table" then
down_display = down_num .. multiple_num_links(remove_suffix and unsuffix(down_data[cur_type]) or down_data[cur_type], lang)
else
down_display = full_link({lang = lang, term = remove_suffix and unsuffix(down_data[cur_type]) or down_data[cur_type], alt = down_num, tr = "-"})
end
end
end
end
local canonical_name = lang:getCanonicalName()
local appendix1 = canonical_name .. ' numerals'
local appendix2 = canonical_name .. ' numbers'
local appendix
local title
if mw.title.new(appendix1, "Appendix").exists then
appendix = appendix1
elseif mw.title.new(appendix2, "Appendix").exists then
appendix = appendix2
end
if appendix then
title = '[[Appendix:' .. appendix .. '|' .. appendix2 .. ']]'
else
title = appendix2
end
local edit_link = ' <sup>(<span class="plainlinks">[' ..
tostring(mw.uri.fullUrl(module_name, { action = "edit" })) ..
" edit]</span>)</sup>"
return [=[{| class="floatright" cellpadding="5" cellspacing="0" style="background: #ffffff; border: 1px #aaa solid; border-collapse: collapse; margin-top: .5em;" rules="all"
|+ ''']=] .. title .. edit_link .. "'''" ..
(up_display and [=[
|- style="text-align: center; background:#dddddd;"
|
| style="font-size:smaller;" | ]=] .. up_display .. [=[
|
]=] or "\n") .. [=[|- style="text-align: center;"
| style="min-width: 6em; font-size:smaller; background:#dddddd;" | ]=] .. prev_display .. [=[
! style="min-width: 6em; font-size:larger;" | ]=] .. cur_display .. [=[
| style="min-width: 6em; font-size:smaller; background:#dddddd;" | ]=] .. next_display .. [=[
]=] .. (down_display and [=[|- style="text-align: center; background:#dddddd;"
|
| style="font-size:smaller;" | ]=] .. down_display .. [=[
|
]=] or "") .. [=[|-
| colspan="3" | ]=] .. table.concat(forms, "<br/>") .. [=[
|}]=]
end
local trim = mw.text.trim
-- Assumes string or nil (or false), the types that can be found in an args table.
local function if_not_empty(val)
if val and trim(val) == "" then
return nil
else
return val
end
end
function export.show_box_manual(frame)
local m_links = require("Module:links")
local num_type = frame.args["type"]
local args = {}
--cloning parent's args while also assigning nil to empty strings
for pname, param in pairs(frame:getParent().args) do
args[pname] = if_not_empty(param)
end
local lang = args[1] or (mw.title.getCurrentTitle().nsText == "Template" and "und") or error("Language code has not been specified. Please pass parameter 1 to the template.")
local sc = args["sc"];
local headlink = args["headlink"]
local wplink = args["wplink"]
local alt = args["alt"]
local tr = args["tr"]
local prev_symbol = if_not_empty(args[2])
local cur_symbol = if_not_empty(args[3]);
local next_symbol = if_not_empty(args[4])
local prev_term = if_not_empty(args[5])
local next_term = if_not_empty(args[6])
local cardinal_term = args["card"]; local cardinal_alt = args["cardalt"]; local cardinal_tr = args["cardtr"]
local ordinal_term = args["ord"]; local ordinal_alt = args["ordalt"]; local ordinal_tr = args["ordtr"]
local adverbial_term = args["adv"]; local adverbial_alt = args["advalt"]; local adverbial_tr = args["advtr"]
local multiplier_term = args["mult"]; local multiplier_alt = args["multalt"]; local multiplier_tr = args["multtr"]
local distributive_term = args["dis"]; local distributive_alt = args["disalt"]; local distributive_tr = args["distr"]
local collective_term = args["coll"]; local collective_alt = args["collalt"]; local collective_tr = args["colltr"]
local fractional_term = args["frac"]; local fractional_alt = args["fracalt"]; local fractional_tr = args["fractr"]
local optional1_title = args["opt"]
local optional1_term = args["optx"]; local optional1_alt = args["optxalt"]; local optional1_tr = args["optxtr"]
local optional2_title = args["opt2"]
local optional2_term = args["opt2x"]; local optional2_alt = args["opt2xalt"]; local optional2_tr = args["opt2xtr"]
lang = require("Module:languages").getByCode(lang) or error("The language code \"" .. lang .. "\" is not valid.")
sc = (sc and (require("Module:scripts").getByCode(sc) or error("The script code \"" .. sc .. "\" is not valid.")) or nil)
require("Module:debug").track("number list/" .. lang:getCode())
if sc then
require("Module:debug").track("number list/sc")
end
if headlink then
require("Module:debug").track("number list/headlink")
end
if wplink then
require("Module:debug").track("number list/wplink")
end
if alt then
require("Module:debug").track("number list/alt")
end
if cardinal_alt or ordinal_alt or adverbial_alt or multiplier_alt or distributive_alt or collective_alt or fractional_alt or optional1_alt or optional2_alt then
require("Module:debug").track("number list/xalt")
end
local lang_type = lang:getType()
local subpage = mw.title.getCurrentTitle().subpageText
local is_reconstructed = lang_type == "reconstructed" or mw.title.getCurrentTitle().nsText == "Reconstruction"
alt = alt or (is_reconstructed and "*" or "") .. subpage
if num_type == "cardinal" then
cardinal_term = (is_reconstructed and "*" or "") .. subpage
cardinal_alt = alt
cardinal_tr = tr
elseif num_type == "ordinal" then
ordinal_term = (is_reconstructed and "*" or "") .. subpage
ordinal_alt = alt
ordinal_tr = tr
end
local header = lang:getCanonicalName() .. " " .. num_type .. " numbers"
if headlink then
header = "[[" .. headlink .. "|" .. header .. "]]"
end
local previous = ""
if prev_term or prev_symbol then
previous = m_links.full_link({lang = lang, sc = sc, term = prev_term, alt = " < " .. prev_symbol, tr = "-"})
end
local current = m_links.full_link({lang = lang, sc = sc, alt = cur_symbol, tr = "-"})
local next = ""
if next_term or next_symbol then
next = m_links.full_link({lang = lang, sc = sc, term = next_term, alt = next_symbol .. " > ", tr = "-"})
end
local forms = {}
if cardinal_term then
table.insert(forms, " ''[[cardinal number|Cardinal]]'' : " .. m_links.full_link({lang = lang, sc = sc, term = cardinal_term, alt = cardinal_alt, tr = cardinal_tr}))
end
if ordinal_term then
table.insert(forms, " ''[[ordinal number|Ordinal]]'' : " .. m_links.full_link({lang = lang, sc = sc, term = ordinal_term, alt = ordinal_alt, tr = ordinal_tr}))
end
if adverbial_term then
table.insert(forms, " ''[[adverbial number|Adverbial]]'' : " .. m_links.full_link({lang = lang, sc = sc, term = adverbial_term, alt = adverbial_alt, tr = adverbial_tr}))
end
if multiplier_term then
table.insert(forms, " ''[[multiplier|Multiplier]]'' : " .. m_links.full_link({lang = lang, sc = sc, term = multiplier_term, alt = multiplier_alt, tr = multiplier_tr}))
end
if distributive_term then
table.insert(forms, " ''[[distributive number|Distributive]]'' : " .. m_links.full_link({lang = lang, sc = sc, term = distributive_term, alt = distributive_alt, tr = distributive_tr}))
end
if collective_term then
table.insert(forms, " ''[[collective number|Collective]]'' : " .. m_links.full_link({lang = lang, sc = sc, term = collective_term, alt = collective_alt, tr = collective_tr}))
end
if fractional_term then
table.insert(forms, " ''[[fractional|Fractional]]'' : " .. m_links.full_link({lang = lang, sc = sc, term = fractional_term, alt = fractional_alt, tr = fractional_tr}))
end
if optional1_title then
table.insert(forms, " ''" .. optional1_title .. "'' : " .. m_links.full_link({lang = lang, sc = sc, term = optional1_term, alt = optional1_alt, tr = optional1_tr}))
end
if optional2_title then
table.insert(forms, " ''" .. optional2_title .. "'' : " .. m_links.full_link({lang = lang, sc = sc, term = optional2_term, alt = optional2_alt, tr = optional2_tr}))
end
local footer = ""
if wplink then
footer =
"[[w:" .. lang:getCode() .. ":Main Page|" .. lang:getCanonicalName() .. " Wikipedia]] article on " ..
m_links.full_link({lang = lang, sc = sc, term = "w:" .. lang:getCode() .. ":" .. wplink, alt = alt, tr = tr})
end
return [=[{| class="floatright" cellpadding="5" cellspacing="0" style="background: #ffffff; border: 1px #aaa solid; border-collapse: collapse; margin-top: .5em;" rules="all"
|+ ''']=] .. header .. [=['''
|-
| style="width: 64px; background:#dddddd; text-align: center; font-size:smaller;" | ]=] .. previous .. [=[
! style="width: 98px; text-align: center; font-size:larger;" | ]=] .. current .. [=[
| style="width: 64px; text-align: center; background:#dddddd; font-size:smaller;" | ]=] .. next .. [=[
|-
| colspan="3" | ]=] .. table.concat(forms, "<br/>") .. [=[
|-
| colspan="3" style="text-align: center; background: #dddddd;" | ]=] .. footer .. [=[
|}]=]
end
return export