-- Credit: Fenakhay for base of the module, adjectives, and verb functions
-- Saam-andar for nouns function and expansion of adjectives and verbs
-- Also Theknightwho and Benwing2
local lang = require("Module:languages").getByCode("fa")
local fa_cls = require("Module:fa-cls-translit")
local fa_ira = require("Module:fa-translit")
local m_parameters = require("Module:parameters")
local export = {}
local pos_functions = {}
local u = mw.ustring.char
local unpack = unpack or table.unpack -- Lua 5.2 compatibility
-- Diacritics
local A = u(0x064E) -- fatha/fathe
local AN = u(0x064B) -- fatHatan (tanwīn-i fatha or nasb/tanvin-e fathe or nasb)
local U = u(0x064F) -- zamma/zamme
local I = u(0x0650) -- kasra/kasre
local SK = u(0x0652) -- sukūn/sokun
local SH = u(0x0651) -- šadda/tašdid
-----------------------
-- Utility functions --
-----------------------
-- version of mw.ustring.gsub() that discards all but the first return value
local function rsub(term, foo, bar)
local retval = mw.ustring.gsub(term, foo, bar)
return retval
end
-- Tracking functions
local trackfn = require("Module:debug/track")
local function track(page)
trackfn("fa-headword/" .. page)
return true
end
-- Clean up translit strings by removing HTML spans and normalizing
local function clean_translit(tr)
if not tr then return nil end
-- Remove HTML spans that might be added by {{fa-xlit}}
tr = rsub(tr, "</?span*>", "")
-- Normalize whitespace around separators
tr = rsub(tr, "%s*/%s*", " / ")
tr = rsub(tr, "%s*<i>or</i>%s*", " <i>or</i> ")
tr = mw.text.trim(tr)
return tr ~= "" and tr or nil
end
-- Check if a parameter is disabled (set to "-" or similar)
local function is_disabled(param)
return param == "-" or param == "—" or param == "–"
end
function export.ZWNJ(word)
word = mw.ustring.gsub(word, "+$", "")
if mw.ustring.match(word, "", -1) then
return "\226\128\140" -- U+200C
end
return ""
end
-- Define parameter specifications
local function get_param_spec(poscat)
local base_params = {
= {},
= {list = true, allow_holes = true},
= {},
= {},
= {list = true, allow_holes = true},
= {list = true, allow_holes = true},
= {list = true, allow_holes = true},
= {list = true, allow_holes = true},
= {list = true, allow_holes = true},
= {type = "boolean"},
}
-- Add qualifier parameters for Tajik forms
for i = 1, 4 do
base_params = {}
end
if poscat == "nouns" then
base_params = {list = true, allow_holes = true}
base_params = {list = true, allow_holes = true}
for i = 1, 4 do
local base_key = "pl" .. (i == 1 and "" or tostring(i))
base_params = {}
base_params = {}
for j = 2, 3 do
base_params = {}
end
end
elseif poscat == "verbs" then
base_params = {list = true, allow_holes = true}
base_params = {list = true, allow_holes = true}
base_params = {list = true, allow_holes = true}
for i = 1, 3 do
local stem_key = "prstem" .. tostring(i)
base_params = {}
base_params = {}
base_params = {}
end
elseif poscat == "adjectives" then
base_params = {}
base_params = {}
base_params = {}
end
return base_params
end
-- Process transliterations for heads or forms
local function process_translits(forms, cls_list, ir_list, tr_list)
local results = {}
for i = 1, #forms do
local tr_overrides = {}
for j = 1, 3 do
local tr = tr_list
if tr and tr ~= "" then
table.insert(tr_overrides, tr)
end
end
local cls_filtered, ira_filtered = {}, {}
for j = 1, 3 do
if not (tr_list and tr_list ~= "") then
if cls_list and cls_list ~= "" then
table.insert(cls_filtered, cls_list)
end
if ir_list and ir_list ~= "" then
table.insert(ira_filtered, ir_list)
end
end
end
local cls_str = table.concat(cls_filtered, ", ")
local ira_str = table.concat(ira_filtered, ", ")
local base_tr = ""
if cls_str ~= "" and ira_str ~= "" then
base_tr = (cls_str == ira_str) and cls_str or (cls_str .. " / " .. ira_str)
elseif cls_str ~= "" then
base_tr = cls_str
elseif ira_str ~= "" then
base_tr = ira_str
end
local final_tr = base_tr
if #tr_overrides > 0 then
local overrides_str = table.concat(tr_overrides, " <i>or</i> ")
final_tr = (final_tr ~= "") and (final_tr .. " <i>or</i> " .. overrides_str) or overrides_str
end
results = final_tr
end
return results
end
-- The main entry point.
function export.show(frame)
local PAGENAME = mw.loadData("Module:headword/data").pagename
local poscat = frame.args or error(
"Part of speech has not been specified. Please pass parameter 1 to the module invocation.")
local params = get_param_spec(poscat)
local args = m_parameters.process(frame:getParent().args, params)
-- Gather parameters
local data = {
lang = lang,
pos_category = poscat,
categories = {},
heads = {},
translits = {},
inflections = {},
id = args,
sort_key = args,
nomultiwordcat = args
}
-- Process heads
local heads = {}
if args then
for i, head in ipairs(args) do
if head then
table.insert(heads, head)
end
end
elseif args then
table.insert(heads, args)
else
table.insert(heads, PAGENAME)
end
if #heads == 0 then
heads = {PAGENAME}
end
-- Process head transliterations
local cls_list = {}
local ir_list = {}
local tr_list = {}
for i = 1, math.max(#heads, 3) do
local cls_raw = args
local ir_raw = args
local tr = clean_translit(args)
local cls_tr = cls_raw and fa_cls.tr(cls_raw) or ""
local ir_tr = ""
-- Check if IR is disabled for this index
if ir_raw and not is_disabled(ir_raw) then
ir_tr = fa_ira.IRA_tr(ir_raw) or ""
elseif not ir_raw and cls_raw and not is_disabled(args) then
ir_tr = fa_ira.IRA_tr(cls_raw) or ""
end
cls_list = cls_tr
ir_list = ir_tr
tr_list = tr
end
if #heads == 1 then
-- Single head: combine all transliterations
local translits = process_translits(heads, cls_list, ir_list, tr_list)
data.heads = heads
data.translits = translits
else
-- Multiple heads: assign translit to head
data.heads = heads
for i = 1, #heads do
local tr = tr_list
if tr and tr ~= "" then
data.translits = tr
else
local cls_tr = cls_list or ""
local ir_tr = ir_list or ""
if cls_tr ~= "" and ir_tr ~= "" then
data.translits = (cls_tr == ir_tr) and cls_tr or (cls_tr .. " / " .. ir_tr)
else
data.translits = cls_tr .. ir_tr
end
end
end
end
data.no_redundant_head_cat = (#heads > 1)
-- Call POS-specific function
if pos_functions then
pos_functions.func(args, data)
end
return require("Module:headword").full_headword(data)
end
-- Helper function to add Tajik forms
local function add_tajik_forms(args, data)
for i = 1, 3 do
local cls = args
local tg = args
local tgq = args or args
if not is_disabled(tg) then
if not tg and cls then
tg = require("Module:tg-Latn-Cyrl-translit").tr(fa_cls.tr(cls))
end
if tg then
local label = (i == 1 and "Tajik spelling" or "<i>or</i>")
if tgq then
label = label .. " <span style=\"font-style: italic;\">(" .. tgq .. ")</span>"
end
table.insert(data.inflections, {
label = label,
{ term = tg, lang = require("Module:languages").getByCode("tg") }
})
end
end
end
end
-- Generate automatic plural transliteration
local function generate_auto_plural_translit(base, base_zwnj, plural, cls_list, ir_list)
local suffix_map = {
= {base_zwnj ~= "" and "-hā" or "hā", base_zwnj ~= "" and "-hâ" or "hâ"},
= {"āt", "ât"},
= {"ān", "ân"},
= {"yān", "yân"}
}
-- Check if base ends in vowel ی
local base_is_vowel_ye = false
if mw.ustring.match(base, "ی$") then
for j = 1, 3 do
local cls_tr = cls_list
local ir_tr = ir_list
if cls_tr and mw.ustring.match(cls_tr, "$") then
base_is_vowel_ye = true
break
end
if ir_tr and mw.ustring.match(ir_tr, "i$") then
base_is_vowel_ye = true
break
end
end
end
-- Determine applied suffix
local applied_suffix
if base_is_vowel_ye and mw.ustring.match(plural, "یان$") then
if plural == base .. "ان" or plural == base .. base_zwnj .. "ان" then
applied_suffix = "یان"
end
end
if not applied_suffix then
for suffix, _ in pairs(suffix_map) do
if plural == base .. suffix or plural == base .. base_zwnj .. suffix then
applied_suffix = suffix
break
end
end
end
if not applied_suffix or not suffix_map then
return nil
end
local cls_suffix = suffix_map
local ir_suffix = suffix_map
local cls_tr_list = {}
local ir_tr_list = {}
for j = 1, 3 do
local cls_tr = cls_list
local ir_tr = ir_list
if cls_tr and cls_tr ~= "" then
local final_cls_suffix = cls_suffix
if applied_suffix == "یان" and mw.ustring.match(cls_tr, "$") then
final_cls_suffix = "yān"
end
table.insert(cls_tr_list, cls_tr .. final_cls_suffix)
end
if ir_tr and ir_tr ~= "" then
local final_ir_suffix = ir_suffix
if applied_suffix == "یان" and mw.ustring.match(ir_tr, "i$") then
final_ir_suffix = "yân"
end
table.insert(ir_tr_list, ir_tr .. final_ir_suffix)
end
end
local cls_str = table.concat(cls_tr_list, ", ")
local ir_str = table.concat(ir_tr_list, ", ")
if cls_str ~= "" and ir_str ~= "" then
return (cls_str == ir_str) and cls_str or (cls_str .. " / " .. ir_str)
elseif cls_str ~= "" then
return cls_str
elseif ir_str ~= "" then
return ir_str
end
return nil
end
pos_functions = {
func = function(args, data)
data.pos_category = "nouns"
for i = 1, 4 do
local pl = args
if not pl then break end
local plq = (args and args) or args
local tr_variants = {}
-- Main translit parameter
local main_tr_key = "pl" .. (i == 1 and "" or tostring(i)) .. "tr"
local main_tr = clean_translit(args)
local alt_tr2 = clean_translit(args)
local alt_tr3 = clean_translit(args)
local base = data.heads
local base_zwnj = export.ZWNJ(base)
-- Get head transliterations
local cls_list = {}
local ir_list = {}
for j = 1, 3 do
local cls_raw = args
local ir_raw = args
if cls_raw then
cls_list = fa_cls.tr(cls_raw) or ""
end
if ir_raw and not is_disabled(ir_raw) then
ir_list = fa_ira.IRA_tr(ir_raw) or ""
elseif not ir_raw and cls_raw then
ir_list = fa_ira.IRA_tr(cls_raw) or ""
end
end
local auto_tr = generate_auto_plural_translit(base, base_zwnj, pl, cls_list, ir_list)
if auto_tr then
table.insert(tr_variants, auto_tr)
end
if main_tr and not auto_tr then
table.insert(tr_variants, main_tr)
end
if alt_tr2 then
table.insert(tr_variants, alt_tr2)
end
if alt_tr3 then
table.insert(tr_variants, alt_tr3)
end
local final_tr = (#tr_variants > 0) and table.concat(tr_variants, " <i>or</i> ") or nil
local label = (i == 1 and "plural" or "<i>or</i>")
if plq then
label = label .. " <span style=\"font-style: italic;\">(" .. plq .. ")</span>"
end
table.insert(data.inflections, {
label = label,
{term = pl, translit = final_tr}
})
end
-- Add Tajik forms
add_tajik_forms(args, data)
end
}
pos_functions = {
func = function(args, data)
data.pos_category = "verbs"
local prstems = {}
for i = 1, 3 do
local stem = args
if stem then
table.insert(prstems, stem)
end
end
if #prstems > 0 then
for i, stem in ipairs(prstems) do
local prstemq = (args and args) or args
local tr = clean_translit((args and args) or args)
local label = (i == 1 and "present stem" or "<i>or</i>")
if prstemq then
label = label .. " <span style=\"font-style: italic;\">(" .. prstemq .. ")</span>"
end
table.insert(data.inflections, {
label = label,
{term = stem, translit = tr}
})
end
end
-- Add Tajik forms
add_tajik_forms(args, data)
end
}
pos_functions = {
func = function(args, data)
data.pos_category = "adjectives"
local word = data.heads
local zwnj = export.ZWNJ(word)
-- Handle comparative/superlative
if args == "+" then
local cmp_forms = {{term = word .. zwnj .. "ت" .. A .. "ر"}}
local sup_forms = {{term = word .. zwnj .. "ت" .. A .. "رین"}}
-- Handle alternative stem
if args then
local alt = args
local alt_zwnj = (args ~= "-") and export.ZWNJ(alt) or ""
local tr_cls = fa_cls.tr(alt) or ""
local tr_ir = fa_ira.IRA_tr(alt) or ""
local cls_suffix_cmp = alt_zwnj ~= "" and "-tar" or "tar"
local cls_suffix_sup = alt_zwnj ~= "" and "-tarīn" or "tarīn"
local ir_suffix_cmp = alt_zwnj ~= "" and "-tar" or "tar"
local ir_suffix_sup = alt_zwnj ~= "" and "-tarin" or "tarin"
local function combine_translit(cls, ira, cls_suffix, ir_suffix)
if cls == ira or ira == "" then
return cls .. cls_suffix
elseif cls == "" then
return ira .. ir_suffix
else
return cls .. cls_suffix .. " / " .. ira .. ir_suffix
end
end
local tr_cmp = combine_translit(tr_cls, tr_ir, cls_suffix_cmp, ir_suffix_cmp)
local tr_sup = combine_translit(tr_cls, tr_ir, cls_suffix_sup, ir_suffix_sup)
table.insert(cmp_forms, {
term = alt .. alt_zwnj .. "ت" .. A .. "ر",
translit = tr_cmp
})
table.insert(sup_forms, {
term = alt .. alt_zwnj .. "ت" .. A .. "رین",
translit = tr_sup
})
end
table.insert(data.inflections, {
label = "comparative",
accel = {form = "comparative"},
unpack(cmp_forms)
})
table.insert(data.inflections, {
label = "superlative",
accel = {form = "superlative"},
unpack(sup_forms)
})
end
-- Add Tajik forms
add_tajik_forms(args, data)
end
}
return export