Toggle menu
Toggle preferences menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.
Revision as of 15:24, 6 March 2025 by Hloth (talk | contribs) (Created page with "-- Credit to https://dev.fandom.com/wiki/Module:Plural ------------------------------------------------------------------ -- Lua script, that replaces {{plural:}} magic word -- Some languages have specific plural logic, but the {{plural:}} can't provide it on English wikis (such as Dev Wiki) -- -- Feel free to add more languages -- The logic was based on localization guide ---- Source: https://docs.translatehouse.org/projects/localization-guide/en/latest/l10n/pluralform...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Documentation for this module may be created at Module:Plural/doc

-- Credit to https://dev.fandom.com/wiki/Module:Plural

------------------------------------------------------------------
-- Lua script, that replaces {{plural:}} magic word
-- Some languages have specific plural logic, but the {{plural:}} can't provide it on English wikis (such as Dev Wiki)
--
-- Feel free to add more languages
-- The logic was based on localization guide
---- Source: https://docs.translatehouse.org/projects/localization-guide/en/latest/l10n/pluralforms.html
------------------------------------------------------------------
local plural = {}

local i18n = require('Dev:i18n').loadMessages('Plural')
------------------------------------------------------------------
-- Logic realization
------------------------------------------------------------------
-- Plural logic with 2 forms
-- If the amount is not 1 then second form, else the first one
-- @param			{number} n — the amout of items
-- @param			{stirng} f — first form
-- @param			{stirng} s — second form
-- @return			{sting} $f or $s
local function plural_not_one(n, f, s)
	return n ~= 1 and s or f
end


-- Plural logic with 2 forms
-- If the amount is more than 1 then second form, else the first one
-- @param			{number} n — the amout of items
-- @param			{stirng} f — first form
-- @param			{stirng} s — second form
-- @return			{sting} $f or $s
local function plural_more_than_one(n, f, s)
	return n > 1 and s or f
end

-- Plural logic for Russian, Serbian, and Ukrainian
-- These languages have 3 plural forms
-- First form — if the amount ends with 1, but is not 11
-- Second form — if the amount ends with 2, 3, or 4, but is not 12, 13, or 14.
-- Third form — anything else
-- @param			{number} n — the amout of items
-- @param			{stirng} f — first form
-- @param			{stirng} s — second form
-- @param			{stirng} t — third form
-- @return			{sting} $f, $s, or $t
local function plural_rus_and_serb(n, f, s, t)
	if n % 10 == 1 and n % 100 ~= 11 then
		return f
	elseif (n % 10 >= 2 and n % 10 <= 4) and (n % 100 < 10 or n % 100 >= 20) then
		return s
	else
		return t
	end
end

-- Plural logic for Polish
-- This language has 3 plural forms
-- First form — if the amount is 1
-- Second form — if the amount ends with 2, 3, or 4, but is not 12, 13, or 14.
-- Third form — anything else
-- @param			{number} n — the amout of items
-- @param			{stirng} f — first form
-- @param			{stirng} s — second form
-- @param			{stirng} t — third form
-- @return			{sting} $f, $s, or $t
local function plural_polish(n, f, s, t)
	if n == 1 then
		return f
	elseif (n % 10 >= 2 and n % 10 <= 4) and (n % 100 < 10 or n % 100 >= 20) then
		return s
	else
		return t
	end
end

-- Plural logic for Romanian
-- This language has 3 plural forms
-- First form — if the amount is 1
-- Second form — if the amount is between 2 and 20 or equals 0
-- Third form — anything else
-- @param			{number} n — the amout of items
-- @param			{stirng} f — first form
-- @param			{stirng} s — second form
-- @param			{stirng} t — third form
-- @return			{sting} $f, $s, or $t
local function plural_romanian(n, f, s, t)
	if n == 1 then
		return f
	elseif n == 0 or (n % 100 > 0 and n % 100 < 20) then
		return s
	else
		return t
	end
end

-- Plural logic for Slovenian
-- This language has 3 plural forms
-- First form — if the amount is 1
-- Second form — if the amount is between 2 and 20 or equals 0
-- Third form — anything else
-- @param			{number} n — the amout of items
-- @param			{stirng} f — first form
-- @param			{stirng} s — second form
-- @param			{stirng} t — third form
-- @param			{stirng} fo — fourth form
-- @return			{sting} $f, $s, or $t
local function plural_slovenian(n, f, s, t, fo)
	if n % 100 == 1 then
		return f
	elseif n % 100 == 2 then
		return s
	elseif n % 100 == 3 or n % 100 == 4 then
		return t
	else
		return fo
	end
end

-- Plural logic for Arabic
-- This language has 6 plural forms
-- First form — if the amount is 0
-- Second form — if the amount is 1
-- Third form — if the amount is 2
-- Fourth form — if the amount ends with 3, 4 ... 9, or 10
-- Fifth form — if the amount ends with 11, 12 ... 98, or 99
-- Sixth form — anything else
-- @param			{number} n — the amout of items
-- @param			{stirng} first — first form
-- @param			{stirng} second — second form
-- @param			{stirng} third — third form
-- @param			{stirng} fourth — fourth form
-- @param			{stirng} fifth — fifth form
-- @param			{stirng} sixth — sixth form
-- @return			{sting} $first, $second, $third, $fourth, $fifth, or $sixth
local function plural_arabic(n, first, second, third, fourth, fifth, sixth)
	if n == 0 then
		return first
	elseif n == 1 then
		return second
	elseif n == 2 then
		return third
	elseif n % 100 >= 3 and n % 100 <= 10 then
		return fourth
	elseif n % 100 >= 11 then
		return fifth
	else
		return sixth
	end
end


------------------------------------------------------------------
-- Tables
------------------------------------------------------------------
-- Table with language codes and functions, associated with them
local langcodes = {
	["af"] = plural_not_one,
	["az"] = plural_not_one,
	["be"] = plural_rus_and_serb,
	["ar"] = plural_arabic,
	["de"] = plural_not_one,
	["en"] = plural_not_one,
	["es"] = plural_not_one,
	["et"] = plural_not_one,
	["fa"] = plural_more_than_one,
	["fi"] = plural_not_one,
	["fr"] = plural_more_than_one,
	["hi"] = plural_not_one,
	["it"] = plural_not_one,
	["kk"] = plural_not_one,
	["ku"] = plural_not_one,
	["nl"] = plural_not_one,
	["pl"] = plural_polish,
	["pt"] = plural_not_one,
	["pt-br"] = plural_more_than_one,
	["ro"] = plural_romanian,
	["ru"] = plural_rus_and_serb,
	["sl"] = plural_slovenian,
	["sr"] = plural_rus_and_serb,
	["tg"] = plural_more_than_one,
	["tr"] = plural_more_than_one,
	["uk"] = plural_rus_and_serb,
	["zh"] = plural_more_than_one, -- Mandarin Chinese doesn't have plural forms in general, however the personal pronounses have plural forms
	["zh-hans"] = plural_more_than_one, -- Mandarin Chinese doesn't have plural forms in general, however the personal pronounses have plural forms
	["zh-hant"] = plural_more_than_one, -- Mandarin Chinese doesn't have plural forms in general, however the personal pronounses have plural forms
}

-- Table with language codes that have 3 plural forms
-- Table lookup is slightly faster and looks compact than comparing all values
-- {{if three_forms[lang_code] then}} is better than {{if lang_code = "ru" or lang_code = "ro" or lang_code = "pl" then}}
local three_forms = {
	["pl"] = true,
	["ro"] = true,
	["ru"] = true,
	["sr"] = true,
	["uk"] = true,
}

local abs = math.abs
------------------------------------------------------------------
-- Invocation of logic's functions
------------------------------------------------------------------
-- The main function that calls functions from "langcodes" table
-- @param			{table} args — table with all args
-- @param			{string} args.lang — language code (optional, content language by default)
-- @param			{number} args[1] — the amount of items
-- @param			{string} args[2] — first form
-- @param			{string} args[3] — second form
-- @param			{string} args[4] — third form (optional, depends on $lang parameter)
-- @param			{string} args[5] — fourth form (optional, depends on $lang parameter)
-- @param			{string} args[6] — fifth form (optional, depends on $lang parameter)
-- @param			{string} args[7] — sixth form (optional, depends on $lang parameter)
-- @return			{string} the plural form
function plural.getPlural(args)
	local lang_code = args["lang"] or mw.getContentLanguage().code
	
	local number = abs(tonumber(args[1]))
	local first = args[2] or ""
	local second = args[3] or ""
	
	if three_forms[lang_code] then
		local third = args[4] or ""
		return langcodes[lang_code](number, first, second, third)
	elseif lang_code == "sl" then
		local third = args[4] or ""
		local fourth = args[5] or ""
		return langcodes[lang_code](number, first, second, third, fourth)
	elseif lang_code == "ar" then
		local third = args[4] or ""
		local fourth = args[5] or ""
		local fifth = args[6] or ""
		local sixth = args[7] or ""
		
		return plural_arabic(number, first, second, third, fouth, fifth, sixth)
	elseif langcodes[lang_code] then
		return langcodes[lang_code](number, first, second)
	end
	
	error(i18n:msg('error', lang_code))
end


-- The function to call plural.getPlural() from {{#invoke}}
-- @param			{table} frame — the frame object
-- @return			{string} the plural form
function plural.main(frame)
	local args = frame:getParent().args
	return plural.getPlural(args)
end

return plural