Module:Convert: Difference between revisions
Jump to navigation
Jump to search
Content added Content deleted
nswiki>Union of Christian States (Created page with "-- Convert a value from one unit of measurement to another. -- Example: {{convert|123|lb|kg}} --> 123 pounds (56 kg) -- See en:Template:Convert/Transwiki guide if copying...") |
(update from sandbox per Template talk:Convert#Module version 4) |
||
Line 15: | Line 15: | ||
-- Conversion data and message text are defined in separate modules. |
-- Conversion data and message text are defined in separate modules. |
||
local config, maxsigfig |
local config, maxsigfig |
||
local numdot -- must be '.' or ',' or a character which works in a regex |
local numdot -- must be '.' or ',' or a character which works in a regex |
||
local numsep, numsep_remove |
local numsep, numsep_remove, numsep_remove2 |
||
local default_exceptions, link_exceptions, all_units |
local default_exceptions, link_exceptions, all_units |
||
local text_code |
local text_code |
||
Line 23: | Line 23: | ||
local to_en_table -- to translate an input string of digits in local language to en |
local to_en_table -- to translate an input string of digits in local language to en |
||
-- Use translation_table in convert/text to change the following. |
-- Use translation_table in convert/text to change the following. |
||
local en_default -- true uses lang=en unless convert has lang=local or local digits |
|||
local group_method = 3 -- code for how many digits are in a group |
local group_method = 3 -- code for how many digits are in a group |
||
local per_word = 'per' -- for units like "liters per kilometer" |
local per_word = 'per' -- for units like "liters per kilometer" |
||
local plural_suffix = 's' -- only other useful value is probably '' to disable plural unit names |
local plural_suffix = 's' -- only other useful value is probably '' to disable plural unit names |
||
local omitsep -- true to omit separator before local symbol/name |
|||
-- All units should be defined in the data module. However, to cater for quick changes |
-- All units should be defined in the data module. However, to cater for quick changes |
||
Line 53: | Line 55: | ||
-- and no separators (they have to be removed here to handle cases like |
-- and no separators (they have to be removed here to handle cases like |
||
-- numsep = '.' and numdot = ',' with input "1.234.567,8"). |
-- numsep = '.' and numdot = ',' with input "1.234.567,8"). |
||
if |
if to_en_table then |
||
text = ustring.gsub(text, '%d', to_en_table) |
|||
end |
|||
if numsep_remove then |
|||
text = text:gsub(numsep_remove, '') |
text = text:gsub(numsep_remove, '') |
||
end |
|||
if numsep_remove2 then |
|||
text = text:gsub(numsep_remove2, '') |
|||
end |
end |
||
if numdot ~= '.' then |
if numdot ~= '.' then |
||
text = text:gsub(numdot, '.') |
text = text:gsub(numdot, '.') |
||
end |
end |
||
return text |
|||
end |
|||
local function decimal_mark(text) |
|||
-- Return ',' if text probably is using comma for decimal mark, or has no decimal mark. |
|||
-- Return '.' if text probably is using dot for decimal mark. |
|||
-- Otherwise return nothing (decimal mark not known). |
|||
if not text:find('[.,]') then return ',' end |
|||
text = text:gsub('^%-', ''):gsub('%+%d+/%d+$', ''):gsub('[Ee]%-?%d+$', '') |
|||
local decimal = |
|||
text:match('^0?([.,])%d+$') or |
|||
text:match('%d([.,])%d?%d?$') or |
|||
text:match('%d([.,])%d%d%d%d+$') |
|||
if decimal then return decimal end |
|||
if text:match('%.%d+%.') then return ',' end |
|||
if text:match('%,%d+,') then return '.' end |
|||
end |
|||
local add_warning, with_separator -- forward declarations |
|||
local function to_en_with_check(text, parms) |
|||
-- Version of to_en() for a wiki using numdot = ',' and numsep = '.' to check |
|||
-- text (an input number as a string) which might have been copied from enwiki. |
|||
-- For example, in '1.234' the '.' could be a decimal mark or a group separator. |
|||
-- From viwiki. |
|||
if to_en_table then |
if to_en_table then |
||
text = ustring.gsub(text, '%d', to_en_table) |
text = ustring.gsub(text, '%d', to_en_table) |
||
end |
|||
if decimal_mark(text) == '.' then |
|||
local original = text |
|||
text = text:gsub(',', '') -- for example, interpret "1,234.5" as an enwiki value |
|||
if parms then |
|||
add_warning(parms, 0, 'cvt_enwiki_num', original, with_separator({}, text)) |
|||
end |
|||
else |
|||
if numsep_remove then |
|||
text = text:gsub(numsep_remove, '') |
|||
end |
|||
if numsep_remove2 then |
|||
text = text:gsub(numsep_remove2, '') |
|||
end |
|||
if numdot ~= '.' then |
|||
text = text:gsub(numdot, '.') |
|||
end |
|||
end |
end |
||
return text |
return text |
||
end |
|||
local function want_separator(id) |
|||
-- Return true if id (a unit symbole or name) should be proceeded by a separator. |
|||
-- For zhwiki, there should be no separator if id uses local characters. |
|||
-- The following kludge should be a sufficient test. |
|||
if id:sub(1, 2) == '-{' then -- for "-{...}-" content language variant |
|||
return false |
|||
end |
|||
if id:byte() > 127 then |
|||
local first = usub(id, 1, 1) |
|||
if first ~= 'Å' and first ~= '°' and first ~= 'µ' then |
|||
return false |
|||
end |
|||
end |
|||
return true |
|||
end |
end |
||
Line 97: | Line 162: | ||
numdot = translation.numdot |
numdot = translation.numdot |
||
numsep = translation.numsep |
numsep = translation.numsep |
||
if numdot == ',' and numsep == '.' then |
|||
if text_code.all_messages.cvt_enwiki_num then |
|||
to_en = to_en_with_check |
|||
end |
|||
end |
|||
if translation.group then |
if translation.group then |
||
group_method = translation.group |
group_method = translation.group |
||
Line 123: | Line 193: | ||
to_en_table = translation.to_en |
to_en_table = translation.to_en |
||
end |
end |
||
if translation.lang == 'en default' then |
|||
en_default = true -- for hiwiki |
|||
end |
|||
omitsep = translation.omitsep -- for zhwiki |
|||
end |
end |
||
numdot = config.numdot or numdot or '.' -- decimal mark before fractional digits |
numdot = config.numdot or numdot or '.' -- decimal mark before fractional digits |
||
Line 128: | Line 202: | ||
-- numsep should be ',' or '.' or '' or ' ' or a Unicode character. |
-- numsep should be ',' or '.' or '' or ' ' or a Unicode character. |
||
-- numsep_remove must work in a regex to identify separators to be removed. |
-- numsep_remove must work in a regex to identify separators to be removed. |
||
if numsep ~= '' then |
|||
numsep_remove = (numsep == '.') and '%.' or numsep |
|||
end |
|||
if numsep ~= ',' and numdot ~= ',' then |
|||
numsep_remove2 = ',' -- so numbers copied from enwiki will work |
|||
end |
|||
end |
end |
||
Line 246: | Line 325: | ||
end |
end |
||
function add_warning(parms, level, key, text1, text2) -- for forward declaration above |
|||
-- If enabled, add a warning that will be displayed after the convert result. |
-- If enabled, add a warning that will be displayed after the convert result. |
||
-- To reduce output noise, only the first warning is displayed. |
-- To reduce output noise, only the first warning is displayed. |
||
Line 252: | Line 331: | ||
if level <= (tonumber(config.warnings) or 1) then |
if level <= (tonumber(config.warnings) or 1) then |
||
if parms.warnings == nil then |
if parms.warnings == nil then |
||
parms.warnings = message({ |
parms.warnings = message({ key, text1, text2 }) |
||
end |
end |
||
end |
end |
||
Line 913: | Line 992: | ||
end |
end |
||
function with_separator(parms, text) -- for forward declaration above |
|||
-- Input text is a number in en digits and optional '.' decimal mark. |
-- Input text is a number in en digits and optional '.' decimal mark. |
||
-- Return an equivalent of text, formatted for display: |
-- Return an equivalent of text, formatted for display: |
||
Line 1,259: | Line 1,338: | ||
-- '+' (if the input text used '+'), or is '' (if no sign in input). |
-- '+' (if the input text used '+'), or is '' (if no sign in input). |
||
text = strip(text or '') |
text = strip(text or '') |
||
local clean = to_en(text) |
local clean = to_en(text, parms) |
||
if clean == '' then |
if clean == '' then |
||
return false, { another and 'cvt_no_num2' or 'cvt_no_num' } |
return false, { another and 'cvt_no_num2' or 'cvt_no_num' } |
||
Line 1,518: | Line 1,597: | ||
while subunit.subdivs do -- subdivs is nil or a table of allowed subdivisions |
while subunit.subdivs do -- subdivs is nil or a table of allowed subdivisions |
||
local subcode = strip(parms[iparm+1]) |
local subcode = strip(parms[iparm+1]) |
||
local subdiv = subunit.subdivs[subcode] |
local subdiv = subunit.subdivs[subcode] or subunit.subdivs[(all_units[subcode] or {}).target] |
||
if not subdiv then |
if not subdiv then |
||
break |
break |
||
Line 1,689: | Line 1,768: | ||
parms.table_joins = { align .. '|', '\n|' .. align .. '|' } |
parms.table_joins = { align .. '|', '\n|' .. align .. '|' } |
||
end |
end |
||
local disp_joins = text_code.disp_joins |
|||
if parms.opt_lang_en then |
|||
local default_joins = disp_joins['b'] |
|||
parms.join_between = default_joins[3] or '; ' |
|||
local disp = parms.disp |
|||
if disp == nil then -- special case for the most common setting |
|||
parms.joins = default_joins |
|||
elseif disp == 'x' then |
|||
-- Later, parms.joins is set from the input parameters. |
|||
else |
|||
-- Old template does this. |
|||
local abbr = parms.abbr |
|||
if disp == 'slash' then |
|||
if parms.abbr_org == nil then |
|||
disp = 'slash-nbsp' |
|||
elseif abbr == 'in' or abbr == 'out' then |
|||
disp = 'slash-sp' |
|||
else |
|||
disp = 'slash-nosp' |
|||
end |
|||
elseif disp == 'sqbr' then |
|||
if abbr == 'on' then |
|||
disp = 'sqbr-nbsp' |
|||
else |
|||
disp = 'sqbr-sp' |
|||
end |
|||
end |
|||
parms.joins = disp_joins[disp] or default_joins |
|||
parms.join_between = parms.joins[3] or parms.join_between |
|||
end |
|||
if (en_default and not parms.opt_lang_local and (parms[1] or ''):find('%d')) or parms.opt_lang_en then |
|||
from_en_table = nil |
from_en_table = nil |
||
end |
|||
if en_default and from_en_table then |
|||
-- For hiwiki: localized symbol/name is defined with the US symbol/name field, |
|||
-- and is used if output uses localized numbers. |
|||
parms.opt_sp_us = true |
|||
end |
end |
||
return true |
return true |
||
Line 1,773: | Line 1,886: | ||
-- and that on average it speeds up converts by 8%. |
-- and that on average it speeds up converts by 8%. |
||
if parms.input_precision or parms.opt_spell_in then return end |
if parms.input_precision or parms.opt_spell_in then return end |
||
local clean = to_en(strip(parms[1] or '')) |
local clean = to_en(strip(parms[1] or ''), parms) |
||
if #clean > 10 or not clean:match('^[0-9.]+$') then return end |
if #clean > 10 or not clean:match('^[0-9.]+$') then return end |
||
local value = tonumber(clean) |
local value = tonumber(clean) |
||
Line 2,409: | Line 2,522: | ||
local function variable_name(clean, unit_table) |
local function variable_name(clean, unit_table) |
||
-- For slwiki |
-- For slwiki, a unit name depends on the value. |
||
-- Parameter clean is the unsigned rounded value in en digits, as a string. |
-- Parameter clean is the unsigned rounded value in en digits, as a string. |
||
-- Value Source Example for "m" |
-- Value Source Example for "m" |
||
Line 2,489: | Line 2,602: | ||
if abbr_on then |
if abbr_on then |
||
result = '/' |
result = '/' |
||
elseif omitsep then |
|||
result = per_word |
|||
elseif unit1 then |
elseif unit1 then |
||
result = ' ' .. per_word .. ' ' |
result = ' ' .. per_word .. ' ' |
||
Line 2,499: | Line 2,614: | ||
else |
else |
||
result = (unit1 and variable_name(clean, unit1) or '') .. result .. variable_name('1', unit2) |
result = (unit1 and variable_name(clean, unit1) or '') .. result .. variable_name('1', unit2) |
||
end |
|||
if omitsep and not want_separator(result) then |
|||
unit_table.sep = '' |
|||
end |
end |
||
return make_link(unit_table.link, result, unit_table) |
return make_link(unit_table.link, result, unit_table) |
||
Line 2,504: | Line 2,622: | ||
if unit1 then |
if unit1 then |
||
result = linked_id(unit1, key_id, want_link, clean) .. result |
result = linked_id(unit1, key_id, want_link, clean) .. result |
||
if unit1.sep then |
|||
unit_table.sep = unit1.sep |
|||
end |
|||
elseif omitsep then |
|||
unit_table.sep = '' |
|||
end |
end |
||
return result .. linked_id(unit2, key_id2, want_link, '1') |
return result .. linked_id(unit2, key_id2, want_link, '1') |
||
Line 2,510: | Line 2,633: | ||
-- A multiplier (like "100" in "100km") forces the unit to be plural. |
-- A multiplier (like "100" in "100km") forces the unit to be plural. |
||
multiplier = from_en(multiplier) |
multiplier = from_en(multiplier) |
||
if |
if not omitsep then |
||
multiplier = multiplier .. ' ' |
multiplier = multiplier .. (abbr_on and ' ' or ' ') |
||
end |
|||
if not abbr_on then |
|||
multiplier = multiplier .. ' ' |
|||
if key_id == 'name1' then |
if key_id == 'name1' then |
||
key_id = 'name2' |
key_id = 'name2' |
||
Line 2,524: | Line 2,647: | ||
end |
end |
||
local id = unit_table.fixed_name or ((varname and not abbr_on) and variable_name(clean, unit_table) or unit_table[key_id]) |
local id = unit_table.fixed_name or ((varname and not abbr_on) and variable_name(clean, unit_table) or unit_table[key_id]) |
||
if omitsep and not want_separator(id) then |
|||
unit_table.sep = '' |
|||
end |
|||
if want_link then |
if want_link then |
||
local link = link_exceptions[unit_table.linkey or unit_table.symbol] or unit_table.link |
local link = link_exceptions[unit_table.linkey or unit_table.symbol] or unit_table.link |
||
Line 2,752: | Line 2,878: | ||
end |
end |
||
return preunit .. id1 |
return preunit .. id1 |
||
end |
|||
local disp_joins = text_code.disp_joins |
|||
local abbr = parms.abbr |
|||
local disp = parms.disp |
|||
if disp == nil then -- special case for the most common setting |
|||
parms.joins = disp_joins['b'] |
|||
elseif disp ~= 'x' then |
|||
-- Old template does this. |
|||
if disp == 'slash' then |
|||
if parms.abbr_org == nil then |
|||
disp = 'slash-nbsp' |
|||
elseif abbr == 'in' or abbr == 'out' then |
|||
disp = 'slash-sp' |
|||
else |
|||
disp = 'slash-nosp' |
|||
end |
|||
elseif disp == 'sqbr' then |
|||
if abbr == 'on' then |
|||
disp = 'sqbr-nbsp' |
|||
else |
|||
disp = 'sqbr-sp' |
|||
end |
|||
end |
|||
parms.joins = disp_joins[disp] or disp_joins['b'] |
|||
end |
end |
||
if parms.opt_also_symbol and not composite then |
if parms.opt_also_symbol and not composite then |
||
Line 2,783: | Line 2,885: | ||
end |
end |
||
end |
end |
||
if in_current.builtin == 'mach' then |
if in_current.builtin == 'mach' and first_unit.sep ~= '' then -- '' means omitsep with non-enwiki name |
||
local prefix = id1 .. ' ' |
local prefix = id1 .. ' ' |
||
local range = parms.range |
local range = parms.range |
||
Line 2,803: | Line 2,905: | ||
sep1 = '-' |
sep1 = '-' |
||
sep2 = '-' |
sep2 = '-' |
||
end |
|||
if omitsep and sep == '' then |
|||
-- Testing the id of the most significant unit should be sufficient. |
|||
sep1 = '' |
|||
sep2 = '' |
|||
end |
end |
||
local parts = { first_unit.valinfo[1].show .. sep1 .. id1 } |
local parts = { first_unit.valinfo[1].show .. sep1 .. id1 } |
||
Line 2,813: | Line 2,920: | ||
end |
end |
||
local result, mos |
local result, mos |
||
local abbr = parms.abbr |
|||
local range = parms.range |
local range = parms.range |
||
if range then |
if range then |
||
Line 2,887: | Line 2,995: | ||
return preunit .. id1 |
return preunit .. id1 |
||
end |
end |
||
if out_current.builtin == 'mach' then |
if out_current.builtin == 'mach' and out_current.sep ~= '' then -- '' means omitsep with non-enwiki name |
||
local prefix = id1 .. ' ' |
local prefix = id1 .. ' ' |
||
local range = parms.range |
local range = parms.range |
||
Line 3,055: | Line 3,163: | ||
else |
else |
||
id = out_current['symbol'] |
id = out_current['symbol'] |
||
end |
|||
if omitsep and i == 1 and not want_separator(id) then |
|||
-- Testing the id of the least significant unit should be sufficient. |
|||
sep1 = '' |
|||
sep2 = '' |
|||
end |
end |
||
if want_link then |
if want_link then |
||
Line 3,188: | Line 3,301: | ||
table.insert(outputs, item) |
table.insert(outputs, item) |
||
end |
end |
||
local sep = parms.table_joins and parms.table_joins[2] or |
local sep = parms.table_joins and parms.table_joins[2] or parms.join_between |
||
parts[part] = parms.opt_input_unit_only and '' or table.concat(outputs, sep) |
parts[part] = parms.opt_input_unit_only and '' or table.concat(outputs, sep) |
||
end |
end |