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