--[===[
MODULE "MENSB" (english substantivo)
"eo.wiktionary.org/wiki/Modulo:mensb" <!--2024-Jun-26-->
"id.wiktionary.org/wiki/Modul:mensb"
Purpose: generates a declension table for an English noun,
correctly guessing the plural form for ca 99.9 % of words
Utilo: generas deklinacian tabelon por angla substantivo,
divenas gxuste pluralan formon por ca 99.9 % da vortoj
Manfaat: menghasilkan tabel deklinasi untuk nomina Inggris,
membuat bentuk jamak dengan benar untuk ca 99.9 % kata
Syfte: genererar en tabell foer ett engelskt substantiv,
gissar raett pluralformen foer ca 99.9 % av ord
Used by templates / Uzata far sxablonoj /
Digunakan oleh templat / Anvaent av mallar:
* {{tf-en-sb}}
This module can accept parameters whether sent to itself (own frame) or
to the caller (caller's frame). If there is a parameter "caller=true"
on the own frame then that own frame is discarded in favor of the
caller's one.
Parameters: * 0 to 6 optional named parameters
* "sa=" : alternative singular form
* "pl=" : explicit plural form that replaces the
automatically generated one
* "pa=" : alternative plural form
* "paa=" : alternative other plural form
* "not=" : notes
* "pagenameoverridetestonly=" : base form of the word, replaces
peeking it from "{{PAGENAME}}"
Returned: * one big string containing the complete table
Possible errors:
* anon parameter
* pagename or "sa=" is shorter than 2 or longer than 100 characters
* pagename or "sa=" contains double apos '' or rectangular brackets [[ ]]
* pagename is equal "sa="
Following rules are applied (this works only with lowercase letters):
* consonant + "y" -> drop "y" add "ies" (try -> tries)
* consonant + "o" -> add "es" (hero -> heroes)
* ends with "s" "x" "z" "ch" "sh" -> add "es"
(virus, fix, ??, reach, clutch, bush)
* otherwise -> add "s"
DO NOT use this module for inherently uncountable
nouns like "news".
NE UZU cxi tiun modulon por kerne nenombreblaj
substantivoj kiel "news".
JANGAN menggunakan modul ini untuk nomina tidak
terhitung seperti "news".
This module is unbreakable (when called with correct module name
and function name).
Cxi tiu modulo estas nerompebla (kiam vokita kun gxustaj nomo de modulo
kaj nomo de funkcio).
Usage examples with results / Ekzemploj de uzo
kun rezultoj / Contoh penggunaan dengan hasil:
{{hr3}} <!-------------------------------->
* #T00 page "star"
* expected result: "stars"
* actual result: "{{#invoke:mensb|ek|pagenameoverridetestonly=star}}"
* {{nevideblafinodesekcio}}
::* #T01 page "try"
::* expected result: "tries"
::* actual result: "{{#invoke:mensb|ek|pagenameoverridetestonly=try}}"
::* {{nevideblafinodesekcio}}
* #T02 page "play"
* expected result: "plays"
* actual result: "{{#invoke:mensb|ek|pagenameoverridetestonly=play}}"
* {{nevideblafinodesekcio}}
::* #T03 page "hero"
::* expected result: "heroes"
::* actual result: "{{#invoke:mensb|ek|pagenameoverridetestonly=hero}}"
::* {{nevideblafinodesekcio}}
* #T04 page "loo"
* expected result: "loos"
* actual result: "{{#invoke:mensb|ek|pagenameoverridetestonly=loo}}"
* {{nevideblafinodesekcio}}
::* #T05 page "metro"
::* expected result: "metroes" (ungrammatical, use "pl" to override)
::* actual result: "{{#invoke:mensb|ek|pagenameoverridetestonly=metro}}"
::* {{nevideblafinodesekcio}}
* #T06 page "zero"
* expected result: "zeroes" (uncommon form, use "pl" to override)
* actual result: "{{#invoke:mensb|ek|pagenameoverridetestonly=zero}}"
* {{nevideblafinodesekcio}}
::* #T07 page "zero", using "pl=zeros" override
::* expected result: "zeros"
::* actual result: "{{#invoke:mensb|ek|pl=zeros|pagenameoverridetestonly=zero}}"
::* {{nevideblafinodesekcio}}
* #T08 page "thief"
* expected result: "thiefs" (ungrammatical, use "pl" to override)
* actual result: "{{#invoke:mensb|ek|pagenameoverridetestonly=thief}}"
* {{nevideblafinodesekcio}}
{{hr3}} <!-------------------------------->
* #T10 page "HERO" (dubious)
* expected result: "HEROs" (dubious)
* actual result: "{{#invoke:mensb|ek|pagenameoverridetestonly=HERO}}"
* {{nevideblafinodesekcio}}
::* #T11 page "virus"
::* expected result: "viruses" (YES this is the most correct form)
::* actual result: "{{#invoke:mensb|ek|pagenameoverridetestonly=virus}}"
::* {{nevideblafinodesekcio}}
* #T12 page "fix"
* expected result: "fixes"
* actual result: "{{#invoke:mensb|ek|pagenameoverridetestonly=fix}}"
* {{nevideblafinodesekcio}}
::* #T13
::* expected result: "reaches"
::* actual result: "{{#invoke:mensb|ek|pagenameoverridetestonly=reach}}"
::* {{nevideblafinodesekcio}}
* #T14
* expected result: "bushes"
* actual result: "{{#invoke:mensb|ek|pagenameoverridetestonly=bush}}"
* {{nevideblafinodesekcio}}
::* #T15
::* expected result: "paths" (good although difficult to pronounce, NOT "pathes")
::* actual result: "{{#invoke:mensb|ek|pagenameoverridetestonly=path}}"
::* {{nevideblafinodesekcio}}
{{hr3}} <!-------------------------------->
* #T20 page "news"
* expected result: "newses" (ungrammatical, don't apply this module to uncountable nouns)
* actual result: "{{#invoke:mensb|ek|pagenameoverridetestonly=news}}"
* {{nevideblafinodesekcio}}
* #T21 page "short story"
* expected result: "short stories" (multiword lemma)
* actual result: "{{#invoke:mensb|ek|pagenameoverridetestonly=short story}}"
* {{nevideblafinodesekcio}}
* #T22 page "ii"
* expected result: "iis" (nonsense)
* actual result: "{{#invoke:mensb|ek|pagenameoverridetestonly=ii}}"
* {{nevideblafinodesekcio}}
<pre>
* #T23 page "i"
* expected result: "bad usage"
* actual result: "{{#invoke:mensb|ek|pagenameoverridetestonly=i}}"
* {{nevideblafinodesekcio}}
* #T24 undesirable anonymous parameter
* expected result: "bad usage"
* actual result: "{{#invoke:mensb|ek|test|pagenameoverridetestonly=test}}"
* {{nevideblafinodesekcio}}
</pre>
{{hr3}} <!-------------------------------->
* note that all tests depend on "nevideblafinodesekcio"
* note that tests #T23 #T24 cannot be reasonably executed on the docs subpage without help of "pate" or "debu"
{{hr3}} <!-------------------------------->
]===]
local exporttable = {}
------------------------------------------------------------------------
---- CONSTANTS [O] ----
------------------------------------------------------------------------
-- uncommentable EO vs ID constant strings (core site-related features)
local constrpriv = "eo" -- EO (privileged site language)
-- local constrpriv = "id" -- ID (privileged site language)
-- surrogate transcoding table (only needed for EO)
local contabtransluteo = {}
contabtransluteo[ 67] = 0xC488 -- CX
contabtransluteo[ 99] = 0xC489 -- cx
contabtransluteo[ 71] = 0xC49C -- GX
contabtransluteo[103] = 0xC49D -- gx
contabtransluteo[ 74] = 0xC4B4 -- JX
contabtransluteo[106] = 0xC4B5 -- jx
contabtransluteo[ 83] = 0xC59C -- SX
contabtransluteo[115] = 0xC59D -- sx
contabtransluteo[ 85] = 0xC5AC -- UX breve
contabtransluteo[117] = 0xC5AD -- ux breve
-- constant strings (generic & misc)
local constrfrco = '707070' -- table frame color (grey)
local constrbkti = 'B0D0F0' -- title cell background color (light grey/blue)
local constrtdsty = '<td style="border:0.25em solid #' .. constrfrco .. ';padding:0.4em;'
local constrtdbg2 = constrtdsty .. '">' -- NOT centered
local constrtdbg4 = constrtdsty .. 'text-align:center;" colspan="2">' -- centered & cs 2
local constrtdbg6 = constrtdsty .. 'background:#' .. constrbkti .. ';text-align:center;" colspan="2">' -- centered & bk & cs 2
-- constant strings (error circumfixes)
local constrlaxhu = ' ** ' -- lagom -> huge circumfix " ** "
local constrelabg = '<span class="error"><b>' -- lagom whining begin
local constrelaen = '</b></span>' -- lagom whining end
local constrehubg = constrlaxhu .. constrelabg -- huge whining begin
local constrehuen = constrelaen .. constrlaxhu -- huge whining end
-- uncommentable EO vs ID constant strings (error messages, depends from above "error circumfixes" section)
local constrbadu = constrehubg .. 'Erara uzo de sxablono "tf-en-sb", legu gxian dokumentajxon' .. constrehuen -- EO
-- local constrbadu = constrehubg .. 'Penggunaan salah templat "tf-en-sb", bacalah dokumentasinya' .. constrehuen -- ID
local constrkates = '[[Kategorio:Erara uzo de sxablono]]' -- EO
local constrkatel = '[[Kategorio:Erara uzo de sxablono (tf-en-sb)]]' -- EO
-- local constrkates = '[[Kategori:Penggunaan salah templat]]' -- ID
-- local constrkatel = '[[Kategori:Penggunaan salah templat (tf-en-sb)]]' -- ID
-- uncommentable EO vs ID constant strings (misc)
local constrtit = 'Angla substantivo<br><small>English noun</small>' -- EO
-- local constrtit = 'Nomina Inggris<br><small>English noun</small>' -- ID
local constrsng = '[[singularo|Singularo]]' -- EO
-- local constrsng = '[[bentuk tunggal|Tunggal (singular)]]' -- ID
local constrplu = '[[pluralo|Pluralo]]' -- EO
-- local constrplu = '[[bentuk jamak|Jamak (plural)]]' -- ID
local constralt = 'alternative' -- EO
-- local constralt = 'alternatif' -- ID
------------------------------------------------------------------------
---- MATH FUNCTIONS [E] ----
------------------------------------------------------------------------
local function mathdiv (xdividend, xdivisor)
local resultdiv = 0 -- DIV operator lacks in LUA :-(
resultdiv = math.floor (xdividend / xdivisor)
return resultdiv
end--function mathdiv
local function mathmod (xdividendo, xdivisoro)
local resultmod = 0 -- MOD operator is "%" and bitwise AND operator lack too
resultmod = xdividendo % xdivisoro
return resultmod
end--function mathmod
------------------------------------------------------------------------
---- HIGH LEVEL STRING FUNCTIONS [I] ----
------------------------------------------------------------------------
local function lfinememlig (strmem,strtuff) -- prevents self-linking
if (strmem~=strtuff) then
strtuff = '[[' .. strtuff .. ']]'
end--if
return strtuff
end--function lfinememlig
------------------------------------------------------------------------
-- Local function LFICHECKFORBAD
-- Output : * booisbaad -- true if string is bad
local function lficheckforbad (streniro)
local booisbaad = false
booisbaad = (string.find(streniro,"''",1,true)~=nil) -- dbl apo
booisbaad = booisbaad or (string.find(streniro,"[[",1,true)~=nil)
booisbaad = booisbaad or (string.find(streniro,"]]",1,true)~=nil)
return booisbaad
end--function lficheckforbad
------------------------------------------------------------------------
-- Local function LFIKODEOSG
-- Transcode eo X-surrogates to cxapeloj in a single string (eo only).
-- Input : * streosurr -- ANSI string (empty is useless but cannot
-- cause major harm)
-- Output : * strutf8eo -- UTF8 string
-- Depends on functions :
-- [E] mathdiv mathmod
-- Depends on constants :
-- * table "contabtransluteo" inherently holy
-- * the "x" in a surr pair is case insensitive,
-- for example both "kacxo" and "kacXo" give same result
-- * avoid "\", thus for example "ka\cxo" would get converted but the "\" kept
-- * double "x" (both case insensitive) prevents conversion and becomes
-- reduced to single "x", for example "kacxxo" becomes "kacxo"
local function lfikodeosg (streosurr)
local vareopeek = 0
local strutf8eo = ''
local numeoinplen = 0
local numinpinx = 0 -- ZERO-based source index
local numknar0k = 0 -- current char
local numknaf1x = 0 -- next char (ZERO is NOT valid)
local numknaf2x = 0 -- post next char (ZERO is NOT valid)
local boonext1x = false
local boonext2x = false
local boosudahdone = false
numeoinplen = string.len(streosurr)
while true do
if (numinpinx>=numeoinplen) then
break
end--if
numknar0k = string.byte(streosurr,(numinpinx+1),(numinpinx+1))
numknaf1x = 0 -- preASSume no char
numknaf2x = 0 -- preASSume no char
if ((numinpinx+1)<numeoinplen) then
numknaf1x = string.byte(streosurr,(numinpinx+2),(numinpinx+2))
end--if
if ((numinpinx+2)<numeoinplen) then
numknaf2x = string.byte(streosurr,(numinpinx+3),(numinpinx+3))
end--if
boonext1x = ((numknaf1x==88) or (numknaf1x==120)) -- case insensitive
boonext2x = ((numknaf2x==88) or (numknaf2x==120)) -- case insensitive
boosudahdone = false
if (boonext1x and boonext2x) then -- got "xx"
strutf8eo = strutf8eo .. string.char(numknar0k,numknaf1x) -- keep one "x" only
numinpinx = numinpinx + 3 -- eaten 3 written 2
boosudahdone = true
end--if
if (boonext1x and (not boonext2x)) then -- got yes-"x" and no-"x"
vareopeek = contabtransluteo[numknar0k] -- UINT16 or type "nil"
if (type(vareopeek)=='number') then
strutf8eo = strutf8eo .. string.char(mathdiv(vareopeek,256),mathmod(vareopeek,256)) -- add UTF8 char
numinpinx = numinpinx + 2 -- eaten 2 written 2
boosudahdone = true
end--if
end--if
if (not boosudahdone) then
strutf8eo = strutf8eo .. string.char(numknar0k) -- copy char
numinpinx = numinpinx + 1 -- eaten 1 written 1
end--if
end--while
return strutf8eo
end--function lfikodeosg
------------------------------------------------------------------------
---- VARIABLES [R] ----
------------------------------------------------------------------------
function exporttable.ek (arxframent)
-- general unknown type
local vartmp = 0 -- variable without type multipurpose
-- special type "args" AKA "arx"
local arxsomons = 0 -- metaized "args" from our own or caller's "frame"
-- param str
local strsa = '' -- this MUST be DIFFERENT from "strpgnm" otherwise ERROR
local strpl = ''
local strpa = ''
local strpaa = ''
local strnot = ''
-- general str
local strpgnm = '' -- base form f "{{PAGENAME}}" "pagenameoverridetestonly"
local stralti = '' -- "br" .. "small" .. alt .. "/small" .. "br"
local strret = '' -- output string
-- general num
local numlength = 0 -- length of the input word
local numlast = 0 -- last char
local numbela = 0 -- char before the last one
-- general boo
local booerr = false -- fatal error flag
local boovowe = false -- vowel flag for char in "numbela" (false if consonant)
local boocut = false -- cut off last letter flag
local booiii = false -- add "i" flag
local booeee = false -- add "e" flag
------------------------------------------------------------------------
---- MAIN [Z] ----
------------------------------------------------------------------------
---- TRANSCODE EO IF NEEDED ----
if (constrpriv=="eo") then
constrbadu = lfikodeosg(constrbadu)
constrkates = lfikodeosg(constrkates)
constrkatel = lfikodeosg(constrkatel)
end--if
---- GET THE ARX (ONE OF TWO) ----
-- must be seized independently on "numerr" even if we already suck
-- give a f**k in possible params other than "caller=true"
arxsomons = arxframent.args -- "args" from our own "frame"
if (type(arxsomons)~='table') then
arxsomons = {} -- guard against indexing error from our own
booerr = true
end--if
if (arxsomons['caller']=='true') then
arxsomons = arxframent:getParent().args -- "args" from caller's "frame"
end--if
if (type(arxsomons)~='table') then
arxsomons = {} -- guard against indexing error again
booerr = true
end--if
---- SEIZE 6 OPTIONAL NAMED PARAMS ----
if ((arxsomons[1])~=nil) then
booerr = true -- anonymous parameters NOT appreciated
end--if
if (not booerr) then
vartmp = arxsomons['sa']
if (type(vartmp)=='string') then
strsa = vartmp
end--if
vartmp = arxsomons['pl']
if (type(vartmp)=='string') then
strpl = vartmp
end--if
vartmp = arxsomons['pa']
if (type(vartmp)=='string') then
strpa = vartmp
end--if
vartmp = arxsomons['paa']
if (type(vartmp)=='string') then
strpaa = vartmp
end--if
vartmp = arxsomons['not']
if (type(vartmp)=='string') then
strnot = vartmp
end--if
vartmp = arxsomons['pagenameoverridetestonly']
if (type(vartmp)=='string') then
strpgnm = vartmp
end--if
end--if
---- SEIZE THE PAGENAME IF NEEDED ----
if ((not booerr) and (strpgnm=='')) then
vartmp = mw.title.getCurrentTitle().text
if (type(vartmp)=="string") then
if (string.len(vartmp)~=0) then
strpgnm = vartmp
end--if
end--if
if (strpgnm=='') then
booerr = true -- would result in "bad usage" but is hopefully impossible
end--if
end--if
---- CHECK WORD LENGTH (NO EN NOUN HAS ONLY 1 LETTER) AND MORE ----
if (not booerr) then
numlength = string.len (strpgnm) -- length of the input word
booerr = (numlength<2) or (numlength>100) or lficheckforbad(strpgnm)
end--if
if ((not booerr) and (strsa~='')) then
numlength = string.len (strsa) -- length of alt singular
booerr = (numlength<2) or (numlength>100) or lficheckforbad(strsa)
end--if
if (not booerr) then
booerr = (strpgnm==strsa) -- must NOT be same
end--if
---- BREW PLURAL FORM IF NEEDED ----
if ((not booerr) and (strpl=='')) then
numlast = string.byte (strpgnm,-1,-1) -- last
numbela = string.byte (strpgnm,-2,-2) -- before last
boovowe = (numbela==97) or (numbela==101) or (numbela==105) or (numbela==111) or (numbela==117) -- damn, it's a vowel
if (not boovowe) then
if (numlast==121) then -- "y" -> "ies"
boocut = true
booiii = true -- "activity" -> "activities" but "key" -> "keys"
booeee = true
end--if
if (numlast==111) then -- "o"
booeee = true -- "hero" -> "heroes" but "kangaroo" -> "kangaroos"
end--if
end--if
booeee = booeee or (numlast==115) or (numlast==120) or (numlast==122) -- "s" "x" "z"
if (numlast==104) then
booeee = booeee or (numbela==99) or (numbela==115) -- "ch" "sh" but not "th"
end--if
if (boocut) then
strpl = string.sub (strpgnm,1,-2) -- omit last letter
else
strpl = strpgnm -- take it all
end--if
if (booiii) then
strpl = strpl .. 'i'
end--if
if (booeee) then
strpl = strpl .. 'e'
end--if
strpl = strpl .. 's' -- always add "s"
end--if
---- BREW EITHER THE TABLE OR ERROR ----
if (not booerr) then
stralti = '<br><small>' .. constralt .. '</small><br>'
strret = '<table style="float:right;clear:right;margin:0.3em 0 0.3em 0.3em;border:0.25em solid #'
strret = strret .. constrfrco .. ';border-collapse:collapse;">'
strret = strret .. '<tr>' .. constrtdbg6 .. constrtit .. '</td></tr>'
strret = strret .. '<tr>' .. constrtdbg2 .. '<b>' ..constrsng .. '</b></td>'
strret = strret .. constrtdbg2 .. '<b>' .. constrplu .. '</b></td></tr>'
strret = strret .. '<tr>' .. constrtdbg2 .. strpgnm
if (strsa~='') then -- lfinememlig NOT needed here
strret = strret .. stralti .. '[[' .. strsa .. ']]'
end--if
strret = strret .. '</td>' .. constrtdbg2 .. lfinememlig(strpgnm,strpl)
if (strpa~='') then
strret = strret .. stralti .. lfinememlig(strpgnm,strpa)
end--if
if (strpaa~='') then
strret = strret .. stralti .. lfinememlig(strpgnm,strpaa)
end--if
strret = strret .. '</td></tr>'
if (strnot~='') then
strret = strret .. '<tr>' .. constrtdbg4 .. '<small>' .. strnot .. '</small></td></tr>'
end--if
strret = strret .. '</table>'
else
strret = constrbadu .. constrkates .. constrkatel -- "bad usage" and diag
end--if
---- RETURN THE JUNK STRING ----
return strret
end--function
---- RETURN THE JUNK TABLE ----
return exporttable