Module:Detect singular

local p = {}

local getArgs = require('Module:Arguments').getArgs

local yesNo = require('Module:Yesno')

local getPlain = require('Module:Text').Text().getPlain

-- function to determine whether "sub" occurs in "s"

local function plainFind(s, sub)

return mw.ustring.find(s, sub, 1, true)

end

-- function to count the number of times "pattern" (a regex) occurs in "s"

local function countMatches(s, pattern)

local _, count = mw.ustring.gsub(s, pattern, '')

return count

end

local singular = 1

local likelyPlural = 2

local plural = 3

-- Determine whether a string is singular or plural (i.e., it represents one

-- item or many)

-- Arguments:

-- origArgs[1]: string to process

-- origArgs.no_comma: if false, use commas to detect plural (default false)

-- origArgs.parse_links: if false, treat wikilinks as opaque singular objects (default false)

-- Returns:

-- singular, likelyPlural, or plural (see constants above), or nil for completely unknown

function p._main(origArgs)

origArgs = type(origArgs) == 'table' and origArgs or {}

local args = {}

-- canonicalize boolean arguments

for key, default in pairs({no_comma=false,parse_links=false,any_comma=false,

no_and=false,parse_number=false}) do

if origArgs[key] == nil then

args[key] = default

else

args[key] = yesNo(origArgs[key],default)

end

end

local checkComma = not args.no_comma

local checkAnd = not args.no_and

local rewriteLinks = not args.parse_links

local anyComma = args.any_comma

local parseNumber = args.parse_number

local s = origArgs[1] -- the input string

if not s then

return nil -- empty input returns nil

end

s = tostring(s)

s = mw.text.decode(s,true) --- replace HTML entities (to avoid spurious semicolons)

if plainFind(s,'data-plural="0"') then -- magic data string to return true

return singular

end

if plainFind(s,'data-plural="1"') then -- magic data string to return false

return plural

end

-- count number of list items

local numListItems = countMatches(s,'<%s*li')

-- if exactly one, then singular, if more than one, then plural

if numListItems == 1 then

return singular

end

if numListItems > 1 then

return plural

end

-- if "list of" occurs inside of wlink, then it's plural

if mw.ustring.find(s:lower(), '%[%^%*list of[^%]]+%]%]') then

return plural

end

if parseNumber then

local m = tonumber(mw.ustring.match(s,"^%s*(%d+)"))

if m then

return m > 1 and plural or singular

end

end

-- fix for trailing br tags passed through template:marriage

s = mw.ustring.gsub(s, '<%s*br[^>]*>%s*(

)', '%1')

-- replace all wikilinks with fixed string

if rewriteLinks then

s = mw.ustring.gsub(s,'%b[]','WIKILINK')

end

-- Five conditions: any one of them can make the string a likely plural or plural

local hasBreak = mw.ustring.find(s,'<%s*br')

-- For the last 4, evaluate on string stripped of wikimarkup

s = getPlain(s)

local hasBullets = countMatches(s,'%*+') > 1

local multipleQids = mw.ustring.find(s,'Q%d+[%p%s]+Q%d+') -- has multiple QIDs in a row

if hasBullets or multipleQids then

return plural

end

local commaPattern = anyComma and '[,;]' or '%D[,;]%D' -- semi-colon similar to comma

local hasComma = checkComma and mw.ustring.find(s, commaPattern)

local hasAnd = checkAnd and mw.ustring.find(s,'[,%s]and%s')

if hasBreak or hasComma or hasAnd then

return likelyPlural

end

return singular

end

function p._pluralize(args)

args = type(args) == 'table' and args or {}

local singularForm = args[3] or args.singular or ""

local pluralForm = args[4] or args.plural or ""

local likelyForm = args.likely or pluralForm

local link = args[5] or args.link

if link then

link = tostring(link)

singularForm = ''..singularForm..''

pluralForm = ''..pluralForm..''

likelyForm = ''..likelyForm..''

end

if args[2] then

return pluralForm

end

local detect = p._main(args)

if detect == nil then

return "" -- return blank on complete failure

end

if detect == singular then

return singularForm

elseif detect == likelyPlural then

return likelyForm

else

return pluralForm

end

end

function p.main(frame)

local args = getArgs(frame)

-- For template, return 1 if singular, blank if plural or empty

local result = p._main(args)

if result == nil then

return 1

end

return result == singular and 1 or ""

end

function p.pluralize(frame)

local args = getArgs(frame)

return p._pluralize(args)

end

return p