Modul:ms-headword
Penampilan
- Laman modul ini kekurangan sublaman pendokumenan. Sila cipta laman pendokumenan tersebut.
- Pautan berguna: senarai sublaman • pautan • transklusi • kes ujian • kotak pasir
local export = {}
local pos_functions = {}
local require = require
local require_when_needed = require("Module:require when needed")
-- Modules
local m_debug_track = require("Module:debug/track")
local m_head = require("Module:headword")
local m_params = require("Module:parameters")
-- Utilities
local u = require_when_needed("Module:string/char")
local PAGENAME = mw.loadData("Module:headword/data").pagename
-- Languages
local lang = require("Module:languages").getByCode("ms")
local langname = lang:getCanonicalName()
local script = lang:findBestScript(PAGENAME) -- Latn or ms-Arab
local function track(page)
return m_debug_track("ms-headword/" .. page)
end
function export.show(frame)
local iparams = {
[1] = {required = true},
}
local params = {
["head"] = {list = true, disallow_holes = true},
["j"] = {list = true, disallow_holes = true},
["id"] = true,
["sort"] = true,
["nolinkhead"] = {type = "boolean"},
["json"] = {type = "boolean"},
["pagename"] = true, -- for testing
}
local par_args = frame:getParent().args
-- Process frame arguments i.e. {{#invoke:ms-headword|show|arguments}}
local iargs = m_params.process(frame.args, iparams)
local poscat = iargs[1]
-- Append additional params in pos_functions to global params in export.show
-- Necessary before processing parent args as each part of speech has their own parameters
-- See pos_functions["adjectives"] for example
if pos_functions[poscat] then
local posparams = pos_functions[poscat].params
if type(posparams) == "function" then
posparams = posparams(lang)
end
for key, val in pairs(posparams) do
params[key] = val
end
end
-- Process frame parent arguments i.e. {{ms-head|arguments}}
local args = m_params.process(par_args, params)
local pagename = args.pagename or mw.title.getCurrentTitle().text
local head = args.head and args.head[1] or PAGENAME
local data = {
lang = lang,
pos_category = poscat,
categories = {},
heads = args.head,
inflections = {},
pagename = pagename,
id = args.id,
sort_key = args.sort,
is_suffix = false,
translits = { "-" },
altform = script:getCode() == "ms-Arab"
}
if (mw.ustring.find(PAGENAME, "اء[وي]")) then
head = mw.ustring
.gsub(PAGENAME, "(ا)(ء)([وي])", "%1<sup>%2</sup>%3") -- superscript hamza
end
if args["ku"] ~= nil or args["mu"] ~= nil or args["nya"] ~= nil then
track("clean up kumunya")
end
if args["r"] ~= nil then
track("clean up r")
end
local function any_match(pattern)
if head and mw.ustring.find(head, pattern) then
return true
end
for _, v in ipairs(args["j"]) do
if v and mw.ustring.find(v, pattern) then
return true
end
end
return false
end
if any_match("[كڬ]") then
track("incorrect Jawi spelling")
error("Please use ک in place of ك and ݢ in place of ڬ.")
end
-- special marks: ZWSP (200B), ZWNJ (200C), ZWJ (200D), LRM (200E), RLM (200F)
local special_marks = "[" .. u(0x200B) .. "-" .. u(0x200F) .. "]"
if any_match(special_marks) then
track("incorrect Jawi spelling")
error("Please remove ZWSP, ZWNJ, ZWJ, LRM, and RLM from title/headword.")
end
local letter_replacements = { ["M"] = "m", ["N#"] = "ng", ["R#"] = "r" }
local pattern_replacements = { }
function export.affixation(text, affix)
local first_consonant = ""
if affix == "-" then
text = text .. "-" .. text
elseif mw.ustring.match(affix, "^%-.+%-$") then
first_consonant = mw.ustring.match(text, "^[^aeiou]+")
text = first_consonant .. mw.ustring.sub(affix, 2, -2) ..
mw.ustring.sub(text, mw.ustring.len(first_consonant) + 1,
-1)
else
text = gsub(affix, "%-", "#" .. text)
text = gsub(text, "N([^aeiou]*)([aeiou])([^aeiou]*)$", "nge%1%2%3")
text = gsub(text, "meN#p", "memp")
for pattern, replacement in pairs(pattern_replacements) do
text = gsub(text, pattern, replacement)
end
text = gsub(text, "[MNR]#?", letter_replacements)
text = gsub(text, "#", "")
end
return text
end
local jawi = { label = "ejaan Jawi" }
for _, term in ipairs(args["j"]) do
if term then
table.insert(jawi, { term = term })
end
end
if #jawi > 0 then table.insert(data.inflections, jawi) end
if script:getCode() == "ms-Arab" then
table.insert(data.categories, "Perkataan dalam tulisan Arab bahasa Melayu")
end
if pos_functions[poscat] and pos_functions[poscat].func then
pos_functions[poscat].func(args, data)
end
return m_head.full_headword(data)
end
pos_functions["kata nama"] = {
params = {
[1] = {list = true},
["pl"] = {alias_of = 1, list = true},
},
func = function(args, data)
-- Main code for noun plurality
local jawi = data.altform
local pl1 = args[1][1]
-- Helpers -------------------------------------------------------
local function insert_plural(tbl, ...)
for _, v in ipairs(...) do
table.insert(tbl, v)
end
return tbl
end
local function make_default_plural(pagename, jawi)
local plural = {}
local jawi_letter = "ابتةثجچحخدذرزسشصضطظعغڠفڤقکݢلمنوۏهءيىڽ"
-- Auto-detect full reduplication
if pagename:match("^([a-zA-Z" .. jawi_letter .. "]+)%-%1$") then
table.insert(
plural, "[[" .. pagename .. "]]"
)
return plural
end
local subwords = mw.text.split(pagename, "%s")
local firstword = subwords[1]
local plural_entry = "[[" .. firstword .. "]]-[[" .. firstword .. "]]"
local plural_entry2_latn = "[[" .. firstword .. "]][[2#" .. langname .. "|<sup>2</sup>]]"
local plural_entry2_j = "[[" .. firstword .. "]][[ـ٢|٢]]"
if #subwords > 1 then
-- Concatenate all the remaining subwords (from 2 onward)
for i = 2, #subwords do
plural_entry = plural_entry .. " [[" .. subwords[i] .. "]]"
if jawi then
plural_entry2_j = plural_entry2_j .. " [[" .. subwords[i] .. "]]"
else
plural_entry2_latn = plural_entry2_latn .. " [[" .. subwords[i] .. "]]"
end
end
end
table.insert(
plural, plural_entry
)
if not jawi then
table.insert(
plural, plural_entry2_latn
)
end
if jawi then
table.insert(
plural, plural_entry2_j
) -- add "2" only first word
end
return plural
end
------------------------------------------------------------------
-- Unknown or uncertain and requests
if pl1 == "req" then
table.insert(data.categories, "Permintaan bentuk jamak dalam entri bahasa " .. langname)
elseif pl1 == "?" then
table.insert(data.categories, "Kata nama dengan bentuk jamak yang tidak diketahui atau tidak pasti bahasa " .. langname)
-- Uncountable and semi-countable
elseif pl1 == "-" then
table.insert(data.categories, "Kata nama tidak berbilang bahasa " .. langname)
table.insert(data.inflections, {label = "[[Lampiran:Glosari#tidak berbilang|tidak berbilang]]"})
elseif pl1 == "u" then
local pl_countable = {label = "biasanya [[Lampiran:Glosari#tidak berbilang|tidak berbilang]]"}
local pl_plural = {label = "jamak"}
table.insert(data.categories, "Kata nama berbilang bahasa " .. langname)
table.insert(data.categories, "Kata nama tidak berbilang bahasa " .. langname)
pl_plural = insert_plural(pl_plural, make_default_plural(data.pagename, jawi))
table.insert(data.inflections, pl_countable)
table.insert(data.inflections, pl_plural)
elseif pl1 == "~" then
local pl_countable = {label = "[[Lampiran:Glosari#berbilang|berbilang]] dan [[Lampiran:Glosari#tidak berbilang|tidak berbilang]]"}
local pl_plural = {label = "jamak"}
table.insert(data.categories, "Kata nama berbilang bahasa " .. langname)
table.insert(data.categories, "Kata nama tidak berbilang bahasa " .. langname)
pl_plural = insert_plural(pl_plural, make_default_plural(data.pagename, jawi))
table.insert(data.inflections, pl_countable)
table.insert(data.inflections, pl_plural)
elseif pl1 == "pt" or pl1 == "p" then
error(langname .. " doesn't have plurale tantum, please delete the causing parameter.")
elseif pl1 == "st" or pl1 == "s" then
error(langname .. " doesn't have singulare tantum, please replace the causing parameter with |-.")
elseif pl1 == "1" then
error("The parameter |pl=1 is invalid. Please specify the plurality with an existing value.")
else
-- Countable
local pl = {label = "jamak"}
if not pl1 or pl1 == "+" then
pl = insert_plural(pl, make_default_plural(data.pagename, jawi))
elseif pl1 == "a" then
pl = insert_plural(pl, make_default_plural(data.pagename, jawi))
table.insert(pl, "[[para]] " .. data.pagename)
elseif pl1 == "*" then
table.insert(pl, data.pagename)
elseif pl1 == "*+" then
table.insert(pl, data.pagename)
pl = insert_plural(pl, make_default_plural(data.pagename, jawi))
else
for _, i in ipairs(args[1]) do
local exclude = {
a = true,
u = true,
req = true,
["+"] = true,
["~"] = true,
["-"] = true,
["*"] = true,
["*+"] = true,
}
-- Incase first argument is used not as the first argument
-- (e.g. {{ms-noun|custom-plural|a}})
if not exclude[i] then
table.insert(pl, i)
end
end
end
table.insert(data.inflections, pl)
end
end
}
-- Generally, proper nouns don't have plural unless in certain cases
-- e.g. Muslim -> Muslimin
pos_functions["kata nama khas"] = {
params = {
[1] = {list = true}, -- allow custom plural
["pl"] = {alias_of = 1, list = true},
},
func = function(args, data)
local pl1 = args[1]
local pl = { label = "plural" }
if #pl1 ~= 0 then
for _, v in ipairs(pl1) do
table.insert(pl, v)
end
table.insert(data.inflections, pl)
end
end
}
pos_functions["kata kerja"] = {
params = {
[1] = { list = true },
active = { list = true },
bare = { list = true },
passive = { list = true },
},
func = function(args, data)
local base = data.pagename
local jawi = data.altform
local utf = mw.ustring
local equivalent_letters = {
k = "ک", t = "ت", s = "س", p = "ڤ",
}
-- Helpers -------------------------------------------------------
local function starts_with(s, prefix)
return utf.sub(s, 1, utf.len(prefix)) == prefix
end
local function first_n(s, n)
return utf.sub(s, 1, n)
end
local function rest_from(s, n)
return utf.sub(s, n + 1)
end
local function is_equiv(first, target)
return first == target or first == equivalent_letters[target]
end
local function is_vowel_char(ch)
return ch:match('[aiueoاويى]') ~= nil
end
local function vowel_count(s)
local count = 0
for ch in utf.gmatch(s, '.') do
if is_vowel_char(ch) then
count = count + 1
if count > 1 then return count end
end
end
return count
end
local function make_form(prefix_latin, prefix_jawi, use_jawi, rest)
if use_jawi then
return prefix_jawi .. (rest or base)
else
return prefix_latin .. (rest or base)
end
end
----------------------------------------------------------------
if args[1] and args[1][1] == "b" then
-- Default base forms
local default_active_latin = "me" .. base
local default_passive_latin = "di" .. base
local default_active_jawi = "م" .. base
local default_passive_jawi = "د" .. base
local active_form = jawi and default_active_jawi or default_active_latin
local passive_form = jawi and default_passive_jawi or default_passive_latin
-- If base has more than 1 vowel, use 'meng'/'مڠ' forms
local vcount = vowel_count(base)
if vcount == 1 then
-- For bases with exactly one vowel: use 'menge' / 'مڠ' + base
if jawi then
active_form = "مڠ" .. base
else
active_form = "menge" .. base
end
else
local first_two = first_n(base, 2)
local first = first_n(base, 1)
-- Mapping for special two-letter onsets
local two_map = {
["sy"] = { latin = "men", jawi = "من", strip = false },
["pl"] = { latin = "mem", jawi = "مم", strip = false },
["ڤل"] = { latin = "mem", jawi = "مم", strip = false },
["pr"] = { latin = "mem", jawi = "مم", strip = false },
["ڤر"] = { latin = "mem", jawi = "مم", strip = false },
["tr"] = { latin = "men", jawi = "من", strip = false },
["تر"] = { latin = "men", jawi = "من", strip = false },
["kl"] = { latin = "meng", jawi = "مڠ", strip = false },
["کل"] = { latin = "meng", jawi = "مڠ", strip = false },
["kr"] = { latin = "meng", jawi = "مڠ", strip = false },
["کر"] = { latin = "meng", jawi = "مڠ", strip = false },
["kh"] = { latin = "meng", jawi = "مڠ", strip = false },
}
if two_map[first_two] then
local m = two_map[first_two]
active_form = make_form(m.latin, m.jawi, jawi)
else
-- Single-letter rules and character-class rules
if is_equiv(first, "k") then
-- drop first letter and use meng / مڠ + rest
if jawi then
active_form = "مڠ" .. rest_from(base, 1)
else
active_form = "meng" .. rest_from(base, 1)
end
elseif is_equiv(first, "t") then
if jawi then
active_form = "من" .. rest_from(base, 1)
else
active_form = "men" .. rest_from(base, 1)
end
elseif is_equiv(first, "s") then
if jawi then
active_form = "مڽ" .. rest_from(base, 1)
else
active_form = "meny" .. rest_from(base, 1)
end
elseif is_equiv(first, "p") then
if jawi then
active_form = "مم" .. rest_from(base, 1)
else
active_form = "mem" .. rest_from(base, 1)
end
else
if first:match("[cjdzچجدزش]") then
active_form = make_form("men", "من", jawi)
elseif first:match("[bfvبفۏ]") then
active_form = make_form("mem", "مم", jawi)
elseif first:match("[ghݢݢحخهة]") then
active_form = make_form("meng", "مڠ", jawi)
elseif is_vowel_char(first) then
active_form = make_form("meng", "مڠ", jawi)
else
-- fallback: default 'me' form
active_form = jawi and ("م" .. base) or ("me" .. base)
end
end
end
end
args.active = { active_form }
args.passive = { passive_form }
elseif args[1] and args[1][1] == "a" then
if #args[1] > 2 then
error("This template doesn't use argument |3=")
elseif #args[1] < 2 then
error("Please enter the bare form of the verb.")
end
local default_passive_latin = "di" .. args[1][2]
local default_passive_jawi = "د" .. args[1][2]
local passive_form = jawi and default_passive_jawi or default_passive_latin
args.bare = { args[1][2] }
args.passive = { passive_form }
else
-- still, if active provided but passive missing, generate passive
if args.active and args.active[1] and (not args.passive or not args.passive[1]) then
args.passive = { jawi and ("د" .. base) or ("di" .. base) }
end
end
if args.active and args.active[1] then
args.active.label = "aktif"
table.insert(data.inflections, args.active)
end
if args.bare and args.bare[1] then
args.bare.label = "bentuk sebenar"
table.insert(data.inflections, args.bare)
end
if args.passive and args.passive[1] then
args.passive.label = "pasif diri ketiga"
table.insert(data.inflections, args.passive)
end
end
}
pos_functions["bentuk kata kerja"] = pos_functions["kata kerja"]
pos_functions["kata sifat"] = {
params = {
[1] = { list = true }, -- allow |~|, |-|, |?|, |+|, |e|
comparative = { alias_of = 1, list = false },
superlative = { list = true },
equative = { list = true },
},
func = function(args, data)
local base = data.pagename
local jawi = data.altform
local first = mw.ustring.sub(base, 1, 1)
local eq_enable = false
-- Comperative & Superlative
local periphrastic = {
comp_latn = "[[lebih]] " .. base,
sup_latn = "[[paling]] " .. base,
comp_j = "[[لبيه]] " .. base,
sup_j = "[[ڤاليڠ]] " .. base,
}
-- Equative is non-periphrastic (seBASE)
local equative = {
latn = "se" .. base,
jawi = "[[س]]" .. base,
}
-- Helpers -------------------------------------------------------
local function eq_check(tbl)
local found = false
for i = #tbl, 1, -1 do -- iterate backwards when removing
if tbl[i] == "e" then
table.remove(tbl, i)
found = true
end
end
return tbl, found
end
------------------------------------------------------------------
args[1], eq_enable = eq_check(args[1])
-- 1) “?” → no inflections, but add request category and stop.
if args[1][1] == "?" then
table.insert(data.categories,
"Permintaan infleksi dalam entri kata sifat bahasa " .. langname)
return
end
-- 2) “-” → just “not comparable” and stop.
if args[1][1] == "-" then
table.insert(data.inflections, { label = "[[Lampiran:Glosari#tidak sebanding|tidak sebanding]]" })
table.insert(data.categories, "Kata sifat tidak sebanding bahasa " .. langname)
return
end
-- 3) “~” → insert the note, but then fall through to normal comp/sup.
if args[1][1] == "~" then
table.insert(data.inflections, { label = "umumnya [[Lampiran:Glosari#tidak sebanding|tidak sebanding]]" })
end
-- 4) Comparative: always show (default)
if args[1][1] == nil or args[1][1] == "+" then
if jawi then
args.comparative = { periphrastic.comp_j }
args.superlative = { periphrastic.sup_j }
else
args.comparative = { periphrastic.comp_latn }
args.superlative = { periphrastic.sup_latn }
end
-- If "+" flag, additionally show morphological superlative
if args[1][1] == "+" then
local is_r = (first == "r" or first == "ر")
local morph = jawi
and (is_r and "ت" or "تر")
or (is_r and "te" or "ter")
-- First show periphrastic form (Jawi or Latin), then synthetic form
args.superlative = {
jawi and periphrastic.sup_j or periphrastic.sup_latn,
morph .. base
}
end
end
-- 5) Equative: only when e is called
if eq_enable then
args.equative = { jawi and equative.jawi or equative.latn }
else
args.equative = nil
end
-- 6) emit in order
if args.comparative and args.comparative[1] then
args.comparative.label = "[[Lampiran:Glosari#sebanding|sebanding]]"
table.insert(data.inflections, args.comparative)
end
if args.superlative and args.superlative[1] then
args.superlative.label = "[[Lampiran:Glosari#superlatif|superlatif]]"
table.insert(data.inflections, args.superlative)
end
if args.equative and args.equative[1] then
args.equative.label = "[[Lampiran:Glosari#samaan|samaan]]"
table.insert(data.inflections, args.equative)
end
end,
}
pos_functions["penjodoh bilangan"] = {
params = {
[1] = { list = true }, -- allow |+| or custom singular
},
func = function(args, data)
local base = data.pagename
local jawi = data.altform
-- Helpers -------------------------------------------------------
local function alt_sing(tbl)
if #tbl == 0 then return false end
if #tbl == 1 and tbl[1] == "+" then
return false
end
return true
end
local function plus_sign(tbl)
for _, v in ipairs(tbl) do
if v == "+" then
return true
end
end
return false
end
------------------------------------------------------------------
-- decide what form(s) to emit
local sing_forms = {label = "mufrad"}
-- if nothing explicit was passed, or "+" is called, auto‑generate
if #args[1] == 0 or plus_sign(args[1]) then
if jawi then
table.insert(sing_forms, "سي" .. base)
else
table.insert(sing_forms, "se" .. base)
end
end
-- If there's alternative singular form
-- {e.g. {{ms-classifier|sorang}}
if alt_sing(args[1]) then
for _, v in ipairs(args[1]) do
table.insert(sing_forms, v)
end
end
-- emit an inflection labeled “singular”
table.insert(data.inflections, sing_forms)
table.insert(data.categories, "Penjodoh bilangan bahasa " .. langname)
end
}
return export