Jump to content
Toggle menu
  • 8 articles
  • 75 files
  • 7 users
  • 37.8K edits
Neodyland Wiki
Toggle preferences menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.
Module documentation[ create · purge ]
This documentation is transcluded from Module:Size/doc.
--[[
    Module:Size (lightweight Neodyland version)
    Displays 1D/2D/3D sizes with unit conversion between metric and imperial.
    Wikidata-free: unit names are taken from the static Module:I18n/units dictionary.
]]

local p = {}
local yesno = require('Module:Yesno')
local langSwitch = require('Module:LangSwitch')
local units = require('Module:I18n/units')

-- unit list order (indices used in conversions)
-- 1..6   metric length: nm, um, mm, cm, m, km
-- 7..10  imperial length: in, ft, yd, mi
local unitList = { 'nm', 'um', 'mm', 'cm', 'm', 'km', 'in', 'ft', 'yd', 'mi' }
local unitType = { 'm', 'm', 'm', 'm', 'm', 'm', 'i', 'i', 'i', 'i' }

-- meters per unit
local unitMult = {
	1e-9,   -- nm
	1e-6,   -- um
	1e-3,   -- mm
	1e-2,   -- cm
	1,      -- m
	1e3,    -- km
	0.0254, -- in
	0.3048, -- ft
	0.9144, -- yd
	1609.344 -- mi
}

local dimOrder = { 'length', 'height', 'width', 'depth', 'thickness', 'diameter' }
local dimName = {
	length = 'Length', height = 'Height', width = 'Width', depth = 'Depth',
	thickness = 'Thickness', diameter = 'Diameter'
}

local function getUnitLabel(unit, lang)
	local tbl = units[unit]
	if tbl and tbl[lang] then return tbl[lang] end
	if tbl then
		-- fallback chain: en, then first entry
		if tbl.en then return tbl.en end
		for _, v in pairs(tbl) do return v end
	end
	return unit
end

local function formatNum(value, lang, precision)
	-- keep precision via format string
	local pat = '%.' .. tostring(precision) .. 'f'
	local str = string.format(pat, value)
	str = mw.ustring.gsub(str, "[,%.]0+$", "")
	return str
end

local function niceNumber(v, iMin, iMax)
	local s = 10
	if v < s * unitMult[iMin] then return iMin end
	for k = iMin, iMax - 1 do
		if v >= s * unitMult[k] and v < s * unitMult[k + 1] then return k end
	end
	return iMax
end

local function unitConversion(val, unit, prec, lang)
	-- find k1 (input unit index)
	local k1
	for i, u in ipairs(unitList) do
		if u == unit then k1 = i; break end
	end
	if not k1 then return nil end

	local valInM = val * unitMult[k1]
	local k2
	if unitType[k1] == 'm' then
		k2 = niceNumber(valInM, 7, 10)
	else
		k2 = niceNumber(valInM, 1, 6)
	end
	local factor = unitMult[k1] / unitMult[k2]
	local unit1 = getUnitLabel(unit, lang)
	local unit2 = getUnitLabel(unitList[k2], lang)
	return unit1, unit2, factor, unitType[k1]
end

local function oneDim(val, unit, prec, lang, withLabel)
	local unit1, unit2, factor, system = unitConversion(val, unit, prec, lang)
	if not unit1 then return '' end
	local wordsep = mw.message.new('Word-separator'):inLanguage(lang):plain()
	local numStr1 = formatNum(val, lang, prec) .. wordsep .. unit1
	if (lang ~= 'en' and lang ~= 'en-US' and system == 'm') or system == '' then
		return numStr1
	end
	local numStr2 = formatNum(val * factor, lang, prec)
	local str = numStr1 .. wordsep .. '(' .. numStr2 .. wordsep .. unit2 .. ')'
	str = mw.ustring.gsub(str, '(%d)%s(%p)', '%1%2')
	return str
end

function p._size(args, unit, prec, lang)
	if not prec then prec = 1; if unit == 'mm' then prec = 2 end end
	local colon = mw.message.new('Colon-separator'):inLanguage(lang):plain()
	local semicolon = mw.message.new('Semicolon-separator'):inLanguage(lang):plain()
	local wordsep = mw.message.new('Word-separator'):inLanguage(lang):plain()

	local results = {}
	for _, field in ipairs(dimOrder) do
		local val = args[field]
		if val then
			val = string.gsub(val, ',', '.')
			val = tonumber(val)
			if type(val) == 'number' and val == val then
				local label = dimName[field]
				local dimStr = label .. colon
				local valStr = oneDim(val, unit, prec, lang)
				table.insert(results, dimStr .. valStr)
			end
		end
	end
	return table.concat(results, semicolon)
end

function p._sizeOld(args, unit, prec, lang)
	-- unnamed positional dimensions: {{Size|unit|12|98}}
	if not prec then prec = 1; if unit == 'mm' then prec = 2 end end
	local wordsep = mw.message.new('Word-separator'):inLanguage(lang):plain()
	local semicolon = mw.message.new('Semicolon-separator'):inLanguage(lang):plain()
	local results = {}
	local vals = {}
	for i = 1, 4 do
		if args[i] then
			local v = string.gsub(args[i], ',', '.')
			v = tonumber(v)
			if type(v) == 'number' and v == v then table.insert(vals, v) end
		end
	end
	for _, v in ipairs(vals) do
		table.insert(results, oneDim(v, unit, prec, lang))
	end
	return table.concat(results, wordsep .. '×' .. wordsep)
end

function p.size(frame)
	local args = require('Module:Arguments').getArgs(frame)
	local unit = args[1] or args.unit or args.units
	if not unit then return '' end
	unit = units.aliases[unit] or unit
	-- normalize unit key
	local ukey
	for _, u in ipairs(unitList) do if u == unit then ukey = u; break end end
	if not ukey then
		-- check aliases
		for k, v in pairs(units.aliases) do
			if tostring(k):lower() == tostring(unit):lower() then ukey = v; break end
		end
	end
	if not ukey then return '' end
	unit = ukey

	local lang = args.lang or mw.language.getContentLanguage().code
	local prec = args.prec and tonumber(args.prec) or nil

	-- positional unnamed args
	local hasPos = args[1] or args[2] or args[3] or args[4]
	if hasPos then
		return p._sizeOld(args, unit, prec, lang)
	end
	return p._size(args, unit, prec, lang)
end

return p
Cookies help us deliver our services. By using our services, you agree to our use of cookies.