উইকিঅভিধান
bnwiktionary
https://bn.wiktionary.org/wiki/%E0%A6%AA%E0%A7%8D%E0%A6%B0%E0%A6%A7%E0%A6%BE%E0%A6%A8_%E0%A6%AA%E0%A6%BE%E0%A6%A4%E0%A6%BE
MediaWiki 1.47.0-wmf.5
case-sensitive
মিডিয়া
বিশেষ
আলাপ
ব্যবহারকারী
ব্যবহারকারী আলাপ
উইকিঅভিধান
উইকিঅভিধান আলোচনা
চিত্র
চিত্র আলোচনা
মিডিয়াউইকি
মিডিয়াউইকি আলোচনা
টেমপ্লেট
টেমপ্লেট আলোচনা
সাহায্য
সাহায্য আলোচনা
বিষয়শ্রেণী
বিষয়শ্রেণী আলোচনা
পরিশিষ্ট
পরিশিষ্ট আলোচনা
ছন্দ
ছন্দ আলোচনা
থিসরাস
থিসরাস আলোচনা
উদ্ধৃতি
উদ্ধৃতি আলোচনা
TimedText
TimedText talk
মডিউল
মডিউল আলাপ
ইভেন্ট
ইভেন্ট আলোচনা
pizza
0
29870
509673
509624
2026-06-04T11:33:51Z
Redmin
6857
লেক্সিম লিংকার এক্সটেনশনের সাহায্যে উইকিউপাত্ত লেক্সিম L1457637-এর সাথে সংযোগ তৈরি করছি
509673
wikitext
text/x-wiki
{{আরও দেখুন|Pizza}}
== {{langname|en}} ==
=== উচ্চারণ ===
* {{উচ্চারণ-ভঙ্গি|RP}} {{আধ্বব|en|/ˈpiːt.sə/}}
* {{উচ্চারণ-ভঙ্গি|GA}} {{আধ্বব|en|/ˈpitsə/|}}
* {{অডিও|en|en-us-pizza.ogg|অডিও (যুক্তরাষ্ট্র)}}
=== বিশেষ্য ===
{{en-বিশেষ্য|~|s|pizze|pl3qual=rare}}
# [[পিজা]]
{{লে|L32383}}
{{লে|L1457637}}
73xzvjt52g932i0htrul0zgwj7peors
509674
509673
2026-06-04T11:34:01Z
Redmin
6857
লেক্সিম লিংকার এক্সটেনশনের সাহায্যে উইকিউপাত্ত লেক্সিম L3055-এর সাথে সংযোগ তৈরি করছি
509674
wikitext
text/x-wiki
{{আরও দেখুন|Pizza}}
== {{langname|en}} ==
=== উচ্চারণ ===
* {{উচ্চারণ-ভঙ্গি|RP}} {{আধ্বব|en|/ˈpiːt.sə/}}
* {{উচ্চারণ-ভঙ্গি|GA}} {{আধ্বব|en|/ˈpitsə/|}}
* {{অডিও|en|en-us-pizza.ogg|অডিও (যুক্তরাষ্ট্র)}}
=== বিশেষ্য ===
{{en-বিশেষ্য|~|s|pizze|pl3qual=rare}}
# [[পিজা]]
{{লে|L32383}}
{{লে|L1457637}}
{{লে|L3055}}
760savvu9c724n9wkh7fvttqke81kbr
মডিউল:আভিধানিক উপাত্ত
828
50158
509636
509627
2026-06-03T12:48:14Z
Redmin
6857
509636
Scribunto
text/plain
local p = {}
local i18n = require('মডিউল:আভিধানিক উপাত্ত/i18n')
local references = require('মডিউল:উইকিউপাত্ত তথ্যসূত্র বিন্যাসকরণ').format
local getArgs = require('Module:Arguments').getArgs
local wb = mw.wikibase
local ustring = mw.ustring
local html = mw.html
local mw_lang = mw.language
local entity_cache = {}
local reference_cache = {}
local forms
local lang_code
local lex_cat
local matched_lemma
local function wrapStringInWikilinks(str)
local exceptions = i18n.nolinks
local result = str:gsub('(%S+)', function(token)
local word, trailing_char = token:match('^(.-)([;,]*)$')
local wrapped = word:gsub('[^(-/]+', function(part)
if exceptions[part] then
return part
else
return '[[' .. part .. '#' .. i18n['content_lang_name'] .. '|' .. part .. ']]'
end
end)
return wrapped .. trailing_char
end)
return result
end
local function serializeTable(val, name, skipnewlines, depth) -- https://stackoverflow.com/a/6081639
skipnewlines = skipnewlines or false
depth = depth or 0
local tmp = string.rep(" ", depth)
if name then tmp = tmp .. name .. " = " end
if type(val) == "table" then
tmp = tmp .. "{" .. (not skipnewlines and "\n" or "")
for k, v in pairs(val) do
tmp = tmp .. serializeTable(v, k, skipnewlines, depth + 1) .. "," .. (not skipnewlines and "\n" or "")
end
tmp = tmp .. string.rep(" ", depth) .. "}"
elseif type(val) == "number" then
tmp = tmp .. tostring(val)
elseif type(val) == "string" then
tmp = tmp .. string.format("%q", val)
elseif type(val) == "boolean" then
tmp = tmp .. (val and "true" or "false")
else
tmp = tmp .. "\"[inserializeable datatype:" .. type(val) .. "]\""
end
return tmp
end
-- Use this to safely expand templates when you are not sure that they exist.
local function safeExpand(frame, title, args)
local _, result = pcall(function()
return frame:expandTemplate{ title = title, args = args }
end)
if result:find('does not exist') then -- expandTemplate{} doesn't seem to throw any error that can be handled with pcall() so string search is the only viable option.
return nil
end
return result
end
-- Use this function to get the label of an item even if that item does not have any label in the wiki's content language or English.
local function getLabel(id)
if id == 'Q11051hi' then
return 'হিন্দি'
elseif id == 'Q11051ur' then
return 'উর্দু'
elseif id == 'Q56356571fa' then
return 'নয়া ফার্সি'
elseif id == 'Q56356571tg' then
return 'তাজিক'
elseif id == 'Q58635pa' or id == 'Q58635pnb' then
return 'পাঞ্জাবি'
end
local label = wb.getLabel(id)
if label then
return label
end
local labels = wb.getEntity(id).labels
if not labels then
return id
end
for _, v in pairs(labels) do
if v and v.value then
return v.value
end
end
end
local function getReference( id, reference )
local out_id = nil
local url_value
if reference_cache[id] == nil then
local ref_text = references(reference, wb, mw_lang, i18n['content_lang_code'], i18n['wikipedia'])
if reference.snaks then
if reference.snaks['P248'] then
for _, snak in pairs(reference.snaks['P248']) do
if snak.datavalue and snak.datavalue.value.id == 'Q428' then -- কুরআন
ref_text = ustring.gsub(ref_text, 'নামক অনুচ্ছেদ', 'নং আয়াত')
break
end
end
end
if reference.snaks['P854'] then
local snak = reference.snaks['P854'][1]
if snak.datavalue then
url_value = snak.datavalue.value
end
end
end
if url_value then
ref_text = ref_text .. ', [' .. url_value .. ' সংযোগ]'
end
reference_cache[id] = ref_text
else
out_id = id
end
return {out_id, reference_cache[id]}
end
local function getEntity( id )
if entity_cache[id] == nil then
entity_cache[id] = wb.getEntity(id)
end
return entity_cache[id] ~= false and entity_cache[id] or nil
end
local function getLexemeLanguageCode(current_lexeme)
local lang_item_id = current_lexeme:getLanguage()
if lang_item_id == nil then
return nil
end
local lang_entity = getEntity(lang_item_id)
if lang_entity == nil then
return nil
end
for i, statement_property in ipairs({'P305','P424', 'P220'}) do -- আইইটিএফ ভাষা ট্যাগ, উইকিমিডিয়া ভাষা কোড, আইএসও ৬৩৯-৩
local statements = lang_entity:getBestStatements(statement_property)
if statements[1] then
return statements[1].mainsnak.datavalue.value
end
end
return nil
end
-- Return the first form of the lexeme which has exactly the given grammatical feature.
local function formWithSingleGrammaticalFeature( item_id )
for i = 1, #forms do
local grammaticalFeatures = forms[i]:getGrammaticalFeatures()
if #grammaticalFeatures == 1 and grammaticalFeatures[1] == item_id then
return forms[i]
end
end
return nil
end
local function getArticleLinkTemplate(frame, stmt_value)
local template = ''
local sitelink = getEntity(stmt_value):getSitelink(i18n['wikipedia'])
if sitelink then
template = frame:expandTemplate{
title=i18n['template_wikipedia'],
args={sitelink}
}
end
return template
end
local function getArticleLinks (frame, sense )
local article_links = ''
for i, stmt in pairs(sense:getAllStatements('P5137')) do -- এই অর্থের জন্য আইটেম
article_links = article_links .. getArticleLinkTemplate(frame, stmt.mainsnak.datavalue.value.id)
end
for i, stmt in pairs(sense:getAllStatements('P9970')) do -- এই অর্থের জন্য বিধেয়
article_links = article_links .. getArticleLinkTemplate(frame, stmt.mainsnak.datavalue.value.id)
end
return article_links
end
-- @TODO: Generalise
local function expandTemplateForProperty(frame, object, property, template)
local lemmas = {}
local n = 0
for _, stmt in pairs(object:getAllStatements(property)) do
local lex = wb.lexeme.splitLexemeId(stmt.mainsnak.datavalue.value.id)
if lex then
lex = getEntity(lex)
n = n + 1
lemmas[n] = lex:getLemma(lang_code)
end
end
if not lang_code or n == 0 then
return ''
end
-- Build args: first lang_code, then lemmas
local args = {lang_code}
for i = 1, n do
args[#args + 1] = lemmas[i]
end
return frame:expandTemplate{
title = template,
args = args
}
end
local function getExternalLinks( entity ) -- T418639
local external_links = {}
if entity.claims == nil then return external_links end
local formatter_urls = require('মডিউল:আভিধানিক উপাত্ত/urls').formatter_urls
for property_id, statements in pairs(entity.claims) do
local formatter_url = formatter_urls[property_id]
if formatter_url then
local property_source = wb.getBestStatements(property_id, 'P9073')
local source_name
if next(property_source) then
source_name = getLabel(property_source[1].mainsnak.datavalue.value.id)
or property_source[1].mainsnak.datavalue.value.id
else
source_name = getLabel(property_id) or property_id
end
for i = 1, #statements do
local stmt = statements[i]
if stmt.mainsnak.datavalue then
local formatted_link = ustring.gsub(
ustring.gsub(formatter_url, '$1', ustring.gsub(stmt.mainsnak.datavalue.value, '%%', '%%%%')),
' ', '+'
)
table.insert(external_links,
'[' .. formatted_link .. ' ' .. source_name .. ']')
end
end
end
end
return external_links
end
p.getExternalLinks = getExternalLinks -- রেখে দিন যাতে ডিবাগিং সম্ভব হয়
local function termSpan( term )
local text = term[1]
local lang = term[2]
local dir = mw_lang.new( lang ):getDir()
local span = html.create( 'span' )
span:attr( 'lang', lang )
:attr( 'dir', dir )
:wikitext( text )
return tostring( span )
end
local function termLink( term, lang_qid )
local text = term[1]
local lang = term[2]
local dir = mw_lang.new( lang ):getDir()
local span = html.create( 'span' )
span:attr( 'lang', lang )
:attr( 'dir', dir )
:wikitext( '[[' .. text .. '#' .. getLabel(lang_qid) .. '|' .. text .. ']]' )
return tostring( span )
end
local function getLemmata( current_lexeme )
local lemma_string = ''
for i, rep in pairs(current_lexeme:getLemmas()) do
if lemma_string == '' then
lemma_string = termSpan(rep)
else
lemma_string = lemma_string .. '/' .. termSpan(rep)
end
end
return lemma_string
end
local function getLinkedLemmata( current_lexeme )
local lemma_string = ''
for i, rep in pairs(current_lexeme:getLemmas()) do
if lemma_string == '' then
lemma_string = termLink(rep, current_lexeme:getLanguage())
else
lemma_string = lemma_string .. '/' .. termLink(rep, current_lexeme:getLanguage())
end
end
return lemma_string
end
local function getExamples( current_lexeme, sense_id, references_seen )
local examples = html.create('dl')
local example_text, example_lang, example_form, example_str
for i, stmt in pairs(current_lexeme:getAllStatements('P5831')) do -- ব্যবহারের উদাহরণ
if stmt.qualifiers and stmt.qualifiers['P6072'] and stmt.qualifiers['P6072'][1].datavalue.value.id == sense_id then -- বিষয়ে লেক্সিমের অর্থ
example_text = ustring.gsub(stmt.mainsnak.datavalue.value.text, ' / ','<br/>')
example_lang = stmt.mainsnak.datavalue.value.language
local example_form_strs = {}
if stmt.qualifiers['P1810'] then
table.insert(example_form_strs, stmt.qualifiers['P1810'][1].datavalue.value)
elseif stmt.qualifiers['P5830'] then
example_form = getEntity(stmt.qualifiers['P5830'][1].datavalue.value.id) -- বিষয়ে লেক্সিমের রূপ
for _, rep in pairs(example_form:getRepresentations()) do
table.insert(example_form_strs, rep[1])
end
end
for i, example_form_str in pairs(example_form_strs) do
new_example_text = ustring.gsub(example_text, example_form_str, "'''" .. example_form_str .. "'''")
if new_example_text ~= example_text then
example_str = termSpan({new_example_text, example_lang})
break
end
new_example_text = example_text
end
if example_str == nil then
example_str = termSpan({example_text, example_lang})
end
local reference_text = ''
if stmt.references then
for j, reference in pairs(stmt.references) do
table.insert(references_seen, reference.hash)
local got_reference = getReference(reference.hash, reference)
reference_text = reference_text .. '\n\n' .. got_reference[2]
end
end
if example_str then
examples:tag('dd'):wikitext("''" .. example_str .. "''")
if reference_text ~= '' then
examples:done():tag('dd'):css('text-indent', '2em'):wikitext(reference_text)
end
end
end
end
for i, stmt in pairs(wb.getAllStatements(sense_id, 'P5831')) do -- ব্যবহারের উদাহরণ
example_text = ustring.gsub(stmt.mainsnak.datavalue.value.text, ' / ','<br/>')
example_lang = stmt.mainsnak.datavalue.value.language
local example_form_str = nil
if stmt.qualifers then
example_form = getEntity(stmt.qualifiers['P5830'][1].datavalue.value.id) -- বিষয়ে লেক্সিমের রূপ
if stmt.qualifiers['P1810'] then
example_form_str = stmt.qualifiers['P1810'][1].datavalue.value
end
end
if example_form and example_form_str == nil then
example_form_str = example_form:getRepresentation(i18n['content_lang_code'])
end
if example_form and example_form_str == nil then
example_form_str = example_form:getRepresentations()[1][1]
end
if example_form_str then
example_text = ustring.gsub(example_text, example_form_str, "'''" .. example_form_str .. "'''")
end
example_str = termSpan({example_text, example_lang})
local reference_text = ''
if stmt.references then
for j, reference in pairs(stmt.references) do
table.insert(references_seen, reference.hash)
local got_reference = getReference(reference.hash, reference)
reference_text = reference_text .. '\n\n' .. got_reference[2]
end
end
if example_str then
examples:tag('dd'):wikitext("''" .. example_str .. "''")
if reference_text ~= '' then
examples:done():tag('dd'):css('text-indent', '2em'):wikitext(reference_text)
end
end
end
return { tostring(examples) , references_seen }
end
-- This calls frame:preprocess() instead of :callParserFunction() because the latter does not work for Wikifunctions function calls yet (see https://www.wikifunctions.org/wiki/Wikifunctions:Embedded_function_calls).
local function callWikifunctionsFunction(args, frame)
return frame:preprocess('{{#function:' .. args .. '}}')
end
local function checkTitleCodePointInRange(title, start_point, end_point)
return ustring.find( title, '[' ..ustring.char(start_point) .. '-' .. ustring.char(end_point) .. ']' )
end
local function getLanguageForCategories( lang_id, current_page_title )
-- বিশেষ ভাষার জন্য
if lang_id == 'Q11051' then -- হিন্দি/উর্দু
if checkTitleCodePointInRange(current_page_title, 0x0600, 0x06ff) then -- উর্দু
lang_id = 'Q11051ur'
elseif checkTitleCodePointInRange(current_page_title, 0x0900, 0x097f) then -- হিন্দি
lang_id = 'Q11051hi'
end
elseif lang_id == 'Q58635' then -- পাঞ্জাবি
if checkTitleCodePointInRange(current_page_title, 0x0600, 0x06ff) then -- শাহমুখী
lang_id = 'Q58635pnb'
elseif checkTitleCodePointInRange(current_page_title, 0x0a00, 0x0a7f) then -- গুরুমুখী
lang_id = 'Q58635pa'
end
elseif lang_id == 'Q56356571' then -- নয়া ফার্সি ভাষা
if checkTitleCodePointInRange(current_page_title, 0x0600, 0x06ff) then -- ফার্সি (ইরান/আফগানিস্তান)
lang_id = 'Q56356571fa'
elseif checkTitleCodePointInRange(current_page_title, 0x0400, 0x04ff) then -- তাজিক
lang_id = 'Q56356571tg'
end
end
return lang_id
end
local function getOneStringForProperty(object, property)
local val
local stmts = object:getAllStatements(property)
if #stmts ~= 0 then
val = stmts[1].mainsnak.datavalue.value
end
return val
end
local function getTranslations(frame, senses) -- TODO: woefully incomplete until T185313 and T199887 are resolved
if #senses == 0 then
return nil
end
local all_translations = {}
for _, sense in pairs(senses) do
local translation_set = {}
local gloss = sense:getGloss('bn')
for _, stmt in pairs(sense:getAllStatements('P5972')) do
local translation = stmt.mainsnak.datavalue.value.id
local lexeme_id = wb.lexeme.splitLexemeId(translation)
local language = getLabel(getEntity(lexeme_id):getLanguage())
table.insert(translation_set, language .. ': ' .. getLinkedLemmata(getEntity(lexeme_id)) .. '<br/>')
end
if #translation_set > 0 then
local block = frame:expandTemplate{ title = i18n['template_trans-top'], args = { gloss } }
block = block .. table.concat(translation_set, '\n') .. frame:expandTemplate{ title = i18n['template_trans-bottom'] }
table.insert(all_translations, block)
end
end
if #all_translations == 0 then
return nil
end
return '====' .. i18n['heading_translation'] .. '==== \n' .. table.concat(all_translations, '\n')
end
local createicon = function(langcode, entityID, propertyID)
langcode = langcode or ""
propertyID = propertyID or ""
local icon = " <span class='penicon autoconfirmed-show'>[["
-- " <span data-bridge-edit-flow='overwrite' class='penicon'>[[" -> enable Wikidata Bridge
.. "File:OOjs UI icon edit-ltr-progressive.svg |frameless |text-top |10px |alt="
.. i18n['edit_wikidata']
.. "|link=https://www.wikidata.org/entity/" .. entityID
if langcode ~= "" then icon = icon .. "?uselang=" .. langcode end
if propertyID ~= "" then icon = icon .. "#" .. propertyID end
icon = icon .. "|" .. i18n['edit_wikidata'] .. "]]</span>"
return icon
end
local function getMeanings( frame, args, current_lexeme, senses, references_seen, language_name)
if #senses == 0 then
return {createicon(i18n['content_lang_code'], current_lexeme:getId()) .. "''" .. i18n['text_category_rfdef'] .. "''" .. i18n.tocatlink(i18n['category_rfdef']), references_seen}
end
local meanings = html.create( 'ol' )
local item_label_gloss_parts = {}
local idlinkset = {}
for i, sense in pairs(senses) do
local gloss_text_parts = {}
local main_gloss_text = frame:expandTemplate{
title=i18n['template_anchor'],
args={sense:getId()}
}
local specifiers = {}
for k, property_id in ipairs({'P6084', 'P6191', 'P9488'}) do -- অবস্থান যেখানে শব্দার্থ ব্যবহৃত, যে রীতিতে শব্দার্থ ব্যবহৃত হয়, যে ক্ষেত্রে ব্যবহৃত
for _, stmt in pairs(sense:getAllStatements(property_id)) do
local stmt_value = stmt.mainsnak.datavalue.value.id
local reference_text = ''
local refs = stmt.references
if refs then
for j, reference in pairs(refs) do
table.insert(references_seen, reference.hash)
local got_reference = getReference(reference.hash, reference)
reference_text = reference_text .. '\n\n' .. frame:extensionTag('ref', got_reference[2])
end
end
local val = getLabel(stmt_value)
table.insert(specifiers, val .. reference_text)
if property_id == 'P9488' then
table.insert(item_label_gloss_parts, i18n.tocatlink(lang_code .. ':' .. val))
end
end
if #specifiers > 0 then
main_gloss_text = main_gloss_text .. "(''" .. table.concat(specifiers, "'', ''") .. "'') "
end
end
local gloss = sense:getGloss( i18n['content_lang_code'] )
if gloss then
main_gloss_text = main_gloss_text .. wrapStringInWikilinks(gloss)
if gloss:match('^প্রদত্ত%s*(%S-)%s*নাম$') then -- given names
main_gloss_text = main_gloss_text .. i18n.tocatlink(language_name .. ' ' .. i18n['category_given_names'])
end
else
local other_gloss_text = nil
local other_gloss_lang = nil
for k, stmt in pairs(sense:getAllStatements('P5137')) do -- যদি 'এই অর্থের জন্য আইটেম' মানের বাংলা লেবেল থাকে
local stmt_value = stmt.mainsnak.datavalue.value.id
local stmt_label = getLabel(stmt_value)
if stmt_label then
table.insert(item_label_gloss_parts, '[[:d:' .. stmt_value .. '|' .. stmt_label .. ']]')
end
end
if #item_label_gloss_parts > 0 then
other_gloss_text = table.concat(item_label_gloss_parts, '; ')
end
if other_gloss_text == nil then
for _, fallback_lang in ipairs(mw_lang.getFallbacksFor( i18n['content_lang_code'] )) do
if sense:getGloss( fallback_lang ) then
other_gloss_text, other_gloss_lang = sense:getGloss( fallback_lang )
end
end
if other_gloss_lang == nil then
local glosses = sense:getGlosses()
for _, gloss in pairs(glosses) do
other_gloss_text = gloss[1]
other_gloss_lang = gloss[2]
break
end
end
main_gloss_text = main_gloss_text .. other_gloss_text .. "<sup><em>" .. mw_lang.fetchLanguageName(other_gloss_lang, i18n['content_lang_code']) .. "</em></sup>"
else
main_gloss_text = main_gloss_text .. "''" .. other_gloss_text .. "''"
end
main_gloss_text = main_gloss_text .. i18n.tocatlink(i18n['category_rfdef_equivalent'])
end
local synonym = expandTemplateForProperty(frame, sense, 'P5973', i18n['template_synonym'])
if synonym ~= '' then
main_gloss_text = main_gloss_text .. ' <br/> ' .. synonym
end
local antonym = expandTemplateForProperty(frame, sense, 'P5974', i18n['template_antonym'])
if antonym ~= '' then
main_gloss_text = main_gloss_text .. ' <br/> ' .. antonym
end
local hypernym = expandTemplateForProperty(frame, sense, 'P6593', i18n['template_hypernym'])
if hypernym ~= '' then
main_gloss_text = main_gloss_text .. ' <br/> ' .. hypernym
end
if lex_cat == 'Q1084' or lex_cat == 'Q147276' then -- noun or proper noun
local demonym = expandTemplateForProperty(frame, sense, 'P6271', i18n['template_demonym-noun'])
main_gloss_text = main_gloss_text .. ' <br/> ' .. demonym
elseif lex_cat == 'Q34698' then
local demonym = expandTemplateForProperty(frame, sense, 'P6271', i18n['template_demonym-adj'])
main_gloss_text = main_gloss_text .. ' <br/> ' .. demonym
end
table.insert(gloss_text_parts, main_gloss_text .. createicon(i18n['content_lang_code'], sense:getId()))
for i, stmt in pairs(sense:getAllStatements('P8394')) do -- টিপ্পনীর উদ্ধৃতি
local gloss_quote = termSpan({stmt.mainsnak.datavalue.value.text, stmt.mainsnak.datavalue.value.language})
if stmt.references[1] then
local got_reference = getReference ( stmt.references[1].hash, stmt.references[1] )
gloss_quote = '"' .. gloss_quote .. '" ' .. got_reference[2]
end
table.insert(references_seen, stmt.references[1].hash)
table.insert(gloss_text_parts, frame:extensionTag('ref', gloss_quote))
end
for i, stmt in pairs(sense:getAllStatements('P1343')) do -- বর্ণিত উৎস
-- TODO: do away with making fake reference objects
local fake_reference = { ['snaks'] = {} }
fake_reference.snaks['P248'] = { [1] = stmt.mainsnak }
qualifiers_order = stmt['qualifiers-order']
if qualifiers_order then
for i, k in ipairs(qualifiers_order) do fake_reference.snaks[k] = stmt.qualifiers[k] end
end
fake_reference.hash = mw.hash.hashValue('sha3-512', serializeTable(fake_reference))
table.insert(references_seen, fake_reference.hash)
local got_reference = getReference(fake_reference.hash, fake_reference)
if got_reference[1] == nil then
table.insert(gloss_text_parts, frame:extensionTag('ref', got_reference[2], {name = fake_reference.hash}))
else
table.insert(gloss_text_parts, frame:extensionTag{name = 'ref', content='', args = {name = got_reference[1]}})
end
end
local first_sense_image = ''
local sense_images = sense:getAllStatements('P18')
if next(sense_images) then
first_sense_image = sense_images[1].mainsnak.datavalue.value
end
if first_sense_image ~= '' then
table.insert(gloss_text_parts, '[[চিত্র:' .. first_sense_image .. "|thumb|'''" .. getLemmata(current_lexeme) .. "'''—" .. main_gloss_text .. ']]')
end
local idlinks = getExternalLinks(sense)
if #idlinks > 0 then
local idlinktext = '<small>('
for _, idlink in pairs(idlinks) do
idlinktext = idlinktext .. idlink .. '\n'
end
idlinktext = idlinktext .. ')</small>'
table.insert(gloss_text_parts, idlinktext)
table.insert(idlinkset, idlinks)
end
local externallinks = getArticleLinks(frame, sense)
if externallinks ~= '' then
table.insert(gloss_text_parts, externallinks)
end
local new_notes = {}
local sense_keys = { sense:getId(), string.sub(sense:getId(), string.find(sense:getId(), '-')+1) }
for _, v in ipairs(sense_keys) do
if args[v] then
table.insert(new_notes, args[v])
end
end
if #new_notes > 0 then
for _, v in ipairs(new_notes) do
if i == 1 then
table.insert(gloss_text_parts, '<br/>' .. v)
else
table.insert(gloss_text_parts, v)
end
end
end
local examples, references_seen = unpack(getExamples( current_lexeme, sense:getId(), references_seen ))
local gloss_text = table.concat(gloss_text_parts, '\n')
meanings:tag('li'):wikitext(gloss_text):wikitext(examples)
end
return {meanings, references_seen, idlinkset}
end
local function getPronunciationBaseForm( lang_name, lex_cat)
local base_form = nil
-- (!) অন্য ভাষার শব্দের যদি অন্য রকম মূল ফর্ম থাকে সেগুলো এখানে নতুন if বিবৃতি দিয়ে যোগ করা যায়।
if lang_name == 'বাংলা' then
if lex_cat == 'Q1084' then -- বিশেষ্য
base_form = formWithSingleGrammaticalFeature( 'Q131105' ) -- কর্তৃকারক
elseif lex_cat == 'Q24905' then -- ক্রিয়া
base_form = formWithSingleGrammaticalFeature( 'Q1350145' ) -- ক্রিয়া বিশেষ্য
end
end
if base_form == nil then
for i, form in pairs(forms) do
base_form = form
break
end
end
return base_form
end
local function getCombines( current_lexeme, frame )
local combines = ''
local index_mappings = {}
for i, stmt in pairs(current_lexeme:getAllStatements('P5238')) do
if stmt.qualifiers and stmt.qualifiers['P1545'] then -- ক্রম
local current_index = tonumber(stmt.qualifiers['P1545'][1].datavalue.value)
index_mappings[current_index] = stmt
end
end
if #index_mappings ~= 0 then
for i, stmt in ipairs(index_mappings) do
if stmt.mainsnak.datavalue then
local part_lexeme_id = stmt.mainsnak.datavalue.value.id
local part_lexeme = getEntity(part_lexeme_id)
local current_substring = getLinkedLemmata(part_lexeme)
local part_etymology = getEtymology(part_lexeme, frame, 'partial')
if part_etymology ~= '' and part_etymology then
current_substring = current_substring .. ' (← ' .. part_etymology .. ')'
end
if combines == '' then
combines = current_substring
else -- @TODO: This shoukd use the 'affix' and 'compound' templates instead.
combines = combines .. ' + ' .. current_substring
end
end
end
end
return combines
end
function getRoots( current_lexeme )
local stmts = current_lexeme:getAllStatements('P5920')
if #stmts == 0 then return '', '', '' end
local root_lexeme = getEntity(stmts[1].mainsnak.datavalue.value.id)
return getLexemeLanguageCode(root_lexeme), '√' .. getLinkedLemmata(root_lexeme), root_lexeme:getLemma('ar')
end
function getEtymology( current_lexeme, frame, mode )
-- @TODO: Fix the etymology chains that are not possible to render
local etymology = ''
local current_combines = getCombines(current_lexeme, frame)
local root_lang, current_roots, root_str = getRoots(current_lexeme)
if mode ~= 'partial' and root_str then
--frame:expandTemplate{title=i18n['template_root'], args={lang_code, root_lang, root_str}}
end
local stmts = current_lexeme:getAllStatements('P5191')
local new_etymology_string
if #stmts == 0 then
if current_roots ~= '' and current_combines ~= '' and current_roots then
return current_roots .. '<br/>(' .. current_combines .. ')'
elseif current_roots ~= '' then
if lang_code == 'ar' and mode ~= 'partial' and root_str ~= matched_lemma then
return frame:expandTemplate{title=i18n['template_ar-rootbox'], args={root_str}}
else
return current_roots
end
else
return current_combines
end
end
local origin_lexeme_string
for i, stmt in pairs(stmts) do
local origin_lexeme_dv = stmt.mainsnak.datavalue -- If this is nil, the origin lexeme is not known.
if origin_lexeme_dv then
local origin_lexeme = getEntity(origin_lexeme_dv.value.id)
local origin_lexeme_lang = getLabel(origin_lexeme:getLanguage())
local sitelink = i18n.wplink(origin_lexeme:getLanguage(), origin_lexeme_lang, wb)
if sitelink ~= '' then
origin_lexeme_string = getLinkedLemmata(origin_lexeme) .. ' (' .. sitelink .. ')'
else
origin_lexeme_string = getLinkedLemmata(origin_lexeme) .. ' (' .. origin_lexeme_lang .. ')'
end
if stmt.qualifiers and stmt.qualifiers['P5886'] then
local mode_of_derivation = stmt.qualifiers['P5886'][1].datavalue.value.id
if mode_of_derivation == 'Q1345001' then
-- @TODO: Add support for showing gender
origin_lexeme_string = frame:expandTemplate{title=i18n['template_borrowed'], args={lang_code, getLexemeLanguageCode(origin_lexeme), getLemmata(origin_lexeme), pos=getLabel(lex_cat)}} .. ' ' .. i18n['etymology_borrowing']
elseif mode_of_derivation == 'Q845079' then
origin_lexeme_string = ustring.gsub(i18n['etymology_learned_borrowing'], '$1', origin_lexeme_string)
elseif mode_of_derivation == 'Q56611986' then
origin_lexeme_string = frame:expandTemplate{title=i18n['template_inherited'], args={lang_code, getLexemeLanguageCode(origin_lexeme), getLemmata(origin_lexeme), pos=getLabel(lex_cat)}} .. ' ' .. i18n['etymology_inheritance']
elseif mode_of_derivation == 'Q189743' then
origin_lexeme_string = frame:expandTemplate{title=i18n['template_ellipsis'], args={lang_code, getLemmata(origin_lexeme)}} .. ' ' .. i18n['etymology_ellipsis']
end
end
local origin_origin = getEtymology(origin_lexeme, frame)
if origin_origin ~= '' and origin_origin then
new_etymology_string = origin_lexeme_string .. ' ← ' .. origin_origin
else
new_etymology_string = origin_lexeme_string
end
end
if etymology == '' then
etymology = new_etymology_string
elseif origin_lexeme_string and etymology then
etymology = etymology .. ' ' .. origin_lexeme_string
elseif origin_lexeme_string and etymology == nil then
etymology = origin_lexeme_string
end
end
if current_roots ~= '' and etymology and current_roots then
etymology = etymology .. ' ' .. current_roots
elseif current_roots ~= '' and etymology == nil then
etymology = current_roots
end
if current_combines ~= '' and etymology then
etymology = etymology .. '<br/>(' .. current_combines .. ')'
end
return etymology
end
local function pronunciationBlock(block, value)
return '* ' .. i18n['text_' .. block] .. ' ' .. value
end
local function getPronunciation( frame, current_lexeme, lang_name, lex_cat )
local pronunciations = {}
local base_form = getPronunciationBaseForm(lang_name, lex_cat )
if base_form then
for i, stmt in pairs(base_form:getAllStatements('P443')) do -- উচ্চারণের অডিও
local pronunciation_file = stmt.mainsnak.datavalue.value
local specifier_text = ''
local specifiers = {}
if stmt.qualifiers then
for k, property_id in ipairs({'P5237'}) do -- উচ্চারণের ধরন
local qual = stmt.qualifiers[property_id]
if qual then
for _, qual in pairs(qual) do
local stmt_value = qual.datavalue.value.id
table.insert(specifiers, getLabel(stmt_value))
end
end
end
end
if #specifiers > 0 then
specifier_text = table.concat(specifiers, "'', ''")
end
local audio_text
if specifier_text ~= '' then
audio_text = i18n['text_audio'] .. ' (' .. specifier_text .. ')'
else
audio_text = i18n['text_audio']
end
table.insert(pronunciations, '* ' .. frame:expandTemplate{
title= i18n['template_audio'],
args = {lang_name, pronunciation_file, audio_text}
})
end
local ipa_transcription = base_form:getAllStatements('P898') -- - আধ্বব প্রতিলিপিকরণ
local iso15919_transcription = getOneStringForProperty(base_form, 'P5825') -- আইএসও ১৫৯১৯ প্রতিলিপিকরণ
local itrans = getOneStringForProperty(base_form, 'P8881') -- ITRANS
local iast = getOneStringForProperty(base_form, 'P7581') -- আইএএসটি প্রতিলিপিকরণ
local xsampa = getOneStringForProperty(base_form, 'P2859')
-- @TODO: অডিও ও প্রতিলিপিকরণ দুটোই থাকলে সেই ক্ষেত্রে একটার ঠিক পরেই আরেকটা দেখানো উচিত
if #ipa_transcription ~= 0 then
for i, stmt in pairs(ipa_transcription) do
local ipa_text = stmt.mainsnak.datavalue.value
local specifier_text = ''
local specifiers = {}
if stmt.qualifiers then
for k, property_id in ipairs({'P5237'}) do -- উচ্চারণের ধরন
for l, qual in ipairs(stmt.qualifiers[property_id]) do
table.insert(specifiers, getLabel( qual.datavalue.value.id ))
end
end
end
if #specifiers > 0 then
specifier_text = "(''" .. table.concat(specifiers, "'', ''") .. "'') "
end
local syllable_count
if lang_code == 'tr' then
syllable_count = i18n['text_syllable_count'] .. ' ' .. callWikifunctionsFunction('Z10029|' .. ipa_text, frame)
else
syllable_count = i18n['text_syllable_count'] .. ' ' .. callWikifunctionsFunction('Z30837|' .. ipa_text, frame)
end
table.insert(pronunciations, '* ' .. specifier_text .. frame:expandTemplate{
title= i18n['template_ipa'],
args = {lang_name, ipa_text}
} .. '\n* ' .. syllable_count)
end
-- The following checks are ordered based on which one is expected to be true in a higher number of cases.
elseif lang_name == 'বাংলা' then
table.insert(pronunciations, '* ' .. frame:expandTemplate{
title='bn-IPA',
})
elseif lang_name == 'আরবি' then
local lemma = current_lexeme:getLemma('ar')
table.insert(pronunciations, '* ' .. frame:expandTemplate{
title='ar-IPA',
args={lemma}
})
elseif lang_name == 'ফালা' then
table.insert(pronunciations, '* ' .. frame:expandTemplate{
title='fax-pron',
})
elseif lang_code == 'fi' then
table.insert(pronunciations, '* ' .. frame:expandTemplate{
title='fi-IPA',
})
elseif lang_code == 'ko' then
table.insert(pronunciations, '* ' .. frame:expandTemplate{
title='ko-IPA',
})
end
if iso15919_transcription then
table.insert(pronunciations, pronunciationBlock('iso15919', iso15919_transcription))
end
if itrans then
table.insert(pronunciations, pronunciationBlock('itrans', itrans))
end
if iast then
table.insert(pronunciations, pronunciationBlock('iast', iast))
end
if xsampa then
table.insert(pronunciations, pronunciationBlock('xsampa', xsampa))
end
end -- {{আধ্বব|en|/ˈɪntəvjuː/}}
return table.concat(pronunciations, '\n')
end
local function getAlternativeSpellings( current_lexeme )
local alt_spellings = {}
for i, stmt in pairs(current_lexeme:getAllStatements('P11577')) do -- বিকল্প বানান
if stmt.mainsnak.datavalue then
table.insert(alt_spellings, '* ' .. getLinkedLemmata(getEntity(stmt.mainsnak.datavalue.value.id)))
end
end
return table.concat(alt_spellings, '\n')
end
local function heading_level(text, level)
local heading_delimiter = string.rep('=', level)
return heading_delimiter .. ' ' .. text .. ' ' .. heading_delimiter
end
function get_any_notes(sections, args, keys)
local notes = {}
for i, v in ipairs(keys) do
if args[v] then
table.insert(notes, args[v])
end
end
return notes
end
function add_specific_notes(sections, notes)
for i, v in ipairs(notes) do
table.insert(sections, v)
end
end
local function add_any_notes(sections, args, keys)
for i, v in ipairs(keys) do
if args[v] then
table.insert(sections, args[v])
end
end
end
local function getMatchingLemmaForPageTitle(lexeme, title)
local lemmas = lexeme:getLemmas()
local matched_lemma
for _, lemma_entry in ipairs(lemmas) do
local lemma = lemma_entry[1]
if lemma == title then
matched_lemma = lemma
break
end
end
if matched_lemma == nil and lang_code == 'ar' then -- Arabic lemmas do not match the title of the entry because those are written with different characters stripped out on Wiktionary. This could be changed to call stripDiactritics() from [[Module:languages]] in the future if desired.
matched_lemma = lexeme:getLemma('ar')
end
return matched_lemma
end
local function buildLanguageAgnosticInflectionTable()
local has_image = false
local form_images = {}
for i, form in ipairs(forms) do
local form_image = form:getAllStatements('P7407')
if next(form_image) then
form_images[i] = form_image[1].mainsnak.datavalue.value
has_image = true
end
end
local table_class = "wikitable mw-collapsible sortable"
if not has_image then
table_class = table_class .. " mw-collapsed"
end
local text = "{| class='" .. table_class .. "' style='border:solid 1px rgb(80%,80%,100%); text-align:center;'\n"
text = text .. "|+ " .. i18n['heading_inflection_table'] .. "\n"
text = text .. "|- \n"
text = text .. "! " .. i18n['heading_form'] .. " !! " .. i18n['heading_grammatical_features']
if has_image then
text = text .. " !! " .. (i18n['heading_image'])
end
text = text .. " \n"
for i, form in ipairs(forms) do
local rep = form:getRepresentations()
local feat = form:getGrammaticalFeatures()
local rep_text = ""
for j, r in pairs(rep) do
if rep_text == "" then
rep_text = r[1]
else
rep_text = rep_text .. " / " .. r[1]
end
end
local feat_text = ""
if feat then
for j, f in ipairs(feat) do
local label = getLabel(f) or f
if feat_text == "" then
feat_text = label
else
feat_text = feat_text .. ", " .. label
end
end
end
text = text .. "|-\n"
text = text .. "| " .. (rep_text ~= "" and rep_text or "—")
text = text .. " || " .. (feat_text ~= "" and feat_text or "—")
if has_image then
local image_cell = "—"
if form_images[i] then
image_cell = "[[চিত্র:" .. form_images[i] .. "|50px]]"
end
text = text .. " || " .. image_cell
end
text = text .. "\n"
end
text = text .. "|}"
return text
end
function p.all( frame )
local args = getArgs(frame)
local lexeme_id = args[1]
local current_lexeme = getEntity(lexeme_id)
local current_language = current_lexeme:getLanguage()
local senses = current_lexeme:getSenses()
local add_heading = true
forms = current_lexeme:getForms()
if args[2] then
local val = mw.text.trim(tostring(args[2]))
if val == "false" or val == "0" or val == "না" then
add_heading = false
end
end
local references_seen = {}
local sections = {}
local title = mw.title.getCurrentTitle().text
local lang_category = getLanguageForCategories(current_language, title)
local lang_name = getLabel(lang_category)
if add_heading == true then
local lang_heading = "== " .. lang_name .. " =="
table.insert(sections, lang_heading)
end
matched_lemma = getMatchingLemmaForPageTitle(current_lexeme, title)
lex_cat = current_lexeme:getLexicalCategory()
lang_code = getLexemeLanguageCode(current_lexeme)
local cat = i18n.lang_category(getLabel(lex_cat), lang_name)
local lex_cat_template
if cat then
table.insert(sections, '===' .. getLabel(lex_cat) .. cat .. frame:expandTemplate{
title = i18n['template_anchor'],
args = { lexeme_id }
} .. '===')
table.insert(sections, frame:expandTemplate{
title= i18n['template_lexeme'],
args = {lexeme_id}
})
add_any_notes(sections, args, i18n['manual_category'])
local etymology = getEtymology( current_lexeme, frame )
if etymology ~= '' and etymology then
table.insert(sections, heading_level(i18n['heading_etymology'], 4))
table.insert(sections, tostring(etymology))
end
add_any_notes(sections, args, i18n['manual_etymology'])
local pronunciation = getPronunciation( frame, current_lexeme, lang_name, lex_cat )
if pronunciation ~= '' then
table.insert(sections, heading_level(i18n['heading_pronunciation'], 4))
table.insert(sections, tostring(pronunciation))
end
add_any_notes(sections, args, i18n['manual_pronunciation'])
local alternative_spellings = getAlternativeSpellings( current_lexeme )
if alternative_spellings ~= '' then
table.insert(sections, heading_level(i18n['heading_alternative_spellings'], 4))
table.insert(sections, alternative_spellings)
end
if lang_code and lang_code ~= 'ksy' and lang_code ~= 'rkt' then -- Skip for Kharia Thar, Rangpuri
if lex_cat == 'Q34698' then -- বিশেষণ
if lang_code == 'en' or lang_code == 'bn' then
if #forms <= 1 then
lex_cat_template = frame:expandTemplate{title= lang_code .. '-বিশেষণ'}
end
else
lex_cat_template = safeExpand(frame, lang_code .. '-adj')
if not lex_cat_template then
lex_cat_template = safeExpand(frame, lang_code .. '-বিশেষণ')
end
end
elseif lex_cat == 'Q1084' then -- @TODO: Also check for plural forms
local gender
local stmts = current_lexeme:getAllStatements('P5185') -- ব্যাকরণগত লিঙ্গ
if #stmts ~= 0 then
if #stmts == 1 then
local gender_qid = stmts[1].mainsnak.datavalue.value.id
if gender_qid == 'Q499327' then
gender = 'm'
elseif gender_qid == 'Q1775415' then
gender = 'f'
elseif gender_qid == 'Q1775461' then
gender = 'n'
elseif gender_qid == 'Q1305037' then
gender = 'c'
end
end
else
for i, stmt in pairs(stmts) do
local qid = stmts[i].mainsnak.datavalue.value.id
if qid == 'Q499327' then
gender = gender .. 'm'
elseif qid == 'Q1775415' then
gender = gender .. 'f'
end
end
end
-- The following checks are ordered based on which one is expected to be true in a higher number of cases.
if current_language == 'Q13955' then
if matched_lemma then
lex_cat_template = safeExpand(frame, {title='ar-noun', args={matched_lemma,gender}})
else
lex_cat_template = frame:expandTemplate{title='ar-noun', args={nil,gender}}
end
elseif current_language == 'Q29919' then
lex_cat_template = frame:expandTemplate{title='arz-noun', args={g=gender}}
elseif current_language == 'Q397' then
if matched_lemma then
lex_cat_template = frame:expandTemplate{title='la-noun', args={matched_lemma,g=gender}}
end
elseif current_language == 'Q11059' then
lex_cat_template = frame:expandTemplate{title='sa-noun', args={g=gender}}
elseif current_language ~= 'Q1860' then -- These templates require the gender to be passed as the 1st argument.
lex_cat_template = safeExpand(frame, lang_code .. i18n['noun_template_suffix'], {gender})
if not lex_cat_template then
lex_cat_template = safeExpand(frame, lang_code .. i18n['noun_template_suffix_fallback'], {gender})
end
end
end
end
-- elseif lex_cat == 'Q147276' then
-- lex_cat_template = safeExpand(frame, lang_code .. '-proper noun', {gender})
-- if not lex_cat_template then
-- lex_cat_template = safeExpand(frame, lang_code .. '-নামবাচক বিশেষ্য', {gender})
-- end
end
if lex_cat_template then
table.insert(sections, lex_cat_template)
else
if matched_lemma then
table.insert(sections, heading_level(matched_lemma, 4))
else
table.insert(sections, i18n.tocatlink(i18n['category_no_matching_lemma']))
end
end
local meanings, references_seen, sense_extlinks = unpack(getMeanings( frame, args, current_lexeme, senses, references_seen, lang_name))
table.insert(sections, tostring(meanings))
add_any_notes(sections, args, i18n['manual_meaning'])
local instance_of = current_lexeme:getAllStatements('P31') -- সত্ত্বার ধরন
if #instance_of ~= 0 then
local instance_of_entity = instance_of[1].mainsnak.datavalue.value.id
if instance_of_entity == 'Q40437546' or instance_of_entity == 'Q120831827' or instance_of_entity == 'Q120717979' or instance_of_entity == 'Q124476844' then -- @TODO: generalise this so all types of roots are shown
table.insert(sections, i18n['text_instance_of'] .. ' ' .. getLabel(instance_of_entity))
elseif instance_of_entity == 'Q376431' then -- বর্ণের নাম
table.insert(sections, i18n.tocatlink(lang_code .. ':রং'))
end
end
local translations = getTranslations(frame, senses)
if translations then
table.insert(sections, translations)
end
-- (!) বিশেষ ভাষার বিভক্তির সারণি যদি থাকে সেগুলো এখানে নতুন if বিবৃতি যোগ করা যায়।
if next(forms) then
if current_language == 'Q9610' then -- বাংলা
if lex_cat == 'Q24905' then
local conjTable = require('মডিউল:আভিধানিক উপাত্ত/Q9610').getConjTable(frame, forms)
table.insert(sections, conjTable)
elseif lex_cat == 'Q1084' then
--table.insert(sections, callWikifunctionsFunction('Z33243|' .. lexeme_id .. '|', frame))
table.insert(sections, buildLanguageAgnosticInflectionTable())
elseif lex_cat == 'Q34698' then
if #forms > 1 then
table.insert(sections, buildLanguageAgnosticInflectionTable())
end
end
--elseif current_language == 'Q13955' then -- আরবি
-- if lex_cat == 'Q1084' then
-- table.insert(sections, frame:expandTemplate{title='ar-decl-noun', args={lemma}})
-- end
elseif current_language == 'Q188' then -- জার্মান
if lex_cat == 'Q1084' then
table.insert(sections, callWikifunctionsFunction('Z28602|' .. lexeme_id .. '|', frame))
end
else
if #forms > 1 then
table.insert(sections, buildLanguageAgnosticInflectionTable())
end
end
end
if lex_cat == 'Q134830' then
table.insert(sections, frame:expandTemplate{title='prefixsee', args={lang_code}})
elseif lex_cat == 'Q102047' then
table.insert(sections, frame:expandTemplate{title='suffixsee', args={lang_code}})
-- elseif lex_cat == 'Q111029' then
-- table.insert(sections, frame:expandTemplate{title='rootsee', args={'+', lang_code, matched_lemma}})
end
local reference_notes = get_any_notes(sections, args, i18n['manual_reference'])
if #references_seen > 0 or #reference_notes > 0 then
table.insert(sections, heading_level(i18n['heading_references'], 4))
table.insert(sections, frame:extensionTag('references'))
add_specific_notes(sections, reference_notes)
end
local external_link_table = getExternalLinks ( current_lexeme )
if #external_link_table > 0 then
local external_links = '* ' .. table.concat(external_link_table, '\n* ')
table.insert(sections, heading_level(i18n['heading_external_links'], 4))
table.insert(sections, external_links)
end
add_any_notes(sections, args, i18n['manual_external_link'])
if #references_seen == 0 and #reference_notes == 0 and sense_extlinks and #sense_extlinks == 0 and #external_link_table == 0 and #get_any_notes(sections, args, i18n['manual_external_link']) == 0 then
table.insert(sections, i18n.rfref_category(lang_name))
end
return table.concat(sections,"\n\n")
end
return p
1fd4uah5vyqvx1gzduokqswi7ne2avx
509642
509636
2026-06-03T14:26:26Z
Redmin
6857
+Z33243
509642
Scribunto
text/plain
local p = {}
local i18n = require('মডিউল:আভিধানিক উপাত্ত/i18n')
local references = require('মডিউল:উইকিউপাত্ত তথ্যসূত্র বিন্যাসকরণ').format
local getArgs = require('Module:Arguments').getArgs
local wb = mw.wikibase
local ustring = mw.ustring
local html = mw.html
local mw_lang = mw.language
local entity_cache = {}
local reference_cache = {}
local forms
local lang_code
local lex_cat
local matched_lemma
local function wrapStringInWikilinks(str)
local exceptions = i18n.nolinks
local result = str:gsub('(%S+)', function(token)
local word, trailing_char = token:match('^(.-)([;,]*)$')
local wrapped = word:gsub('[^(-/]+', function(part)
if exceptions[part] then
return part
else
return '[[' .. part .. '#' .. i18n['content_lang_name'] .. '|' .. part .. ']]'
end
end)
return wrapped .. trailing_char
end)
return result
end
local function serializeTable(val, name, skipnewlines, depth) -- https://stackoverflow.com/a/6081639
skipnewlines = skipnewlines or false
depth = depth or 0
local tmp = string.rep(" ", depth)
if name then tmp = tmp .. name .. " = " end
if type(val) == "table" then
tmp = tmp .. "{" .. (not skipnewlines and "\n" or "")
for k, v in pairs(val) do
tmp = tmp .. serializeTable(v, k, skipnewlines, depth + 1) .. "," .. (not skipnewlines and "\n" or "")
end
tmp = tmp .. string.rep(" ", depth) .. "}"
elseif type(val) == "number" then
tmp = tmp .. tostring(val)
elseif type(val) == "string" then
tmp = tmp .. string.format("%q", val)
elseif type(val) == "boolean" then
tmp = tmp .. (val and "true" or "false")
else
tmp = tmp .. "\"[inserializeable datatype:" .. type(val) .. "]\""
end
return tmp
end
-- Use this to safely expand templates when you are not sure that they exist.
local function safeExpand(frame, title, args)
local _, result = pcall(function()
return frame:expandTemplate{ title = title, args = args }
end)
if result:find('does not exist') then -- expandTemplate{} doesn't seem to throw any error that can be handled with pcall() so string search is the only viable option.
return nil
end
return result
end
-- Use this function to get the label of an item even if that item does not have any label in the wiki's content language or English.
local function getLabel(id)
if id == 'Q11051hi' then
return 'হিন্দি'
elseif id == 'Q11051ur' then
return 'উর্দু'
elseif id == 'Q56356571fa' then
return 'নয়া ফার্সি'
elseif id == 'Q56356571tg' then
return 'তাজিক'
elseif id == 'Q58635pa' or id == 'Q58635pnb' then
return 'পাঞ্জাবি'
end
local label = wb.getLabel(id)
if label then
return label
end
local labels = wb.getEntity(id).labels
if not labels then
return id
end
for _, v in pairs(labels) do
if v and v.value then
return v.value
end
end
end
local function getReference( id, reference )
local out_id = nil
local url_value
if reference_cache[id] == nil then
local ref_text = references(reference, wb, mw_lang, i18n['content_lang_code'], i18n['wikipedia'])
if reference.snaks then
if reference.snaks['P248'] then
for _, snak in pairs(reference.snaks['P248']) do
if snak.datavalue and snak.datavalue.value.id == 'Q428' then -- কুরআন
ref_text = ustring.gsub(ref_text, 'নামক অনুচ্ছেদ', 'নং আয়াত')
break
end
end
end
if reference.snaks['P854'] then
local snak = reference.snaks['P854'][1]
if snak.datavalue then
url_value = snak.datavalue.value
end
end
end
if url_value then
ref_text = ref_text .. ', [' .. url_value .. ' সংযোগ]'
end
reference_cache[id] = ref_text
else
out_id = id
end
return {out_id, reference_cache[id]}
end
local function getEntity( id )
if entity_cache[id] == nil then
entity_cache[id] = wb.getEntity(id)
end
return entity_cache[id] ~= false and entity_cache[id] or nil
end
local function getLexemeLanguageCode(current_lexeme)
local lang_item_id = current_lexeme:getLanguage()
if lang_item_id == nil then
return nil
end
local lang_entity = getEntity(lang_item_id)
if lang_entity == nil then
return nil
end
for i, statement_property in ipairs({'P305','P424', 'P220'}) do -- আইইটিএফ ভাষা ট্যাগ, উইকিমিডিয়া ভাষা কোড, আইএসও ৬৩৯-৩
local statements = lang_entity:getBestStatements(statement_property)
if statements[1] then
return statements[1].mainsnak.datavalue.value
end
end
return nil
end
-- Return the first form of the lexeme which has exactly the given grammatical feature.
local function formWithSingleGrammaticalFeature( item_id )
for i = 1, #forms do
local grammaticalFeatures = forms[i]:getGrammaticalFeatures()
if #grammaticalFeatures == 1 and grammaticalFeatures[1] == item_id then
return forms[i]
end
end
return nil
end
local function getArticleLinkTemplate(frame, stmt_value)
local template = ''
local sitelink = getEntity(stmt_value):getSitelink(i18n['wikipedia'])
if sitelink then
template = frame:expandTemplate{
title=i18n['template_wikipedia'],
args={sitelink}
}
end
return template
end
local function getArticleLinks (frame, sense )
local article_links = ''
for i, stmt in pairs(sense:getAllStatements('P5137')) do -- এই অর্থের জন্য আইটেম
article_links = article_links .. getArticleLinkTemplate(frame, stmt.mainsnak.datavalue.value.id)
end
for i, stmt in pairs(sense:getAllStatements('P9970')) do -- এই অর্থের জন্য বিধেয়
article_links = article_links .. getArticleLinkTemplate(frame, stmt.mainsnak.datavalue.value.id)
end
return article_links
end
-- @TODO: Generalise
local function expandTemplateForProperty(frame, object, property, template)
local lemmas = {}
local n = 0
for _, stmt in pairs(object:getAllStatements(property)) do
local lex = wb.lexeme.splitLexemeId(stmt.mainsnak.datavalue.value.id)
if lex then
lex = getEntity(lex)
n = n + 1
lemmas[n] = lex:getLemma(lang_code)
end
end
if not lang_code or n == 0 then
return ''
end
-- Build args: first lang_code, then lemmas
local args = {lang_code}
for i = 1, n do
args[#args + 1] = lemmas[i]
end
return frame:expandTemplate{
title = template,
args = args
}
end
local function getExternalLinks( entity ) -- T418639
local external_links = {}
if entity.claims == nil then return external_links end
local formatter_urls = require('মডিউল:আভিধানিক উপাত্ত/urls').formatter_urls
for property_id, statements in pairs(entity.claims) do
local formatter_url = formatter_urls[property_id]
if formatter_url then
local property_source = wb.getBestStatements(property_id, 'P9073')
local source_name
if next(property_source) then
source_name = getLabel(property_source[1].mainsnak.datavalue.value.id)
or property_source[1].mainsnak.datavalue.value.id
else
source_name = getLabel(property_id) or property_id
end
for i = 1, #statements do
local stmt = statements[i]
if stmt.mainsnak.datavalue then
local formatted_link = ustring.gsub(
ustring.gsub(formatter_url, '$1', ustring.gsub(stmt.mainsnak.datavalue.value, '%%', '%%%%')),
' ', '+'
)
table.insert(external_links,
'[' .. formatted_link .. ' ' .. source_name .. ']')
end
end
end
end
return external_links
end
p.getExternalLinks = getExternalLinks -- রেখে দিন যাতে ডিবাগিং সম্ভব হয়
local function termSpan( term )
local text = term[1]
local lang = term[2]
local dir = mw_lang.new( lang ):getDir()
local span = html.create( 'span' )
span:attr( 'lang', lang )
:attr( 'dir', dir )
:wikitext( text )
return tostring( span )
end
local function termLink( term, lang_qid )
local text = term[1]
local lang = term[2]
local dir = mw_lang.new( lang ):getDir()
local span = html.create( 'span' )
span:attr( 'lang', lang )
:attr( 'dir', dir )
:wikitext( '[[' .. text .. '#' .. getLabel(lang_qid) .. '|' .. text .. ']]' )
return tostring( span )
end
local function getLemmata( current_lexeme )
local lemma_string = ''
for i, rep in pairs(current_lexeme:getLemmas()) do
if lemma_string == '' then
lemma_string = termSpan(rep)
else
lemma_string = lemma_string .. '/' .. termSpan(rep)
end
end
return lemma_string
end
local function getLinkedLemmata( current_lexeme )
local lemma_string = ''
for i, rep in pairs(current_lexeme:getLemmas()) do
if lemma_string == '' then
lemma_string = termLink(rep, current_lexeme:getLanguage())
else
lemma_string = lemma_string .. '/' .. termLink(rep, current_lexeme:getLanguage())
end
end
return lemma_string
end
local function getExamples( current_lexeme, sense_id, references_seen )
local examples = html.create('dl')
local example_text, example_lang, example_form, example_str
for i, stmt in pairs(current_lexeme:getAllStatements('P5831')) do -- ব্যবহারের উদাহরণ
if stmt.qualifiers and stmt.qualifiers['P6072'] and stmt.qualifiers['P6072'][1].datavalue.value.id == sense_id then -- বিষয়ে লেক্সিমের অর্থ
example_text = ustring.gsub(stmt.mainsnak.datavalue.value.text, ' / ','<br/>')
example_lang = stmt.mainsnak.datavalue.value.language
local example_form_strs = {}
if stmt.qualifiers['P1810'] then
table.insert(example_form_strs, stmt.qualifiers['P1810'][1].datavalue.value)
elseif stmt.qualifiers['P5830'] then
example_form = getEntity(stmt.qualifiers['P5830'][1].datavalue.value.id) -- বিষয়ে লেক্সিমের রূপ
for _, rep in pairs(example_form:getRepresentations()) do
table.insert(example_form_strs, rep[1])
end
end
for i, example_form_str in pairs(example_form_strs) do
new_example_text = ustring.gsub(example_text, example_form_str, "'''" .. example_form_str .. "'''")
if new_example_text ~= example_text then
example_str = termSpan({new_example_text, example_lang})
break
end
new_example_text = example_text
end
if example_str == nil then
example_str = termSpan({example_text, example_lang})
end
local reference_text = ''
if stmt.references then
for j, reference in pairs(stmt.references) do
table.insert(references_seen, reference.hash)
local got_reference = getReference(reference.hash, reference)
reference_text = reference_text .. '\n\n' .. got_reference[2]
end
end
if example_str then
examples:tag('dd'):wikitext("''" .. example_str .. "''")
if reference_text ~= '' then
examples:done():tag('dd'):css('text-indent', '2em'):wikitext(reference_text)
end
end
end
end
for i, stmt in pairs(wb.getAllStatements(sense_id, 'P5831')) do -- ব্যবহারের উদাহরণ
example_text = ustring.gsub(stmt.mainsnak.datavalue.value.text, ' / ','<br/>')
example_lang = stmt.mainsnak.datavalue.value.language
local example_form_str = nil
if stmt.qualifers then
example_form = getEntity(stmt.qualifiers['P5830'][1].datavalue.value.id) -- বিষয়ে লেক্সিমের রূপ
if stmt.qualifiers['P1810'] then
example_form_str = stmt.qualifiers['P1810'][1].datavalue.value
end
end
if example_form and example_form_str == nil then
example_form_str = example_form:getRepresentation(i18n['content_lang_code'])
end
if example_form and example_form_str == nil then
example_form_str = example_form:getRepresentations()[1][1]
end
if example_form_str then
example_text = ustring.gsub(example_text, example_form_str, "'''" .. example_form_str .. "'''")
end
example_str = termSpan({example_text, example_lang})
local reference_text = ''
if stmt.references then
for j, reference in pairs(stmt.references) do
table.insert(references_seen, reference.hash)
local got_reference = getReference(reference.hash, reference)
reference_text = reference_text .. '\n\n' .. got_reference[2]
end
end
if example_str then
examples:tag('dd'):wikitext("''" .. example_str .. "''")
if reference_text ~= '' then
examples:done():tag('dd'):css('text-indent', '2em'):wikitext(reference_text)
end
end
end
return { tostring(examples) , references_seen }
end
-- This calls frame:preprocess() instead of :callParserFunction() because the latter does not work for Wikifunctions function calls yet (see https://www.wikifunctions.org/wiki/Wikifunctions:Embedded_function_calls).
local function callWikifunctionsFunction(args, frame)
return frame:preprocess('{{#function:' .. args .. '}}')
end
local function checkTitleCodePointInRange(title, start_point, end_point)
return ustring.find( title, '[' ..ustring.char(start_point) .. '-' .. ustring.char(end_point) .. ']' )
end
local function getLanguageForCategories( lang_id, current_page_title )
-- বিশেষ ভাষার জন্য
if lang_id == 'Q11051' then -- হিন্দি/উর্দু
if checkTitleCodePointInRange(current_page_title, 0x0600, 0x06ff) then -- উর্দু
lang_id = 'Q11051ur'
elseif checkTitleCodePointInRange(current_page_title, 0x0900, 0x097f) then -- হিন্দি
lang_id = 'Q11051hi'
end
elseif lang_id == 'Q58635' then -- পাঞ্জাবি
if checkTitleCodePointInRange(current_page_title, 0x0600, 0x06ff) then -- শাহমুখী
lang_id = 'Q58635pnb'
elseif checkTitleCodePointInRange(current_page_title, 0x0a00, 0x0a7f) then -- গুরুমুখী
lang_id = 'Q58635pa'
end
elseif lang_id == 'Q56356571' then -- নয়া ফার্সি ভাষা
if checkTitleCodePointInRange(current_page_title, 0x0600, 0x06ff) then -- ফার্সি (ইরান/আফগানিস্তান)
lang_id = 'Q56356571fa'
elseif checkTitleCodePointInRange(current_page_title, 0x0400, 0x04ff) then -- তাজিক
lang_id = 'Q56356571tg'
end
end
return lang_id
end
local function getOneStringForProperty(object, property)
local val
local stmts = object:getAllStatements(property)
if #stmts ~= 0 then
val = stmts[1].mainsnak.datavalue.value
end
return val
end
local function getTranslations(frame, senses) -- TODO: woefully incomplete until T185313 and T199887 are resolved
if #senses == 0 then
return nil
end
local all_translations = {}
for _, sense in pairs(senses) do
local translation_set = {}
local gloss = sense:getGloss('bn')
for _, stmt in pairs(sense:getAllStatements('P5972')) do
local translation = stmt.mainsnak.datavalue.value.id
local lexeme_id = wb.lexeme.splitLexemeId(translation)
local language = getLabel(getEntity(lexeme_id):getLanguage())
table.insert(translation_set, language .. ': ' .. getLinkedLemmata(getEntity(lexeme_id)) .. '<br/>')
end
if #translation_set > 0 then
local block = frame:expandTemplate{ title = i18n['template_trans-top'], args = { gloss } }
block = block .. table.concat(translation_set, '\n') .. frame:expandTemplate{ title = i18n['template_trans-bottom'] }
table.insert(all_translations, block)
end
end
if #all_translations == 0 then
return nil
end
return '====' .. i18n['heading_translation'] .. '==== \n' .. table.concat(all_translations, '\n')
end
local createicon = function(langcode, entityID, propertyID)
langcode = langcode or ""
propertyID = propertyID or ""
local icon = " <span class='penicon autoconfirmed-show'>[["
-- " <span data-bridge-edit-flow='overwrite' class='penicon'>[[" -> enable Wikidata Bridge
.. "File:OOjs UI icon edit-ltr-progressive.svg |frameless |text-top |10px |alt="
.. i18n['edit_wikidata']
.. "|link=https://www.wikidata.org/entity/" .. entityID
if langcode ~= "" then icon = icon .. "?uselang=" .. langcode end
if propertyID ~= "" then icon = icon .. "#" .. propertyID end
icon = icon .. "|" .. i18n['edit_wikidata'] .. "]]</span>"
return icon
end
local function getMeanings( frame, args, current_lexeme, senses, references_seen, language_name)
if #senses == 0 then
return {createicon(i18n['content_lang_code'], current_lexeme:getId()) .. "''" .. i18n['text_category_rfdef'] .. "''" .. i18n.tocatlink(i18n['category_rfdef']), references_seen}
end
local meanings = html.create( 'ol' )
local item_label_gloss_parts = {}
local idlinkset = {}
for i, sense in pairs(senses) do
local gloss_text_parts = {}
local main_gloss_text = frame:expandTemplate{
title=i18n['template_anchor'],
args={sense:getId()}
}
local specifiers = {}
for k, property_id in ipairs({'P6084', 'P6191', 'P9488'}) do -- অবস্থান যেখানে শব্দার্থ ব্যবহৃত, যে রীতিতে শব্দার্থ ব্যবহৃত হয়, যে ক্ষেত্রে ব্যবহৃত
for _, stmt in pairs(sense:getAllStatements(property_id)) do
local stmt_value = stmt.mainsnak.datavalue.value.id
local reference_text = ''
local refs = stmt.references
if refs then
for j, reference in pairs(refs) do
table.insert(references_seen, reference.hash)
local got_reference = getReference(reference.hash, reference)
reference_text = reference_text .. '\n\n' .. frame:extensionTag('ref', got_reference[2])
end
end
local val = getLabel(stmt_value)
table.insert(specifiers, val .. reference_text)
if property_id == 'P9488' then
table.insert(item_label_gloss_parts, i18n.tocatlink(lang_code .. ':' .. val))
end
end
if #specifiers > 0 then
main_gloss_text = main_gloss_text .. "(''" .. table.concat(specifiers, "'', ''") .. "'') "
end
end
local gloss = sense:getGloss( i18n['content_lang_code'] )
if gloss then
main_gloss_text = main_gloss_text .. wrapStringInWikilinks(gloss)
if gloss:match('^প্রদত্ত%s*(%S-)%s*নাম$') then -- given names
main_gloss_text = main_gloss_text .. i18n.tocatlink(language_name .. ' ' .. i18n['category_given_names'])
end
else
local other_gloss_text = nil
local other_gloss_lang = nil
for k, stmt in pairs(sense:getAllStatements('P5137')) do -- যদি 'এই অর্থের জন্য আইটেম' মানের বাংলা লেবেল থাকে
local stmt_value = stmt.mainsnak.datavalue.value.id
local stmt_label = getLabel(stmt_value)
if stmt_label then
table.insert(item_label_gloss_parts, '[[:d:' .. stmt_value .. '|' .. stmt_label .. ']]')
end
end
if #item_label_gloss_parts > 0 then
other_gloss_text = table.concat(item_label_gloss_parts, '; ')
end
if other_gloss_text == nil then
for _, fallback_lang in ipairs(mw_lang.getFallbacksFor( i18n['content_lang_code'] )) do
if sense:getGloss( fallback_lang ) then
other_gloss_text, other_gloss_lang = sense:getGloss( fallback_lang )
end
end
if other_gloss_lang == nil then
local glosses = sense:getGlosses()
for _, gloss in pairs(glosses) do
other_gloss_text = gloss[1]
other_gloss_lang = gloss[2]
break
end
end
main_gloss_text = main_gloss_text .. other_gloss_text .. "<sup><em>" .. mw_lang.fetchLanguageName(other_gloss_lang, i18n['content_lang_code']) .. "</em></sup>"
else
main_gloss_text = main_gloss_text .. "''" .. other_gloss_text .. "''"
end
main_gloss_text = main_gloss_text .. i18n.tocatlink(i18n['category_rfdef_equivalent'])
end
local synonym = expandTemplateForProperty(frame, sense, 'P5973', i18n['template_synonym'])
if synonym ~= '' then
main_gloss_text = main_gloss_text .. ' <br/> ' .. synonym
end
local antonym = expandTemplateForProperty(frame, sense, 'P5974', i18n['template_antonym'])
if antonym ~= '' then
main_gloss_text = main_gloss_text .. ' <br/> ' .. antonym
end
local hypernym = expandTemplateForProperty(frame, sense, 'P6593', i18n['template_hypernym'])
if hypernym ~= '' then
main_gloss_text = main_gloss_text .. ' <br/> ' .. hypernym
end
if lex_cat == 'Q1084' or lex_cat == 'Q147276' then -- noun or proper noun
local demonym = expandTemplateForProperty(frame, sense, 'P6271', i18n['template_demonym-noun'])
main_gloss_text = main_gloss_text .. ' <br/> ' .. demonym
elseif lex_cat == 'Q34698' then
local demonym = expandTemplateForProperty(frame, sense, 'P6271', i18n['template_demonym-adj'])
main_gloss_text = main_gloss_text .. ' <br/> ' .. demonym
end
table.insert(gloss_text_parts, main_gloss_text .. createicon(i18n['content_lang_code'], sense:getId()))
for i, stmt in pairs(sense:getAllStatements('P8394')) do -- টিপ্পনীর উদ্ধৃতি
local gloss_quote = termSpan({stmt.mainsnak.datavalue.value.text, stmt.mainsnak.datavalue.value.language})
if stmt.references[1] then
local got_reference = getReference ( stmt.references[1].hash, stmt.references[1] )
gloss_quote = '"' .. gloss_quote .. '" ' .. got_reference[2]
end
table.insert(references_seen, stmt.references[1].hash)
table.insert(gloss_text_parts, frame:extensionTag('ref', gloss_quote))
end
for i, stmt in pairs(sense:getAllStatements('P1343')) do -- বর্ণিত উৎস
-- TODO: do away with making fake reference objects
local fake_reference = { ['snaks'] = {} }
fake_reference.snaks['P248'] = { [1] = stmt.mainsnak }
qualifiers_order = stmt['qualifiers-order']
if qualifiers_order then
for i, k in ipairs(qualifiers_order) do fake_reference.snaks[k] = stmt.qualifiers[k] end
end
fake_reference.hash = mw.hash.hashValue('sha3-512', serializeTable(fake_reference))
table.insert(references_seen, fake_reference.hash)
local got_reference = getReference(fake_reference.hash, fake_reference)
if got_reference[1] == nil then
table.insert(gloss_text_parts, frame:extensionTag('ref', got_reference[2], {name = fake_reference.hash}))
else
table.insert(gloss_text_parts, frame:extensionTag{name = 'ref', content='', args = {name = got_reference[1]}})
end
end
local first_sense_image = ''
local sense_images = sense:getAllStatements('P18')
if next(sense_images) then
first_sense_image = sense_images[1].mainsnak.datavalue.value
end
if first_sense_image ~= '' then
table.insert(gloss_text_parts, '[[চিত্র:' .. first_sense_image .. "|thumb|'''" .. getLemmata(current_lexeme) .. "'''—" .. main_gloss_text .. ']]')
end
local idlinks = getExternalLinks(sense)
if #idlinks > 0 then
local idlinktext = '<small>('
for _, idlink in pairs(idlinks) do
idlinktext = idlinktext .. idlink .. '\n'
end
idlinktext = idlinktext .. ')</small>'
table.insert(gloss_text_parts, idlinktext)
table.insert(idlinkset, idlinks)
end
local externallinks = getArticleLinks(frame, sense)
if externallinks ~= '' then
table.insert(gloss_text_parts, externallinks)
end
local new_notes = {}
local sense_keys = { sense:getId(), string.sub(sense:getId(), string.find(sense:getId(), '-')+1) }
for _, v in ipairs(sense_keys) do
if args[v] then
table.insert(new_notes, args[v])
end
end
if #new_notes > 0 then
for _, v in ipairs(new_notes) do
if i == 1 then
table.insert(gloss_text_parts, '<br/>' .. v)
else
table.insert(gloss_text_parts, v)
end
end
end
local examples, references_seen = unpack(getExamples( current_lexeme, sense:getId(), references_seen ))
local gloss_text = table.concat(gloss_text_parts, '\n')
meanings:tag('li'):wikitext(gloss_text):wikitext(examples)
end
return {meanings, references_seen, idlinkset}
end
local function getPronunciationBaseForm( lang_name, lex_cat)
local base_form = nil
-- (!) অন্য ভাষার শব্দের যদি অন্য রকম মূল ফর্ম থাকে সেগুলো এখানে নতুন if বিবৃতি দিয়ে যোগ করা যায়।
if lang_name == 'বাংলা' then
if lex_cat == 'Q1084' then -- বিশেষ্য
base_form = formWithSingleGrammaticalFeature( 'Q131105' ) -- কর্তৃকারক
elseif lex_cat == 'Q24905' then -- ক্রিয়া
base_form = formWithSingleGrammaticalFeature( 'Q1350145' ) -- ক্রিয়া বিশেষ্য
end
end
if base_form == nil then
for i, form in pairs(forms) do
base_form = form
break
end
end
return base_form
end
local function getCombines( current_lexeme, frame )
local combines = ''
local index_mappings = {}
for i, stmt in pairs(current_lexeme:getAllStatements('P5238')) do
if stmt.qualifiers and stmt.qualifiers['P1545'] then -- ক্রম
local current_index = tonumber(stmt.qualifiers['P1545'][1].datavalue.value)
index_mappings[current_index] = stmt
end
end
if #index_mappings ~= 0 then
for i, stmt in ipairs(index_mappings) do
if stmt.mainsnak.datavalue then
local part_lexeme_id = stmt.mainsnak.datavalue.value.id
local part_lexeme = getEntity(part_lexeme_id)
local current_substring = getLinkedLemmata(part_lexeme)
local part_etymology = getEtymology(part_lexeme, frame, 'partial')
if part_etymology ~= '' and part_etymology then
current_substring = current_substring .. ' (← ' .. part_etymology .. ')'
end
if combines == '' then
combines = current_substring
else -- @TODO: This shoukd use the 'affix' and 'compound' templates instead.
combines = combines .. ' + ' .. current_substring
end
end
end
end
return combines
end
function getRoots( current_lexeme )
local stmts = current_lexeme:getAllStatements('P5920')
if #stmts == 0 then return '', '', '' end
local root_lexeme = getEntity(stmts[1].mainsnak.datavalue.value.id)
return getLexemeLanguageCode(root_lexeme), '√' .. getLinkedLemmata(root_lexeme), root_lexeme:getLemma('ar')
end
function getEtymology( current_lexeme, frame, mode )
-- @TODO: Fix the etymology chains that are not possible to render
local etymology = ''
local current_combines = getCombines(current_lexeme, frame)
local root_lang, current_roots, root_str = getRoots(current_lexeme)
if mode ~= 'partial' and root_str then
--frame:expandTemplate{title=i18n['template_root'], args={lang_code, root_lang, root_str}}
end
local stmts = current_lexeme:getAllStatements('P5191')
local new_etymology_string
if #stmts == 0 then
if current_roots ~= '' and current_combines ~= '' and current_roots then
return current_roots .. '<br/>(' .. current_combines .. ')'
elseif current_roots ~= '' then
if lang_code == 'ar' and mode ~= 'partial' and root_str ~= matched_lemma then
return frame:expandTemplate{title=i18n['template_ar-rootbox'], args={root_str}}
else
return current_roots
end
else
return current_combines
end
end
local origin_lexeme_string
for i, stmt in pairs(stmts) do
local origin_lexeme_dv = stmt.mainsnak.datavalue -- If this is nil, the origin lexeme is not known.
if origin_lexeme_dv then
local origin_lexeme = getEntity(origin_lexeme_dv.value.id)
local origin_lexeme_lang = getLabel(origin_lexeme:getLanguage())
local sitelink = i18n.wplink(origin_lexeme:getLanguage(), origin_lexeme_lang, wb)
if sitelink ~= '' then
origin_lexeme_string = getLinkedLemmata(origin_lexeme) .. ' (' .. sitelink .. ')'
else
origin_lexeme_string = getLinkedLemmata(origin_lexeme) .. ' (' .. origin_lexeme_lang .. ')'
end
if stmt.qualifiers and stmt.qualifiers['P5886'] then
local mode_of_derivation = stmt.qualifiers['P5886'][1].datavalue.value.id
if mode_of_derivation == 'Q1345001' then
-- @TODO: Add support for showing gender
origin_lexeme_string = frame:expandTemplate{title=i18n['template_borrowed'], args={lang_code, getLexemeLanguageCode(origin_lexeme), getLemmata(origin_lexeme), pos=getLabel(lex_cat)}} .. ' ' .. i18n['etymology_borrowing']
elseif mode_of_derivation == 'Q845079' then
origin_lexeme_string = ustring.gsub(i18n['etymology_learned_borrowing'], '$1', origin_lexeme_string)
elseif mode_of_derivation == 'Q56611986' then
origin_lexeme_string = frame:expandTemplate{title=i18n['template_inherited'], args={lang_code, getLexemeLanguageCode(origin_lexeme), getLemmata(origin_lexeme), pos=getLabel(lex_cat)}} .. ' ' .. i18n['etymology_inheritance']
elseif mode_of_derivation == 'Q189743' then
origin_lexeme_string = frame:expandTemplate{title=i18n['template_ellipsis'], args={lang_code, getLemmata(origin_lexeme)}} .. ' ' .. i18n['etymology_ellipsis']
end
end
local origin_origin = getEtymology(origin_lexeme, frame)
if origin_origin ~= '' and origin_origin then
new_etymology_string = origin_lexeme_string .. ' ← ' .. origin_origin
else
new_etymology_string = origin_lexeme_string
end
end
if etymology == '' then
etymology = new_etymology_string
elseif origin_lexeme_string and etymology then
etymology = etymology .. ' ' .. origin_lexeme_string
elseif origin_lexeme_string and etymology == nil then
etymology = origin_lexeme_string
end
end
if current_roots ~= '' and etymology and current_roots then
etymology = etymology .. ' ' .. current_roots
elseif current_roots ~= '' and etymology == nil then
etymology = current_roots
end
if current_combines ~= '' and etymology then
etymology = etymology .. '<br/>(' .. current_combines .. ')'
end
return etymology
end
local function pronunciationBlock(block, value)
return '* ' .. i18n['text_' .. block] .. ' ' .. value
end
local function getPronunciation( frame, current_lexeme, lang_name, lex_cat )
local pronunciations = {}
local base_form = getPronunciationBaseForm(lang_name, lex_cat )
if base_form then
for i, stmt in pairs(base_form:getAllStatements('P443')) do -- উচ্চারণের অডিও
local pronunciation_file = stmt.mainsnak.datavalue.value
local specifier_text = ''
local specifiers = {}
if stmt.qualifiers then
for k, property_id in ipairs({'P5237'}) do -- উচ্চারণের ধরন
local qual = stmt.qualifiers[property_id]
if qual then
for _, qual in pairs(qual) do
local stmt_value = qual.datavalue.value.id
table.insert(specifiers, getLabel(stmt_value))
end
end
end
end
if #specifiers > 0 then
specifier_text = table.concat(specifiers, "'', ''")
end
local audio_text
if specifier_text ~= '' then
audio_text = i18n['text_audio'] .. ' (' .. specifier_text .. ')'
else
audio_text = i18n['text_audio']
end
table.insert(pronunciations, '* ' .. frame:expandTemplate{
title= i18n['template_audio'],
args = {lang_name, pronunciation_file, audio_text}
})
end
local ipa_transcription = base_form:getAllStatements('P898') -- - আধ্বব প্রতিলিপিকরণ
local iso15919_transcription = getOneStringForProperty(base_form, 'P5825') -- আইএসও ১৫৯১৯ প্রতিলিপিকরণ
local itrans = getOneStringForProperty(base_form, 'P8881') -- ITRANS
local iast = getOneStringForProperty(base_form, 'P7581') -- আইএএসটি প্রতিলিপিকরণ
local xsampa = getOneStringForProperty(base_form, 'P2859')
-- @TODO: অডিও ও প্রতিলিপিকরণ দুটোই থাকলে সেই ক্ষেত্রে একটার ঠিক পরেই আরেকটা দেখানো উচিত
if #ipa_transcription ~= 0 then
for i, stmt in pairs(ipa_transcription) do
local ipa_text = stmt.mainsnak.datavalue.value
local specifier_text = ''
local specifiers = {}
if stmt.qualifiers then
for k, property_id in ipairs({'P5237'}) do -- উচ্চারণের ধরন
for l, qual in ipairs(stmt.qualifiers[property_id]) do
table.insert(specifiers, getLabel( qual.datavalue.value.id ))
end
end
end
if #specifiers > 0 then
specifier_text = "(''" .. table.concat(specifiers, "'', ''") .. "'') "
end
local syllable_count
if lang_code == 'tr' then
syllable_count = i18n['text_syllable_count'] .. ' ' .. callWikifunctionsFunction('Z10029|' .. ipa_text, frame)
else
syllable_count = i18n['text_syllable_count'] .. ' ' .. callWikifunctionsFunction('Z30837|' .. ipa_text, frame)
end
table.insert(pronunciations, '* ' .. specifier_text .. frame:expandTemplate{
title= i18n['template_ipa'],
args = {lang_name, ipa_text}
} .. '\n* ' .. syllable_count)
end
-- The following checks are ordered based on which one is expected to be true in a higher number of cases.
elseif lang_name == 'বাংলা' then
table.insert(pronunciations, '* ' .. frame:expandTemplate{
title='bn-IPA',
})
elseif lang_name == 'আরবি' then
local lemma = current_lexeme:getLemma('ar')
table.insert(pronunciations, '* ' .. frame:expandTemplate{
title='ar-IPA',
args={lemma}
})
elseif lang_name == 'ফালা' then
table.insert(pronunciations, '* ' .. frame:expandTemplate{
title='fax-pron',
})
elseif lang_code == 'fi' then
table.insert(pronunciations, '* ' .. frame:expandTemplate{
title='fi-IPA',
})
elseif lang_code == 'ko' then
table.insert(pronunciations, '* ' .. frame:expandTemplate{
title='ko-IPA',
})
end
if iso15919_transcription then
table.insert(pronunciations, pronunciationBlock('iso15919', iso15919_transcription))
end
if itrans then
table.insert(pronunciations, pronunciationBlock('itrans', itrans))
end
if iast then
table.insert(pronunciations, pronunciationBlock('iast', iast))
end
if xsampa then
table.insert(pronunciations, pronunciationBlock('xsampa', xsampa))
end
end -- {{আধ্বব|en|/ˈɪntəvjuː/}}
return table.concat(pronunciations, '\n')
end
local function getAlternativeSpellings( current_lexeme )
local alt_spellings = {}
for i, stmt in pairs(current_lexeme:getAllStatements('P11577')) do -- বিকল্প বানান
if stmt.mainsnak.datavalue then
table.insert(alt_spellings, '* ' .. getLinkedLemmata(getEntity(stmt.mainsnak.datavalue.value.id)))
end
end
return table.concat(alt_spellings, '\n')
end
local function heading_level(text, level)
local heading_delimiter = string.rep('=', level)
return heading_delimiter .. ' ' .. text .. ' ' .. heading_delimiter
end
function get_any_notes(sections, args, keys)
local notes = {}
for i, v in ipairs(keys) do
if args[v] then
table.insert(notes, args[v])
end
end
return notes
end
function add_specific_notes(sections, notes)
for i, v in ipairs(notes) do
table.insert(sections, v)
end
end
local function add_any_notes(sections, args, keys)
for i, v in ipairs(keys) do
if args[v] then
table.insert(sections, args[v])
end
end
end
local function getMatchingLemmaForPageTitle(lexeme, title)
local lemmas = lexeme:getLemmas()
local matched_lemma
for _, lemma_entry in ipairs(lemmas) do
local lemma = lemma_entry[1]
if lemma == title then
matched_lemma = lemma
break
end
end
if matched_lemma == nil and lang_code == 'ar' then -- Arabic lemmas do not match the title of the entry because those are written with different characters stripped out on Wiktionary. This could be changed to call stripDiactritics() from [[Module:languages]] in the future if desired.
matched_lemma = lexeme:getLemma('ar')
end
return matched_lemma
end
local function buildLanguageAgnosticInflectionTable()
local has_image = false
local form_images = {}
for i, form in ipairs(forms) do
local form_image = form:getAllStatements('P7407')
if next(form_image) then
form_images[i] = form_image[1].mainsnak.datavalue.value
has_image = true
end
end
local table_class = "wikitable mw-collapsible sortable"
if not has_image then
table_class = table_class .. " mw-collapsed"
end
local text = "{| class='" .. table_class .. "' style='border:solid 1px rgb(80%,80%,100%); text-align:center;'\n"
text = text .. "|+ " .. i18n['heading_inflection_table'] .. "\n"
text = text .. "|- \n"
text = text .. "! " .. i18n['heading_form'] .. " !! " .. i18n['heading_grammatical_features']
if has_image then
text = text .. " !! " .. (i18n['heading_image'])
end
text = text .. " \n"
for i, form in ipairs(forms) do
local rep = form:getRepresentations()
local feat = form:getGrammaticalFeatures()
local rep_text = ""
for j, r in pairs(rep) do
if rep_text == "" then
rep_text = r[1]
else
rep_text = rep_text .. " / " .. r[1]
end
end
local feat_text = ""
if feat then
for j, f in ipairs(feat) do
local label = getLabel(f) or f
if feat_text == "" then
feat_text = label
else
feat_text = feat_text .. ", " .. label
end
end
end
text = text .. "|-\n"
text = text .. "| " .. (rep_text ~= "" and rep_text or "—")
text = text .. " || " .. (feat_text ~= "" and feat_text or "—")
if has_image then
local image_cell = "—"
if form_images[i] then
image_cell = "[[চিত্র:" .. form_images[i] .. "|50px]]"
end
text = text .. " || " .. image_cell
end
text = text .. "\n"
end
text = text .. "|}"
return text
end
function p.all( frame )
local args = getArgs(frame)
local lexeme_id = args[1]
local current_lexeme = getEntity(lexeme_id)
local current_language = current_lexeme:getLanguage()
local senses = current_lexeme:getSenses()
local add_heading = true
forms = current_lexeme:getForms()
if args[2] then
local val = mw.text.trim(tostring(args[2]))
if val == "false" or val == "0" or val == "না" then
add_heading = false
end
end
local references_seen = {}
local sections = {}
local title = mw.title.getCurrentTitle().text
local lang_category = getLanguageForCategories(current_language, title)
local lang_name = getLabel(lang_category)
if add_heading == true then
local lang_heading = "== " .. lang_name .. " =="
table.insert(sections, lang_heading)
end
matched_lemma = getMatchingLemmaForPageTitle(current_lexeme, title)
lex_cat = current_lexeme:getLexicalCategory()
lang_code = getLexemeLanguageCode(current_lexeme)
local cat = i18n.lang_category(getLabel(lex_cat), lang_name)
local lex_cat_template
if cat then
table.insert(sections, '===' .. getLabel(lex_cat) .. cat .. frame:expandTemplate{
title = i18n['template_anchor'],
args = { lexeme_id }
} .. '===')
table.insert(sections, frame:expandTemplate{
title= i18n['template_lexeme'],
args = {lexeme_id}
})
add_any_notes(sections, args, i18n['manual_category'])
local etymology = getEtymology( current_lexeme, frame )
if etymology ~= '' and etymology then
table.insert(sections, heading_level(i18n['heading_etymology'], 4))
table.insert(sections, tostring(etymology))
end
add_any_notes(sections, args, i18n['manual_etymology'])
local pronunciation = getPronunciation( frame, current_lexeme, lang_name, lex_cat )
if pronunciation ~= '' then
table.insert(sections, heading_level(i18n['heading_pronunciation'], 4))
table.insert(sections, tostring(pronunciation))
end
add_any_notes(sections, args, i18n['manual_pronunciation'])
local alternative_spellings = getAlternativeSpellings( current_lexeme )
if alternative_spellings ~= '' then
table.insert(sections, heading_level(i18n['heading_alternative_spellings'], 4))
table.insert(sections, alternative_spellings)
end
if lang_code and lang_code ~= 'ksy' and lang_code ~= 'rkt' then -- Skip for Kharia Thar, Rangpuri
if lex_cat == 'Q34698' then -- বিশেষণ
if lang_code == 'en' or lang_code == 'bn' then
if #forms <= 1 then
lex_cat_template = frame:expandTemplate{title= lang_code .. '-বিশেষণ'}
end
else
lex_cat_template = safeExpand(frame, lang_code .. '-adj')
if not lex_cat_template then
lex_cat_template = safeExpand(frame, lang_code .. '-বিশেষণ')
end
end
elseif lex_cat == 'Q1084' then -- @TODO: Also check for plural forms
local gender
local stmts = current_lexeme:getAllStatements('P5185') -- ব্যাকরণগত লিঙ্গ
if #stmts ~= 0 then
if #stmts == 1 then
local gender_qid = stmts[1].mainsnak.datavalue.value.id
if gender_qid == 'Q499327' then
gender = 'm'
elseif gender_qid == 'Q1775415' then
gender = 'f'
elseif gender_qid == 'Q1775461' then
gender = 'n'
elseif gender_qid == 'Q1305037' then
gender = 'c'
end
end
else
for i, stmt in pairs(stmts) do
local qid = stmts[i].mainsnak.datavalue.value.id
if qid == 'Q499327' then
gender = gender .. 'm'
elseif qid == 'Q1775415' then
gender = gender .. 'f'
end
end
end
-- The following checks are ordered based on which one is expected to be true in a higher number of cases.
if current_language == 'Q13955' then
if matched_lemma then
lex_cat_template = safeExpand(frame, {title='ar-noun', args={matched_lemma,gender}})
else
lex_cat_template = frame:expandTemplate{title='ar-noun', args={nil,gender}}
end
elseif current_language == 'Q29919' then
lex_cat_template = frame:expandTemplate{title='arz-noun', args={g=gender}}
elseif current_language == 'Q397' then
if matched_lemma then
lex_cat_template = frame:expandTemplate{title='la-noun', args={matched_lemma,g=gender}}
end
elseif current_language == 'Q11059' then
lex_cat_template = frame:expandTemplate{title='sa-noun', args={g=gender}}
elseif current_language ~= 'Q1860' then -- These templates require the gender to be passed as the 1st argument.
lex_cat_template = safeExpand(frame, lang_code .. i18n['noun_template_suffix'], {gender})
if not lex_cat_template then
lex_cat_template = safeExpand(frame, lang_code .. i18n['noun_template_suffix_fallback'], {gender})
end
end
end
end
-- elseif lex_cat == 'Q147276' then
-- lex_cat_template = safeExpand(frame, lang_code .. '-proper noun', {gender})
-- if not lex_cat_template then
-- lex_cat_template = safeExpand(frame, lang_code .. '-নামবাচক বিশেষ্য', {gender})
-- end
end
if lex_cat_template then
table.insert(sections, lex_cat_template)
else
if matched_lemma then
table.insert(sections, heading_level(matched_lemma, 4))
else
table.insert(sections, i18n.tocatlink(i18n['category_no_matching_lemma']))
end
end
local meanings, references_seen, sense_extlinks = unpack(getMeanings( frame, args, current_lexeme, senses, references_seen, lang_name))
table.insert(sections, tostring(meanings))
add_any_notes(sections, args, i18n['manual_meaning'])
local instance_of = current_lexeme:getAllStatements('P31') -- সত্ত্বার ধরন
if #instance_of ~= 0 then
local instance_of_entity = instance_of[1].mainsnak.datavalue.value.id
if instance_of_entity == 'Q40437546' or instance_of_entity == 'Q120831827' or instance_of_entity == 'Q120717979' or instance_of_entity == 'Q124476844' then -- @TODO: generalise this so all types of roots are shown
table.insert(sections, i18n['text_instance_of'] .. ' ' .. getLabel(instance_of_entity))
elseif instance_of_entity == 'Q376431' then -- বর্ণের নাম
table.insert(sections, i18n.tocatlink(lang_code .. ':রং'))
end
end
local translations = getTranslations(frame, senses)
if translations then
table.insert(sections, translations)
end
-- (!) বিশেষ ভাষার বিভক্তির সারণি যদি থাকে সেগুলো এখানে নতুন if বিবৃতি যোগ করা যায়।
if next(forms) then
if current_language == 'Q9610' then -- বাংলা
if lex_cat == 'Q24905' then
local conjTable = require('মডিউল:আভিধানিক উপাত্ত/Q9610').getConjTable(frame, forms)
table.insert(sections, conjTable)
elseif lex_cat == 'Q1084' then
table.insert(sections, callWikifunctionsFunction('Z33243|' .. lexeme_id .. '|', frame))
elseif lex_cat == 'Q34698' then
if #forms > 1 then
table.insert(sections, buildLanguageAgnosticInflectionTable())
end
end
--elseif current_language == 'Q13955' then -- আরবি
-- if lex_cat == 'Q1084' then
-- table.insert(sections, frame:expandTemplate{title='ar-decl-noun', args={lemma}})
-- end
elseif current_language == 'Q188' then -- জার্মান
if lex_cat == 'Q1084' then
table.insert(sections, callWikifunctionsFunction('Z28602|' .. lexeme_id .. '|', frame))
end
else
if #forms > 1 then
table.insert(sections, buildLanguageAgnosticInflectionTable())
end
end
end
if lex_cat == 'Q134830' then
table.insert(sections, frame:expandTemplate{title='prefixsee', args={lang_code}})
elseif lex_cat == 'Q102047' then
table.insert(sections, frame:expandTemplate{title='suffixsee', args={lang_code}})
-- elseif lex_cat == 'Q111029' then
-- table.insert(sections, frame:expandTemplate{title='rootsee', args={'+', lang_code, matched_lemma}})
end
local reference_notes = get_any_notes(sections, args, i18n['manual_reference'])
if #references_seen > 0 or #reference_notes > 0 then
table.insert(sections, heading_level(i18n['heading_references'], 4))
table.insert(sections, frame:extensionTag('references'))
add_specific_notes(sections, reference_notes)
end
local external_link_table = getExternalLinks ( current_lexeme )
if #external_link_table > 0 then
local external_links = '* ' .. table.concat(external_link_table, '\n* ')
table.insert(sections, heading_level(i18n['heading_external_links'], 4))
table.insert(sections, external_links)
end
add_any_notes(sections, args, i18n['manual_external_link'])
if #references_seen == 0 and #reference_notes == 0 and sense_extlinks and #sense_extlinks == 0 and #external_link_table == 0 and #get_any_notes(sections, args, i18n['manual_external_link']) == 0 then
table.insert(sections, i18n.rfref_category(lang_name))
end
return table.concat(sections,"\n\n")
end
return p
7kr505izzzew4zh1n0rlsslpow9drjn
509644
509642
2026-06-03T15:29:26Z
Redmin
6857
509644
Scribunto
text/plain
local p = {}
local i18n = require('মডিউল:আভিধানিক উপাত্ত/i18n')
local references = require('মডিউল:উইকিউপাত্ত তথ্যসূত্র বিন্যাসকরণ').format
local getArgs = require('Module:Arguments').getArgs
local wb = mw.wikibase
local ustring = mw.ustring
local html = mw.html
local mw_lang = mw.language
local entity_cache = {}
local reference_cache = {}
local forms
local lang_code
local lex_cat
local matched_lemma
local function wrapStringInWikilinks(str)
local exceptions = i18n.nolinks
local result = str:gsub('(%S+)', function(token)
local word, trailing_char = token:match('^(.-)([;,]*)$')
local wrapped = word:gsub('[^(-/]+', function(part)
if exceptions[part] then
return part
else
return '[[' .. part .. '#' .. i18n['content_lang_name'] .. '|' .. part .. ']]'
end
end)
return wrapped .. trailing_char
end)
return result
end
local function serializeTable(val, name, skipnewlines, depth) -- https://stackoverflow.com/a/6081639
skipnewlines = skipnewlines or false
depth = depth or 0
local tmp = string.rep(" ", depth)
if name then tmp = tmp .. name .. " = " end
if type(val) == "table" then
tmp = tmp .. "{" .. (not skipnewlines and "\n" or "")
for k, v in pairs(val) do
tmp = tmp .. serializeTable(v, k, skipnewlines, depth + 1) .. "," .. (not skipnewlines and "\n" or "")
end
tmp = tmp .. string.rep(" ", depth) .. "}"
elseif type(val) == "number" then
tmp = tmp .. tostring(val)
elseif type(val) == "string" then
tmp = tmp .. string.format("%q", val)
elseif type(val) == "boolean" then
tmp = tmp .. (val and "true" or "false")
else
tmp = tmp .. "\"[inserializeable datatype:" .. type(val) .. "]\""
end
return tmp
end
-- Use this to safely expand templates when you are not sure that they exist.
local function safeExpand(frame, title, args)
local _, result = pcall(function()
return frame:expandTemplate{ title = title, args = args }
end)
if result:find('does not exist') then -- expandTemplate{} doesn't seem to throw any error that can be handled with pcall() so string search is the only viable option.
return nil
end
return result
end
-- Use this function to get the label of an item even if that item does not have any label in the wiki's content language or English.
local function getLabel(id)
if id == 'Q11051hi' then
return 'হিন্দি'
elseif id == 'Q11051ur' then
return 'উর্দু'
elseif id == 'Q56356571fa' then
return 'নয়া ফার্সি'
elseif id == 'Q56356571tg' then
return 'তাজিক'
elseif id == 'Q58635pa' or id == 'Q58635pnb' then
return 'পাঞ্জাবি'
end
local label = wb.getLabel(id)
if label then
return label
end
local labels = wb.getEntity(id).labels
if not labels then
return id
end
for _, v in pairs(labels) do
if v and v.value then
return v.value
end
end
end
local function getReference( id, reference )
local out_id = nil
local url_value
if reference_cache[id] == nil then
local ref_text = references(reference, wb, mw_lang, i18n['content_lang_code'], i18n['wikipedia'])
if reference.snaks then
if reference.snaks['P248'] then
for _, snak in pairs(reference.snaks['P248']) do
if snak.datavalue and snak.datavalue.value.id == 'Q428' then -- কুরআন
ref_text = ustring.gsub(ref_text, 'নামক অনুচ্ছেদ', 'নং আয়াত')
break
end
end
end
if reference.snaks['P854'] then
local snak = reference.snaks['P854'][1]
if snak.datavalue then
url_value = snak.datavalue.value
end
end
end
if url_value then
ref_text = ref_text .. ', [' .. url_value .. ' সংযোগ]'
end
reference_cache[id] = ref_text
else
out_id = id
end
return {out_id, reference_cache[id]}
end
local function getEntity( id )
if entity_cache[id] == nil then
entity_cache[id] = wb.getEntity(id)
end
return entity_cache[id] ~= false and entity_cache[id] or nil
end
local function getLexemeLanguageCode(current_lexeme)
local lang_item_id = current_lexeme:getLanguage()
if lang_item_id == nil then
return nil
end
local lang_entity = getEntity(lang_item_id)
if lang_entity == nil then
return nil
end
for i, statement_property in ipairs({'P305','P424', 'P220'}) do -- আইইটিএফ ভাষা ট্যাগ, উইকিমিডিয়া ভাষা কোড, আইএসও ৬৩৯-৩
local statements = lang_entity:getBestStatements(statement_property)
if statements[1] then
return statements[1].mainsnak.datavalue.value
end
end
return nil
end
-- Return the first form of the lexeme which has exactly the given grammatical feature.
local function formWithSingleGrammaticalFeature( item_id )
for i = 1, #forms do
local grammaticalFeatures = forms[i]:getGrammaticalFeatures()
if #grammaticalFeatures == 1 and grammaticalFeatures[1] == item_id then
return forms[i]
end
end
return nil
end
local function getArticleLinkTemplate(frame, stmt_value)
local template = ''
local sitelink = getEntity(stmt_value):getSitelink(i18n['wikipedia'])
if sitelink then
template = frame:expandTemplate{
title=i18n['template_wikipedia'],
args={sitelink}
}
end
return template
end
local function getArticleLinks (frame, sense )
local article_links = ''
for i, stmt in pairs(sense:getAllStatements('P5137')) do -- এই অর্থের জন্য আইটেম
article_links = article_links .. getArticleLinkTemplate(frame, stmt.mainsnak.datavalue.value.id)
end
for i, stmt in pairs(sense:getAllStatements('P9970')) do -- এই অর্থের জন্য বিধেয়
article_links = article_links .. getArticleLinkTemplate(frame, stmt.mainsnak.datavalue.value.id)
end
return article_links
end
-- @TODO: Generalise
local function expandTemplateForProperty(frame, object, property, template)
local lemmas = {}
local n = 0
for _, stmt in pairs(object:getAllStatements(property)) do
local lex = wb.lexeme.splitLexemeId(stmt.mainsnak.datavalue.value.id)
if lex then
lex = getEntity(lex)
n = n + 1
lemmas[n] = lex:getLemma(lang_code)
end
end
if not lang_code or n == 0 then
return ''
end
-- Build args: first lang_code, then lemmas
local args = {lang_code}
for i = 1, n do
args[#args + 1] = lemmas[i]
end
return frame:expandTemplate{
title = template,
args = args
}
end
local function getExternalLinks( entity ) -- T418639
local external_links = {}
if entity.claims == nil then return external_links end
local formatter_urls = require('মডিউল:আভিধানিক উপাত্ত/urls').formatter_urls
for property_id, statements in pairs(entity.claims) do
local formatter_url = formatter_urls[property_id]
if formatter_url then
local property_source = wb.getBestStatements(property_id, 'P9073')
local source_name
if next(property_source) then
source_name = getLabel(property_source[1].mainsnak.datavalue.value.id)
or property_source[1].mainsnak.datavalue.value.id
else
source_name = getLabel(property_id) or property_id
end
for i = 1, #statements do
local stmt = statements[i]
if stmt.mainsnak.datavalue then
local formatted_link = ustring.gsub(
ustring.gsub(formatter_url, '$1', stmt.mainsnak.datavalue.value),
' ', '+'
)
table.insert(external_links,
'[' .. formatted_link .. ' ' .. source_name .. ']')
end
end
end
end
return external_links
end
p.getExternalLinks = getExternalLinks -- রেখে দিন যাতে ডিবাগিং সম্ভব হয়
local function termSpan( term )
local text = term[1]
local lang = term[2]
local dir = mw_lang.new( lang ):getDir()
local span = html.create( 'span' )
span:attr( 'lang', lang )
:attr( 'dir', dir )
:wikitext( text )
return tostring( span )
end
local function termLink( term, lang_qid )
local text = term[1]
local lang = term[2]
local dir = mw_lang.new( lang ):getDir()
local span = html.create( 'span' )
span:attr( 'lang', lang )
:attr( 'dir', dir )
:wikitext( '[[' .. text .. '#' .. getLabel(lang_qid) .. '|' .. text .. ']]' )
return tostring( span )
end
local function getLemmata( current_lexeme )
local lemma_string = ''
for i, rep in pairs(current_lexeme:getLemmas()) do
if lemma_string == '' then
lemma_string = termSpan(rep)
else
lemma_string = lemma_string .. '/' .. termSpan(rep)
end
end
return lemma_string
end
local function getLinkedLemmata( current_lexeme )
local lemma_string = ''
for i, rep in pairs(current_lexeme:getLemmas()) do
if lemma_string == '' then
lemma_string = termLink(rep, current_lexeme:getLanguage())
else
lemma_string = lemma_string .. '/' .. termLink(rep, current_lexeme:getLanguage())
end
end
return lemma_string
end
local function getExamples( current_lexeme, sense_id, references_seen )
local examples = html.create('dl')
local example_text, example_lang, example_form, example_str
for i, stmt in pairs(current_lexeme:getAllStatements('P5831')) do -- ব্যবহারের উদাহরণ
if stmt.qualifiers and stmt.qualifiers['P6072'] and stmt.qualifiers['P6072'][1].datavalue.value.id == sense_id then -- বিষয়ে লেক্সিমের অর্থ
example_text = ustring.gsub(stmt.mainsnak.datavalue.value.text, ' / ','<br/>')
example_lang = stmt.mainsnak.datavalue.value.language
local example_form_strs = {}
if stmt.qualifiers['P1810'] then
table.insert(example_form_strs, stmt.qualifiers['P1810'][1].datavalue.value)
elseif stmt.qualifiers['P5830'] then
example_form = getEntity(stmt.qualifiers['P5830'][1].datavalue.value.id) -- বিষয়ে লেক্সিমের রূপ
for _, rep in pairs(example_form:getRepresentations()) do
table.insert(example_form_strs, rep[1])
end
end
for i, example_form_str in pairs(example_form_strs) do
new_example_text = ustring.gsub(example_text, example_form_str, "'''" .. example_form_str .. "'''")
if new_example_text ~= example_text then
example_str = termSpan({new_example_text, example_lang})
break
end
new_example_text = example_text
end
if example_str == nil then
example_str = termSpan({example_text, example_lang})
end
local reference_text = ''
if stmt.references then
for j, reference in pairs(stmt.references) do
table.insert(references_seen, reference.hash)
local got_reference = getReference(reference.hash, reference)
reference_text = reference_text .. '\n\n' .. got_reference[2]
end
end
if example_str then
examples:tag('dd'):wikitext("''" .. example_str .. "''")
if reference_text ~= '' then
examples:done():tag('dd'):css('text-indent', '2em'):wikitext(reference_text)
end
end
end
end
for i, stmt in pairs(wb.getAllStatements(sense_id, 'P5831')) do -- ব্যবহারের উদাহরণ
example_text = ustring.gsub(stmt.mainsnak.datavalue.value.text, ' / ','<br/>')
example_lang = stmt.mainsnak.datavalue.value.language
local example_form_str = nil
if stmt.qualifers then
example_form = getEntity(stmt.qualifiers['P5830'][1].datavalue.value.id) -- বিষয়ে লেক্সিমের রূপ
if stmt.qualifiers['P1810'] then
example_form_str = stmt.qualifiers['P1810'][1].datavalue.value
end
end
if example_form and example_form_str == nil then
example_form_str = example_form:getRepresentation(i18n['content_lang_code'])
end
if example_form and example_form_str == nil then
example_form_str = example_form:getRepresentations()[1][1]
end
if example_form_str then
example_text = ustring.gsub(example_text, example_form_str, "'''" .. example_form_str .. "'''")
end
example_str = termSpan({example_text, example_lang})
local reference_text = ''
if stmt.references then
for j, reference in pairs(stmt.references) do
table.insert(references_seen, reference.hash)
local got_reference = getReference(reference.hash, reference)
reference_text = reference_text .. '\n\n' .. got_reference[2]
end
end
if example_str then
examples:tag('dd'):wikitext("''" .. example_str .. "''")
if reference_text ~= '' then
examples:done():tag('dd'):css('text-indent', '2em'):wikitext(reference_text)
end
end
end
return { tostring(examples) , references_seen }
end
-- This calls frame:preprocess() instead of :callParserFunction() because the latter does not work for Wikifunctions function calls yet (see https://www.wikifunctions.org/wiki/Wikifunctions:Embedded_function_calls).
local function callWikifunctionsFunction(args, frame)
return frame:preprocess('{{#function:' .. args .. '}}')
end
local function checkTitleCodePointInRange(title, start_point, end_point)
return ustring.find( title, '[' ..ustring.char(start_point) .. '-' .. ustring.char(end_point) .. ']' )
end
local function getLanguageForCategories( lang_id, current_page_title )
-- বিশেষ ভাষার জন্য
if lang_id == 'Q11051' then -- হিন্দি/উর্দু
if checkTitleCodePointInRange(current_page_title, 0x0600, 0x06ff) then -- উর্দু
lang_id = 'Q11051ur'
elseif checkTitleCodePointInRange(current_page_title, 0x0900, 0x097f) then -- হিন্দি
lang_id = 'Q11051hi'
end
elseif lang_id == 'Q58635' then -- পাঞ্জাবি
if checkTitleCodePointInRange(current_page_title, 0x0600, 0x06ff) then -- শাহমুখী
lang_id = 'Q58635pnb'
elseif checkTitleCodePointInRange(current_page_title, 0x0a00, 0x0a7f) then -- গুরুমুখী
lang_id = 'Q58635pa'
end
elseif lang_id == 'Q56356571' then -- নয়া ফার্সি ভাষা
if checkTitleCodePointInRange(current_page_title, 0x0600, 0x06ff) then -- ফার্সি (ইরান/আফগানিস্তান)
lang_id = 'Q56356571fa'
elseif checkTitleCodePointInRange(current_page_title, 0x0400, 0x04ff) then -- তাজিক
lang_id = 'Q56356571tg'
end
end
return lang_id
end
local function getOneStringForProperty(object, property)
local val
local stmts = object:getAllStatements(property)
if #stmts ~= 0 then
val = stmts[1].mainsnak.datavalue.value
end
return val
end
local function getTranslations(frame, senses) -- TODO: woefully incomplete until T185313 and T199887 are resolved
if #senses == 0 then
return nil
end
local all_translations = {}
for _, sense in pairs(senses) do
local translation_set = {}
local gloss = sense:getGloss('bn')
for _, stmt in pairs(sense:getAllStatements('P5972')) do
local translation = stmt.mainsnak.datavalue.value.id
local lexeme_id = wb.lexeme.splitLexemeId(translation)
local language = getLabel(getEntity(lexeme_id):getLanguage())
table.insert(translation_set, language .. ': ' .. getLinkedLemmata(getEntity(lexeme_id)) .. '<br/>')
end
if #translation_set > 0 then
local block = frame:expandTemplate{ title = i18n['template_trans-top'], args = { gloss } }
block = block .. table.concat(translation_set, '\n') .. frame:expandTemplate{ title = i18n['template_trans-bottom'] }
table.insert(all_translations, block)
end
end
if #all_translations == 0 then
return nil
end
return '====' .. i18n['heading_translation'] .. '==== \n' .. table.concat(all_translations, '\n')
end
local createicon = function(langcode, entityID, propertyID)
langcode = langcode or ""
propertyID = propertyID or ""
local icon = " <span class='penicon autoconfirmed-show'>[["
-- " <span data-bridge-edit-flow='overwrite' class='penicon'>[[" -> enable Wikidata Bridge
.. "File:OOjs UI icon edit-ltr-progressive.svg |frameless |text-top |10px |alt="
.. i18n['edit_wikidata']
.. "|link=https://www.wikidata.org/entity/" .. entityID
if langcode ~= "" then icon = icon .. "?uselang=" .. langcode end
if propertyID ~= "" then icon = icon .. "#" .. propertyID end
icon = icon .. "|" .. i18n['edit_wikidata'] .. "]]</span>"
return icon
end
local function getMeanings( frame, args, current_lexeme, senses, references_seen, language_name)
if #senses == 0 then
return {createicon(i18n['content_lang_code'], current_lexeme:getId()) .. "''" .. i18n['text_category_rfdef'] .. "''" .. i18n.tocatlink(i18n['category_rfdef']), references_seen}
end
local meanings = html.create( 'ol' )
local item_label_gloss_parts = {}
local idlinkset = {}
for i, sense in pairs(senses) do
local gloss_text_parts = {}
local main_gloss_text = frame:expandTemplate{
title=i18n['template_anchor'],
args={sense:getId()}
}
local specifiers = {}
for k, property_id in ipairs({'P6084', 'P6191', 'P9488'}) do -- অবস্থান যেখানে শব্দার্থ ব্যবহৃত, যে রীতিতে শব্দার্থ ব্যবহৃত হয়, যে ক্ষেত্রে ব্যবহৃত
for _, stmt in pairs(sense:getAllStatements(property_id)) do
local stmt_value = stmt.mainsnak.datavalue.value.id
local reference_text = ''
local refs = stmt.references
if refs then
for j, reference in pairs(refs) do
table.insert(references_seen, reference.hash)
local got_reference = getReference(reference.hash, reference)
reference_text = reference_text .. '\n\n' .. frame:extensionTag('ref', got_reference[2])
end
end
local val = getLabel(stmt_value)
table.insert(specifiers, val .. reference_text)
if property_id == 'P9488' then
table.insert(item_label_gloss_parts, i18n.tocatlink(lang_code .. ':' .. val))
end
end
if #specifiers > 0 then
main_gloss_text = main_gloss_text .. "(''" .. table.concat(specifiers, "'', ''") .. "'') "
end
end
local gloss = sense:getGloss( i18n['content_lang_code'] )
if gloss then
main_gloss_text = main_gloss_text .. wrapStringInWikilinks(gloss)
if gloss:match('^প্রদত্ত%s*(%S-)%s*নাম$') then -- given names
main_gloss_text = main_gloss_text .. i18n.tocatlink(language_name .. ' ' .. i18n['category_given_names'])
end
else
local other_gloss_text = nil
local other_gloss_lang = nil
for k, stmt in pairs(sense:getAllStatements('P5137')) do -- যদি 'এই অর্থের জন্য আইটেম' মানের বাংলা লেবেল থাকে
local stmt_value = stmt.mainsnak.datavalue.value.id
local stmt_label = getLabel(stmt_value)
if stmt_label then
table.insert(item_label_gloss_parts, '[[:d:' .. stmt_value .. '|' .. stmt_label .. ']]')
end
end
if #item_label_gloss_parts > 0 then
other_gloss_text = table.concat(item_label_gloss_parts, '; ')
end
if other_gloss_text == nil then
for _, fallback_lang in ipairs(mw_lang.getFallbacksFor( i18n['content_lang_code'] )) do
if sense:getGloss( fallback_lang ) then
other_gloss_text, other_gloss_lang = sense:getGloss( fallback_lang )
end
end
if other_gloss_lang == nil then
local glosses = sense:getGlosses()
for _, gloss in pairs(glosses) do
other_gloss_text = gloss[1]
other_gloss_lang = gloss[2]
break
end
end
main_gloss_text = main_gloss_text .. other_gloss_text .. "<sup><em>" .. mw_lang.fetchLanguageName(other_gloss_lang, i18n['content_lang_code']) .. "</em></sup>"
else
main_gloss_text = main_gloss_text .. "''" .. other_gloss_text .. "''"
end
main_gloss_text = main_gloss_text .. i18n.tocatlink(i18n['category_rfdef_equivalent'])
end
local synonym = expandTemplateForProperty(frame, sense, 'P5973', i18n['template_synonym'])
if synonym ~= '' then
main_gloss_text = main_gloss_text .. ' <br/> ' .. synonym
end
local antonym = expandTemplateForProperty(frame, sense, 'P5974', i18n['template_antonym'])
if antonym ~= '' then
main_gloss_text = main_gloss_text .. ' <br/> ' .. antonym
end
local hypernym = expandTemplateForProperty(frame, sense, 'P6593', i18n['template_hypernym'])
if hypernym ~= '' then
main_gloss_text = main_gloss_text .. ' <br/> ' .. hypernym
end
if lex_cat == 'Q1084' or lex_cat == 'Q147276' then -- noun or proper noun
local demonym = expandTemplateForProperty(frame, sense, 'P6271', i18n['template_demonym-noun'])
main_gloss_text = main_gloss_text .. ' <br/> ' .. demonym
elseif lex_cat == 'Q34698' then
local demonym = expandTemplateForProperty(frame, sense, 'P6271', i18n['template_demonym-adj'])
main_gloss_text = main_gloss_text .. ' <br/> ' .. demonym
end
table.insert(gloss_text_parts, main_gloss_text .. createicon(i18n['content_lang_code'], sense:getId()))
for i, stmt in pairs(sense:getAllStatements('P8394')) do -- টিপ্পনীর উদ্ধৃতি
local gloss_quote = termSpan({stmt.mainsnak.datavalue.value.text, stmt.mainsnak.datavalue.value.language})
if stmt.references[1] then
local got_reference = getReference ( stmt.references[1].hash, stmt.references[1] )
gloss_quote = '"' .. gloss_quote .. '" ' .. got_reference[2]
end
table.insert(references_seen, stmt.references[1].hash)
table.insert(gloss_text_parts, frame:extensionTag('ref', gloss_quote))
end
for i, stmt in pairs(sense:getAllStatements('P1343')) do -- বর্ণিত উৎস
-- TODO: do away with making fake reference objects
local fake_reference = { ['snaks'] = {} }
fake_reference.snaks['P248'] = { [1] = stmt.mainsnak }
qualifiers_order = stmt['qualifiers-order']
if qualifiers_order then
for i, k in ipairs(qualifiers_order) do fake_reference.snaks[k] = stmt.qualifiers[k] end
end
fake_reference.hash = mw.hash.hashValue('sha3-512', serializeTable(fake_reference))
table.insert(references_seen, fake_reference.hash)
local got_reference = getReference(fake_reference.hash, fake_reference)
if got_reference[1] == nil then
table.insert(gloss_text_parts, frame:extensionTag('ref', got_reference[2], {name = fake_reference.hash}))
else
table.insert(gloss_text_parts, frame:extensionTag{name = 'ref', content='', args = {name = got_reference[1]}})
end
end
local first_sense_image = ''
local sense_images = sense:getAllStatements('P18')
if next(sense_images) then
first_sense_image = sense_images[1].mainsnak.datavalue.value
end
if first_sense_image ~= '' then
table.insert(gloss_text_parts, '[[চিত্র:' .. first_sense_image .. "|thumb|'''" .. getLemmata(current_lexeme) .. "'''—" .. main_gloss_text .. ']]')
end
local idlinks = getExternalLinks(sense)
if #idlinks > 0 then
local idlinktext = '<small>('
for _, idlink in pairs(idlinks) do
idlinktext = idlinktext .. idlink .. '\n'
end
idlinktext = idlinktext .. ')</small>'
table.insert(gloss_text_parts, idlinktext)
table.insert(idlinkset, idlinks)
end
local externallinks = getArticleLinks(frame, sense)
if externallinks ~= '' then
table.insert(gloss_text_parts, externallinks)
end
local new_notes = {}
local sense_keys = { sense:getId(), string.sub(sense:getId(), string.find(sense:getId(), '-')+1) }
for _, v in ipairs(sense_keys) do
if args[v] then
table.insert(new_notes, args[v])
end
end
if #new_notes > 0 then
for _, v in ipairs(new_notes) do
if i == 1 then
table.insert(gloss_text_parts, '<br/>' .. v)
else
table.insert(gloss_text_parts, v)
end
end
end
local examples, references_seen = unpack(getExamples( current_lexeme, sense:getId(), references_seen ))
local gloss_text = table.concat(gloss_text_parts, '\n')
meanings:tag('li'):wikitext(gloss_text):wikitext(examples)
end
return {meanings, references_seen, idlinkset}
end
local function getPronunciationBaseForm( lang_name, lex_cat)
local base_form = nil
-- (!) অন্য ভাষার শব্দের যদি অন্য রকম মূল ফর্ম থাকে সেগুলো এখানে নতুন if বিবৃতি দিয়ে যোগ করা যায়।
if lang_name == 'বাংলা' then
if lex_cat == 'Q1084' then -- বিশেষ্য
base_form = formWithSingleGrammaticalFeature( 'Q131105' ) -- কর্তৃকারক
elseif lex_cat == 'Q24905' then -- ক্রিয়া
base_form = formWithSingleGrammaticalFeature( 'Q1350145' ) -- ক্রিয়া বিশেষ্য
end
end
if base_form == nil then
for i, form in pairs(forms) do
base_form = form
break
end
end
return base_form
end
local function getCombines( current_lexeme, frame )
local combines = ''
local index_mappings = {}
for i, stmt in pairs(current_lexeme:getAllStatements('P5238')) do
if stmt.qualifiers and stmt.qualifiers['P1545'] then -- ক্রম
local current_index = tonumber(stmt.qualifiers['P1545'][1].datavalue.value)
index_mappings[current_index] = stmt
end
end
if #index_mappings ~= 0 then
for i, stmt in ipairs(index_mappings) do
if stmt.mainsnak.datavalue then
local part_lexeme_id = stmt.mainsnak.datavalue.value.id
local part_lexeme = getEntity(part_lexeme_id)
local current_substring = getLinkedLemmata(part_lexeme)
local part_etymology = getEtymology(part_lexeme, frame, 'partial')
if part_etymology ~= '' and part_etymology then
current_substring = current_substring .. ' (← ' .. part_etymology .. ')'
end
if combines == '' then
combines = current_substring
else -- @TODO: This shoukd use the 'affix' and 'compound' templates instead.
combines = combines .. ' + ' .. current_substring
end
end
end
end
return combines
end
function getRoots( current_lexeme )
local stmts = current_lexeme:getAllStatements('P5920')
if #stmts == 0 then return '', '', '' end
local root_lexeme = getEntity(stmts[1].mainsnak.datavalue.value.id)
return getLexemeLanguageCode(root_lexeme), '√' .. getLinkedLemmata(root_lexeme), root_lexeme:getLemma('ar')
end
function getEtymology( current_lexeme, frame, mode )
-- @TODO: Fix the etymology chains that are not possible to render
local etymology = ''
local current_combines = getCombines(current_lexeme, frame)
local root_lang, current_roots, root_str = getRoots(current_lexeme)
if mode ~= 'partial' and root_str then
--frame:expandTemplate{title=i18n['template_root'], args={lang_code, root_lang, root_str}}
end
local stmts = current_lexeme:getAllStatements('P5191')
local new_etymology_string
if #stmts == 0 then
if current_roots ~= '' and current_combines ~= '' and current_roots then
return current_roots .. '<br/>(' .. current_combines .. ')'
elseif current_roots ~= '' then
if lang_code == 'ar' and mode ~= 'partial' and root_str ~= matched_lemma then
return frame:expandTemplate{title=i18n['template_ar-rootbox'], args={root_str}}
else
return current_roots
end
else
return current_combines
end
end
local origin_lexeme_string
for i, stmt in pairs(stmts) do
local origin_lexeme_dv = stmt.mainsnak.datavalue -- If this is nil, the origin lexeme is not known.
if origin_lexeme_dv then
local origin_lexeme = getEntity(origin_lexeme_dv.value.id)
local origin_lexeme_lang = getLabel(origin_lexeme:getLanguage())
local sitelink = i18n.wplink(origin_lexeme:getLanguage(), origin_lexeme_lang, wb)
if sitelink ~= '' then
origin_lexeme_string = getLinkedLemmata(origin_lexeme) .. ' (' .. sitelink .. ')'
else
origin_lexeme_string = getLinkedLemmata(origin_lexeme) .. ' (' .. origin_lexeme_lang .. ')'
end
if stmt.qualifiers and stmt.qualifiers['P5886'] then
local mode_of_derivation = stmt.qualifiers['P5886'][1].datavalue.value.id
if mode_of_derivation == 'Q1345001' then
-- @TODO: Add support for showing gender
origin_lexeme_string = frame:expandTemplate{title=i18n['template_borrowed'], args={lang_code, getLexemeLanguageCode(origin_lexeme), getLemmata(origin_lexeme), pos=getLabel(lex_cat)}} .. ' ' .. i18n['etymology_borrowing']
elseif mode_of_derivation == 'Q845079' then
origin_lexeme_string = ustring.gsub(i18n['etymology_learned_borrowing'], '$1', origin_lexeme_string)
elseif mode_of_derivation == 'Q56611986' then
origin_lexeme_string = frame:expandTemplate{title=i18n['template_inherited'], args={lang_code, getLexemeLanguageCode(origin_lexeme), getLemmata(origin_lexeme), pos=getLabel(lex_cat)}} .. ' ' .. i18n['etymology_inheritance']
elseif mode_of_derivation == 'Q189743' then
origin_lexeme_string = frame:expandTemplate{title=i18n['template_ellipsis'], args={lang_code, getLemmata(origin_lexeme)}} .. ' ' .. i18n['etymology_ellipsis']
end
end
local origin_origin = getEtymology(origin_lexeme, frame)
if origin_origin ~= '' and origin_origin then
new_etymology_string = origin_lexeme_string .. ' ← ' .. origin_origin
else
new_etymology_string = origin_lexeme_string
end
end
if etymology == '' then
etymology = new_etymology_string
elseif origin_lexeme_string and etymology then
etymology = etymology .. ' ' .. origin_lexeme_string
elseif origin_lexeme_string and etymology == nil then
etymology = origin_lexeme_string
end
end
if current_roots ~= '' and etymology and current_roots then
etymology = etymology .. ' ' .. current_roots
elseif current_roots ~= '' and etymology == nil then
etymology = current_roots
end
if current_combines ~= '' and etymology then
etymology = etymology .. '<br/>(' .. current_combines .. ')'
end
return etymology
end
local function pronunciationBlock(block, value)
return '* ' .. i18n['text_' .. block] .. ' ' .. value
end
local function getPronunciation( frame, current_lexeme, lang_name, lex_cat )
local pronunciations = {}
local base_form = getPronunciationBaseForm(lang_name, lex_cat )
if base_form then
for i, stmt in pairs(base_form:getAllStatements('P443')) do -- উচ্চারণের অডিও
local pronunciation_file = stmt.mainsnak.datavalue.value
local specifier_text = ''
local specifiers = {}
if stmt.qualifiers then
for k, property_id in ipairs({'P5237'}) do -- উচ্চারণের ধরন
local qual = stmt.qualifiers[property_id]
if qual then
for _, qual in pairs(qual) do
local stmt_value = qual.datavalue.value.id
table.insert(specifiers, getLabel(stmt_value))
end
end
end
end
if #specifiers > 0 then
specifier_text = table.concat(specifiers, "'', ''")
end
local audio_text
if specifier_text ~= '' then
audio_text = i18n['text_audio'] .. ' (' .. specifier_text .. ')'
else
audio_text = i18n['text_audio']
end
table.insert(pronunciations, '* ' .. frame:expandTemplate{
title= i18n['template_audio'],
args = {lang_name, pronunciation_file, audio_text}
})
end
local ipa_transcription = base_form:getAllStatements('P898') -- - আধ্বব প্রতিলিপিকরণ
local iso15919_transcription = getOneStringForProperty(base_form, 'P5825') -- আইএসও ১৫৯১৯ প্রতিলিপিকরণ
local itrans = getOneStringForProperty(base_form, 'P8881') -- ITRANS
local iast = getOneStringForProperty(base_form, 'P7581') -- আইএএসটি প্রতিলিপিকরণ
local xsampa = getOneStringForProperty(base_form, 'P2859')
-- @TODO: অডিও ও প্রতিলিপিকরণ দুটোই থাকলে সেই ক্ষেত্রে একটার ঠিক পরেই আরেকটা দেখানো উচিত
if #ipa_transcription ~= 0 then
for i, stmt in pairs(ipa_transcription) do
local ipa_text = stmt.mainsnak.datavalue.value
local specifier_text = ''
local specifiers = {}
if stmt.qualifiers then
for k, property_id in ipairs({'P5237'}) do -- উচ্চারণের ধরন
for l, qual in ipairs(stmt.qualifiers[property_id]) do
table.insert(specifiers, getLabel( qual.datavalue.value.id ))
end
end
end
if #specifiers > 0 then
specifier_text = "(''" .. table.concat(specifiers, "'', ''") .. "'') "
end
local syllable_count
if lang_code == 'tr' then
syllable_count = i18n['text_syllable_count'] .. ' ' .. callWikifunctionsFunction('Z10029|' .. ipa_text, frame)
else
syllable_count = i18n['text_syllable_count'] .. ' ' .. callWikifunctionsFunction('Z30837|' .. ipa_text, frame)
end
table.insert(pronunciations, '* ' .. specifier_text .. frame:expandTemplate{
title= i18n['template_ipa'],
args = {lang_name, ipa_text}
} .. '\n* ' .. syllable_count)
end
-- The following checks are ordered based on which one is expected to be true in a higher number of cases.
elseif lang_name == 'বাংলা' then
table.insert(pronunciations, '* ' .. frame:expandTemplate{
title='bn-IPA',
})
elseif lang_name == 'আরবি' then
local lemma = current_lexeme:getLemma('ar')
table.insert(pronunciations, '* ' .. frame:expandTemplate{
title='ar-IPA',
args={lemma}
})
elseif lang_name == 'ফালা' then
table.insert(pronunciations, '* ' .. frame:expandTemplate{
title='fax-pron',
})
elseif lang_code == 'fi' then
table.insert(pronunciations, '* ' .. frame:expandTemplate{
title='fi-IPA',
})
elseif lang_code == 'ko' then
table.insert(pronunciations, '* ' .. frame:expandTemplate{
title='ko-IPA',
})
end
if iso15919_transcription then
table.insert(pronunciations, pronunciationBlock('iso15919', iso15919_transcription))
end
if itrans then
table.insert(pronunciations, pronunciationBlock('itrans', itrans))
end
if iast then
table.insert(pronunciations, pronunciationBlock('iast', iast))
end
if xsampa then
table.insert(pronunciations, pronunciationBlock('xsampa', xsampa))
end
end -- {{আধ্বব|en|/ˈɪntəvjuː/}}
return table.concat(pronunciations, '\n')
end
local function getAlternativeSpellings( current_lexeme )
local alt_spellings = {}
for i, stmt in pairs(current_lexeme:getAllStatements('P11577')) do -- বিকল্প বানান
if stmt.mainsnak.datavalue then
table.insert(alt_spellings, '* ' .. getLinkedLemmata(getEntity(stmt.mainsnak.datavalue.value.id)))
end
end
return table.concat(alt_spellings, '\n')
end
local function heading_level(text, level)
local heading_delimiter = string.rep('=', level)
return heading_delimiter .. ' ' .. text .. ' ' .. heading_delimiter
end
function get_any_notes(sections, args, keys)
local notes = {}
for i, v in ipairs(keys) do
if args[v] then
table.insert(notes, args[v])
end
end
return notes
end
function add_specific_notes(sections, notes)
for i, v in ipairs(notes) do
table.insert(sections, v)
end
end
local function add_any_notes(sections, args, keys)
for i, v in ipairs(keys) do
if args[v] then
table.insert(sections, args[v])
end
end
end
local function getMatchingLemmaForPageTitle(lexeme, title)
local lemmas = lexeme:getLemmas()
local matched_lemma
for _, lemma_entry in ipairs(lemmas) do
local lemma = lemma_entry[1]
if lemma == title then
matched_lemma = lemma
break
end
end
if matched_lemma == nil and lang_code == 'ar' then -- Arabic lemmas do not match the title of the entry because those are written with different characters stripped out on Wiktionary. This could be changed to call stripDiactritics() from [[Module:languages]] in the future if desired.
matched_lemma = lexeme:getLemma('ar')
end
return matched_lemma
end
local function buildLanguageAgnosticInflectionTable()
local has_image = false
local form_images = {}
for i, form in ipairs(forms) do
local form_image = form:getAllStatements('P7407')
if next(form_image) then
form_images[i] = form_image[1].mainsnak.datavalue.value
has_image = true
end
end
local table_class = "wikitable mw-collapsible sortable"
if not has_image then
table_class = table_class .. " mw-collapsed"
end
local text = "{| class='" .. table_class .. "' style='border:solid 1px rgb(80%,80%,100%); text-align:center;'\n"
text = text .. "|+ " .. i18n['heading_inflection_table'] .. "\n"
text = text .. "|- \n"
text = text .. "! " .. i18n['heading_form'] .. " !! " .. i18n['heading_grammatical_features']
if has_image then
text = text .. " !! " .. (i18n['heading_image'])
end
text = text .. " \n"
for i, form in ipairs(forms) do
local rep = form:getRepresentations()
local feat = form:getGrammaticalFeatures()
local rep_text = ""
for j, r in pairs(rep) do
if rep_text == "" then
rep_text = r[1]
else
rep_text = rep_text .. " / " .. r[1]
end
end
local feat_text = ""
if feat then
for j, f in ipairs(feat) do
local label = getLabel(f) or f
if feat_text == "" then
feat_text = label
else
feat_text = feat_text .. ", " .. label
end
end
end
text = text .. "|-\n"
text = text .. "| " .. (rep_text ~= "" and rep_text or "—")
text = text .. " || " .. (feat_text ~= "" and feat_text or "—")
if has_image then
local image_cell = "—"
if form_images[i] then
image_cell = "[[চিত্র:" .. form_images[i] .. "|50px]]"
end
text = text .. " || " .. image_cell
end
text = text .. "\n"
end
text = text .. "|}"
return text
end
function p.all( frame )
local args = getArgs(frame)
local lexeme_id = args[1]
local current_lexeme = getEntity(lexeme_id)
local current_language = current_lexeme:getLanguage()
local senses = current_lexeme:getSenses()
local add_heading = true
forms = current_lexeme:getForms()
if args[2] then
local val = mw.text.trim(tostring(args[2]))
if val == "false" or val == "0" or val == "না" then
add_heading = false
end
end
local references_seen = {}
local sections = {}
local title = mw.title.getCurrentTitle().text
local lang_category = getLanguageForCategories(current_language, title)
local lang_name = getLabel(lang_category)
if add_heading == true then
local lang_heading = "== " .. lang_name .. " =="
table.insert(sections, lang_heading)
end
matched_lemma = getMatchingLemmaForPageTitle(current_lexeme, title)
lex_cat = current_lexeme:getLexicalCategory()
lang_code = getLexemeLanguageCode(current_lexeme)
local cat = i18n.lang_category(getLabel(lex_cat), lang_name)
local lex_cat_template
if cat then
table.insert(sections, '===' .. getLabel(lex_cat) .. cat .. frame:expandTemplate{
title = i18n['template_anchor'],
args = { lexeme_id }
} .. '===')
table.insert(sections, frame:expandTemplate{
title= i18n['template_lexeme'],
args = {lexeme_id}
})
add_any_notes(sections, args, i18n['manual_category'])
local etymology = getEtymology( current_lexeme, frame )
if etymology ~= '' and etymology then
table.insert(sections, heading_level(i18n['heading_etymology'], 4))
table.insert(sections, tostring(etymology))
end
add_any_notes(sections, args, i18n['manual_etymology'])
local pronunciation = getPronunciation( frame, current_lexeme, lang_name, lex_cat )
if pronunciation ~= '' then
table.insert(sections, heading_level(i18n['heading_pronunciation'], 4))
table.insert(sections, tostring(pronunciation))
end
add_any_notes(sections, args, i18n['manual_pronunciation'])
local alternative_spellings = getAlternativeSpellings( current_lexeme )
if alternative_spellings ~= '' then
table.insert(sections, heading_level(i18n['heading_alternative_spellings'], 4))
table.insert(sections, alternative_spellings)
end
if lang_code and lang_code ~= 'ksy' and lang_code ~= 'rkt' then -- Skip for Kharia Thar, Rangpuri
if lex_cat == 'Q34698' then -- বিশেষণ
if lang_code == 'en' or lang_code == 'bn' then
if #forms <= 1 then
lex_cat_template = frame:expandTemplate{title= lang_code .. '-বিশেষণ'}
end
else
lex_cat_template = safeExpand(frame, lang_code .. '-adj')
if not lex_cat_template then
lex_cat_template = safeExpand(frame, lang_code .. '-বিশেষণ')
end
end
elseif lex_cat == 'Q1084' then -- @TODO: Also check for plural forms
local gender
local stmts = current_lexeme:getAllStatements('P5185') -- ব্যাকরণগত লিঙ্গ
if #stmts ~= 0 then
if #stmts == 1 then
local gender_qid = stmts[1].mainsnak.datavalue.value.id
if gender_qid == 'Q499327' then
gender = 'm'
elseif gender_qid == 'Q1775415' then
gender = 'f'
elseif gender_qid == 'Q1775461' then
gender = 'n'
elseif gender_qid == 'Q1305037' then
gender = 'c'
end
end
else
for i, stmt in pairs(stmts) do
local qid = stmts[i].mainsnak.datavalue.value.id
if qid == 'Q499327' then
gender = gender .. 'm'
elseif qid == 'Q1775415' then
gender = gender .. 'f'
end
end
end
-- The following checks are ordered based on which one is expected to be true in a higher number of cases.
if current_language == 'Q13955' then
if matched_lemma then
lex_cat_template = safeExpand(frame, {title='ar-noun', args={matched_lemma,gender}})
else
lex_cat_template = frame:expandTemplate{title='ar-noun', args={nil,gender}}
end
elseif current_language == 'Q29919' then
lex_cat_template = frame:expandTemplate{title='arz-noun', args={g=gender}}
elseif current_language == 'Q397' then
if matched_lemma then
lex_cat_template = frame:expandTemplate{title='la-noun', args={matched_lemma,g=gender}}
end
elseif current_language == 'Q11059' then
lex_cat_template = frame:expandTemplate{title='sa-noun', args={g=gender}}
elseif current_language ~= 'Q1860' then -- These templates require the gender to be passed as the 1st argument.
lex_cat_template = safeExpand(frame, lang_code .. i18n['noun_template_suffix'], {gender})
if not lex_cat_template then
lex_cat_template = safeExpand(frame, lang_code .. i18n['noun_template_suffix_fallback'], {gender})
end
end
end
end
-- elseif lex_cat == 'Q147276' then
-- lex_cat_template = safeExpand(frame, lang_code .. '-proper noun', {gender})
-- if not lex_cat_template then
-- lex_cat_template = safeExpand(frame, lang_code .. '-নামবাচক বিশেষ্য', {gender})
-- end
end
if lex_cat_template then
table.insert(sections, lex_cat_template)
else
if matched_lemma then
table.insert(sections, heading_level(matched_lemma, 4))
else
table.insert(sections, i18n.tocatlink(i18n['category_no_matching_lemma']))
end
end
local meanings, references_seen, sense_extlinks = unpack(getMeanings( frame, args, current_lexeme, senses, references_seen, lang_name))
table.insert(sections, tostring(meanings))
add_any_notes(sections, args, i18n['manual_meaning'])
local instance_of = current_lexeme:getAllStatements('P31') -- সত্ত্বার ধরন
if #instance_of ~= 0 then
local instance_of_entity = instance_of[1].mainsnak.datavalue.value.id
if instance_of_entity == 'Q40437546' or instance_of_entity == 'Q120831827' or instance_of_entity == 'Q120717979' or instance_of_entity == 'Q124476844' then -- @TODO: generalise this so all types of roots are shown
table.insert(sections, i18n['text_instance_of'] .. ' ' .. getLabel(instance_of_entity))
elseif instance_of_entity == 'Q376431' then -- বর্ণের নাম
table.insert(sections, i18n.tocatlink(lang_code .. ':রং'))
end
end
local translations = getTranslations(frame, senses)
if translations then
table.insert(sections, translations)
end
-- (!) বিশেষ ভাষার বিভক্তির সারণি যদি থাকে সেগুলো এখানে নতুন if বিবৃতি যোগ করা যায়।
if next(forms) then
if current_language == 'Q9610' then -- বাংলা
if lex_cat == 'Q24905' then
local conjTable = require('মডিউল:আভিধানিক উপাত্ত/Q9610').getConjTable(frame, forms)
table.insert(sections, conjTable)
elseif lex_cat == 'Q1084' then
table.insert(sections, callWikifunctionsFunction('Z33243|' .. lexeme_id .. '|', frame))
elseif lex_cat == 'Q34698' then
if #forms > 1 then
table.insert(sections, buildLanguageAgnosticInflectionTable())
end
end
--elseif current_language == 'Q13955' then -- আরবি
-- if lex_cat == 'Q1084' then
-- table.insert(sections, frame:expandTemplate{title='ar-decl-noun', args={lemma}})
-- end
elseif current_language == 'Q188' then -- জার্মান
if lex_cat == 'Q1084' then
table.insert(sections, callWikifunctionsFunction('Z28602|' .. lexeme_id .. '|', frame))
end
else
if #forms > 1 then
table.insert(sections, buildLanguageAgnosticInflectionTable())
end
end
end
if lex_cat == 'Q134830' then
table.insert(sections, frame:expandTemplate{title='prefixsee', args={lang_code}})
elseif lex_cat == 'Q102047' then
table.insert(sections, frame:expandTemplate{title='suffixsee', args={lang_code}})
-- elseif lex_cat == 'Q111029' then
-- table.insert(sections, frame:expandTemplate{title='rootsee', args={'+', lang_code, matched_lemma}})
end
local reference_notes = get_any_notes(sections, args, i18n['manual_reference'])
if #references_seen > 0 or #reference_notes > 0 then
table.insert(sections, heading_level(i18n['heading_references'], 4))
table.insert(sections, frame:extensionTag('references'))
add_specific_notes(sections, reference_notes)
end
local external_link_table = getExternalLinks ( current_lexeme )
if #external_link_table > 0 then
local external_links = '* ' .. table.concat(external_link_table, '\n* ')
table.insert(sections, heading_level(i18n['heading_external_links'], 4))
table.insert(sections, external_links)
end
add_any_notes(sections, args, i18n['manual_external_link'])
if #references_seen == 0 and #reference_notes == 0 and sense_extlinks and #sense_extlinks == 0 and #external_link_table == 0 and #get_any_notes(sections, args, i18n['manual_external_link']) == 0 then
table.insert(sections, i18n.rfref_category(lang_name))
end
return table.concat(sections,"\n\n")
end
return p
4rzwmt7bnz7jde5jn1bi5024v3kt91w
509669
509644
2026-06-04T10:10:41Z
Redmin
6857
509669
Scribunto
text/plain
local p = {}
local i18n = require('মডিউল:আভিধানিক উপাত্ত/i18n')
local references = require('মডিউল:উইকিউপাত্ত তথ্যসূত্র বিন্যাসকরণ').format
local getArgs = require('Module:Arguments').getArgs
local wb = mw.wikibase
local ustring = mw.ustring
local html = mw.html
local mw_lang = mw.language
local entity_cache = {}
local reference_cache = {}
local forms
local lang_code
local lex_cat
local matched_lemma
local function wrapStringInWikilinks(str)
local exceptions = i18n.nolinks
local result = str:gsub('(%S+)', function(token)
local word, trailing_char = token:match('^(.-)([;,]*)$')
local wrapped = word:gsub('[^(-/]+', function(part)
if exceptions[part] then
return part
else
return '[[' .. part .. '#' .. i18n['content_lang_name'] .. '|' .. part .. ']]'
end
end)
return wrapped .. trailing_char
end)
return result
end
local function serializeTable(val, name, skipnewlines, depth) -- https://stackoverflow.com/a/6081639
skipnewlines = skipnewlines or false
depth = depth or 0
local tmp = string.rep(" ", depth)
if name then tmp = tmp .. name .. " = " end
if type(val) == "table" then
tmp = tmp .. "{" .. (not skipnewlines and "\n" or "")
for k, v in pairs(val) do
tmp = tmp .. serializeTable(v, k, skipnewlines, depth + 1) .. "," .. (not skipnewlines and "\n" or "")
end
tmp = tmp .. string.rep(" ", depth) .. "}"
elseif type(val) == "number" then
tmp = tmp .. tostring(val)
elseif type(val) == "string" then
tmp = tmp .. string.format("%q", val)
elseif type(val) == "boolean" then
tmp = tmp .. (val and "true" or "false")
else
tmp = tmp .. "\"[inserializeable datatype:" .. type(val) .. "]\""
end
return tmp
end
-- Use this to safely expand templates when you are not sure that they exist.
local function safeExpand(frame, title, args)
local _, result = pcall(function()
return frame:expandTemplate{ title = title, args = args }
end)
if result:find('does not exist') then -- expandTemplate{} doesn't seem to throw any error that can be handled with pcall() so string search is the only viable option.
return nil
end
return result
end
-- Use this function to get the label of an item even if that item does not have any label in the wiki's content language or English.
local function getLabel(id)
if id == 'Q11051hi' then
return 'হিন্দি'
elseif id == 'Q11051ur' then
return 'উর্দু'
elseif id == 'Q56356571fa' then
return 'নয়া ফার্সি'
elseif id == 'Q56356571tg' then
return 'তাজিক'
elseif id == 'Q58635pa' or id == 'Q58635pnb' then
return 'পাঞ্জাবি'
end
local label = wb.getLabel(id)
if label then
return label
end
local labels = wb.getEntity(id).labels
if not labels then
return id
end
for _, v in pairs(labels) do
if v and v.value then
return v.value
end
end
end
local function getReference( id, reference )
local out_id = nil
local url_value
if reference_cache[id] == nil then
local ref_text = references(reference, wb, mw_lang, i18n['content_lang_code'], i18n['wikipedia'])
if reference.snaks then
if reference.snaks['P248'] then
for _, snak in pairs(reference.snaks['P248']) do
if snak.datavalue and snak.datavalue.value.id == 'Q428' then -- কুরআন
ref_text = ustring.gsub(ref_text, 'নামক অনুচ্ছেদ', 'নং আয়াত')
break
end
end
end
if reference.snaks['P854'] then
local snak = reference.snaks['P854'][1]
if snak.datavalue then
url_value = snak.datavalue.value
end
end
end
if url_value then
ref_text = ref_text .. ', [' .. url_value .. ' সংযোগ]'
end
reference_cache[id] = ref_text
else
out_id = id
end
return {out_id, reference_cache[id]}
end
local function getEntity( id )
if entity_cache[id] == nil then
entity_cache[id] = wb.getEntity(id)
end
return entity_cache[id] ~= false and entity_cache[id] or nil
end
local function getLexemeLanguageCode(current_lexeme)
local lang_item_id = current_lexeme:getLanguage()
if lang_item_id == nil then
return nil
end
local lang_entity = getEntity(lang_item_id)
if lang_entity == nil then
return nil
end
for i, statement_property in ipairs({'P305','P424', 'P220'}) do -- আইইটিএফ ভাষা ট্যাগ, উইকিমিডিয়া ভাষা কোড, আইএসও ৬৩৯-৩
local statements = lang_entity:getBestStatements(statement_property)
if statements[1] then
return statements[1].mainsnak.datavalue.value
end
end
return nil
end
-- Return the first form of the lexeme which has exactly the given grammatical feature.
local function formWithSingleGrammaticalFeature( item_id )
for i = 1, #forms do
local grammaticalFeatures = forms[i]:getGrammaticalFeatures()
if #grammaticalFeatures == 1 and grammaticalFeatures[1] == item_id then
return forms[i]
end
end
return nil
end
local function getArticleLinkTemplate(frame, stmt_value)
local template = ''
local sitelink = getEntity(stmt_value):getSitelink(i18n['wikipedia'])
if sitelink then
template = frame:expandTemplate{
title=i18n['template_wikipedia'],
args={sitelink}
}
end
return template
end
local function getArticleLinks (frame, sense )
local article_links = ''
for i, stmt in pairs(sense:getAllStatements('P5137')) do -- এই অর্থের জন্য আইটেম
article_links = article_links .. getArticleLinkTemplate(frame, stmt.mainsnak.datavalue.value.id)
end
for i, stmt in pairs(sense:getAllStatements('P9970')) do -- এই অর্থের জন্য বিধেয়
article_links = article_links .. getArticleLinkTemplate(frame, stmt.mainsnak.datavalue.value.id)
end
return article_links
end
-- @TODO: Generalise
local function expandTemplateForProperty(frame, object, property, template)
local lemmas = {}
local n = 0
for _, stmt in pairs(object:getAllStatements(property)) do
local lex = wb.lexeme.splitLexemeId(stmt.mainsnak.datavalue.value.id)
if lex then
lex = getEntity(lex)
n = n + 1
lemmas[n] = lex:getLemma(lang_code)
end
end
if not lang_code or n == 0 then
return ''
end
-- Build args: first lang_code, then lemmas
local args = {lang_code}
for i = 1, n do
args[#args + 1] = lemmas[i]
end
return frame:expandTemplate{
title = template,
args = args
}
end
local function getExternalLinks( entity ) -- T418639
local external_links = {}
if entity.claims == nil then return external_links end
local formatter_urls = require('মডিউল:আভিধানিক উপাত্ত/urls').formatter_urls
for property_id, statements in pairs(entity.claims) do
local formatter_url = formatter_urls[property_id]
if formatter_url then
local property_source = wb.getBestStatements(property_id, 'P9073')
local source_name
if next(property_source) then
source_name = getLabel(property_source[1].mainsnak.datavalue.value.id)
or property_source[1].mainsnak.datavalue.value.id
else
source_name = getLabel(property_id) or property_id
end
for i = 1, #statements do
local stmt = statements[i]
if stmt.mainsnak.datavalue then
local formatted_link = ustring.gsub(
ustring.gsub(formatter_url, '$1', stmt.mainsnak.datavalue.value),
' ', '+'
)
table.insert(external_links,
'[' .. formatted_link .. ' ' .. source_name .. ']')
end
end
end
end
return external_links
end
p.getExternalLinks = getExternalLinks -- রেখে দিন যাতে ডিবাগিং সম্ভব হয়
local function termSpan( term )
local text = term[1]
local lang = term[2]
local dir = mw_lang.new( lang ):getDir()
local span = html.create( 'span' )
span:attr( 'lang', lang )
:attr( 'dir', dir )
:wikitext( text )
return tostring( span )
end
local function termLink( term, lang_qid )
local text = term[1]
local lang = term[2]
local dir = mw_lang.new( lang ):getDir()
local span = html.create( 'span' )
span:attr( 'lang', lang )
:attr( 'dir', dir )
:wikitext( '[[' .. text .. '#' .. getLabel(lang_qid) .. '|' .. text .. ']]' )
return tostring( span )
end
local function getLemmata( current_lexeme )
local lemma_string = ''
for i, rep in pairs(current_lexeme:getLemmas()) do
if lemma_string == '' then
lemma_string = termSpan(rep)
else
lemma_string = lemma_string .. '/' .. termSpan(rep)
end
end
return lemma_string
end
local function getLinkedLemmata( current_lexeme )
local lemma_string = ''
for i, rep in pairs(current_lexeme:getLemmas()) do
if lemma_string == '' then
lemma_string = termLink(rep, current_lexeme:getLanguage())
else
lemma_string = lemma_string .. '/' .. termLink(rep, current_lexeme:getLanguage())
end
end
return lemma_string
end
local function getExamples( current_lexeme, sense_id, references_seen )
local examples = html.create('dl')
local example_text, example_lang, example_form, example_str
for i, stmt in pairs(current_lexeme:getAllStatements('P5831')) do -- ব্যবহারের উদাহরণ
if stmt.qualifiers and stmt.qualifiers['P6072'] and stmt.qualifiers['P6072'][1].datavalue.value.id == sense_id then -- বিষয়ে লেক্সিমের অর্থ
example_text = ustring.gsub(stmt.mainsnak.datavalue.value.text, ' / ','<br/>')
example_lang = stmt.mainsnak.datavalue.value.language
local example_form_strs = {}
if stmt.qualifiers['P1810'] then
table.insert(example_form_strs, stmt.qualifiers['P1810'][1].datavalue.value)
elseif stmt.qualifiers['P5830'] then
example_form = getEntity(stmt.qualifiers['P5830'][1].datavalue.value.id) -- বিষয়ে লেক্সিমের রূপ
for _, rep in pairs(example_form:getRepresentations()) do
table.insert(example_form_strs, rep[1])
end
end
for i, example_form_str in pairs(example_form_strs) do
new_example_text = ustring.gsub(example_text, example_form_str, "'''" .. example_form_str .. "'''")
if new_example_text ~= example_text then
example_str = termSpan({new_example_text, example_lang})
break
end
new_example_text = example_text
end
if example_str == nil then
example_str = termSpan({example_text, example_lang})
end
local reference_text = ''
if stmt.references then
for j, reference in pairs(stmt.references) do
table.insert(references_seen, reference.hash)
local got_reference = getReference(reference.hash, reference)
reference_text = reference_text .. '\n\n' .. got_reference[2]
end
end
if example_str then
examples:tag('dd'):wikitext("''" .. example_str .. "''")
if reference_text ~= '' then
examples:done():tag('dd'):css('text-indent', '2em'):wikitext(reference_text)
end
end
end
end
for i, stmt in pairs(wb.getAllStatements(sense_id, 'P5831')) do -- ব্যবহারের উদাহরণ
example_text = ustring.gsub(stmt.mainsnak.datavalue.value.text, ' / ','<br/>')
example_lang = stmt.mainsnak.datavalue.value.language
local example_form_str = nil
if stmt.qualifers then
example_form = getEntity(stmt.qualifiers['P5830'][1].datavalue.value.id) -- বিষয়ে লেক্সিমের রূপ
if stmt.qualifiers['P1810'] then
example_form_str = stmt.qualifiers['P1810'][1].datavalue.value
end
end
if example_form and example_form_str == nil then
example_form_str = example_form:getRepresentation(i18n['content_lang_code'])
end
if example_form and example_form_str == nil then
example_form_str = example_form:getRepresentations()[1][1]
end
if example_form_str then
example_text = ustring.gsub(example_text, example_form_str, "'''" .. example_form_str .. "'''")
end
example_str = termSpan({example_text, example_lang})
local reference_text = ''
if stmt.references then
for j, reference in pairs(stmt.references) do
table.insert(references_seen, reference.hash)
local got_reference = getReference(reference.hash, reference)
reference_text = reference_text .. '\n\n' .. got_reference[2]
end
end
if example_str then
examples:tag('dd'):wikitext("''" .. example_str .. "''")
if reference_text ~= '' then
examples:done():tag('dd'):css('text-indent', '2em'):wikitext(reference_text)
end
end
end
return { tostring(examples) , references_seen }
end
-- This calls frame:preprocess() instead of :callParserFunction() because the latter does not work for Wikifunctions function calls yet (see https://www.wikifunctions.org/wiki/Wikifunctions:Embedded_function_calls).
local function callWikifunctionsFunction(args, frame)
return frame:preprocess('{{#function:' .. args .. '}}')
end
local function checkTitleCodePointInRange(title, start_point, end_point)
return ustring.find( title, '[' ..ustring.char(start_point) .. '-' .. ustring.char(end_point) .. ']' )
end
local function getLanguageForCategories( lang_id, current_page_title )
-- বিশেষ ভাষার জন্য
if lang_id == 'Q11051' then -- হিন্দি/উর্দু
if checkTitleCodePointInRange(current_page_title, 0x0600, 0x06ff) then -- উর্দু
lang_id = 'Q11051ur'
elseif checkTitleCodePointInRange(current_page_title, 0x0900, 0x097f) then -- হিন্দি
lang_id = 'Q11051hi'
end
elseif lang_id == 'Q58635' then -- পাঞ্জাবি
if checkTitleCodePointInRange(current_page_title, 0x0600, 0x06ff) then -- শাহমুখী
lang_id = 'Q58635pnb'
elseif checkTitleCodePointInRange(current_page_title, 0x0a00, 0x0a7f) then -- গুরুমুখী
lang_id = 'Q58635pa'
end
elseif lang_id == 'Q56356571' then -- নয়া ফার্সি ভাষা
if checkTitleCodePointInRange(current_page_title, 0x0600, 0x06ff) then -- ফার্সি (ইরান/আফগানিস্তান)
lang_id = 'Q56356571fa'
elseif checkTitleCodePointInRange(current_page_title, 0x0400, 0x04ff) then -- তাজিক
lang_id = 'Q56356571tg'
end
end
return lang_id
end
local function getOneStringForProperty(object, property)
local val
local stmts = object:getAllStatements(property)
if #stmts ~= 0 then
val = stmts[1].mainsnak.datavalue.value
end
return val
end
local function getTranslations(frame, senses) -- TODO: woefully incomplete until T185313 and T199887 are resolved
if #senses == 0 then
return nil
end
local all_translations = {}
for _, sense in pairs(senses) do
local translation_set = {}
local gloss = sense:getGloss('bn')
for _, stmt in pairs(sense:getAllStatements('P5972')) do
local translation = stmt.mainsnak.datavalue.value.id
local lexeme_id = wb.lexeme.splitLexemeId(translation)
local trans_lexeme = getEntity(lexeme_id)
local lang_name = getLabel(trans_lexeme:getLanguage())
table.insert(translation_set, lang_name .. ': ' .. getLemmata(trans_lexeme) .. '<br/>')
end
if #translation_set > 0 then
local block = frame:expandTemplate{ title = i18n['template_trans-top'], args = { gloss } }
block = block .. table.concat(translation_set, '\n') .. frame:expandTemplate{ title = i18n['template_trans-bottom'] }
table.insert(all_translations, block)
end
end
if #all_translations == 0 then
return nil
end
return '====' .. i18n['heading_translation'] .. '==== \n' .. table.concat(all_translations, '\n')
end
local createicon = function(langcode, entityID, propertyID)
langcode = langcode or ""
propertyID = propertyID or ""
local icon = " <span class='penicon autoconfirmed-show'>[["
-- " <span data-bridge-edit-flow='overwrite' class='penicon'>[[" -> enable Wikidata Bridge
.. "File:OOjs UI icon edit-ltr-progressive.svg |frameless |text-top |10px |alt="
.. i18n['edit_wikidata']
.. "|link=https://www.wikidata.org/entity/" .. entityID
if langcode ~= "" then icon = icon .. "?uselang=" .. langcode end
if propertyID ~= "" then icon = icon .. "#" .. propertyID end
icon = icon .. "|" .. i18n['edit_wikidata'] .. "]]</span>"
return icon
end
local function getMeanings( frame, args, current_lexeme, senses, references_seen, language_name)
if #senses == 0 then
return {createicon(i18n['content_lang_code'], current_lexeme:getId()) .. "''" .. i18n['text_category_rfdef'] .. "''" .. i18n.tocatlink(i18n['category_rfdef']), references_seen}
end
local meanings = html.create( 'ol' )
local item_label_gloss_parts = {}
local idlinkset = {}
for i, sense in pairs(senses) do
local gloss_text_parts = {}
local main_gloss_text = frame:expandTemplate{
title=i18n['template_anchor'],
args={sense:getId()}
}
local specifiers = {}
for k, property_id in ipairs({'P6084', 'P6191', 'P9488'}) do -- অবস্থান যেখানে শব্দার্থ ব্যবহৃত, যে রীতিতে শব্দার্থ ব্যবহৃত হয়, যে ক্ষেত্রে ব্যবহৃত
for _, stmt in pairs(sense:getAllStatements(property_id)) do
local stmt_value = stmt.mainsnak.datavalue.value.id
local reference_text = ''
local refs = stmt.references
if refs then
for j, reference in pairs(refs) do
table.insert(references_seen, reference.hash)
local got_reference = getReference(reference.hash, reference)
reference_text = reference_text .. '\n\n' .. frame:extensionTag('ref', got_reference[2])
end
end
local val = getLabel(stmt_value)
table.insert(specifiers, val .. reference_text)
if property_id == 'P9488' then
table.insert(item_label_gloss_parts, i18n.tocatlink(lang_code .. ':' .. val))
end
end
if #specifiers > 0 then
main_gloss_text = main_gloss_text .. "(''" .. table.concat(specifiers, "'', ''") .. "'') "
end
end
local gloss = sense:getGloss( i18n['content_lang_code'] )
if gloss then
main_gloss_text = main_gloss_text .. wrapStringInWikilinks(gloss)
if gloss:match('^প্রদত্ত%s*(%S-)%s*নাম$') then -- given names
main_gloss_text = main_gloss_text .. i18n.tocatlink(language_name .. ' ' .. i18n['category_given_names'])
end
else
local other_gloss_text = nil
local other_gloss_lang = nil
for k, stmt in pairs(sense:getAllStatements('P5137')) do -- যদি 'এই অর্থের জন্য আইটেম' মানের বাংলা লেবেল থাকে
local stmt_value = stmt.mainsnak.datavalue.value.id
local stmt_label = getLabel(stmt_value)
if stmt_label then
table.insert(item_label_gloss_parts, '[[:d:' .. stmt_value .. '|' .. stmt_label .. ']]')
end
end
if #item_label_gloss_parts > 0 then
other_gloss_text = table.concat(item_label_gloss_parts, '; ')
end
if other_gloss_text == nil then
for _, fallback_lang in ipairs(mw_lang.getFallbacksFor( i18n['content_lang_code'] )) do
if sense:getGloss( fallback_lang ) then
other_gloss_text, other_gloss_lang = sense:getGloss( fallback_lang )
end
end
if other_gloss_lang == nil then
local glosses = sense:getGlosses()
for _, gloss in pairs(glosses) do
other_gloss_text = gloss[1]
other_gloss_lang = gloss[2]
break
end
end
main_gloss_text = main_gloss_text .. other_gloss_text .. "<sup><em>" .. mw_lang.fetchLanguageName(other_gloss_lang, i18n['content_lang_code']) .. "</em></sup>"
else
main_gloss_text = main_gloss_text .. "''" .. other_gloss_text .. "''"
end
main_gloss_text = main_gloss_text .. i18n.tocatlink(i18n['category_rfdef_equivalent'])
end
local synonym = expandTemplateForProperty(frame, sense, 'P5973', i18n['template_synonym'])
if synonym ~= '' then
main_gloss_text = main_gloss_text .. ' <br/> ' .. synonym
end
local antonym = expandTemplateForProperty(frame, sense, 'P5974', i18n['template_antonym'])
if antonym ~= '' then
main_gloss_text = main_gloss_text .. ' <br/> ' .. antonym
end
local hypernym = expandTemplateForProperty(frame, sense, 'P6593', i18n['template_hypernym'])
if hypernym ~= '' then
main_gloss_text = main_gloss_text .. ' <br/> ' .. hypernym
end
if lex_cat == 'Q1084' or lex_cat == 'Q147276' then -- noun or proper noun
local demonym = expandTemplateForProperty(frame, sense, 'P6271', i18n['template_demonym-noun'])
main_gloss_text = main_gloss_text .. ' <br/> ' .. demonym
elseif lex_cat == 'Q34698' then
local demonym = expandTemplateForProperty(frame, sense, 'P6271', i18n['template_demonym-adj'])
main_gloss_text = main_gloss_text .. ' <br/> ' .. demonym
end
table.insert(gloss_text_parts, main_gloss_text .. createicon(i18n['content_lang_code'], sense:getId()))
for i, stmt in pairs(sense:getAllStatements('P8394')) do -- টিপ্পনীর উদ্ধৃতি
local gloss_quote = termSpan({stmt.mainsnak.datavalue.value.text, stmt.mainsnak.datavalue.value.language})
if stmt.references[1] then
local got_reference = getReference ( stmt.references[1].hash, stmt.references[1] )
gloss_quote = '"' .. gloss_quote .. '" ' .. got_reference[2]
end
table.insert(references_seen, stmt.references[1].hash)
table.insert(gloss_text_parts, frame:extensionTag('ref', gloss_quote))
end
for i, stmt in pairs(sense:getAllStatements('P1343')) do -- বর্ণিত উৎস
-- TODO: do away with making fake reference objects
local fake_reference = { ['snaks'] = {} }
fake_reference.snaks['P248'] = { [1] = stmt.mainsnak }
qualifiers_order = stmt['qualifiers-order']
if qualifiers_order then
for i, k in ipairs(qualifiers_order) do fake_reference.snaks[k] = stmt.qualifiers[k] end
end
fake_reference.hash = mw.hash.hashValue('sha3-512', serializeTable(fake_reference))
table.insert(references_seen, fake_reference.hash)
local got_reference = getReference(fake_reference.hash, fake_reference)
if got_reference[1] == nil then
table.insert(gloss_text_parts, frame:extensionTag('ref', got_reference[2], {name = fake_reference.hash}))
else
table.insert(gloss_text_parts, frame:extensionTag{name = 'ref', content='', args = {name = got_reference[1]}})
end
end
local first_sense_image = ''
local sense_images = sense:getAllStatements('P18')
if next(sense_images) then
first_sense_image = sense_images[1].mainsnak.datavalue.value
end
if first_sense_image ~= '' then
table.insert(gloss_text_parts, '[[চিত্র:' .. first_sense_image .. "|thumb|'''" .. getLemmata(current_lexeme) .. "'''—" .. main_gloss_text .. ']]')
end
local idlinks = getExternalLinks(sense)
if #idlinks > 0 then
local idlinktext = '<small>('
for _, idlink in pairs(idlinks) do
idlinktext = idlinktext .. idlink .. '\n'
end
idlinktext = idlinktext .. ')</small>'
table.insert(gloss_text_parts, idlinktext)
table.insert(idlinkset, idlinks)
end
local externallinks = getArticleLinks(frame, sense)
if externallinks ~= '' then
table.insert(gloss_text_parts, externallinks)
end
local new_notes = {}
local sense_keys = { sense:getId(), string.sub(sense:getId(), string.find(sense:getId(), '-')+1) }
for _, v in ipairs(sense_keys) do
if args[v] then
table.insert(new_notes, args[v])
end
end
if #new_notes > 0 then
for _, v in ipairs(new_notes) do
if i == 1 then
table.insert(gloss_text_parts, '<br/>' .. v)
else
table.insert(gloss_text_parts, v)
end
end
end
local examples, references_seen = unpack(getExamples( current_lexeme, sense:getId(), references_seen ))
local gloss_text = table.concat(gloss_text_parts, '\n')
meanings:tag('li'):wikitext(gloss_text):wikitext(examples)
end
return {meanings, references_seen, idlinkset}
end
local function getPronunciationBaseForm( lang_name, lex_cat)
local base_form = nil
-- (!) অন্য ভাষার শব্দের যদি অন্য রকম মূল ফর্ম থাকে সেগুলো এখানে নতুন if বিবৃতি দিয়ে যোগ করা যায়।
if lang_name == 'বাংলা' then
if lex_cat == 'Q1084' then -- বিশেষ্য
base_form = formWithSingleGrammaticalFeature( 'Q131105' ) -- কর্তৃকারক
elseif lex_cat == 'Q24905' then -- ক্রিয়া
base_form = formWithSingleGrammaticalFeature( 'Q1350145' ) -- ক্রিয়া বিশেষ্য
end
end
if base_form == nil then
for i, form in pairs(forms) do
base_form = form
break
end
end
return base_form
end
local function getCombines( current_lexeme, frame )
local combines = ''
local index_mappings = {}
for i, stmt in pairs(current_lexeme:getAllStatements('P5238')) do
if stmt.qualifiers and stmt.qualifiers['P1545'] then -- ক্রম
local current_index = tonumber(stmt.qualifiers['P1545'][1].datavalue.value)
index_mappings[current_index] = stmt
end
end
if #index_mappings ~= 0 then
for i, stmt in ipairs(index_mappings) do
if stmt.mainsnak.datavalue then
local part_lexeme_id = stmt.mainsnak.datavalue.value.id
local part_lexeme = getEntity(part_lexeme_id)
local current_substring = getLinkedLemmata(part_lexeme)
local part_etymology = getEtymology(part_lexeme, frame, 'partial')
if part_etymology ~= '' and part_etymology then
current_substring = current_substring .. ' (← ' .. part_etymology .. ')'
end
if combines == '' then
combines = current_substring
else -- @TODO: This shoukd use the 'affix' and 'compound' templates instead.
combines = combines .. ' + ' .. current_substring
end
end
end
end
return combines
end
function getRoots( current_lexeme )
local stmts = current_lexeme:getAllStatements('P5920')
if #stmts == 0 then return '', '', '' end
local root_lexeme = getEntity(stmts[1].mainsnak.datavalue.value.id)
return getLexemeLanguageCode(root_lexeme), '√' .. getLinkedLemmata(root_lexeme), root_lexeme:getLemma('ar')
end
function getEtymology( current_lexeme, frame, mode )
-- @TODO: Fix the etymology chains that are not possible to render
local etymology = ''
local current_combines = getCombines(current_lexeme, frame)
local root_lang, current_roots, root_str = getRoots(current_lexeme)
if mode ~= 'partial' and root_str then
--frame:expandTemplate{title=i18n['template_root'], args={lang_code, root_lang, root_str}}
end
local stmts = current_lexeme:getAllStatements('P5191')
local new_etymology_string
if #stmts == 0 then
if current_roots ~= '' and current_combines ~= '' and current_roots then
return current_roots .. '<br/>(' .. current_combines .. ')'
elseif current_roots ~= '' then
if lang_code == 'ar' and mode ~= 'partial' and root_str ~= matched_lemma then
return frame:expandTemplate{title=i18n['template_ar-rootbox'], args={root_str}}
else
return current_roots
end
else
return current_combines
end
end
local origin_lexeme_string
for i, stmt in pairs(stmts) do
local origin_lexeme_dv = stmt.mainsnak.datavalue -- If this is nil, the origin lexeme is not known.
if origin_lexeme_dv then
local origin_lexeme = getEntity(origin_lexeme_dv.value.id)
local origin_lexeme_lang = getLabel(origin_lexeme:getLanguage())
local sitelink = i18n.wplink(origin_lexeme:getLanguage(), origin_lexeme_lang, wb)
if sitelink ~= '' then
origin_lexeme_string = getLinkedLemmata(origin_lexeme) .. ' (' .. sitelink .. ')'
else
origin_lexeme_string = getLinkedLemmata(origin_lexeme) .. ' (' .. origin_lexeme_lang .. ')'
end
if stmt.qualifiers and stmt.qualifiers['P5886'] then
local mode_of_derivation = stmt.qualifiers['P5886'][1].datavalue.value.id
if mode_of_derivation == 'Q1345001' then
-- @TODO: Add support for showing gender
origin_lexeme_string = frame:expandTemplate{title=i18n['template_borrowed'], args={lang_code, getLexemeLanguageCode(origin_lexeme), getLemmata(origin_lexeme), pos=getLabel(lex_cat)}} .. ' ' .. i18n['etymology_borrowing']
elseif mode_of_derivation == 'Q845079' then
origin_lexeme_string = ustring.gsub(i18n['etymology_learned_borrowing'], '$1', origin_lexeme_string)
elseif mode_of_derivation == 'Q56611986' then
origin_lexeme_string = frame:expandTemplate{title=i18n['template_inherited'], args={lang_code, getLexemeLanguageCode(origin_lexeme), getLemmata(origin_lexeme), pos=getLabel(lex_cat)}} .. ' ' .. i18n['etymology_inheritance']
elseif mode_of_derivation == 'Q189743' then
origin_lexeme_string = frame:expandTemplate{title=i18n['template_ellipsis'], args={lang_code, getLemmata(origin_lexeme)}} .. ' ' .. i18n['etymology_ellipsis']
end
end
local origin_origin = getEtymology(origin_lexeme, frame)
if origin_origin ~= '' and origin_origin then
new_etymology_string = origin_lexeme_string .. ' ← ' .. origin_origin
else
new_etymology_string = origin_lexeme_string
end
end
if etymology == '' then
etymology = new_etymology_string
elseif origin_lexeme_string and etymology then
etymology = etymology .. ' ' .. origin_lexeme_string
elseif origin_lexeme_string and etymology == nil then
etymology = origin_lexeme_string
end
end
if current_roots ~= '' and etymology and current_roots then
etymology = etymology .. ' ' .. current_roots
elseif current_roots ~= '' and etymology == nil then
etymology = current_roots
end
if current_combines ~= '' and etymology then
etymology = etymology .. '<br/>(' .. current_combines .. ')'
end
return etymology
end
local function pronunciationBlock(block, value)
return '* ' .. i18n['text_' .. block] .. ' ' .. value
end
local function getPronunciation( frame, current_lexeme, lang_name, lex_cat )
local pronunciations = {}
local base_form = getPronunciationBaseForm(lang_name, lex_cat )
if base_form then
for i, stmt in pairs(base_form:getAllStatements('P443')) do -- উচ্চারণের অডিও
local pronunciation_file = stmt.mainsnak.datavalue.value
local specifier_text = ''
local specifiers = {}
if stmt.qualifiers then
for k, property_id in ipairs({'P5237'}) do -- উচ্চারণের ধরন
local qual = stmt.qualifiers[property_id]
if qual then
for _, qual in pairs(qual) do
local stmt_value = qual.datavalue.value.id
table.insert(specifiers, getLabel(stmt_value))
end
end
end
end
if #specifiers > 0 then
specifier_text = table.concat(specifiers, "'', ''")
end
local audio_text
if specifier_text ~= '' then
audio_text = i18n['text_audio'] .. ' (' .. specifier_text .. ')'
else
audio_text = i18n['text_audio']
end
table.insert(pronunciations, '* ' .. frame:expandTemplate{
title= i18n['template_audio'],
args = {lang_name, pronunciation_file, audio_text}
})
end
local ipa_transcription = base_form:getAllStatements('P898') -- - আধ্বব প্রতিলিপিকরণ
local iso15919_transcription = getOneStringForProperty(base_form, 'P5825') -- আইএসও ১৫৯১৯ প্রতিলিপিকরণ
local itrans = getOneStringForProperty(base_form, 'P8881') -- ITRANS
local iast = getOneStringForProperty(base_form, 'P7581') -- আইএএসটি প্রতিলিপিকরণ
local xsampa = getOneStringForProperty(base_form, 'P2859')
-- @TODO: অডিও ও প্রতিলিপিকরণ দুটোই থাকলে সেই ক্ষেত্রে একটার ঠিক পরেই আরেকটা দেখানো উচিত
if #ipa_transcription ~= 0 then
for i, stmt in pairs(ipa_transcription) do
local ipa_text = stmt.mainsnak.datavalue.value
local specifier_text = ''
local specifiers = {}
if stmt.qualifiers then
for k, property_id in ipairs({'P5237'}) do -- উচ্চারণের ধরন
for l, qual in ipairs(stmt.qualifiers[property_id]) do
table.insert(specifiers, getLabel( qual.datavalue.value.id ))
end
end
end
if #specifiers > 0 then
specifier_text = "(''" .. table.concat(specifiers, "'', ''") .. "'') "
end
local syllable_count
if lang_code == 'tr' then
syllable_count = i18n['text_syllable_count'] .. ' ' .. callWikifunctionsFunction('Z10029|' .. ipa_text, frame)
else
syllable_count = i18n['text_syllable_count'] .. ' ' .. callWikifunctionsFunction('Z30837|' .. ipa_text, frame)
end
table.insert(pronunciations, '* ' .. specifier_text .. frame:expandTemplate{
title= i18n['template_ipa'],
args = {lang_name, ipa_text}
} .. '\n* ' .. syllable_count)
end
-- The following checks are ordered based on which one is expected to be true in a higher number of cases.
elseif lang_name == 'বাংলা' then
table.insert(pronunciations, '* ' .. frame:expandTemplate{
title='bn-IPA',
})
elseif lang_name == 'আরবি' then
local lemma = current_lexeme:getLemma('ar')
table.insert(pronunciations, '* ' .. frame:expandTemplate{
title='ar-IPA',
args={lemma}
})
elseif lang_name == 'ফালা' then
table.insert(pronunciations, '* ' .. frame:expandTemplate{
title='fax-pron',
})
elseif lang_code == 'fi' then
table.insert(pronunciations, '* ' .. frame:expandTemplate{
title='fi-IPA',
})
elseif lang_code == 'ko' then
table.insert(pronunciations, '* ' .. frame:expandTemplate{
title='ko-IPA',
})
end
if iso15919_transcription then
table.insert(pronunciations, pronunciationBlock('iso15919', iso15919_transcription))
end
if itrans then
table.insert(pronunciations, pronunciationBlock('itrans', itrans))
end
if iast then
table.insert(pronunciations, pronunciationBlock('iast', iast))
end
if xsampa then
table.insert(pronunciations, pronunciationBlock('xsampa', xsampa))
end
end -- {{আধ্বব|en|/ˈɪntəvjuː/}}
return table.concat(pronunciations, '\n')
end
local function getAlternativeSpellings( current_lexeme )
local alt_spellings = {}
for i, stmt in pairs(current_lexeme:getAllStatements('P11577')) do -- বিকল্প বানান
if stmt.mainsnak.datavalue then
table.insert(alt_spellings, '* ' .. getLinkedLemmata(getEntity(stmt.mainsnak.datavalue.value.id)))
end
end
return table.concat(alt_spellings, '\n')
end
local function heading_level(text, level)
local heading_delimiter = string.rep('=', level)
return heading_delimiter .. ' ' .. text .. ' ' .. heading_delimiter
end
function get_any_notes(sections, args, keys)
local notes = {}
for i, v in ipairs(keys) do
if args[v] then
table.insert(notes, args[v])
end
end
return notes
end
function add_specific_notes(sections, notes)
for i, v in ipairs(notes) do
table.insert(sections, v)
end
end
local function add_any_notes(sections, args, keys)
for i, v in ipairs(keys) do
if args[v] then
table.insert(sections, args[v])
end
end
end
local function getMatchingLemmaForPageTitle(lexeme, title)
local lemmas = lexeme:getLemmas()
local matched_lemma
for _, lemma_entry in ipairs(lemmas) do
local lemma = lemma_entry[1]
if lemma == title then
matched_lemma = lemma
break
end
end
if matched_lemma == nil and lang_code == 'ar' then -- Arabic lemmas do not match the title of the entry because those are written with different characters stripped out on Wiktionary. This could be changed to call stripDiactritics() from [[Module:languages]] in the future if desired.
matched_lemma = lexeme:getLemma('ar')
end
return matched_lemma
end
local function buildLanguageAgnosticInflectionTable()
local has_image = false
local form_images = {}
for i, form in ipairs(forms) do
local form_image = form:getAllStatements('P7407')
if next(form_image) then
form_images[i] = form_image[1].mainsnak.datavalue.value
has_image = true
end
end
local table_class = "wikitable mw-collapsible sortable"
if not has_image then
table_class = table_class .. " mw-collapsed"
end
local text = "{| class='" .. table_class .. "' style='border:solid 1px rgb(80%,80%,100%); text-align:center;'\n"
text = text .. "|+ " .. i18n['heading_inflection_table'] .. "\n"
text = text .. "|- \n"
text = text .. "! " .. i18n['heading_form'] .. " !! " .. i18n['heading_grammatical_features']
if has_image then
text = text .. " !! " .. (i18n['heading_image'])
end
text = text .. " \n"
for i, form in ipairs(forms) do
local rep = form:getRepresentations()
local feat = form:getGrammaticalFeatures()
local rep_text = ""
for j, r in pairs(rep) do
if rep_text == "" then
rep_text = r[1]
else
rep_text = rep_text .. " / " .. r[1]
end
end
local feat_text = ""
if feat then
for j, f in ipairs(feat) do
local label = getLabel(f) or f
if feat_text == "" then
feat_text = label
else
feat_text = feat_text .. ", " .. label
end
end
end
text = text .. "|-\n"
text = text .. "| " .. (rep_text ~= "" and rep_text or "—")
text = text .. " || " .. (feat_text ~= "" and feat_text or "—")
if has_image then
local image_cell = "—"
if form_images[i] then
image_cell = "[[চিত্র:" .. form_images[i] .. "|50px]]"
end
text = text .. " || " .. image_cell
end
text = text .. "\n"
end
text = text .. "|}"
return text
end
function p.all( frame )
local args = getArgs(frame)
local lexeme_id = args[1]
local current_lexeme = getEntity(lexeme_id)
local current_language = current_lexeme:getLanguage()
local senses = current_lexeme:getSenses()
local add_heading = true
forms = current_lexeme:getForms()
if args[2] then
local val = mw.text.trim(tostring(args[2]))
if val == "false" or val == "0" or val == "না" then
add_heading = false
end
end
local references_seen = {}
local sections = {}
local title = mw.title.getCurrentTitle().text
local lang_category = getLanguageForCategories(current_language, title)
local lang_name = getLabel(lang_category)
if add_heading == true then
local lang_heading = "== " .. lang_name .. " =="
table.insert(sections, lang_heading)
end
matched_lemma = getMatchingLemmaForPageTitle(current_lexeme, title)
lex_cat = current_lexeme:getLexicalCategory()
lang_code = getLexemeLanguageCode(current_lexeme)
local cat = i18n.lang_category(getLabel(lex_cat), lang_name)
local lex_cat_template
if cat then
table.insert(sections, '===' .. getLabel(lex_cat) .. cat .. frame:expandTemplate{
title = i18n['template_anchor'],
args = { lexeme_id }
} .. '===')
table.insert(sections, frame:expandTemplate{
title= i18n['template_lexeme'],
args = {lexeme_id}
})
add_any_notes(sections, args, i18n['manual_category'])
local etymology = getEtymology( current_lexeme, frame )
if etymology ~= '' and etymology then
table.insert(sections, heading_level(i18n['heading_etymology'], 4))
table.insert(sections, tostring(etymology))
end
add_any_notes(sections, args, i18n['manual_etymology'])
local pronunciation = getPronunciation( frame, current_lexeme, lang_name, lex_cat )
if pronunciation ~= '' then
table.insert(sections, heading_level(i18n['heading_pronunciation'], 4))
table.insert(sections, tostring(pronunciation))
end
add_any_notes(sections, args, i18n['manual_pronunciation'])
local alternative_spellings = getAlternativeSpellings( current_lexeme )
if alternative_spellings ~= '' then
table.insert(sections, heading_level(i18n['heading_alternative_spellings'], 4))
table.insert(sections, alternative_spellings)
end
if lang_code and lang_code ~= 'ksy' and lang_code ~= 'rkt' then -- Skip for Kharia Thar, Rangpuri
if lex_cat == 'Q34698' then -- বিশেষণ
if lang_code == 'en' or lang_code == 'bn' then
if #forms <= 1 then
lex_cat_template = frame:expandTemplate{title= lang_code .. '-বিশেষণ'}
end
else
lex_cat_template = safeExpand(frame, lang_code .. '-adj')
if not lex_cat_template then
lex_cat_template = safeExpand(frame, lang_code .. '-বিশেষণ')
end
end
elseif lex_cat == 'Q1084' then -- @TODO: Also check for plural forms
local gender
local stmts = current_lexeme:getAllStatements('P5185') -- ব্যাকরণগত লিঙ্গ
if #stmts ~= 0 then
if #stmts == 1 then
local gender_qid = stmts[1].mainsnak.datavalue.value.id
if gender_qid == 'Q499327' then
gender = 'm'
elseif gender_qid == 'Q1775415' then
gender = 'f'
elseif gender_qid == 'Q1775461' then
gender = 'n'
elseif gender_qid == 'Q1305037' then
gender = 'c'
end
end
else
for i, stmt in pairs(stmts) do
local qid = stmts[i].mainsnak.datavalue.value.id
if qid == 'Q499327' then
gender = gender .. 'm'
elseif qid == 'Q1775415' then
gender = gender .. 'f'
end
end
end
-- The following checks are ordered based on which one is expected to be true in a higher number of cases.
if current_language == 'Q13955' then
if matched_lemma then
lex_cat_template = safeExpand(frame, {title='ar-noun', args={matched_lemma,gender}})
else
lex_cat_template = frame:expandTemplate{title='ar-noun', args={nil,gender}}
end
elseif current_language == 'Q29919' then
lex_cat_template = frame:expandTemplate{title='arz-noun', args={g=gender}}
elseif current_language == 'Q397' then
if matched_lemma then
lex_cat_template = frame:expandTemplate{title='la-noun', args={matched_lemma,g=gender}}
end
elseif current_language == 'Q11059' then
lex_cat_template = frame:expandTemplate{title='sa-noun', args={g=gender}}
elseif current_language ~= 'Q1860' then -- These templates require the gender to be passed as the 1st argument.
lex_cat_template = safeExpand(frame, lang_code .. i18n['noun_template_suffix'], {gender})
if not lex_cat_template then
lex_cat_template = safeExpand(frame, lang_code .. i18n['noun_template_suffix_fallback'], {gender})
end
end
end
end
-- elseif lex_cat == 'Q147276' then
-- lex_cat_template = safeExpand(frame, lang_code .. '-proper noun', {gender})
-- if not lex_cat_template then
-- lex_cat_template = safeExpand(frame, lang_code .. '-নামবাচক বিশেষ্য', {gender})
-- end
end
if lex_cat_template then
table.insert(sections, lex_cat_template)
else
if matched_lemma then
table.insert(sections, heading_level(matched_lemma, 4))
else
table.insert(sections, i18n.tocatlink(i18n['category_no_matching_lemma']))
end
end
local meanings, references_seen, sense_extlinks = unpack(getMeanings( frame, args, current_lexeme, senses, references_seen, lang_name))
table.insert(sections, tostring(meanings))
add_any_notes(sections, args, i18n['manual_meaning'])
local instance_of = current_lexeme:getAllStatements('P31') -- সত্ত্বার ধরন
if #instance_of ~= 0 then
local instance_of_entity = instance_of[1].mainsnak.datavalue.value.id
if instance_of_entity == 'Q40437546' or instance_of_entity == 'Q120831827' or instance_of_entity == 'Q120717979' or instance_of_entity == 'Q124476844' then -- @TODO: generalise this so all types of roots are shown
table.insert(sections, i18n['text_instance_of'] .. ' ' .. getLabel(instance_of_entity))
elseif instance_of_entity == 'Q376431' then -- বর্ণের নাম
table.insert(sections, i18n.tocatlink(lang_code .. ':রং'))
end
end
local translations = getTranslations(frame, senses)
if translations then
table.insert(sections, translations)
end
-- (!) বিশেষ ভাষার বিভক্তির সারণি যদি থাকে সেগুলো এখানে নতুন if বিবৃতি যোগ করা যায়।
if next(forms) then
if current_language == 'Q9610' then -- বাংলা
if lex_cat == 'Q24905' then
local conjTable = require('মডিউল:আভিধানিক উপাত্ত/Q9610').getConjTable(frame, forms)
table.insert(sections, conjTable)
elseif lex_cat == 'Q1084' then
table.insert(sections, callWikifunctionsFunction('Z33243|' .. lexeme_id .. '|', frame))
elseif lex_cat == 'Q34698' then
if #forms > 1 then
table.insert(sections, buildLanguageAgnosticInflectionTable())
end
end
--elseif current_language == 'Q13955' then -- আরবি
-- if lex_cat == 'Q1084' then
-- table.insert(sections, frame:expandTemplate{title='ar-decl-noun', args={lemma}})
-- end
elseif current_language == 'Q188' then -- জার্মান
if lex_cat == 'Q1084' then
table.insert(sections, callWikifunctionsFunction('Z28602|' .. lexeme_id .. '|', frame))
end
else
if #forms > 1 then
table.insert(sections, buildLanguageAgnosticInflectionTable())
end
end
end
if lex_cat == 'Q134830' then
table.insert(sections, frame:expandTemplate{title='prefixsee', args={lang_code}})
elseif lex_cat == 'Q102047' then
table.insert(sections, frame:expandTemplate{title='suffixsee', args={lang_code}})
-- elseif lex_cat == 'Q111029' then
-- table.insert(sections, frame:expandTemplate{title='rootsee', args={'+', lang_code, matched_lemma}})
end
local reference_notes = get_any_notes(sections, args, i18n['manual_reference'])
if #references_seen > 0 or #reference_notes > 0 then
table.insert(sections, heading_level(i18n['heading_references'], 4))
table.insert(sections, frame:extensionTag('references'))
add_specific_notes(sections, reference_notes)
end
local external_link_table = getExternalLinks ( current_lexeme )
if #external_link_table > 0 then
local external_links = '* ' .. table.concat(external_link_table, '\n* ')
table.insert(sections, heading_level(i18n['heading_external_links'], 4))
table.insert(sections, external_links)
end
add_any_notes(sections, args, i18n['manual_external_link'])
if #references_seen == 0 and #reference_notes == 0 and sense_extlinks and #sense_extlinks == 0 and #external_link_table == 0 and #get_any_notes(sections, args, i18n['manual_external_link']) == 0 then
table.insert(sections, i18n.rfref_category(lang_name))
end
return table.concat(sections,"\n\n")
end
return p
8hvt6bn148tf85lirm324zbhv4kv9jd
509670
509669
2026-06-04T10:45:16Z
Redmin
6857
509670
Scribunto
text/plain
local p = {}
local i18n = require('মডিউল:আভিধানিক উপাত্ত/i18n')
local references = require('মডিউল:উইকিউপাত্ত তথ্যসূত্র বিন্যাসকরণ').format
local getArgs = require('Module:Arguments').getArgs
local wb = mw.wikibase
local ustring = mw.ustring
local html = mw.html
local mw_lang = mw.language
local entity_cache = {}
local reference_cache = {}
local forms
local lang_code
local lex_cat
local matched_lemma
local function wrapStringInWikilinks(str)
local exceptions = i18n.nolinks
local result = str:gsub('(%S+)', function(token)
local word, trailing_char = token:match('^(.-)([;,]*)$')
local wrapped = word:gsub('[^(-/]+', function(part)
if exceptions[part] then
return part
else
return '[[' .. part .. '#' .. i18n['content_lang_name'] .. '|' .. part .. ']]'
end
end)
return wrapped .. trailing_char
end)
return result
end
local function serializeTable(val, name, skipnewlines, depth) -- https://stackoverflow.com/a/6081639
skipnewlines = skipnewlines or false
depth = depth or 0
local tmp = string.rep(" ", depth)
if name then tmp = tmp .. name .. " = " end
if type(val) == "table" then
tmp = tmp .. "{" .. (not skipnewlines and "\n" or "")
for k, v in pairs(val) do
tmp = tmp .. serializeTable(v, k, skipnewlines, depth + 1) .. "," .. (not skipnewlines and "\n" or "")
end
tmp = tmp .. string.rep(" ", depth) .. "}"
elseif type(val) == "number" then
tmp = tmp .. tostring(val)
elseif type(val) == "string" then
tmp = tmp .. string.format("%q", val)
elseif type(val) == "boolean" then
tmp = tmp .. (val and "true" or "false")
else
tmp = tmp .. "\"[inserializeable datatype:" .. type(val) .. "]\""
end
return tmp
end
-- Use this to safely expand templates when you are not sure that they exist.
local function safeExpand(frame, title, args)
local _, result = pcall(function()
return frame:expandTemplate{ title = title, args = args }
end)
if result:find('does not exist') then -- expandTemplate{} doesn't seem to throw any error that can be handled with pcall() so string search is the only viable option.
return nil
end
return result
end
-- Use this function to get the label of an item even if that item does not have any label in the wiki's content language or English.
local function getLabel(id)
if id == 'Q11051hi' then
return 'হিন্দি'
elseif id == 'Q11051ur' then
return 'উর্দু'
elseif id == 'Q56356571fa' then
return 'নয়া ফার্সি'
elseif id == 'Q56356571tg' then
return 'তাজিক'
elseif id == 'Q58635pa' or id == 'Q58635pnb' then
return 'পাঞ্জাবি'
end
local label = wb.getLabel(id)
if label then
return label
end
local labels = wb.getEntity(id).labels
if not labels then
return id
end
for _, v in pairs(labels) do
if v and v.value then
return v.value
end
end
end
local function getReference( id, reference )
local out_id = nil
local url_value
if reference_cache[id] == nil then
local ref_text = references(reference, wb, mw_lang, i18n['content_lang_code'], i18n['wikipedia'])
if reference.snaks then
if reference.snaks['P248'] then
for _, snak in pairs(reference.snaks['P248']) do
if snak.datavalue and snak.datavalue.value.id == 'Q428' then -- কুরআন
ref_text = ustring.gsub(ref_text, 'নামক অনুচ্ছেদ', 'নং আয়াত')
break
end
end
end
if reference.snaks['P854'] then
local snak = reference.snaks['P854'][1]
if snak.datavalue then
url_value = snak.datavalue.value
end
end
end
if url_value then
ref_text = ref_text .. ', [' .. url_value .. ' সংযোগ]'
end
reference_cache[id] = ref_text
else
out_id = id
end
return {out_id, reference_cache[id]}
end
local function getEntity( id )
if entity_cache[id] == nil then
entity_cache[id] = wb.getEntity(id)
end
return entity_cache[id] ~= false and entity_cache[id] or nil
end
local function getLexemeLanguageCode(current_lexeme)
local lang_item_id = current_lexeme:getLanguage()
if lang_item_id == nil then
return nil
end
local lang_entity = getEntity(lang_item_id)
if lang_entity == nil then
return nil
end
for i, statement_property in ipairs({'P305','P424', 'P220'}) do -- আইইটিএফ ভাষা ট্যাগ, উইকিমিডিয়া ভাষা কোড, আইএসও ৬৩৯-৩
local statements = lang_entity:getBestStatements(statement_property)
if statements[1] then
return statements[1].mainsnak.datavalue.value
end
end
return nil
end
-- Return the first form of the lexeme which has exactly the given grammatical feature.
local function formWithSingleGrammaticalFeature( item_id )
for i = 1, #forms do
local grammaticalFeatures = forms[i]:getGrammaticalFeatures()
if #grammaticalFeatures == 1 and grammaticalFeatures[1] == item_id then
return forms[i]
end
end
return nil
end
local function getArticleLinkTemplate(frame, stmt_value)
local template = ''
local sitelink = getEntity(stmt_value):getSitelink(i18n['wikipedia'])
if sitelink then
template = frame:expandTemplate{
title=i18n['template_wikipedia'],
args={sitelink}
}
end
return template
end
local function getArticleLinks (frame, sense )
local article_links = ''
for i, stmt in pairs(sense:getAllStatements('P5137')) do -- এই অর্থের জন্য আইটেম
article_links = article_links .. getArticleLinkTemplate(frame, stmt.mainsnak.datavalue.value.id)
end
for i, stmt in pairs(sense:getAllStatements('P9970')) do -- এই অর্থের জন্য বিধেয়
article_links = article_links .. getArticleLinkTemplate(frame, stmt.mainsnak.datavalue.value.id)
end
return article_links
end
-- @TODO: Generalise
local function expandTemplateForProperty(frame, object, property, template)
local lemmas = {}
local n = 0
for _, stmt in pairs(object:getAllStatements(property)) do
local lex = wb.lexeme.splitLexemeId(stmt.mainsnak.datavalue.value.id)
if lex then
lex = getEntity(lex)
n = n + 1
lemmas[n] = lex:getLemma(lang_code)
end
end
if not lang_code or n == 0 then
return ''
end
-- Build args: first lang_code, then lemmas
local args = {lang_code}
for i = 1, n do
args[#args + 1] = lemmas[i]
end
return frame:expandTemplate{
title = template,
args = args
}
end
local function getExternalLinks( entity ) -- T418639
local external_links = {}
if entity.claims == nil then return external_links end
local formatter_urls = require('মডিউল:আভিধানিক উপাত্ত/urls').formatter_urls
for property_id, statements in pairs(entity.claims) do
local formatter_url = formatter_urls[property_id]
if formatter_url then
local property_source = wb.getBestStatements(property_id, 'P9073')
local source_name
if next(property_source) then
source_name = getLabel(property_source[1].mainsnak.datavalue.value.id)
or property_source[1].mainsnak.datavalue.value.id
else
source_name = getLabel(property_id) or property_id
end
for i = 1, #statements do
local stmt = statements[i]
if stmt.mainsnak.datavalue then
local formatted_link = ustring.gsub(
ustring.gsub(formatter_url, '$1', stmt.mainsnak.datavalue.value),
' ', '+'
)
table.insert(external_links,
'[' .. formatted_link .. ' ' .. source_name .. ']')
end
end
end
end
return external_links
end
p.getExternalLinks = getExternalLinks -- রেখে দিন যাতে ডিবাগিং সম্ভব হয়
local function termSpan( term )
local text = term[1]
local lang = term[2]
local dir = mw_lang.new( lang ):getDir()
local span = html.create( 'span' )
span:attr( 'lang', lang )
:attr( 'dir', dir )
:wikitext( text )
return tostring( span )
end
local function termLink( term, lang_qid )
local text = term[1]
local lang = term[2]
local dir = mw_lang.new( lang ):getDir()
local span = html.create( 'span' )
span:attr( 'lang', lang )
:attr( 'dir', dir )
:wikitext( '[[' .. text .. '#' .. getLabel(lang_qid) .. '|' .. text .. ']]' )
return tostring( span )
end
local function getLemmata( current_lexeme )
local lemma_string = ''
for i, rep in pairs(current_lexeme:getLemmas()) do
if lemma_string == '' then
lemma_string = termSpan(rep)
else
lemma_string = lemma_string .. '/' .. termSpan(rep)
end
end
return lemma_string
end
local function getLinkedLemmata( current_lexeme )
local lemma_string = ''
for i, rep in pairs(current_lexeme:getLemmas()) do
if lemma_string == '' then
lemma_string = termLink(rep, current_lexeme:getLanguage())
else
lemma_string = lemma_string .. '/' .. termLink(rep, current_lexeme:getLanguage())
end
end
return lemma_string
end
local function getExamples( current_lexeme, sense_id, references_seen )
local examples = html.create('dl')
local example_text, example_lang, example_form, example_str
for i, stmt in pairs(current_lexeme:getAllStatements('P5831')) do -- ব্যবহারের উদাহরণ
if stmt.qualifiers and stmt.qualifiers['P6072'] and stmt.qualifiers['P6072'][1].datavalue.value.id == sense_id then -- বিষয়ে লেক্সিমের অর্থ
example_text = ustring.gsub(stmt.mainsnak.datavalue.value.text, ' / ','<br/>')
example_lang = stmt.mainsnak.datavalue.value.language
local example_form_strs = {}
if stmt.qualifiers['P1810'] then
table.insert(example_form_strs, stmt.qualifiers['P1810'][1].datavalue.value)
elseif stmt.qualifiers['P5830'] then
example_form = getEntity(stmt.qualifiers['P5830'][1].datavalue.value.id) -- বিষয়ে লেক্সিমের রূপ
for _, rep in pairs(example_form:getRepresentations()) do
table.insert(example_form_strs, rep[1])
end
end
for i, example_form_str in pairs(example_form_strs) do
new_example_text = ustring.gsub(example_text, example_form_str, "'''" .. example_form_str .. "'''")
if new_example_text ~= example_text then
example_str = termSpan({new_example_text, example_lang})
break
end
new_example_text = example_text
end
if example_str == nil then
example_str = termSpan({example_text, example_lang})
end
local reference_text = ''
if stmt.references then
for j, reference in pairs(stmt.references) do
table.insert(references_seen, reference.hash)
local got_reference = getReference(reference.hash, reference)
reference_text = reference_text .. '\n\n' .. got_reference[2]
end
end
if example_str then
examples:tag('dd'):wikitext("''" .. example_str .. "''")
if reference_text ~= '' then
examples:done():tag('dd'):css('text-indent', '2em'):wikitext(reference_text)
end
end
end
end
for i, stmt in pairs(wb.getAllStatements(sense_id, 'P5831')) do -- ব্যবহারের উদাহরণ
example_text = ustring.gsub(stmt.mainsnak.datavalue.value.text, ' / ','<br/>')
example_lang = stmt.mainsnak.datavalue.value.language
local example_form_str = nil
if stmt.qualifers then
example_form = getEntity(stmt.qualifiers['P5830'][1].datavalue.value.id) -- বিষয়ে লেক্সিমের রূপ
if stmt.qualifiers['P1810'] then
example_form_str = stmt.qualifiers['P1810'][1].datavalue.value
end
end
if example_form and example_form_str == nil then
example_form_str = example_form:getRepresentation(i18n['content_lang_code'])
end
if example_form and example_form_str == nil then
example_form_str = example_form:getRepresentations()[1][1]
end
if example_form_str then
example_text = ustring.gsub(example_text, example_form_str, "'''" .. example_form_str .. "'''")
end
example_str = termSpan({example_text, example_lang})
local reference_text = ''
if stmt.references then
for j, reference in pairs(stmt.references) do
table.insert(references_seen, reference.hash)
local got_reference = getReference(reference.hash, reference)
reference_text = reference_text .. '\n\n' .. got_reference[2]
end
end
if example_str then
examples:tag('dd'):wikitext("''" .. example_str .. "''")
if reference_text ~= '' then
examples:done():tag('dd'):css('text-indent', '2em'):wikitext(reference_text)
end
end
end
return { tostring(examples) , references_seen }
end
-- This calls frame:preprocess() instead of :callParserFunction() because the latter does not work for Wikifunctions function calls yet (see https://www.wikifunctions.org/wiki/Wikifunctions:Embedded_function_calls).
local function callWikifunctionsFunction(args, frame)
return frame:preprocess('{{#function:' .. args .. '}}')
end
local function checkTitleCodePointInRange(title, start_point, end_point)
return ustring.find( title, '[' ..ustring.char(start_point) .. '-' .. ustring.char(end_point) .. ']' )
end
local function getLanguageForCategories( lang_id, current_page_title )
-- বিশেষ ভাষার জন্য
if lang_id == 'Q11051' then -- হিন্দি/উর্দু
if checkTitleCodePointInRange(current_page_title, 0x0600, 0x06ff) then -- উর্দু
lang_id = 'Q11051ur'
elseif checkTitleCodePointInRange(current_page_title, 0x0900, 0x097f) then -- হিন্দি
lang_id = 'Q11051hi'
end
elseif lang_id == 'Q58635' then -- পাঞ্জাবি
if checkTitleCodePointInRange(current_page_title, 0x0600, 0x06ff) then -- শাহমুখী
lang_id = 'Q58635pnb'
elseif checkTitleCodePointInRange(current_page_title, 0x0a00, 0x0a7f) then -- গুরুমুখী
lang_id = 'Q58635pa'
end
elseif lang_id == 'Q56356571' then -- নয়া ফার্সি ভাষা
if checkTitleCodePointInRange(current_page_title, 0x0600, 0x06ff) then -- ফার্সি (ইরান/আফগানিস্তান)
lang_id = 'Q56356571fa'
elseif checkTitleCodePointInRange(current_page_title, 0x0400, 0x04ff) then -- তাজিক
lang_id = 'Q56356571tg'
end
end
return lang_id
end
local function getOneStringForProperty(object, property)
local val
local stmts = object:getAllStatements(property)
if #stmts ~= 0 then
val = stmts[1].mainsnak.datavalue.value
end
return val
end
local function getTranslations(frame, senses, label) -- TODO: woefully incomplete until T185313 and T199887 are resolved
if #senses == 0 then
return nil
end
local all_translations = {}
for _, sense in pairs(senses) do
local translation_set = {}
local gloss = sense:getGloss('bn')
for _, stmt in pairs(sense:getAllStatements('P5972')) do
local translation = stmt.mainsnak.datavalue.value.id
local lexeme_id = wb.lexeme.splitLexemeId(translation)
local trans_lexeme = getEntity(lexeme_id)
local lang_name = getLabel(trans_lexeme:getLanguage())
table.insert(translation_set, lang_name .. ': ' .. frame:expandTemplate{title='t', args={getLexemeLanguageCode(trans_lexeme), getLemmata(trans_lexeme) .. '<br/>'}})
end
if #translation_set > 0 then
local block = frame:expandTemplate{ title = i18n['template_trans-top'], args = { gloss or label } }
block = block .. table.concat(translation_set, '\n') .. frame:expandTemplate{ title = i18n['template_trans-bottom'] }
table.insert(all_translations, block)
end
end
if #all_translations == 0 then
return nil
end
return '====' .. i18n['heading_translation'] .. '==== \n' .. table.concat(all_translations, '\n')
end
local createicon = function(langcode, entityID, propertyID)
langcode = langcode or ""
propertyID = propertyID or ""
local icon = " <span class='penicon autoconfirmed-show'>[["
-- " <span data-bridge-edit-flow='overwrite' class='penicon'>[[" -> enable Wikidata Bridge
.. "File:OOjs UI icon edit-ltr-progressive.svg |frameless |text-top |10px |alt="
.. i18n['edit_wikidata']
.. "|link=https://www.wikidata.org/entity/" .. entityID
if langcode ~= "" then icon = icon .. "?uselang=" .. langcode end
if propertyID ~= "" then icon = icon .. "#" .. propertyID end
icon = icon .. "|" .. i18n['edit_wikidata'] .. "]]</span>"
return icon
end
local function getMeanings( frame, args, current_lexeme, senses, references_seen, language_name)
local item_label
if #senses == 0 then
return {createicon(i18n['content_lang_code'], current_lexeme:getId()) .. "''" .. i18n['text_category_rfdef'] .. "''" .. i18n.tocatlink(i18n['category_rfdef']), references_seen}
end
local meanings = html.create( 'ol' )
local item_label_gloss_parts = {}
local idlinkset = {}
for i, sense in pairs(senses) do
local gloss_text_parts = {}
local main_gloss_text = frame:expandTemplate{
title=i18n['template_anchor'],
args={sense:getId()}
}
local specifiers = {}
for k, property_id in ipairs({'P6084', 'P6191', 'P9488'}) do -- অবস্থান যেখানে শব্দার্থ ব্যবহৃত, যে রীতিতে শব্দার্থ ব্যবহৃত হয়, যে ক্ষেত্রে ব্যবহৃত
for _, stmt in pairs(sense:getAllStatements(property_id)) do
local stmt_value = stmt.mainsnak.datavalue.value.id
local reference_text = ''
local refs = stmt.references
if refs then
for j, reference in pairs(refs) do
table.insert(references_seen, reference.hash)
local got_reference = getReference(reference.hash, reference)
reference_text = reference_text .. '\n\n' .. frame:extensionTag('ref', got_reference[2])
end
end
local val = getLabel(stmt_value)
table.insert(specifiers, val .. reference_text)
if property_id == 'P9488' then
table.insert(item_label_gloss_parts, i18n.tocatlink(lang_code .. ':' .. val))
end
end
if #specifiers > 0 then
main_gloss_text = main_gloss_text .. "(''" .. table.concat(specifiers, "'', ''") .. "'') "
end
end
local gloss = sense:getGloss( i18n['content_lang_code'] )
if gloss then
main_gloss_text = main_gloss_text .. wrapStringInWikilinks(gloss)
if gloss:match('^প্রদত্ত%s*(%S-)%s*নাম$') then -- given names
main_gloss_text = main_gloss_text .. i18n.tocatlink(language_name .. ' ' .. i18n['category_given_names'])
end
else
local other_gloss_text = nil
local other_gloss_lang = nil
for k, stmt in pairs(sense:getAllStatements('P5137')) do -- যদি 'এই অর্থের জন্য আইটেম' মানের বাংলা লেবেল থাকে
local stmt_value = stmt.mainsnak.datavalue.value.id
item_label = getLabel(stmt_value)
if item_label then
table.insert(item_label_gloss_parts, '[[:d:' .. stmt_value .. '|' .. item_label .. ']]')
end
end
if #item_label_gloss_parts > 0 then
other_gloss_text = table.concat(item_label_gloss_parts, '; ')
end
if other_gloss_text == nil then
for _, fallback_lang in ipairs(mw_lang.getFallbacksFor( i18n['content_lang_code'] )) do
if sense:getGloss( fallback_lang ) then
other_gloss_text, other_gloss_lang = sense:getGloss( fallback_lang )
end
end
if other_gloss_lang == nil then
local glosses = sense:getGlosses()
for _, gloss in pairs(glosses) do
other_gloss_text = gloss[1]
other_gloss_lang = gloss[2]
break
end
end
main_gloss_text = main_gloss_text .. other_gloss_text .. "<sup><em>" .. mw_lang.fetchLanguageName(other_gloss_lang, i18n['content_lang_code']) .. "</em></sup>"
else
main_gloss_text = main_gloss_text .. "''" .. other_gloss_text .. "''"
end
main_gloss_text = main_gloss_text .. i18n.tocatlink(i18n['category_rfdef_equivalent'])
end
local synonym = expandTemplateForProperty(frame, sense, 'P5973', i18n['template_synonym'])
if synonym ~= '' then
main_gloss_text = main_gloss_text .. ' <br/> ' .. synonym
end
local antonym = expandTemplateForProperty(frame, sense, 'P5974', i18n['template_antonym'])
if antonym ~= '' then
main_gloss_text = main_gloss_text .. ' <br/> ' .. antonym
end
local hypernym = expandTemplateForProperty(frame, sense, 'P6593', i18n['template_hypernym'])
if hypernym ~= '' then
main_gloss_text = main_gloss_text .. ' <br/> ' .. hypernym
end
if lex_cat == 'Q1084' or lex_cat == 'Q147276' then -- noun or proper noun
local demonym = expandTemplateForProperty(frame, sense, 'P6271', i18n['template_demonym-noun'])
main_gloss_text = main_gloss_text .. ' <br/> ' .. demonym
elseif lex_cat == 'Q34698' then
local demonym = expandTemplateForProperty(frame, sense, 'P6271', i18n['template_demonym-adj'])
main_gloss_text = main_gloss_text .. ' <br/> ' .. demonym
end
table.insert(gloss_text_parts, main_gloss_text .. createicon(i18n['content_lang_code'], sense:getId()))
for i, stmt in pairs(sense:getAllStatements('P8394')) do -- টিপ্পনীর উদ্ধৃতি
local gloss_quote = termSpan({stmt.mainsnak.datavalue.value.text, stmt.mainsnak.datavalue.value.language})
if stmt.references[1] then
local got_reference = getReference ( stmt.references[1].hash, stmt.references[1] )
gloss_quote = '"' .. gloss_quote .. '" ' .. got_reference[2]
end
table.insert(references_seen, stmt.references[1].hash)
table.insert(gloss_text_parts, frame:extensionTag('ref', gloss_quote))
end
for i, stmt in pairs(sense:getAllStatements('P1343')) do -- বর্ণিত উৎস
-- TODO: do away with making fake reference objects
local fake_reference = { ['snaks'] = {} }
fake_reference.snaks['P248'] = { [1] = stmt.mainsnak }
qualifiers_order = stmt['qualifiers-order']
if qualifiers_order then
for i, k in ipairs(qualifiers_order) do fake_reference.snaks[k] = stmt.qualifiers[k] end
end
fake_reference.hash = mw.hash.hashValue('sha3-512', serializeTable(fake_reference))
table.insert(references_seen, fake_reference.hash)
local got_reference = getReference(fake_reference.hash, fake_reference)
if got_reference[1] == nil then
table.insert(gloss_text_parts, frame:extensionTag('ref', got_reference[2], {name = fake_reference.hash}))
else
table.insert(gloss_text_parts, frame:extensionTag{name = 'ref', content='', args = {name = got_reference[1]}})
end
end
local first_sense_image = ''
local sense_images = sense:getAllStatements('P18')
if next(sense_images) then
first_sense_image = sense_images[1].mainsnak.datavalue.value
end
if first_sense_image ~= '' then
table.insert(gloss_text_parts, '[[চিত্র:' .. first_sense_image .. "|thumb|'''" .. getLemmata(current_lexeme) .. "'''—" .. main_gloss_text .. ']]')
end
local idlinks = getExternalLinks(sense)
if #idlinks > 0 then
local idlinktext = '<small>('
for _, idlink in pairs(idlinks) do
idlinktext = idlinktext .. idlink .. '\n'
end
idlinktext = idlinktext .. ')</small>'
table.insert(gloss_text_parts, idlinktext)
table.insert(idlinkset, idlinks)
end
local externallinks = getArticleLinks(frame, sense)
if externallinks ~= '' then
table.insert(gloss_text_parts, externallinks)
end
local new_notes = {}
local sense_keys = { sense:getId(), string.sub(sense:getId(), string.find(sense:getId(), '-')+1) }
for _, v in ipairs(sense_keys) do
if args[v] then
table.insert(new_notes, args[v])
end
end
if #new_notes > 0 then
for _, v in ipairs(new_notes) do
if i == 1 then
table.insert(gloss_text_parts, '<br/>' .. v)
else
table.insert(gloss_text_parts, v)
end
end
end
local examples, references_seen = unpack(getExamples( current_lexeme, sense:getId(), references_seen ))
local gloss_text = table.concat(gloss_text_parts, '\n')
meanings:tag('li'):wikitext(gloss_text):wikitext(examples)
end
return {meanings, references_seen, idlinkset}
end
local function getPronunciationBaseForm( lang_name, lex_cat)
local base_form = nil
-- (!) অন্য ভাষার শব্দের যদি অন্য রকম মূল ফর্ম থাকে সেগুলো এখানে নতুন if বিবৃতি দিয়ে যোগ করা যায়।
if lang_name == 'বাংলা' then
if lex_cat == 'Q1084' then -- বিশেষ্য
base_form = formWithSingleGrammaticalFeature( 'Q131105' ) -- কর্তৃকারক
elseif lex_cat == 'Q24905' then -- ক্রিয়া
base_form = formWithSingleGrammaticalFeature( 'Q1350145' ) -- ক্রিয়া বিশেষ্য
end
end
if base_form == nil then
for i, form in pairs(forms) do
base_form = form
break
end
end
return base_form
end
local function getCombines( current_lexeme, frame )
local combines = ''
local index_mappings = {}
for i, stmt in pairs(current_lexeme:getAllStatements('P5238')) do
if stmt.qualifiers and stmt.qualifiers['P1545'] then -- ক্রম
local current_index = tonumber(stmt.qualifiers['P1545'][1].datavalue.value)
index_mappings[current_index] = stmt
end
end
if #index_mappings ~= 0 then
for i, stmt in ipairs(index_mappings) do
if stmt.mainsnak.datavalue then
local part_lexeme_id = stmt.mainsnak.datavalue.value.id
local part_lexeme = getEntity(part_lexeme_id)
local current_substring = getLinkedLemmata(part_lexeme)
local part_etymology = getEtymology(part_lexeme, frame, 'partial')
if part_etymology ~= '' and part_etymology then
current_substring = current_substring .. ' (← ' .. part_etymology .. ')'
end
if combines == '' then
combines = current_substring
else -- @TODO: This shoukd use the 'affix' and 'compound' templates instead.
combines = combines .. ' + ' .. current_substring
end
end
end
end
return combines
end
function getRoots( current_lexeme )
local stmts = current_lexeme:getAllStatements('P5920')
if #stmts == 0 then return '', '', '' end
local root_lexeme = getEntity(stmts[1].mainsnak.datavalue.value.id)
return getLexemeLanguageCode(root_lexeme), '√' .. getLinkedLemmata(root_lexeme), root_lexeme:getLemma('ar')
end
function getEtymology( current_lexeme, frame, mode )
-- @TODO: Fix the etymology chains that are not possible to render
local etymology = ''
local current_combines = getCombines(current_lexeme, frame)
local root_lang, current_roots, root_str = getRoots(current_lexeme)
if mode ~= 'partial' and root_str then
--frame:expandTemplate{title=i18n['template_root'], args={lang_code, root_lang, root_str}}
end
local stmts = current_lexeme:getAllStatements('P5191')
local new_etymology_string
if #stmts == 0 then
if current_roots ~= '' and current_combines ~= '' and current_roots then
return current_roots .. '<br/>(' .. current_combines .. ')'
elseif current_roots ~= '' then
if lang_code == 'ar' and mode ~= 'partial' and root_str ~= matched_lemma then
return frame:expandTemplate{title=i18n['template_ar-rootbox'], args={root_str}}
else
return current_roots
end
else
return current_combines
end
end
local origin_lexeme_string
for i, stmt in pairs(stmts) do
local origin_lexeme_dv = stmt.mainsnak.datavalue -- If this is nil, the origin lexeme is not known.
if origin_lexeme_dv then
local origin_lexeme = getEntity(origin_lexeme_dv.value.id)
local origin_lexeme_lang = getLabel(origin_lexeme:getLanguage())
local sitelink = i18n.wplink(origin_lexeme:getLanguage(), origin_lexeme_lang, wb)
if sitelink ~= '' then
origin_lexeme_string = getLinkedLemmata(origin_lexeme) .. ' (' .. sitelink .. ')'
else
origin_lexeme_string = getLinkedLemmata(origin_lexeme) .. ' (' .. origin_lexeme_lang .. ')'
end
if stmt.qualifiers and stmt.qualifiers['P5886'] then
local mode_of_derivation = stmt.qualifiers['P5886'][1].datavalue.value.id
if mode_of_derivation == 'Q1345001' then
-- @TODO: Add support for showing gender
origin_lexeme_string = frame:expandTemplate{title=i18n['template_borrowed'], args={lang_code, getLexemeLanguageCode(origin_lexeme), getLemmata(origin_lexeme), pos=getLabel(lex_cat)}} .. ' ' .. i18n['etymology_borrowing']
elseif mode_of_derivation == 'Q845079' then
origin_lexeme_string = ustring.gsub(i18n['etymology_learned_borrowing'], '$1', origin_lexeme_string)
elseif mode_of_derivation == 'Q56611986' then
origin_lexeme_string = frame:expandTemplate{title=i18n['template_inherited'], args={lang_code, getLexemeLanguageCode(origin_lexeme), getLemmata(origin_lexeme), pos=getLabel(lex_cat)}} .. ' ' .. i18n['etymology_inheritance']
elseif mode_of_derivation == 'Q189743' then
origin_lexeme_string = frame:expandTemplate{title=i18n['template_ellipsis'], args={lang_code, getLemmata(origin_lexeme)}} .. ' ' .. i18n['etymology_ellipsis']
end
end
local origin_origin = getEtymology(origin_lexeme, frame)
if origin_origin ~= '' and origin_origin then
new_etymology_string = origin_lexeme_string .. ' ← ' .. origin_origin
else
new_etymology_string = origin_lexeme_string
end
end
if etymology == '' then
etymology = new_etymology_string
elseif origin_lexeme_string and etymology then
etymology = etymology .. ' ' .. origin_lexeme_string
elseif origin_lexeme_string and etymology == nil then
etymology = origin_lexeme_string
end
end
if current_roots ~= '' and etymology and current_roots then
etymology = etymology .. ' ' .. current_roots
elseif current_roots ~= '' and etymology == nil then
etymology = current_roots
end
if current_combines ~= '' and etymology then
etymology = etymology .. '<br/>(' .. current_combines .. ')'
end
return etymology
end
local function pronunciationBlock(block, value)
return '* ' .. i18n['text_' .. block] .. ' ' .. value
end
local function getPronunciation( frame, current_lexeme, lang_name, lex_cat )
local pronunciations = {}
local base_form = getPronunciationBaseForm(lang_name, lex_cat )
if base_form then
for i, stmt in pairs(base_form:getAllStatements('P443')) do -- উচ্চারণের অডিও
local pronunciation_file = stmt.mainsnak.datavalue.value
local specifier_text = ''
local specifiers = {}
if stmt.qualifiers then
for k, property_id in ipairs({'P5237'}) do -- উচ্চারণের ধরন
local qual = stmt.qualifiers[property_id]
if qual then
for _, qual in pairs(qual) do
local stmt_value = qual.datavalue.value.id
table.insert(specifiers, getLabel(stmt_value))
end
end
end
end
if #specifiers > 0 then
specifier_text = table.concat(specifiers, "'', ''")
end
local audio_text
if specifier_text ~= '' then
audio_text = i18n['text_audio'] .. ' (' .. specifier_text .. ')'
else
audio_text = i18n['text_audio']
end
table.insert(pronunciations, '* ' .. frame:expandTemplate{
title= i18n['template_audio'],
args = {lang_name, pronunciation_file, audio_text}
})
end
local ipa_transcription = base_form:getAllStatements('P898') -- - আধ্বব প্রতিলিপিকরণ
local iso15919_transcription = getOneStringForProperty(base_form, 'P5825') -- আইএসও ১৫৯১৯ প্রতিলিপিকরণ
local itrans = getOneStringForProperty(base_form, 'P8881') -- ITRANS
local iast = getOneStringForProperty(base_form, 'P7581') -- আইএএসটি প্রতিলিপিকরণ
local xsampa = getOneStringForProperty(base_form, 'P2859')
-- @TODO: অডিও ও প্রতিলিপিকরণ দুটোই থাকলে সেই ক্ষেত্রে একটার ঠিক পরেই আরেকটা দেখানো উচিত
if #ipa_transcription ~= 0 then
for i, stmt in pairs(ipa_transcription) do
local ipa_text = stmt.mainsnak.datavalue.value
local specifier_text = ''
local specifiers = {}
if stmt.qualifiers then
for k, property_id in ipairs({'P5237'}) do -- উচ্চারণের ধরন
for l, qual in ipairs(stmt.qualifiers[property_id]) do
table.insert(specifiers, getLabel( qual.datavalue.value.id ))
end
end
end
if #specifiers > 0 then
specifier_text = "(''" .. table.concat(specifiers, "'', ''") .. "'') "
end
local syllable_count
if lang_code == 'tr' then
syllable_count = i18n['text_syllable_count'] .. ' ' .. callWikifunctionsFunction('Z10029|' .. ipa_text, frame)
else
syllable_count = i18n['text_syllable_count'] .. ' ' .. callWikifunctionsFunction('Z30837|' .. ipa_text, frame)
end
table.insert(pronunciations, '* ' .. specifier_text .. frame:expandTemplate{
title= i18n['template_ipa'],
args = {lang_name, ipa_text}
} .. '\n* ' .. syllable_count)
end
-- The following checks are ordered based on which one is expected to be true in a higher number of cases.
elseif lang_name == 'বাংলা' then
table.insert(pronunciations, '* ' .. frame:expandTemplate{
title='bn-IPA',
})
elseif lang_name == 'আরবি' then
local lemma = current_lexeme:getLemma('ar')
table.insert(pronunciations, '* ' .. frame:expandTemplate{
title='ar-IPA',
args={lemma}
})
elseif lang_name == 'ফালা' then
table.insert(pronunciations, '* ' .. frame:expandTemplate{
title='fax-pron',
})
elseif lang_code == 'fi' then
table.insert(pronunciations, '* ' .. frame:expandTemplate{
title='fi-IPA',
})
elseif lang_code == 'ko' then
table.insert(pronunciations, '* ' .. frame:expandTemplate{
title='ko-IPA',
})
end
if iso15919_transcription then
table.insert(pronunciations, pronunciationBlock('iso15919', iso15919_transcription))
end
if itrans then
table.insert(pronunciations, pronunciationBlock('itrans', itrans))
end
if iast then
table.insert(pronunciations, pronunciationBlock('iast', iast))
end
if xsampa then
table.insert(pronunciations, pronunciationBlock('xsampa', xsampa))
end
end -- {{আধ্বব|en|/ˈɪntəvjuː/}}
return table.concat(pronunciations, '\n')
end
local function getAlternativeSpellings( current_lexeme )
local alt_spellings = {}
for i, stmt in pairs(current_lexeme:getAllStatements('P11577')) do -- বিকল্প বানান
if stmt.mainsnak.datavalue then
table.insert(alt_spellings, '* ' .. getLinkedLemmata(getEntity(stmt.mainsnak.datavalue.value.id)))
end
end
return table.concat(alt_spellings, '\n')
end
local function heading_level(text, level)
local heading_delimiter = string.rep('=', level)
return heading_delimiter .. ' ' .. text .. ' ' .. heading_delimiter
end
function get_any_notes(sections, args, keys)
local notes = {}
for i, v in ipairs(keys) do
if args[v] then
table.insert(notes, args[v])
end
end
return notes
end
function add_specific_notes(sections, notes)
for i, v in ipairs(notes) do
table.insert(sections, v)
end
end
local function add_any_notes(sections, args, keys)
for i, v in ipairs(keys) do
if args[v] then
table.insert(sections, args[v])
end
end
end
local function getMatchingLemmaForPageTitle(lexeme, title)
local lemmas = lexeme:getLemmas()
local matched_lemma
for _, lemma_entry in ipairs(lemmas) do
local lemma = lemma_entry[1]
if lemma == title then
matched_lemma = lemma
break
end
end
if matched_lemma == nil and lang_code == 'ar' then -- Arabic lemmas do not match the title of the entry because those are written with different characters stripped out on Wiktionary. This could be changed to call stripDiactritics() from [[Module:languages]] in the future if desired.
matched_lemma = lexeme:getLemma('ar')
end
return matched_lemma
end
local function buildLanguageAgnosticInflectionTable()
local has_image = false
local form_images = {}
for i, form in ipairs(forms) do
local form_image = form:getAllStatements('P7407')
if next(form_image) then
form_images[i] = form_image[1].mainsnak.datavalue.value
has_image = true
end
end
local table_class = "wikitable mw-collapsible sortable"
if not has_image then
table_class = table_class .. " mw-collapsed"
end
local text = "{| class='" .. table_class .. "' style='border:solid 1px rgb(80%,80%,100%); text-align:center;'\n"
text = text .. "|+ " .. i18n['heading_inflection_table'] .. "\n"
text = text .. "|- \n"
text = text .. "! " .. i18n['heading_form'] .. " !! " .. i18n['heading_grammatical_features']
if has_image then
text = text .. " !! " .. (i18n['heading_image'])
end
text = text .. " \n"
for i, form in ipairs(forms) do
local rep = form:getRepresentations()
local feat = form:getGrammaticalFeatures()
local rep_text = ""
for j, r in pairs(rep) do
if rep_text == "" then
rep_text = r[1]
else
rep_text = rep_text .. " / " .. r[1]
end
end
local feat_text = ""
if feat then
for j, f in ipairs(feat) do
local label = getLabel(f) or f
if feat_text == "" then
feat_text = label
else
feat_text = feat_text .. ", " .. label
end
end
end
text = text .. "|-\n"
text = text .. "| " .. (rep_text ~= "" and rep_text or "—")
text = text .. " || " .. (feat_text ~= "" and feat_text or "—")
if has_image then
local image_cell = "—"
if form_images[i] then
image_cell = "[[চিত্র:" .. form_images[i] .. "|50px]]"
end
text = text .. " || " .. image_cell
end
text = text .. "\n"
end
text = text .. "|}"
return text
end
function p.all( frame )
local args = getArgs(frame)
local lexeme_id = args[1]
local current_lexeme = getEntity(lexeme_id)
local current_language = current_lexeme:getLanguage()
local senses = current_lexeme:getSenses()
local add_heading = true
forms = current_lexeme:getForms()
if args[2] then
local val = mw.text.trim(tostring(args[2]))
if val == "false" or val == "0" or val == "না" then
add_heading = false
end
end
local references_seen = {}
local sections = {}
local title = mw.title.getCurrentTitle().text
local lang_category = getLanguageForCategories(current_language, title)
local lang_name = getLabel(lang_category)
if add_heading == true then
local lang_heading = "== " .. lang_name .. " =="
table.insert(sections, lang_heading)
end
matched_lemma = getMatchingLemmaForPageTitle(current_lexeme, title)
lex_cat = current_lexeme:getLexicalCategory()
lang_code = getLexemeLanguageCode(current_lexeme)
local cat = i18n.lang_category(getLabel(lex_cat), lang_name)
local lex_cat_template
if cat then
table.insert(sections, '===' .. getLabel(lex_cat) .. cat .. frame:expandTemplate{
title = i18n['template_anchor'],
args = { lexeme_id }
} .. '===')
table.insert(sections, frame:expandTemplate{
title= i18n['template_lexeme'],
args = {lexeme_id}
})
add_any_notes(sections, args, i18n['manual_category'])
local etymology = getEtymology( current_lexeme, frame )
if etymology ~= '' and etymology then
table.insert(sections, heading_level(i18n['heading_etymology'], 4))
table.insert(sections, tostring(etymology))
end
add_any_notes(sections, args, i18n['manual_etymology'])
local pronunciation = getPronunciation( frame, current_lexeme, lang_name, lex_cat )
if pronunciation ~= '' then
table.insert(sections, heading_level(i18n['heading_pronunciation'], 4))
table.insert(sections, tostring(pronunciation))
end
add_any_notes(sections, args, i18n['manual_pronunciation'])
local alternative_spellings = getAlternativeSpellings( current_lexeme )
if alternative_spellings ~= '' then
table.insert(sections, heading_level(i18n['heading_alternative_spellings'], 4))
table.insert(sections, alternative_spellings)
end
if lang_code and lang_code ~= 'ksy' and lang_code ~= 'rkt' then -- Skip for Kharia Thar, Rangpuri
if lex_cat == 'Q34698' then -- বিশেষণ
if lang_code == 'en' or lang_code == 'bn' then
if #forms <= 1 then
lex_cat_template = frame:expandTemplate{title= lang_code .. '-বিশেষণ'}
end
else
lex_cat_template = safeExpand(frame, lang_code .. '-adj')
if not lex_cat_template then
lex_cat_template = safeExpand(frame, lang_code .. '-বিশেষণ')
end
end
elseif lex_cat == 'Q1084' then -- @TODO: Also check for plural forms
local gender
local stmts = current_lexeme:getAllStatements('P5185') -- ব্যাকরণগত লিঙ্গ
if #stmts ~= 0 then
if #stmts == 1 then
local gender_qid = stmts[1].mainsnak.datavalue.value.id
if gender_qid == 'Q499327' then
gender = 'm'
elseif gender_qid == 'Q1775415' then
gender = 'f'
elseif gender_qid == 'Q1775461' then
gender = 'n'
elseif gender_qid == 'Q1305037' then
gender = 'c'
end
end
else
for i, stmt in pairs(stmts) do
local qid = stmts[i].mainsnak.datavalue.value.id
if qid == 'Q499327' then
gender = gender .. 'm'
elseif qid == 'Q1775415' then
gender = gender .. 'f'
end
end
end
-- The following checks are ordered based on which one is expected to be true in a higher number of cases.
if current_language == 'Q13955' then
if matched_lemma then
lex_cat_template = safeExpand(frame, {title='ar-noun', args={matched_lemma,gender}})
else
lex_cat_template = frame:expandTemplate{title='ar-noun', args={nil,gender}}
end
elseif current_language == 'Q29919' then
lex_cat_template = frame:expandTemplate{title='arz-noun', args={g=gender}}
elseif current_language == 'Q397' then
if matched_lemma then
lex_cat_template = frame:expandTemplate{title='la-noun', args={matched_lemma,g=gender}}
end
elseif current_language == 'Q11059' then
lex_cat_template = frame:expandTemplate{title='sa-noun', args={g=gender}}
elseif current_language ~= 'Q1860' then -- These templates require the gender to be passed as the 1st argument.
lex_cat_template = safeExpand(frame, lang_code .. i18n['noun_template_suffix'], {gender})
if not lex_cat_template then
lex_cat_template = safeExpand(frame, lang_code .. i18n['noun_template_suffix_fallback'], {gender})
end
end
end
end
-- elseif lex_cat == 'Q147276' then
-- lex_cat_template = safeExpand(frame, lang_code .. '-proper noun', {gender})
-- if not lex_cat_template then
-- lex_cat_template = safeExpand(frame, lang_code .. '-নামবাচক বিশেষ্য', {gender})
-- end
end
if lex_cat_template then
table.insert(sections, lex_cat_template)
else
if matched_lemma then
table.insert(sections, heading_level(matched_lemma, 4))
else
table.insert(sections, i18n.tocatlink(i18n['category_no_matching_lemma']))
end
end
local meanings, references_seen, sense_extlinks = unpack(getMeanings( frame, args, current_lexeme, senses, references_seen, lang_name))
table.insert(sections, tostring(meanings))
add_any_notes(sections, args, i18n['manual_meaning'])
local instance_of = current_lexeme:getAllStatements('P31') -- সত্ত্বার ধরন
if #instance_of ~= 0 then
local instance_of_entity = instance_of[1].mainsnak.datavalue.value.id
if instance_of_entity == 'Q40437546' or instance_of_entity == 'Q120831827' or instance_of_entity == 'Q120717979' or instance_of_entity == 'Q124476844' then -- @TODO: generalise this so all types of roots are shown
table.insert(sections, i18n['text_instance_of'] .. ' ' .. getLabel(instance_of_entity))
elseif instance_of_entity == 'Q376431' then -- বর্ণের নাম
table.insert(sections, i18n.tocatlink(lang_code .. ':রং'))
end
end
local translations = getTranslations(frame, senses, item_label)
if translations then
table.insert(sections, translations)
end
-- (!) বিশেষ ভাষার বিভক্তির সারণি যদি থাকে সেগুলো এখানে নতুন if বিবৃতি যোগ করা যায়।
if next(forms) then
if current_language == 'Q9610' then -- বাংলা
if lex_cat == 'Q24905' then
local conjTable = require('মডিউল:আভিধানিক উপাত্ত/Q9610').getConjTable(frame, forms)
table.insert(sections, conjTable)
elseif lex_cat == 'Q1084' then
table.insert(sections, callWikifunctionsFunction('Z33243|' .. lexeme_id .. '|', frame))
elseif lex_cat == 'Q34698' then
if #forms > 1 then
table.insert(sections, buildLanguageAgnosticInflectionTable())
end
end
--elseif current_language == 'Q13955' then -- আরবি
-- if lex_cat == 'Q1084' then
-- table.insert(sections, frame:expandTemplate{title='ar-decl-noun', args={lemma}})
-- end
elseif current_language == 'Q188' then -- জার্মান
if lex_cat == 'Q1084' then
table.insert(sections, callWikifunctionsFunction('Z28602|' .. lexeme_id .. '|', frame))
end
else
if #forms > 1 then
table.insert(sections, buildLanguageAgnosticInflectionTable())
end
end
end
if lex_cat == 'Q134830' then
table.insert(sections, frame:expandTemplate{title='prefixsee', args={lang_code}})
elseif lex_cat == 'Q102047' then
table.insert(sections, frame:expandTemplate{title='suffixsee', args={lang_code}})
-- elseif lex_cat == 'Q111029' then
-- table.insert(sections, frame:expandTemplate{title='rootsee', args={'+', lang_code, matched_lemma}})
end
local reference_notes = get_any_notes(sections, args, i18n['manual_reference'])
if #references_seen > 0 or #reference_notes > 0 then
table.insert(sections, heading_level(i18n['heading_references'], 4))
table.insert(sections, frame:extensionTag('references'))
add_specific_notes(sections, reference_notes)
end
local external_link_table = getExternalLinks ( current_lexeme )
if #external_link_table > 0 then
local external_links = '* ' .. table.concat(external_link_table, '\n* ')
table.insert(sections, heading_level(i18n['heading_external_links'], 4))
table.insert(sections, external_links)
end
add_any_notes(sections, args, i18n['manual_external_link'])
if #references_seen == 0 and #reference_notes == 0 and sense_extlinks and #sense_extlinks == 0 and #external_link_table == 0 and #get_any_notes(sections, args, i18n['manual_external_link']) == 0 then
table.insert(sections, i18n.rfref_category(lang_name))
end
return table.concat(sections,"\n\n")
end
return p
iofttka6n5dokuqdi4w3gopoljaw8ue
509672
509670
2026-06-04T11:27:52Z
Redmin
6857
509672
Scribunto
text/plain
local p = {}
local i18n = require('মডিউল:আভিধানিক উপাত্ত/i18n')
local references = require('মডিউল:উইকিউপাত্ত তথ্যসূত্র বিন্যাসকরণ').format
local getArgs = require('Module:Arguments').getArgs
local wb = mw.wikibase
local ustring = mw.ustring
local html = mw.html
local mw_lang = mw.language
local entity_cache = {}
local reference_cache = {}
local forms
local lang_code
local lex_cat
local matched_lemma
local function wrapStringInWikilinks(str)
local exceptions = i18n.nolinks
local result = str:gsub('(%S+)', function(token)
local word, trailing_char = token:match('^(.-)([;,]*)$')
local wrapped = word:gsub('[^(-/]+', function(part)
if exceptions[part] then
return part
else
return '[[' .. part .. '#' .. i18n['content_lang_name'] .. '|' .. part .. ']]'
end
end)
return wrapped .. trailing_char
end)
return result
end
local function serializeTable(val, name, skipnewlines, depth) -- https://stackoverflow.com/a/6081639
skipnewlines = skipnewlines or false
depth = depth or 0
local tmp = string.rep(" ", depth)
if name then tmp = tmp .. name .. " = " end
if type(val) == "table" then
tmp = tmp .. "{" .. (not skipnewlines and "\n" or "")
for k, v in pairs(val) do
tmp = tmp .. serializeTable(v, k, skipnewlines, depth + 1) .. "," .. (not skipnewlines and "\n" or "")
end
tmp = tmp .. string.rep(" ", depth) .. "}"
elseif type(val) == "number" then
tmp = tmp .. tostring(val)
elseif type(val) == "string" then
tmp = tmp .. string.format("%q", val)
elseif type(val) == "boolean" then
tmp = tmp .. (val and "true" or "false")
else
tmp = tmp .. "\"[inserializeable datatype:" .. type(val) .. "]\""
end
return tmp
end
-- Use this to safely expand templates when you are not sure that they exist.
local function safeExpand(frame, title, args)
local _, result = pcall(function()
return frame:expandTemplate{ title = title, args = args }
end)
if result:find('does not exist') then -- expandTemplate{} doesn't seem to throw any error that can be handled with pcall() so string search is the only viable option.
return nil
end
return result
end
-- Use this function to get the label of an item even if that item does not have any label in the wiki's content language or English.
local function getLabel(id)
if id == 'Q11051hi' then
return 'হিন্দি'
elseif id == 'Q11051ur' then
return 'উর্দু'
elseif id == 'Q56356571fa' then
return 'নয়া ফার্সি'
elseif id == 'Q56356571tg' then
return 'তাজিক'
elseif id == 'Q58635pa' or id == 'Q58635pnb' then
return 'পাঞ্জাবি'
end
local label = wb.getLabel(id)
if label then
return label
end
local labels = wb.getEntity(id).labels
if not labels then
return id
end
for _, v in pairs(labels) do
if v and v.value then
return v.value
end
end
end
local function getReference( id, reference )
local out_id = nil
local url_value
if reference_cache[id] == nil then
local ref_text = references(reference, wb, mw_lang, i18n['content_lang_code'], i18n['wikipedia'])
if reference.snaks then
if reference.snaks['P248'] then
for _, snak in pairs(reference.snaks['P248']) do
if snak.datavalue and snak.datavalue.value.id == 'Q428' then -- কুরআন
ref_text = ustring.gsub(ref_text, 'নামক অনুচ্ছেদ', 'নং আয়াত')
break
end
end
end
if reference.snaks['P854'] then
local snak = reference.snaks['P854'][1]
if snak.datavalue then
url_value = snak.datavalue.value
end
end
end
if url_value then
ref_text = ref_text .. ', [' .. url_value .. ' সংযোগ]'
end
reference_cache[id] = ref_text
else
out_id = id
end
return {out_id, reference_cache[id]}
end
local function getEntity( id )
if entity_cache[id] == nil then
entity_cache[id] = wb.getEntity(id)
end
return entity_cache[id] ~= false and entity_cache[id] or nil
end
local function getLexemeLanguageCode(current_lexeme)
local lang_item_id = current_lexeme:getLanguage()
if lang_item_id == nil then
return nil
end
local lang_entity = getEntity(lang_item_id)
if lang_entity == nil then
return nil
end
for i, statement_property in ipairs({'P305','P424', 'P220'}) do -- আইইটিএফ ভাষা ট্যাগ, উইকিমিডিয়া ভাষা কোড, আইএসও ৬৩৯-৩
local statements = lang_entity:getBestStatements(statement_property)
if statements[1] then
local code = statements[1].mainsnak.datavalue.value
if code ~= 'hr' then
return statements[1].mainsnak.datavalue.value
else
return 'sh'
end
end
end
return nil
end
-- Return the first form of the lexeme which has exactly the given grammatical feature.
local function formWithSingleGrammaticalFeature( item_id )
for i = 1, #forms do
local grammaticalFeatures = forms[i]:getGrammaticalFeatures()
if #grammaticalFeatures == 1 and grammaticalFeatures[1] == item_id then
return forms[i]
end
end
return nil
end
local function getArticleLinkTemplate(frame, stmt_value)
local template = ''
local sitelink = getEntity(stmt_value):getSitelink(i18n['wikipedia'])
if sitelink then
template = frame:expandTemplate{
title=i18n['template_wikipedia'],
args={sitelink}
}
end
return template
end
local function getArticleLinks (frame, sense )
local article_links = ''
for i, stmt in pairs(sense:getAllStatements('P5137')) do -- এই অর্থের জন্য আইটেম
article_links = article_links .. getArticleLinkTemplate(frame, stmt.mainsnak.datavalue.value.id)
end
for i, stmt in pairs(sense:getAllStatements('P9970')) do -- এই অর্থের জন্য বিধেয়
article_links = article_links .. getArticleLinkTemplate(frame, stmt.mainsnak.datavalue.value.id)
end
return article_links
end
-- @TODO: Generalise
local function expandTemplateForProperty(frame, object, property, template)
local lemmas = {}
local n = 0
for _, stmt in pairs(object:getAllStatements(property)) do
local lex = wb.lexeme.splitLexemeId(stmt.mainsnak.datavalue.value.id)
if lex then
lex = getEntity(lex)
n = n + 1
lemmas[n] = lex:getLemma(lang_code)
end
end
if not lang_code or n == 0 then
return ''
end
-- Build args: first lang_code, then lemmas
local args = {lang_code}
for i = 1, n do
args[#args + 1] = lemmas[i]
end
return frame:expandTemplate{
title = template,
args = args
}
end
local function getExternalLinks( entity ) -- T418639
local external_links = {}
if entity.claims == nil then return external_links end
local formatter_urls = require('মডিউল:আভিধানিক উপাত্ত/urls').formatter_urls
for property_id, statements in pairs(entity.claims) do
local formatter_url = formatter_urls[property_id]
if formatter_url then
local property_source = wb.getBestStatements(property_id, 'P9073')
local source_name
if next(property_source) then
source_name = getLabel(property_source[1].mainsnak.datavalue.value.id)
or property_source[1].mainsnak.datavalue.value.id
else
source_name = getLabel(property_id) or property_id
end
for i = 1, #statements do
local stmt = statements[i]
if stmt.mainsnak.datavalue then
local formatted_link = ustring.gsub(
ustring.gsub(formatter_url, '$1', stmt.mainsnak.datavalue.value),
' ', '+'
)
table.insert(external_links,
'[' .. formatted_link .. ' ' .. source_name .. ']')
end
end
end
end
return external_links
end
p.getExternalLinks = getExternalLinks -- রেখে দিন যাতে ডিবাগিং সম্ভব হয়
local function termSpan( term )
local text = term[1]
local lang = term[2]
local dir = mw_lang.new( lang ):getDir()
local span = html.create( 'span' )
span:attr( 'lang', lang )
:attr( 'dir', dir )
:wikitext( text )
return tostring( span )
end
local function termLink( term, lang_qid )
local text = term[1]
local lang = term[2]
local dir = mw_lang.new( lang ):getDir()
local span = html.create( 'span' )
span:attr( 'lang', lang )
:attr( 'dir', dir )
:wikitext( '[[' .. text .. '#' .. getLabel(lang_qid) .. '|' .. text .. ']]' )
return tostring( span )
end
local function getLemmata( current_lexeme )
local lemma_string = ''
for i, rep in pairs(current_lexeme:getLemmas()) do
if lemma_string == '' then
lemma_string = termSpan(rep)
else
lemma_string = lemma_string .. '/' .. termSpan(rep)
end
end
return lemma_string
end
local function getLinkedLemmata( current_lexeme )
local lemma_string = ''
for i, rep in pairs(current_lexeme:getLemmas()) do
if lemma_string == '' then
lemma_string = termLink(rep, current_lexeme:getLanguage())
else
lemma_string = lemma_string .. '/' .. termLink(rep, current_lexeme:getLanguage())
end
end
return lemma_string
end
local function getExamples( current_lexeme, sense_id, references_seen )
local examples = html.create('dl')
local example_text, example_lang, example_form, example_str
for i, stmt in pairs(current_lexeme:getAllStatements('P5831')) do -- ব্যবহারের উদাহরণ
if stmt.qualifiers and stmt.qualifiers['P6072'] and stmt.qualifiers['P6072'][1].datavalue.value.id == sense_id then -- বিষয়ে লেক্সিমের অর্থ
example_text = ustring.gsub(stmt.mainsnak.datavalue.value.text, ' / ','<br/>')
example_lang = stmt.mainsnak.datavalue.value.language
local example_form_strs = {}
if stmt.qualifiers['P1810'] then
table.insert(example_form_strs, stmt.qualifiers['P1810'][1].datavalue.value)
elseif stmt.qualifiers['P5830'] then
example_form = getEntity(stmt.qualifiers['P5830'][1].datavalue.value.id) -- বিষয়ে লেক্সিমের রূপ
for _, rep in pairs(example_form:getRepresentations()) do
table.insert(example_form_strs, rep[1])
end
end
for i, example_form_str in pairs(example_form_strs) do
new_example_text = ustring.gsub(example_text, example_form_str, "'''" .. example_form_str .. "'''")
if new_example_text ~= example_text then
example_str = termSpan({new_example_text, example_lang})
break
end
new_example_text = example_text
end
if example_str == nil then
example_str = termSpan({example_text, example_lang})
end
local reference_text = ''
if stmt.references then
for j, reference in pairs(stmt.references) do
table.insert(references_seen, reference.hash)
local got_reference = getReference(reference.hash, reference)
reference_text = reference_text .. '\n\n' .. got_reference[2]
end
end
if example_str then
examples:tag('dd'):wikitext("''" .. example_str .. "''")
if reference_text ~= '' then
examples:done():tag('dd'):css('text-indent', '2em'):wikitext(reference_text)
end
end
end
end
for i, stmt in pairs(wb.getAllStatements(sense_id, 'P5831')) do -- ব্যবহারের উদাহরণ
example_text = ustring.gsub(stmt.mainsnak.datavalue.value.text, ' / ','<br/>')
example_lang = stmt.mainsnak.datavalue.value.language
local example_form_str = nil
if stmt.qualifers then
example_form = getEntity(stmt.qualifiers['P5830'][1].datavalue.value.id) -- বিষয়ে লেক্সিমের রূপ
if stmt.qualifiers['P1810'] then
example_form_str = stmt.qualifiers['P1810'][1].datavalue.value
end
end
if example_form and example_form_str == nil then
example_form_str = example_form:getRepresentation(i18n['content_lang_code'])
end
if example_form and example_form_str == nil then
example_form_str = example_form:getRepresentations()[1][1]
end
if example_form_str then
example_text = ustring.gsub(example_text, example_form_str, "'''" .. example_form_str .. "'''")
end
example_str = termSpan({example_text, example_lang})
local reference_text = ''
if stmt.references then
for j, reference in pairs(stmt.references) do
table.insert(references_seen, reference.hash)
local got_reference = getReference(reference.hash, reference)
reference_text = reference_text .. '\n\n' .. got_reference[2]
end
end
if example_str then
examples:tag('dd'):wikitext("''" .. example_str .. "''")
if reference_text ~= '' then
examples:done():tag('dd'):css('text-indent', '2em'):wikitext(reference_text)
end
end
end
return { tostring(examples) , references_seen }
end
-- This calls frame:preprocess() instead of :callParserFunction() because the latter does not work for Wikifunctions function calls yet (see https://www.wikifunctions.org/wiki/Wikifunctions:Embedded_function_calls).
local function callWikifunctionsFunction(args, frame)
return frame:preprocess('{{#function:' .. args .. '}}')
end
local function checkTitleCodePointInRange(title, start_point, end_point)
return ustring.find( title, '[' ..ustring.char(start_point) .. '-' .. ustring.char(end_point) .. ']' )
end
local function getLanguageForCategories( lang_id, current_page_title )
-- বিশেষ ভাষার জন্য
if lang_id == 'Q11051' then -- হিন্দি/উর্দু
if checkTitleCodePointInRange(current_page_title, 0x0600, 0x06ff) then -- উর্দু
lang_id = 'Q11051ur'
elseif checkTitleCodePointInRange(current_page_title, 0x0900, 0x097f) then -- হিন্দি
lang_id = 'Q11051hi'
end
elseif lang_id == 'Q58635' then -- পাঞ্জাবি
if checkTitleCodePointInRange(current_page_title, 0x0600, 0x06ff) then -- শাহমুখী
lang_id = 'Q58635pnb'
elseif checkTitleCodePointInRange(current_page_title, 0x0a00, 0x0a7f) then -- গুরুমুখী
lang_id = 'Q58635pa'
end
elseif lang_id == 'Q56356571' then -- নয়া ফার্সি ভাষা
if checkTitleCodePointInRange(current_page_title, 0x0600, 0x06ff) then -- ফার্সি (ইরান/আফগানিস্তান)
lang_id = 'Q56356571fa'
elseif checkTitleCodePointInRange(current_page_title, 0x0400, 0x04ff) then -- তাজিক
lang_id = 'Q56356571tg'
end
end
return lang_id
end
local function getOneStringForProperty(object, property)
local val
local stmts = object:getAllStatements(property)
if #stmts ~= 0 then
val = stmts[1].mainsnak.datavalue.value
end
return val
end
local function getTranslations(frame, senses, label) -- TODO: woefully incomplete until T185313 and T199887 are resolved
if #senses == 0 then
return nil
end
local all_translations = {}
for _, sense in pairs(senses) do
local translation_set = {}
local gloss = sense:getGloss('bn')
for _, stmt in pairs(sense:getAllStatements('P5972')) do
local translation = stmt.mainsnak.datavalue.value.id
local lexeme_id = wb.lexeme.splitLexemeId(translation)
local trans_lexeme = getEntity(lexeme_id)
local lang_name = getLabel(trans_lexeme:getLanguage())
local trans_code = getLexemeLanguageCode(trans_lexeme)
if trans_code then
table.insert(translation_set, lang_name .. ': ' .. frame:expandTemplate{title=i18n['template_t'], args={trans_code, getLemmata(trans_lexeme) .. '<br/>'}})
else
table.insert(translation_set, lang_name .. ': ' .. getLinkedLemmata(trans_lexeme) .. '<br/>' .. i18n.trans_category(lang_name))
end
end
if #translation_set > 0 then
local block = frame:expandTemplate{ title = i18n['template_trans-top'], args = { gloss or label } }
block = block .. table.concat(translation_set, '\n') .. frame:expandTemplate{ title = i18n['template_trans-bottom'] }
table.insert(all_translations, block)
end
end
if #all_translations == 0 then
return nil
end
return '====' .. i18n['heading_translation'] .. '==== \n' .. table.concat(all_translations, '\n')
end
local createicon = function(langcode, entityID, propertyID)
langcode = langcode or ""
propertyID = propertyID or ""
local icon = " <span class='penicon autoconfirmed-show'>[["
-- " <span data-bridge-edit-flow='overwrite' class='penicon'>[[" -> enable Wikidata Bridge
.. "File:OOjs UI icon edit-ltr-progressive.svg |frameless |text-top |10px |alt="
.. i18n['edit_wikidata']
.. "|link=https://www.wikidata.org/entity/" .. entityID
if langcode ~= "" then icon = icon .. "?uselang=" .. langcode end
if propertyID ~= "" then icon = icon .. "#" .. propertyID end
icon = icon .. "|" .. i18n['edit_wikidata'] .. "]]</span>"
return icon
end
local function getMeanings( frame, args, current_lexeme, senses, references_seen, language_name)
local item_label
if #senses == 0 then
return {createicon(i18n['content_lang_code'], current_lexeme:getId()) .. "''" .. i18n['text_category_rfdef'] .. "''" .. i18n.tocatlink(i18n['category_rfdef']), references_seen}
end
local meanings = html.create( 'ol' )
local item_label_gloss_parts = {}
local idlinkset = {}
for i, sense in pairs(senses) do
local gloss_text_parts = {}
local main_gloss_text = frame:expandTemplate{
title=i18n['template_anchor'],
args={sense:getId()}
}
local specifiers = {}
for k, property_id in ipairs({'P6084', 'P6191', 'P9488'}) do -- অবস্থান যেখানে শব্দার্থ ব্যবহৃত, যে রীতিতে শব্দার্থ ব্যবহৃত হয়, যে ক্ষেত্রে ব্যবহৃত
for _, stmt in pairs(sense:getAllStatements(property_id)) do
local stmt_value = stmt.mainsnak.datavalue.value.id
local reference_text = ''
local refs = stmt.references
if refs then
for j, reference in pairs(refs) do
table.insert(references_seen, reference.hash)
local got_reference = getReference(reference.hash, reference)
reference_text = reference_text .. '\n\n' .. frame:extensionTag('ref', got_reference[2])
end
end
local val = getLabel(stmt_value)
table.insert(specifiers, val .. reference_text)
if property_id == 'P9488' then
table.insert(item_label_gloss_parts, i18n.tocatlink(lang_code .. ':' .. val))
end
end
if #specifiers > 0 then
main_gloss_text = main_gloss_text .. "(''" .. table.concat(specifiers, "'', ''") .. "'') "
end
end
local gloss = sense:getGloss( i18n['content_lang_code'] )
if gloss then
main_gloss_text = main_gloss_text .. wrapStringInWikilinks(gloss)
if gloss:match('^প্রদত্ত%s*(%S-)%s*নাম$') then -- given names
main_gloss_text = main_gloss_text .. i18n.tocatlink(language_name .. ' ' .. i18n['category_given_names'])
end
else
local other_gloss_text = nil
local other_gloss_lang = nil
for k, stmt in pairs(sense:getAllStatements('P5137')) do -- যদি 'এই অর্থের জন্য আইটেম' মানের বাংলা লেবেল থাকে
local stmt_value = stmt.mainsnak.datavalue.value.id
item_label = getLabel(stmt_value)
if item_label then
table.insert(item_label_gloss_parts, '[[:d:' .. stmt_value .. '|' .. item_label .. ']]')
end
end
if #item_label_gloss_parts > 0 then
other_gloss_text = table.concat(item_label_gloss_parts, '; ')
end
if other_gloss_text == nil then
for _, fallback_lang in ipairs(mw_lang.getFallbacksFor( i18n['content_lang_code'] )) do
if sense:getGloss( fallback_lang ) then
other_gloss_text, other_gloss_lang = sense:getGloss( fallback_lang )
end
end
if other_gloss_lang == nil then
local glosses = sense:getGlosses()
for _, gloss in pairs(glosses) do
other_gloss_text = gloss[1]
other_gloss_lang = gloss[2]
break
end
end
main_gloss_text = main_gloss_text .. other_gloss_text .. "<sup><em>" .. mw_lang.fetchLanguageName(other_gloss_lang, i18n['content_lang_code']) .. "</em></sup>"
else
main_gloss_text = main_gloss_text .. "''" .. other_gloss_text .. "''"
end
main_gloss_text = main_gloss_text .. i18n.tocatlink(i18n['category_rfdef_equivalent'])
end
local synonym = expandTemplateForProperty(frame, sense, 'P5973', i18n['template_synonym'])
if synonym ~= '' then
main_gloss_text = main_gloss_text .. ' <br/> ' .. synonym
end
local antonym = expandTemplateForProperty(frame, sense, 'P5974', i18n['template_antonym'])
if antonym ~= '' then
main_gloss_text = main_gloss_text .. ' <br/> ' .. antonym
end
local hypernym = expandTemplateForProperty(frame, sense, 'P6593', i18n['template_hypernym'])
if hypernym ~= '' then
main_gloss_text = main_gloss_text .. ' <br/> ' .. hypernym
end
if lex_cat == 'Q1084' or lex_cat == 'Q147276' then -- noun or proper noun
local demonym = expandTemplateForProperty(frame, sense, 'P6271', i18n['template_demonym-noun'])
main_gloss_text = main_gloss_text .. ' <br/> ' .. demonym
elseif lex_cat == 'Q34698' then
local demonym = expandTemplateForProperty(frame, sense, 'P6271', i18n['template_demonym-adj'])
main_gloss_text = main_gloss_text .. ' <br/> ' .. demonym
end
table.insert(gloss_text_parts, main_gloss_text .. createicon(i18n['content_lang_code'], sense:getId()))
for i, stmt in pairs(sense:getAllStatements('P8394')) do -- টিপ্পনীর উদ্ধৃতি
local gloss_quote = termSpan({stmt.mainsnak.datavalue.value.text, stmt.mainsnak.datavalue.value.language})
if stmt.references[1] then
local got_reference = getReference ( stmt.references[1].hash, stmt.references[1] )
gloss_quote = '"' .. gloss_quote .. '" ' .. got_reference[2]
end
table.insert(references_seen, stmt.references[1].hash)
table.insert(gloss_text_parts, frame:extensionTag('ref', gloss_quote))
end
for i, stmt in pairs(sense:getAllStatements('P1343')) do -- বর্ণিত উৎস
-- TODO: do away with making fake reference objects
local fake_reference = { ['snaks'] = {} }
fake_reference.snaks['P248'] = { [1] = stmt.mainsnak }
qualifiers_order = stmt['qualifiers-order']
if qualifiers_order then
for i, k in ipairs(qualifiers_order) do fake_reference.snaks[k] = stmt.qualifiers[k] end
end
fake_reference.hash = mw.hash.hashValue('sha3-512', serializeTable(fake_reference))
table.insert(references_seen, fake_reference.hash)
local got_reference = getReference(fake_reference.hash, fake_reference)
if got_reference[1] == nil then
table.insert(gloss_text_parts, frame:extensionTag('ref', got_reference[2], {name = fake_reference.hash}))
else
table.insert(gloss_text_parts, frame:extensionTag{name = 'ref', content='', args = {name = got_reference[1]}})
end
end
local first_sense_image = ''
local sense_images = sense:getAllStatements('P18')
if next(sense_images) then
first_sense_image = sense_images[1].mainsnak.datavalue.value
end
if first_sense_image ~= '' then
table.insert(gloss_text_parts, '[[চিত্র:' .. first_sense_image .. "|thumb|'''" .. getLemmata(current_lexeme) .. "'''—" .. main_gloss_text .. ']]')
end
local idlinks = getExternalLinks(sense)
if #idlinks > 0 then
local idlinktext = '<small>('
for _, idlink in pairs(idlinks) do
idlinktext = idlinktext .. idlink .. '\n'
end
idlinktext = idlinktext .. ')</small>'
table.insert(gloss_text_parts, idlinktext)
table.insert(idlinkset, idlinks)
end
local externallinks = getArticleLinks(frame, sense)
if externallinks ~= '' then
table.insert(gloss_text_parts, externallinks)
end
local new_notes = {}
local sense_keys = { sense:getId(), string.sub(sense:getId(), string.find(sense:getId(), '-')+1) }
for _, v in ipairs(sense_keys) do
if args[v] then
table.insert(new_notes, args[v])
end
end
if #new_notes > 0 then
for _, v in ipairs(new_notes) do
if i == 1 then
table.insert(gloss_text_parts, '<br/>' .. v)
else
table.insert(gloss_text_parts, v)
end
end
end
local examples, references_seen = unpack(getExamples( current_lexeme, sense:getId(), references_seen ))
local gloss_text = table.concat(gloss_text_parts, '\n')
meanings:tag('li'):wikitext(gloss_text):wikitext(examples)
end
return {meanings, references_seen, idlinkset}
end
local function getPronunciationBaseForm( lang_name, lex_cat)
local base_form = nil
-- (!) অন্য ভাষার শব্দের যদি অন্য রকম মূল ফর্ম থাকে সেগুলো এখানে নতুন if বিবৃতি দিয়ে যোগ করা যায়।
if lang_name == 'বাংলা' then
if lex_cat == 'Q1084' then -- বিশেষ্য
base_form = formWithSingleGrammaticalFeature( 'Q131105' ) -- কর্তৃকারক
elseif lex_cat == 'Q24905' then -- ক্রিয়া
base_form = formWithSingleGrammaticalFeature( 'Q1350145' ) -- ক্রিয়া বিশেষ্য
end
end
if base_form == nil then
for i, form in pairs(forms) do
base_form = form
break
end
end
return base_form
end
local function getCombines( current_lexeme, frame )
local combines = ''
local index_mappings = {}
for i, stmt in pairs(current_lexeme:getAllStatements('P5238')) do
if stmt.qualifiers and stmt.qualifiers['P1545'] then -- ক্রম
local current_index = tonumber(stmt.qualifiers['P1545'][1].datavalue.value)
index_mappings[current_index] = stmt
end
end
if #index_mappings ~= 0 then
for i, stmt in ipairs(index_mappings) do
if stmt.mainsnak.datavalue then
local part_lexeme_id = stmt.mainsnak.datavalue.value.id
local part_lexeme = getEntity(part_lexeme_id)
local current_substring = getLinkedLemmata(part_lexeme)
local part_etymology = getEtymology(part_lexeme, frame, 'partial')
if part_etymology ~= '' and part_etymology then
current_substring = current_substring .. ' (← ' .. part_etymology .. ')'
end
if combines == '' then
combines = current_substring
else -- @TODO: This shoukd use the 'affix' and 'compound' templates instead.
combines = combines .. ' + ' .. current_substring
end
end
end
end
return combines
end
function getRoots( current_lexeme )
local stmts = current_lexeme:getAllStatements('P5920')
if #stmts == 0 then return '', '', '' end
local root_lexeme = getEntity(stmts[1].mainsnak.datavalue.value.id)
return getLexemeLanguageCode(root_lexeme), '√' .. getLinkedLemmata(root_lexeme), root_lexeme:getLemma('ar')
end
function getEtymology( current_lexeme, frame, mode )
-- @TODO: Fix the etymology chains that are not possible to render
local etymology = ''
local current_combines = getCombines(current_lexeme, frame)
local root_lang, current_roots, root_str = getRoots(current_lexeme)
if mode ~= 'partial' and root_str then
--frame:expandTemplate{title=i18n['template_root'], args={lang_code, root_lang, root_str}}
end
local stmts = current_lexeme:getAllStatements('P5191')
local new_etymology_string
if #stmts == 0 then
if current_roots ~= '' and current_combines ~= '' and current_roots then
return current_roots .. '<br/>(' .. current_combines .. ')'
elseif current_roots ~= '' then
if lang_code == 'ar' and mode ~= 'partial' and root_str ~= matched_lemma then
return frame:expandTemplate{title=i18n['template_ar-rootbox'], args={root_str}}
else
return current_roots
end
else
return current_combines
end
end
local origin_lexeme_string
for i, stmt in pairs(stmts) do
local origin_lexeme_dv = stmt.mainsnak.datavalue -- If this is nil, the origin lexeme is not known.
if origin_lexeme_dv then
local origin_lexeme = getEntity(origin_lexeme_dv.value.id)
local origin_lexeme_lang = getLabel(origin_lexeme:getLanguage())
local sitelink = i18n.wplink(origin_lexeme:getLanguage(), origin_lexeme_lang, wb)
if sitelink ~= '' then
origin_lexeme_string = getLinkedLemmata(origin_lexeme) .. ' (' .. sitelink .. ')'
else
origin_lexeme_string = getLinkedLemmata(origin_lexeme) .. ' (' .. origin_lexeme_lang .. ')'
end
if stmt.qualifiers and stmt.qualifiers['P5886'] then
local mode_of_derivation = stmt.qualifiers['P5886'][1].datavalue.value.id
if mode_of_derivation == 'Q1345001' then
-- @TODO: Add support for showing gender
origin_lexeme_string = frame:expandTemplate{title=i18n['template_borrowed'], args={lang_code, getLexemeLanguageCode(origin_lexeme), getLemmata(origin_lexeme), pos=getLabel(lex_cat)}} .. ' ' .. i18n['etymology_borrowing']
elseif mode_of_derivation == 'Q845079' then
origin_lexeme_string = ustring.gsub(i18n['etymology_learned_borrowing'], '$1', origin_lexeme_string)
elseif mode_of_derivation == 'Q56611986' then
origin_lexeme_string = frame:expandTemplate{title=i18n['template_inherited'], args={lang_code, getLexemeLanguageCode(origin_lexeme), getLemmata(origin_lexeme), pos=getLabel(lex_cat)}} .. ' ' .. i18n['etymology_inheritance']
elseif mode_of_derivation == 'Q189743' then
origin_lexeme_string = frame:expandTemplate{title=i18n['template_ellipsis'], args={lang_code, getLemmata(origin_lexeme)}} .. ' ' .. i18n['etymology_ellipsis']
end
end
local origin_origin = getEtymology(origin_lexeme, frame)
if origin_origin ~= '' and origin_origin then
new_etymology_string = origin_lexeme_string .. ' ← ' .. origin_origin
else
new_etymology_string = origin_lexeme_string
end
end
if etymology == '' then
etymology = new_etymology_string
elseif origin_lexeme_string and etymology then
etymology = etymology .. ' ' .. origin_lexeme_string
elseif origin_lexeme_string and etymology == nil then
etymology = origin_lexeme_string
end
end
if current_roots ~= '' and etymology and current_roots then
etymology = etymology .. ' ' .. current_roots
elseif current_roots ~= '' and etymology == nil then
etymology = current_roots
end
if current_combines ~= '' and etymology then
etymology = etymology .. '<br/>(' .. current_combines .. ')'
end
return etymology
end
local function pronunciationBlock(block, value)
return '* ' .. i18n['text_' .. block] .. ' ' .. value
end
local function getPronunciation( frame, current_lexeme, lang_name, lex_cat )
local pronunciations = {}
local base_form = getPronunciationBaseForm(lang_name, lex_cat )
if base_form then
for i, stmt in pairs(base_form:getAllStatements('P443')) do -- উচ্চারণের অডিও
local pronunciation_file = stmt.mainsnak.datavalue.value
local specifier_text = ''
local specifiers = {}
if stmt.qualifiers then
for k, property_id in ipairs({'P5237'}) do -- উচ্চারণের ধরন
local qual = stmt.qualifiers[property_id]
if qual then
for _, qual in pairs(qual) do
local stmt_value = qual.datavalue.value.id
table.insert(specifiers, getLabel(stmt_value))
end
end
end
end
if #specifiers > 0 then
specifier_text = table.concat(specifiers, "'', ''")
end
local audio_text
if specifier_text ~= '' then
audio_text = i18n['text_audio'] .. ' (' .. specifier_text .. ')'
else
audio_text = i18n['text_audio']
end
table.insert(pronunciations, '* ' .. frame:expandTemplate{
title= i18n['template_audio'],
args = {lang_name, pronunciation_file, audio_text}
})
end
local ipa_transcription = base_form:getAllStatements('P898') -- - আধ্বব প্রতিলিপিকরণ
local iso15919_transcription = getOneStringForProperty(base_form, 'P5825') -- আইএসও ১৫৯১৯ প্রতিলিপিকরণ
local itrans = getOneStringForProperty(base_form, 'P8881') -- ITRANS
local iast = getOneStringForProperty(base_form, 'P7581') -- আইএএসটি প্রতিলিপিকরণ
local xsampa = getOneStringForProperty(base_form, 'P2859')
-- @TODO: অডিও ও প্রতিলিপিকরণ দুটোই থাকলে সেই ক্ষেত্রে একটার ঠিক পরেই আরেকটা দেখানো উচিত
if #ipa_transcription ~= 0 then
for i, stmt in pairs(ipa_transcription) do
local ipa_text = stmt.mainsnak.datavalue.value
local specifier_text = ''
local specifiers = {}
if stmt.qualifiers then
for k, property_id in ipairs({'P5237'}) do -- উচ্চারণের ধরন
for l, qual in ipairs(stmt.qualifiers[property_id]) do
table.insert(specifiers, getLabel( qual.datavalue.value.id ))
end
end
end
if #specifiers > 0 then
specifier_text = "(''" .. table.concat(specifiers, "'', ''") .. "'') "
end
local syllable_count
if lang_code == 'tr' then
syllable_count = i18n['text_syllable_count'] .. ' ' .. callWikifunctionsFunction('Z10029|' .. ipa_text, frame)
else
syllable_count = i18n['text_syllable_count'] .. ' ' .. callWikifunctionsFunction('Z30837|' .. ipa_text, frame)
end
table.insert(pronunciations, '* ' .. specifier_text .. frame:expandTemplate{
title= i18n['template_ipa'],
args = {lang_name, ipa_text}
} .. '\n* ' .. syllable_count)
end
-- The following checks are ordered based on which one is expected to be true in a higher number of cases.
elseif lang_name == 'বাংলা' then
table.insert(pronunciations, '* ' .. frame:expandTemplate{
title='bn-IPA',
})
elseif lang_name == 'আরবি' then
local lemma = current_lexeme:getLemma('ar')
table.insert(pronunciations, '* ' .. frame:expandTemplate{
title='ar-IPA',
args={lemma}
})
elseif lang_name == 'ফালা' then
table.insert(pronunciations, '* ' .. frame:expandTemplate{
title='fax-pron',
})
elseif lang_code == 'fi' then
table.insert(pronunciations, '* ' .. frame:expandTemplate{
title='fi-IPA',
})
elseif lang_code == 'ko' then
table.insert(pronunciations, '* ' .. frame:expandTemplate{
title='ko-IPA',
})
end
if iso15919_transcription then
table.insert(pronunciations, pronunciationBlock('iso15919', iso15919_transcription))
end
if itrans then
table.insert(pronunciations, pronunciationBlock('itrans', itrans))
end
if iast then
table.insert(pronunciations, pronunciationBlock('iast', iast))
end
if xsampa then
table.insert(pronunciations, pronunciationBlock('xsampa', xsampa))
end
end -- {{আধ্বব|en|/ˈɪntəvjuː/}}
return table.concat(pronunciations, '\n')
end
local function getAlternativeSpellings( current_lexeme )
local alt_spellings = {}
for i, stmt in pairs(current_lexeme:getAllStatements('P11577')) do -- বিকল্প বানান
if stmt.mainsnak.datavalue then
table.insert(alt_spellings, '* ' .. getLinkedLemmata(getEntity(stmt.mainsnak.datavalue.value.id)))
end
end
return table.concat(alt_spellings, '\n')
end
local function heading_level(text, level)
local heading_delimiter = string.rep('=', level)
return heading_delimiter .. ' ' .. text .. ' ' .. heading_delimiter
end
function get_any_notes(sections, args, keys)
local notes = {}
for i, v in ipairs(keys) do
if args[v] then
table.insert(notes, args[v])
end
end
return notes
end
function add_specific_notes(sections, notes)
for i, v in ipairs(notes) do
table.insert(sections, v)
end
end
local function add_any_notes(sections, args, keys)
for i, v in ipairs(keys) do
if args[v] then
table.insert(sections, args[v])
end
end
end
local function getMatchingLemmaForPageTitle(lexeme, title)
local lemmas = lexeme:getLemmas()
local matched_lemma
for _, lemma_entry in ipairs(lemmas) do
local lemma = lemma_entry[1]
if lemma == title then
matched_lemma = lemma
break
end
end
if matched_lemma == nil and lang_code == 'ar' then -- Arabic lemmas do not match the title of the entry because those are written with different characters stripped out on Wiktionary. This could be changed to call stripDiactritics() from [[Module:languages]] in the future if desired.
matched_lemma = lexeme:getLemma('ar')
end
return matched_lemma
end
local function buildLanguageAgnosticInflectionTable()
local has_image = false
local form_images = {}
for i, form in ipairs(forms) do
local form_image = form:getAllStatements('P7407')
if next(form_image) then
form_images[i] = form_image[1].mainsnak.datavalue.value
has_image = true
end
end
local table_class = "wikitable mw-collapsible sortable"
if not has_image then
table_class = table_class .. " mw-collapsed"
end
local text = "{| class='" .. table_class .. "' style='border:solid 1px rgb(80%,80%,100%); text-align:center;'\n"
text = text .. "|+ " .. i18n['heading_inflection_table'] .. "\n"
text = text .. "|- \n"
text = text .. "! " .. i18n['heading_form'] .. " !! " .. i18n['heading_grammatical_features']
if has_image then
text = text .. " !! " .. (i18n['heading_image'])
end
text = text .. " \n"
for i, form in ipairs(forms) do
local rep = form:getRepresentations()
local feat = form:getGrammaticalFeatures()
local rep_text = ""
for j, r in pairs(rep) do
if rep_text == "" then
rep_text = r[1]
else
rep_text = rep_text .. " / " .. r[1]
end
end
local feat_text = ""
if feat then
for j, f in ipairs(feat) do
local label = getLabel(f) or f
if feat_text == "" then
feat_text = label
else
feat_text = feat_text .. ", " .. label
end
end
end
text = text .. "|-\n"
text = text .. "| " .. (rep_text ~= "" and rep_text or "—")
text = text .. " || " .. (feat_text ~= "" and feat_text or "—")
if has_image then
local image_cell = "—"
if form_images[i] then
image_cell = "[[চিত্র:" .. form_images[i] .. "|50px]]"
end
text = text .. " || " .. image_cell
end
text = text .. "\n"
end
text = text .. "|}"
return text
end
function p.all( frame )
local args = getArgs(frame)
local lexeme_id = args[1]
local current_lexeme = getEntity(lexeme_id)
local current_language = current_lexeme:getLanguage()
local senses = current_lexeme:getSenses()
local add_heading = true
forms = current_lexeme:getForms()
if args[2] then
local val = mw.text.trim(tostring(args[2]))
if val == "false" or val == "0" or val == "না" then
add_heading = false
end
end
local references_seen = {}
local sections = {}
local title = mw.title.getCurrentTitle().text
local lang_category = getLanguageForCategories(current_language, title)
local lang_name = getLabel(lang_category)
if add_heading == true then
local lang_heading = "== " .. lang_name .. " =="
table.insert(sections, lang_heading)
end
matched_lemma = getMatchingLemmaForPageTitle(current_lexeme, title)
lex_cat = current_lexeme:getLexicalCategory()
lang_code = getLexemeLanguageCode(current_lexeme)
local cat = i18n.lang_category(getLabel(lex_cat), lang_name)
local lex_cat_template
if cat then
table.insert(sections, '===' .. getLabel(lex_cat) .. cat .. frame:expandTemplate{
title = i18n['template_anchor'],
args = { lexeme_id }
} .. '===')
table.insert(sections, frame:expandTemplate{
title= i18n['template_lexeme'],
args = {lexeme_id}
})
add_any_notes(sections, args, i18n['manual_category'])
local etymology = getEtymology( current_lexeme, frame )
if etymology ~= '' and etymology then
table.insert(sections, heading_level(i18n['heading_etymology'], 4))
table.insert(sections, tostring(etymology))
end
add_any_notes(sections, args, i18n['manual_etymology'])
local pronunciation = getPronunciation( frame, current_lexeme, lang_name, lex_cat )
if pronunciation ~= '' then
table.insert(sections, heading_level(i18n['heading_pronunciation'], 4))
table.insert(sections, tostring(pronunciation))
end
add_any_notes(sections, args, i18n['manual_pronunciation'])
local alternative_spellings = getAlternativeSpellings( current_lexeme )
if alternative_spellings ~= '' then
table.insert(sections, heading_level(i18n['heading_alternative_spellings'], 4))
table.insert(sections, alternative_spellings)
end
if lang_code and lang_code ~= 'ksy' and lang_code ~= 'rkt' then -- Skip for Kharia Thar, Rangpuri
if lex_cat == 'Q34698' then -- বিশেষণ
if lang_code == 'en' or lang_code == 'bn' then
if #forms <= 1 then
lex_cat_template = frame:expandTemplate{title= lang_code .. '-বিশেষণ'}
end
else
lex_cat_template = safeExpand(frame, lang_code .. '-adj')
if not lex_cat_template then
lex_cat_template = safeExpand(frame, lang_code .. '-বিশেষণ')
end
end
elseif lex_cat == 'Q1084' then -- @TODO: Also check for plural forms
local gender
local stmts = current_lexeme:getAllStatements('P5185') -- ব্যাকরণগত লিঙ্গ
if #stmts ~= 0 then
if #stmts == 1 then
local gender_qid = stmts[1].mainsnak.datavalue.value.id
if gender_qid == 'Q499327' then
gender = 'm'
elseif gender_qid == 'Q1775415' then
gender = 'f'
elseif gender_qid == 'Q1775461' then
gender = 'n'
elseif gender_qid == 'Q1305037' then
gender = 'c'
end
end
else
for i, stmt in pairs(stmts) do
local qid = stmts[i].mainsnak.datavalue.value.id
if qid == 'Q499327' then
gender = gender .. 'm'
elseif qid == 'Q1775415' then
gender = gender .. 'f'
end
end
end
-- The following checks are ordered based on which one is expected to be true in a higher number of cases.
if current_language == 'Q13955' then
if matched_lemma then
lex_cat_template = safeExpand(frame, {title='ar-noun', args={matched_lemma,gender}})
else
lex_cat_template = frame:expandTemplate{title='ar-noun', args={nil,gender}}
end
elseif current_language == 'Q29919' then
lex_cat_template = frame:expandTemplate{title='arz-noun', args={g=gender}}
elseif current_language == 'Q397' then
if matched_lemma then
lex_cat_template = frame:expandTemplate{title='la-noun', args={matched_lemma,g=gender}}
end
elseif current_language == 'Q11059' then
lex_cat_template = frame:expandTemplate{title='sa-noun', args={g=gender}}
elseif current_language ~= 'Q1860' then -- These templates require the gender to be passed as the 1st argument.
lex_cat_template = safeExpand(frame, lang_code .. i18n['noun_template_suffix'], {gender})
if not lex_cat_template then
lex_cat_template = safeExpand(frame, lang_code .. i18n['noun_template_suffix_fallback'], {gender})
end
end
end
end
-- elseif lex_cat == 'Q147276' then
-- lex_cat_template = safeExpand(frame, lang_code .. '-proper noun', {gender})
-- if not lex_cat_template then
-- lex_cat_template = safeExpand(frame, lang_code .. '-নামবাচক বিশেষ্য', {gender})
-- end
end
if lex_cat_template then
table.insert(sections, lex_cat_template)
else
if matched_lemma then
table.insert(sections, heading_level(matched_lemma, 4))
else
table.insert(sections, i18n.tocatlink(i18n['category_no_matching_lemma']))
end
end
local meanings, references_seen, sense_extlinks = unpack(getMeanings( frame, args, current_lexeme, senses, references_seen, lang_name))
table.insert(sections, tostring(meanings))
add_any_notes(sections, args, i18n['manual_meaning'])
local instance_of = current_lexeme:getAllStatements('P31') -- সত্ত্বার ধরন
if #instance_of ~= 0 then
local instance_of_entity = instance_of[1].mainsnak.datavalue.value.id
if instance_of_entity == 'Q40437546' or instance_of_entity == 'Q120831827' or instance_of_entity == 'Q120717979' or instance_of_entity == 'Q124476844' then -- @TODO: generalise this so all types of roots are shown
table.insert(sections, i18n['text_instance_of'] .. ' ' .. getLabel(instance_of_entity))
elseif instance_of_entity == 'Q376431' then -- বর্ণের নাম
table.insert(sections, i18n.tocatlink(lang_code .. ':রং'))
end
end
local translations = getTranslations(frame, senses, item_label)
if translations then
table.insert(sections, translations)
end
-- (!) বিশেষ ভাষার বিভক্তির সারণি যদি থাকে সেগুলো এখানে নতুন if বিবৃতি যোগ করা যায়।
if next(forms) then
if current_language == 'Q9610' then -- বাংলা
if lex_cat == 'Q24905' then
local conjTable = require('মডিউল:আভিধানিক উপাত্ত/Q9610').getConjTable(frame, forms)
table.insert(sections, conjTable)
elseif lex_cat == 'Q1084' then
table.insert(sections, callWikifunctionsFunction('Z33243|' .. lexeme_id .. '|', frame))
elseif lex_cat == 'Q34698' then
if #forms > 1 then
table.insert(sections, buildLanguageAgnosticInflectionTable())
end
end
--elseif current_language == 'Q13955' then -- আরবি
-- if lex_cat == 'Q1084' then
-- table.insert(sections, frame:expandTemplate{title='ar-decl-noun', args={lemma}})
-- end
elseif current_language == 'Q188' then -- জার্মান
if lex_cat == 'Q1084' then
table.insert(sections, callWikifunctionsFunction('Z28602|' .. lexeme_id .. '|', frame))
end
else
if #forms > 1 then
table.insert(sections, buildLanguageAgnosticInflectionTable())
end
end
end
if lex_cat == 'Q134830' then
table.insert(sections, frame:expandTemplate{title='prefixsee', args={lang_code}})
elseif lex_cat == 'Q102047' then
table.insert(sections, frame:expandTemplate{title='suffixsee', args={lang_code}})
-- elseif lex_cat == 'Q111029' then
-- table.insert(sections, frame:expandTemplate{title='rootsee', args={'+', lang_code, matched_lemma}})
end
local reference_notes = get_any_notes(sections, args, i18n['manual_reference'])
if #references_seen > 0 or #reference_notes > 0 then
table.insert(sections, heading_level(i18n['heading_references'], 4))
table.insert(sections, frame:extensionTag('references'))
add_specific_notes(sections, reference_notes)
end
local external_link_table = getExternalLinks ( current_lexeme )
if #external_link_table > 0 then
local external_links = '* ' .. table.concat(external_link_table, '\n* ')
table.insert(sections, heading_level(i18n['heading_external_links'], 4))
table.insert(sections, external_links)
end
add_any_notes(sections, args, i18n['manual_external_link'])
if #references_seen == 0 and #reference_notes == 0 and sense_extlinks and #sense_extlinks == 0 and #external_link_table == 0 and #get_any_notes(sections, args, i18n['manual_external_link']) == 0 then
table.insert(sections, i18n.rfref_category(lang_name))
end
return table.concat(sections,"\n\n")
end
return p
2yyeh4cj3rgqeolmfdyz32dj655scfv
মডিউল:আভিধানিক উপাত্ত/নথি
828
50210
509647
509084
2026-06-04T05:15:13Z
Redmin
6857
/* ব্যবহার */ +
509647
wikitext
text/x-wiki
এই মডিউলটি বর্তমানে পরীক্ষামূলক পর্যায়ে রয়েছে।
==ব্যবহার==
<code><nowiki>{{#invoke:আভিধানিক উপাত্ত|all| লেক্সিম নং }}</nowiki></code>
উদা:
<code><nowiki>{{#invoke:আভিধানিক উপাত্ত|all|L301993}}</nowiki></code>
ভাষার সেকশন হেডিং বাদ দিতে চাইলে টেম্পলেটটি এভাবে ব্যবহার করবেন:
<code><nowiki>{{#invoke:আভিধানিক উপাত্ত|all|L301993|না}}</nowiki></code>
এই মডিউলের কিছু ব্যবহার দেখতে এই পাতাগুলো দেখুন: '''[[হৃদয়]]''', '''[[knife]]''', '''[[س ك ن]]''', '''[[double]]''', '''[[mouse]]''', '''[[clinical psychology]]''', '''[[Aggression]]''', '''[[bygning]]''', '''[[ডাকঘর]]''', '''[[big]]''', '''[[চলা]]''', '''[[abdomen]]''', '''[[pizzabagare]]'''।
fxzsb62ljl9msxlwavpxdzmzb6j9449
মডিউল:ar-verb
828
51914
509635
323235
2026-06-03T12:45:31Z
Redmin
6857
[[en:Module:ar-verb|ইংরেজি উইকিঅভিধান]] থেকে হালনাগাদ করলাম
509635
Scribunto
text/plain
local export = {}
--[=[
This module implements {{ar-conj}} and provides the underlying conjugation functions for {{ar-verb}}
(whose actual formatting is done in [[Module:ar-headword]]).
Author: User:Benwing, from an early version (2013-2014) by User:Atitarev, User:ZxxZxxZ.
]=]
--[=[
TERMINOLOGY:
-- "slot" = A particular combination of tense/mood/person/number/etc.
Example slot names for verbs are "past_1s" (past tense first-person singular), "juss_pass_3fp" (non-past jussive
passive third-person feminine plural) "ap" (active participle). Each slot is filled with zero or more forms.
-- "form" = The conjugated Arabic form representing the value of a given slot.
-- "lemma" = The dictionary form of a given Arabic term. For Arabic, normally the third person masculine singular past,
although other forms may be used if this form is missing (e.g. in passive-only verbs or verbs lacking the past).
]=]
--[=[
FIXME:
1. Finish unimplemented conjugation types. Only IX-final-weak left (extremely rare, possibly only one verb اِعْمَايَ
(according to Haywood and Nahmad p. 244, who are very specific about the irregular occurrence of alif + yā instead
of expected اِعْمَيَّ with doubled yā). Not in Hans Wehr. NOTE: Not true about this, cf. form IX اِرْعَوَى "to desist,
to repent, to see the light". Also note form XII اِخْضَوْضَرَ = form IX اِخْضَرَّ "to be or become green".
[DONE except for اِعْمَايَ]
2. Implement irregular verbs as special cases and recognize them, e.g.
-- laysa "to not be"; only exists in the past tense, no non-past, no imperative, no participles, no passive, no
verbal noun. Irregular alternation las-/lays-. [IMPLEMENTABLE USING OVERRIDES]
-- istaḥā yastaḥī "be ashamed of" -- this is complex according to Hans Wehr because there are two verbs, regular
istaḥyā yastaḥyī "to spare (someone)'s life" and irregular istaḥyā yastaḥyī "to be ashamed to face (someone)",
which is irregular because it has the alternate irregular form istaḥā yastaḥī which only applies to this meaning.
Currently we follow Haywood and Nahmad in saying that both varieties can be spelled istaḥyā/istaḥā/istaḥḥā, but we
should instead use a variant= param similar to حَيَّ to distinguish the two possibilities, and maybe not include
istaḥḥā.
-- ʿayya/ʿayiya yaʿayyu/yaʿyā "to not find the right way, be incapable of, stammer, falter, fall ill". This appears
to be a mixture of a geminate and final-weak verb. Unclear what the whole paradigm looks like. Do the
consonant-ending parts in the past follow the final-weak paradigm? Is it the same in the non-past? Or can you
conjugate the non-past fully as either geminate or final-weak?
-- اِنْمَحَى inmaḥā or يمَّحَى immaḥā "to be effaced, obliterated; to disappear, vanish" has irregular assimilation of inm-
to imm- as an alternative. inmalasa "to become smooth; to glide; to slip away; to escape" also has immalasa as an
alternative. The only other form VII verbs in Hans Wehr beginning with -m- are inmalaḵa "to be pulled out, torn
out, wrenched" and inmāʿa "to be melted, to melt, to dissolve", which are not listed with imm- alternatives, but
might have them; if so, we should handle this generally. [DONE]
-- يَرَعَ yaraʕa yariʕu "to be a coward, to be chickenhearted" as an alternative form of يَرِعَ yariʕa yayraʕu (as given in
Wehr). [IMPLEMENTABLE USING OVERRIDES]
3. Implement individual override parameters for each paradigm part. See Module:fro-verb for an example of how to do this
generally. Note that {{temp|ar-conj-I}} and other of the older templates already had such individual override params.
[DONE]
Irregular verbs already implemented:
-- [ḥayya/ḥayiya yaḥyā "live" -- behaves like a normal final-weak verb
(e.g. past first singular ḥayītu) except in the past-tense parts with
vowel-initial endings (all the third person except for the third feminine
plural). The normal singular and dual endings have -yiya- in them, which
compresses to -yya-, with the normal endings the less preferred ones.
In masculine third plural, expected ḥayū is replaced by ḥayyū by
analogy to the -yy- parts, and the regular form is not given as an
alternant in John Mace. Barron's 201 verbs appears to have the regular
ḥayū as the part, however. Note also that final -yā appears with tall
alif. This appears to be a spelling convention of Arabic, also applying
in ḥayyā (form II, "to keep (someone) alive") and 'aḥyā (form IV,
"to animate, revive, give birth to, give new life to").] -- implemented
-- [ittaxadha yattaxidhu "take"] -- implemented
-- [sa'ala yas'alu "ask" with alternative jussive/imperative yasal/sal] -- implemented
-- [ra'ā yarā "see"] -- implemented
-- ['arā yurī "show"] -- implemented
-- ['akala ya'kulu "eat" with imperative kul] -- implemented
-- ['axadha ya'xudhu "take" with imperative xudh] -- implemented
-- ['amara ya'muru "order" with imperative mur] -- implemented
--]=]
local force_cat = false -- set to true for debugging
-- if true, always maintain manual translit during processing, and compare against full translit at the end
local debug_translit = false
local lang = require("Module:languages").getByCode("ar")
local m_links = require("Module:links")
local m_string_utilities = require("Module:string utilities")
local m_table = require("Module:table")
local ar_utilities = require("Module:ar-utilities")
local ar_nominals = require("Module:ar-nominals")
local iut = require("Module:inflection utilities")
local put = require("Module:parse utilities")
local pron_qualifier_module = "Module:pron qualifier"
local list_to_text = mw.text.listToText
local rfind = m_string_utilities.find
local rsubn = m_string_utilities.gsub
local rmatch = m_string_utilities.match
local rsplit = m_string_utilities.split
local usub = m_string_utilities.sub
local ulen = m_string_utilities.len
local u = m_string_utilities.char
local unpack = unpack or table.unpack -- Lua 5.2 compatibility
local dump = mw.dumpObject
-- Within this module, conjugations are the functions that do the actual
-- conjugating by creating the parts of a basic verb.
-- They are defined further down.
local conjugations = {}
-- hamza variants
local HAMZA = u(0x0621) -- hamza on the line (stand-alone hamza) = ء
local HAMZA_ON_ALIF = u(0x0623)
local HAMZA_ON_W = u(0x0624)
local HAMZA_UNDER_ALIF = u(0x0625)
local HAMZA_ON_Y = u(0x0626)
local HAMZA_ANY = "[" .. HAMZA .. HAMZA_ON_ALIF .. HAMZA_UNDER_ALIF .. HAMZA_ON_W .. HAMZA_ON_Y .. "]"
local HAMZA_PH = u(0xFFF0) -- hamza placeholder
local BAD = u(0xFFF1)
local BORDER = u(0xFFF2)
-- diacritics
local A = u(0x064E) -- fatḥa
local AN = u(0x064B) -- fatḥatān (fatḥa tanwīn)
local U = u(0x064F) -- ḍamma
local UN = u(0x064C) -- ḍammatān (ḍamma tanwīn)
local I = u(0x0650) -- kasra
local IN = u(0x064D) -- kasratān (kasra tanwīn)
local SK = u(0x0652) -- sukūn = no vowel
local SH = u(0x0651) -- šadda = gemination of consonants
local DAGGER_ALIF = u(0x0670)
local DIACRITIC_ANY_BUT_SH = "[" .. A .. I .. U .. AN .. IN .. UN .. SK .. DAGGER_ALIF .. "]"
-- Pattern matching short vowels
local AIU = "[" .. A .. I .. U .. "]"
-- Pattern matching short vowels or sukūn
local AIUSK = "[" .. A .. I .. U .. SK .. "]"
-- Pattern matching any diacritics that may be on a consonant
local DIACRITIC = SH .. "?" .. DIACRITIC_ANY_BUT_SH
-- translit_patterns
local vowels = "aeiouāēīōū"
local NV = "[^" .. vowels .. "]"
local dia = {a = A, i = I, u = U}
local undia = {[A] = "a", [I] = "i", [U] = "u", ["-"] = "-"}
-- various letters and signs
local ALIF = u(0x0627) -- ʾalif = ا
local AMAQ = u(0x0649) -- ʾalif maqṣūra = ى
local AMAD = u(0x0622) -- ʾalif madda = آ
local TAM = u(0x0629) -- tāʾ marbūṭa = ة
local T = u(0x062A) -- tāʾ = ت
local HYPHEN = u(0x0640)
local N = u(0x0646) -- nūn = ن
local W = u(0x0648) -- wāw = و
local Y = u(0x064A) -- yāʾ = ي
local S = "س"
local M = "م"
local LRM = u(0x200e) -- left-to-right mark
-- common combinations
local AH = A .. TAM
local AT = A .. T
local AA = A .. ALIF
local AAMAQ = A .. AMAQ
local AAH = AA .. TAM
local AAT = AA .. T
local II = I .. Y
local UU = U .. W
local AY = A .. Y
local AW = A .. W
local AYSK = AY .. SK
local AWSK = AW .. SK
local NA = N .. A
local NI = N .. I
local AAN = AA .. N
local AANI = AA .. NI
local AYNI = AYSK .. NI
local AWNA = AWSK .. NA
local AYNA = AYSK .. NA
local AYAAT = AY .. AAT
local UNU = "[" .. UN .. U .. "]"
local MA = M .. A
local MU = M .. U
local TA = T .. A
local TU = T .. U
local _I = ALIF .. I
local _U = ALIF .. U
local translit_cache = {
-- hamza variants
[HAMZA] = "ʔ",
[HAMZA_ON_ALIF] = "ʔ",
[HAMZA_ON_W] = "ʔ",
[HAMZA_UNDER_ALIF] = "ʔ",
[HAMZA_ON_Y] = "ʔ",
[HAMZA_PH] = "ʔ",
-- diacritics
[A] = "a",
[AN] = "an",
[U] = "u",
[UN] = "un",
[I] = "i",
[IN] = "in",
[SK] = "",
[SH] = "*", -- handled specially
[DAGGER_ALIF] = "ā",
-- various letters and signs
[""] = "",
[ALIF] = BAD, -- we should never be transliterating ALIF by itself, as its translit in isolation is ambiguous
[AMAQ] = BAD,
[AMAD] = "ʔā",
[TAM] = "",
[T] = "t",
[N] = "n",
[W] = "w",
[Y] = "y",
[S] = "s",
[M] = "m",
[LRM] = "",
-- common combinations
[AH] = "a",
[AT] = "at",
[AA] = "ā",
[AAMAQ] = "ā",
[AAH] = "āh",
[AAT] = "āt",
[II] = "ī",
[UU] = "ū",
[AY] = "ay",
[AW] = "aw",
[AYSK] = "ay",
[AWSK] = "aw",
[NA] = "na",
[NI] = "ni",
[AAN] = "ān",
[AANI] = "āni",
[AYNI] = "ayni",
[AWNA] = "awna",
[AYNA] = "ayna",
[AYAAT] = "ayāt",
[MA] = "ma",
[MU] = "mu",
[TA] = "ta",
[TU] = "tu",
[_I] = "i",
[_U] = "u",
}
local function transliterate(text)
local cached = translit_cache[text]
if cached then
if cached == BAD then
error(("Internal error: Unable to transliterate %s because explicitly marked as BAD"):format(text))
end
return cached
end
local tr = (lang:transliterate(text))
if not tr then
error(("Internal error: Unable to transliterate: %s"):format(text))
end
translit_cache[text] = tr
return tr
end
local all_person_number_list = {
"1s",
"2ms",
"2fs",
"3ms",
"3fs",
"2d",
"3md",
"3fd",
"1p",
"2mp",
"2fp",
"3mp",
"3fp"
}
local function make_person_number_slot_accel_list(list)
local slot_accel_list = {}
return slot_accel_list
end
local imp_person_number_list = {}
for _, pn in ipairs(all_person_number_list) do
if pn:find("^2") then
table.insert(imp_person_number_list, pn)
end
end
local passive_types = m_table.listToSet {
"pass", -- verb has both active and passive
"ipass", -- verb is active with impersonal passive
"nopass", -- verb is active-only
"onlypass", -- verb is passive-only
"onlypass-impers", -- verb itself is impersonal, meaning passive-only with impersonal passive
}
local indicator_flags = m_table.listToSet {
"nopast", "no_nonpast", "noimp",
"nocat", -- don't categorize or include annotations about this; useful in suppletive parts of verbs
"reduced", -- verb has assimilation/reduction of initial coronals
"altgem", -- form X with alternative past geminate forms with final-weak endings
}
export.potential_lemma_slots = {"past_3ms", "past_pass_3ms", "ind_3ms", "ind_pass_3ms", "imp_2ms"}
export.unsettable_slots = {}
for _, potential_lemma_slot in ipairs(export.potential_lemma_slots) do
table.insert(export.unsettable_slots, potential_lemma_slot .. "_linked")
end
-- We don't set the active participle directly for form I because we don't want stative verbs (with past vowel i or u)
-- to default to فَاعِل. Instead we set the special slot 'ap1' and later copy it to 'ap' for non-stative verbs. The user
-- meanwhile can explicitly request the فَاعِل form for active participles for stative verbs using `ap:+`.
table.insert(export.unsettable_slots, "ap1") -- primary default فَاعِل for form I active participles
table.insert(export.unsettable_slots, "ap2") -- secondary default فَعِيل for form I active participles (stative I)
table.insert(export.unsettable_slots, "ap3") -- secondary default فَعِل for form I active participles (stative II)
table.insert(export.unsettable_slots, "apcd") -- secondary default أَفْعَل for form I active participles (color/defect)
table.insert(export.unsettable_slots, "apan") -- secondary default فَعْلَان for form I active participles (in -ān)
table.insert(export.unsettable_slots, "pp2") -- secondary default فَعِيل for form I passive participles (same as ap2)
table.insert(export.unsettable_slots, "vn2") -- secondary default فِعَال for form III verbal nouns
export.unsettable_slots_set = m_table.listToSet(export.unsettable_slots)
local default_indicator_to_active_participle_slot = {
["+"] = "ap1",
["++"] = "ap2",
["+++"] = "ap3",
["+cd"] = "apcd",
["+an"] = "apan",
}
local slots_that_may_be_uncertain = {
vn = "verbal noun",
ap = "active participle",
}
-- Initialize all the slots for which we generate forms.
local function add_slots(alternant_multiword_spec)
alternant_multiword_spec.verb_slots = {
{"ap", "act|part"},
{"pp", "pass|part"},
{"vn", "vnoun"},
}
for _, unsettable_slot in ipairs(export.unsettable_slots) do
table.insert(alternant_multiword_spec.verb_slots, {unsettable_slot, "-"})
end
-- Add entries for a slot with person/number variants.
-- `slot_prefix` is the prefix of the slot, typically specifying the tense/aspect.
-- `tag_suffix` is a string listing the set of inflection tags to add after the person/number tags.
-- `person_number_list` is a list of the person/number slot suffixes to add to `slot_prefix`.
local function add_personal_slot(slot_prefix, tag_suffix, person_number_list)
for _, persnum in ipairs(person_number_list) do
local slot = slot_prefix .. "_" .. persnum
local accel = persnum:gsub("(.)", "%1|") .. tag_suffix
table.insert(alternant_multiword_spec.verb_slots, {slot, accel})
end
end
local tenses = {
{"past", "past|%s"},
{"ind", "non-past|%s|ind"},
{"sub", "non-past|%s|sub"},
{"juss", "non-past|%s|juss"},
}
for _, slot_accel in ipairs(tenses) do
local slot, accel = unpack(slot_accel)
for _, voice in ipairs {"act", "pass"} do
add_personal_slot(voice == "act" and slot or slot .. "_pass", accel:format(voice),
all_person_number_list)
end
end
add_personal_slot("imp", "imp", imp_person_number_list)
alternant_multiword_spec.verb_slots_map = {}
for _, slot_accel in ipairs(alternant_multiword_spec.verb_slots) do
local slot, accel = unpack(slot_accel)
alternant_multiword_spec.verb_slots_map[slot] = accel
end
end
local overridable_stems = {}
local slot_override_param_mods = {
footnote = {
item_dest = "footnotes",
store = "insert",
},
alt = {},
t = {
-- [[Module:links]] expects the gloss in "gloss".
item_dest = "gloss",
},
gloss = {},
g = {
-- [[Module:links]] expects the genders in "g". `sublist = true` automatically splits on comma (optionally
-- with surrounding whitespace).
item_dest = "genders",
sublist = true,
},
pos = {},
lit = {},
id = {},
-- Qualifiers and labels
q = {
type = "qualifier",
},
qq = {
type = "qualifier",
},
l = {
type = "labels",
},
ll = {
type = "labels",
},
}
local function generate_obj(formval, parse_err, prefix, is_slot_override)
local val, uncertain = formval:match("^(.*)(%?)$")
val = val or formval
uncertain = not not uncertain
local ar, translit = val:match("^(.*)//(.*)$")
if not ar then
ar = val
end
if ar == "" then
if uncertain then
ar = "?"
else
error(("Can't specify blank value for override for %s override '%s'"):format(
is_slot_override and "slot" or "stem", prefix))
end
end
return {form = ar, translit = translit, uncertain = uncertain}
end
local function parse_inline_modifiers(comma_separated_group, parse_err, prefix, is_slot_override)
local function this_generate_obj(formval, parse_err)
return generate_obj(formval, parse_err, prefix, is_slot_override)
end
return put.parse_inline_modifiers_from_segments {
group = comma_separated_group,
props = {
param_mods = slot_override_param_mods,
parse_err = parse_err,
generate_obj = this_generate_obj,
pre_normalize_modifiers = function(data)
local modtext = data.modtext
modtext = modtext:match("^(%[.*%])$")
if modtext then
return ("<footnote:%s>"):format(modtext)
end
return data.modtext
end,
},
}
end
local function allow_multiple_values_for_override(comma_separated_groups, data, is_slot_override)
local retvals = {}
for _, comma_separated_group in ipairs(comma_separated_groups) do
local retval
if is_slot_override then
retval = parse_inline_modifiers(comma_separated_group, data.parse_err)
else
retval = generate_obj(comma_separated_group[1], data.parse_err, data.prefix, is_slot_override)
retval.footnotes = data.fetch_footnotes(comma_separated_group)
end
table.insert(retvals, retval)
end
for _, form in ipairs(retvals) do
if form.form == "+" or default_indicator_to_active_participle_slot[form.form] then
if form.form ~= "+" and default_indicator_to_active_participle_slot[form.form] and not is_slot_override then
error(("Stem override '%s' cannot use %s to request a secondary default"):format(
data.prefix, form.form))
end
data.base.slot_override_uses_default[data.prefix] = true
end
end
for _, form in ipairs(retvals) do
if form.form == "-" then
data.base.slot_explicitly_missing[data.prefix] = true
break
end
end
if data.base.slot_explicitly_missing[data.prefix] then
for _, form in ipairs(retvals) do
if form.form ~= "-" then
data.parse_err(("For slot or stem '%s', saw both - and a value other than -, which isn't allowed"):
format(data.prefix))
end
end
return nil
end
return retvals
end
local function simple_choice(choices)
return function(separated_groups, data)
if #separated_groups > 1 then
data.parse_err("For spec '" .. data.prefix .. ":', only one value currently allowed")
end
if #separated_groups[1] > 1 then
data.parse_err("For spec '" .. data.prefix .. ":', no footnotes currently allowed")
end
local choice = separated_groups[1][1]
if not m_table.contains(choices, choice) then
data.parse_err("For spec '" .. data.prefix .. ":', saw value '" .. choice .. "' but expected one of '" ..
table.concat(choices, ",") .. "'")
end
return choice
end
end
for _, overridable_stem in ipairs {
"past",
"past_v",
"past_c",
"past_pass",
"past_pass_v",
"past_pass_c",
"nonpast",
"nonpast_v",
"nonpast_c",
"nonpast_pass",
"nonpast_pass_v",
"nonpast_pass_c",
"imp",
"imp_v",
"imp_c",
} do
overridable_stems[overridable_stem] = allow_multiple_values_for_override
end
overridable_stems.past_final_weak_vowel = simple_choice { "ay", "aw", "ī", "ū" }
overridable_stems.past_pass_final_weak_vowel = simple_choice { "ay", "aw", "ī", "ū" }
overridable_stems.nonpast_final_weak_vowel = simple_choice { "ā", "ī", "ū" }
overridable_stems.nonpast_pass_final_weak_vowel = simple_choice { "ā", "ī", "ū" }
-------------------------------------------------------------------------------
-- Utility functions --
-------------------------------------------------------------------------------
-- version of rsubn() that discards all but the first return value
local function rsub(term, foo, bar)
return (rsubn(term, foo, bar))
end
-- version of rsubn() that returns a 2nd argument boolean indicating whether a substitution was made.
local function rsubb(term, foo, bar)
local retval, nsubs = rsubn(term, foo, bar)
return retval, nsubs > 0
end
-- Concatenate one or more strings or form objects.
local function q(...)
local not_all_strings = debug_translit
local has_manual_translit = debug_translit
for i = 1, select("#", ...) do
local argt = select(i, ...)
if not argt then
error(("Internal error: Saw nil at index %s: %s"):format(i, dump({...})))
end
if type(argt) ~= "string" then
not_all_strings = true
if argt.translit then
has_manual_translit = true
break
end
end
end
if not not_all_strings then
-- just strings, concatenate directly
return table.concat({...})
end
local formvals = {}
local translit = has_manual_translit and {} or nil
local footnotes
for i = 1, select("#", ...) do
local argt = select(i, ...)
if type(argt) == "string" then
formvals[i] = argt
if has_manual_translit then
translit[i] = transliterate(argt)
end
else
formvals[i] = argt.form
if has_manual_translit then
translit[i] = argt.translit or transliterate(argt.form)
end
footnotes = iut.combine_footnotes(footnotes, argt.footnotes)
end
end
-- FIXME: Do we want to support other properties?
return {
form = table.concat(formvals),
translit = has_manual_translit and table.concat(translit) or nil,
footnotes = footnotes,
}
end
-- Return the formval associated with `rad` (a radical or past/non-past vowel, either a string or form object).
local function rget(rad)
if type(rad) == "string" then
return rad
elseif type(rad) == "table" then
return rad.form
else
error(("Internal error: Unexpected type for radical or past/non-past vowel: %s"):format(dump(rad)))
end
end
export.rget = rget -- for use in [[Module:ar-headword]]
-- Return the footnotes associated with `rad` (a radical or past/non-past vowel, either a string or form object).
local function rget_footnotes(rad)
if type(rad) == "string" then
return nil
elseif type(rad) == "table" then
return rad.footnotes
else
error(("Internal error: Unexpected type for radical or past/non-past vowel: %s"):format(dump(rad)))
end
end
-- Return true if the formval associated with `rad` (a radical or past/non-past vowel, either a string or form object)
-- is `val`.
local function req(rad, val)
return rget(rad) == val
end
-- Map `vow` (a past/non-past vowel, either a string or form object without translit) by passing the formval through
-- `fn`. Don't call this on radicals because they may have manual translit and it isn't clear how to handle that.
local function map_vowel(vow, fn)
if type(vow) == "string" then
return fn(vow)
elseif type(vow) == "table" then
return {form = fn(vow.form), footnotes = vow.footnotes}
else
error(("Internal error: Unexpected type for past/non-past vowel: %s"):format(dump(vow)))
end
end
local function get_radicals_3(vowel_spec)
return vowel_spec.rad1, vowel_spec.rad2, vowel_spec.rad3, vowel_spec.past, vowel_spec.nonpast
end
local function get_radicals_4(vowel_spec)
return vowel_spec.rad1, vowel_spec.rad2, vowel_spec.rad3, vowel_spec.rad4
end
local function is_final_weak(base, vowel_spec)
return vowel_spec.weakness == "final-weak" or base.form == "XV"
end
local function link_term(text, face, id)
return m_links.full_link({lang = lang, term = text, tr = "-", id = id}, face)
end
local function tag_text(text, tag, class)
return m_links.full_link({lang = lang, alt = text, tr = "-"})
end
local function track(page)
require("Module:debug/track")("ar-verb/" .. page)
return true
end
local function track_if_ar_conj(base, page)
if base.alternant_multiword_spec.source_template == "ar-conj" then
require("Module:debug/track")("ar-verb/" .. page)
end
return true
end
local function reorder_shadda(word)
-- shadda+short-vowel (including tanwīn vowels, i.e. -an -in -un) gets
-- replaced with short-vowel+shadda during NFC normalisation, which
-- MediaWiki does for all Unicode strings; however, it makes various
-- processes inconvenient, so undo it.
word = rsub(word, "(" .. DIACRITIC_ANY_BUT_SH .. ")" .. SH, SH .. "%1")
return word
end
-------------------------------------------------------------------------------
-- Basic functions to inflect tenses --
-------------------------------------------------------------------------------
local function skip_slot(base, slot, allow_overrides)
if base.slot_explicitly_missing[slot] then
return true
end
if not allow_overrides and base.slot_overrides[slot] and not base.slot_override_uses_default[slot] then
-- Skip any slots for which there are overrides, except those that request the default value using +, ++, etc.
return true
end
if base.passive == "nopass" and (slot == "pp" or slot:find("_pass")) then
return true
elseif base.passive == "onlypass" and slot ~= "pp" and slot ~= "vn" and not slot:find("_pass") then
return true
elseif base.passive == "ipass" and slot:find("_pass") and not slot:find("3ms") then
return true
elseif base.passive == "onlypass-impers" and slot ~= "pp" and slot ~= "vn" and (not slot:find("_pass") or
slot:find("_pass") and not slot:find("3ms")) then
return true
end
if base.nopast and slot:find("^past_") then
return true
end
if base.noimp and slot:find("^imp_") then
return true
end
if base.no_nonpast and (slot:find("^ind_") or slot:find("^sub_") or slot:find("^juss")) then
return true
end
return false
end
local function basic_combine_stem_ending(stem, ending)
return stem .. ending
end
local function basic_combine_stem_ending_tr(stem, ending)
return stem .. ending
end
-- Concatenate `prefixes`, `stems` and `endings` (any of which may be an abbreviate form list, i.e. strings, form
-- objects or lists of strings or form objects) and store into `slot`. If a user-supplied override exists for the slot,
-- nothing will happen unless `allow_overrides` is provided.
local function add3(base, slot, prefixes, stems, endings, allow_overrides)
if skip_slot(base, slot, allow_overrides) then
return
end
-- Optimization since the prefixes are almost always single strings.
if type(prefixes) == "string" then
local function do_combine_stem_ending(stem, ending)
return prefixes .. stem .. ending
end
local function do_combine_stem_ending_tr(stem, ending)
return transliterate(prefixes) .. stem .. ending
end
iut.add_forms(base.forms, slot, stems, endings, do_combine_stem_ending, transliterate,
do_combine_stem_ending_tr, base.form_footnotes)
else
iut.add_multiple_forms(base.forms, slot, {prefixes, stems, endings}, basic_combine_stem_ending, transliterate,
basic_combine_stem_ending_tr, base.form_footnotes)
end
end
-- Insert one or more forms in `form_or_forms` into `slot`. `form_or_forms` is an abbreviated form list (see comment at
-- top of [[Module:inflection utilities]]). If a user-supplied override exists for the slot, nothing will happen unless
-- `allow_overrides` is provided. BEWARE: One form object should never occur in two different slots, or twice in a given
-- slot; if taking a form object from an existing slot, make sure to shallowCopy() it.
local function insert_form_or_forms(base, slot, form_or_forms, allow_overrides, uncertain)
if not skip_slot(base, slot, allow_overrides) then
-- Some optimizations of the most common case of inserting a single string.
if type(form_or_forms) == "string" and not base.form_footnotes then
form_or_forms = {form = form_or_forms, uncertain = uncertain}
iut.insert_form(base.forms, slot, form_or_forms)
else
local list = iut.convert_to_general_list_form(form_or_forms, base.form_footnotes)
if uncertain then
for _, formobj in ipairs(list) do
formobj.uncertain = true
end
end
iut.insert_forms(base.forms, slot, list)
end
end
end
-- Insert `string_or_form` into both the ap2 and pp2 slots, shallowCopying a form object to make sure no form objects
-- occur in two slots.
local function insert_ap2_pp2(base, string_or_form)
insert_form_or_forms(base, "ap2", string_or_form)
if type(string_or_form) == "table" then
string_or_form = m_table.shallowCopy(string_or_form)
end
insert_form_or_forms(base, "pp2", string_or_form)
end
-- Convert `stemforms` (a string, a form object, or a list of strings and/or form objects) into "general form" (a list
-- of form objects) and map `fn` over the list of objects. `fn` is passed two arguments (form value and translit) and
-- should likewise return the new form value and translit. Footnotes will be preserved. FIXME: Preserve other metadata.
local function map_general(stemforms, fn)
return iut.map_forms(iut.convert_to_general_list_form(stemforms), fn)
end
-- Similar to map_general() except that `fn` should return a single value (one or more strings or form objects), instead
-- of two values (form value and translit), and the resulting value(s) from all calls to `fn` will be flattened to
-- construct the overall return value. Footnotes will be preserved. FIXME: Preserve other metadata.
local function flatmap_general(stemforms, fn)
return iut.flatmap_forms(iut.convert_to_general_list_form(stemforms), fn)
end
-- Given user-supplied stem overrides in `base`, construct any derived stem overrides (e.g. vowel-specific or
-- consonant-specific variants), and truncate initial y-/ي- in any non-past overrides.
local function construct_stems(base)
local stems = base.stem_overrides
stems.past_v = stems.past_v or stems.past
stems.past_c = stems.past_c or stems.past
stems.past_pass_v = stems.past_pass_v or stems.past_pass
stems.past_pass_c = stems.past_pass_c or stems.past_pass
stems.nonpast_v = stems.nonpast_v or stems.nonpast
stems.nonpast_c = stems.nonpast_c or stems.nonpast
stems.nonpast_pass_v = stems.nonpast_pass_v or stems.nonpast_pass
stems.nonpast_pass_c = stems.nonpast_pass_c or stems.nonpast_pass
stems.imp_v = stems.imp_v or stems.imp
stems.imp_c = stems.imp_c or stems.imp
local function truncate_nonpast_initial_cons(stem_type, form, translit)
if form == "+" then
return form, translit
end
if not form:find("^" .. Y) then
error(("Form value %s for stem type '%s' should begin with ي"):format(form, stem_type))
end
form = form:gsub("^" .. Y, "")
if translit then
if not translit:find("^y") then
error(("Translit value %s for stem type '%s' should begin with y"):format(translit, stem_type))
end
translit = translit:gsub("^y", "")
end
return form, translit
end
for _, nonpast_stem_type in ipairs { "nonpast_v", "nonpast_c", "nonpast_pass_v", "nonpast_pass_c" } do
if stems[nonpast_stem_type] then
stems[nonpast_stem_type] = map_general(stems[nonpast_stem_type], function(form, translit)
return truncate_nonpast_initial_cons(nonpast_stem_type, form, translit)
end)
end
end
end
-- Given user-specified overrides for stem `stemname`, return overrides with occurrences of + replaced by
-- `default_stem`. If no overrides, return `default_stem`, or {} if no default.
local function override_stem_if_needed(base, stemname, default_stem)
local overrides = base.stem_overrides[stemname]
if not overrides then
return default_stem or {}
end
return map_general(overrides, function(form, translit)
if form ~= "+" and default_indicator_to_active_participle_slot[form] then
error(("Stem overrides cannot use secondary default indicators but saw %s in stem override '%s'"):format(
form, stemname))
end
if form == "+" then
if translit then
error(("Cannot supply manual translit along with + for stem override '%s'"):format(stemname))
end
if not default_stem then
error(("Cannot use + for stem override '%s' because no default is available"):format(stemname))
end
if type(default_stem) ~= "string" then
error(("Internal error: Default stem for '%s' is not a string: %s"):format(stemname, dump(default_stem)))
end
return default_stem
end
return form, translit
end)
end
-------------------------------------------------------------------------------
-- Properties of different verbal forms --
-------------------------------------------------------------------------------
local allowed_vforms = {"I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX",
"X", "XI", "XII", "XIII", "XIV", "XV", "Iq", "IIq", "IIIq", "IVq"}
local allowed_vforms_set = m_table.listToSet(allowed_vforms)
local allowed_vforms_with_weakness = m_table.shallowCopy(allowed_vforms)
-- The user needs to be able to explicitly specify that a form-I verb (specifically one whose initial radical is و) is
-- sound. Cf. wajiʕa yawjaʕu (not #yajaʕu) "to ache, to hurt". In general, i~a and u~u verbs whose initial radical is و
-- seem to not assimilate the first radical; cf. وقح "to be shameless", variously waqaḥa~yaqiḥu, waquḥa~yawquḥu and
-- waqiḥa~yawqaḥu, whereas a~i verbs (wafaḍa~yafiḍu "to rush"), i~i verbs (wafiqa~yafiqu "to be proper, to be suitable")
-- and a~a verbs (waḍaʕa~yaḍaʕu "to set down, to place") do assimilate. But there are naturally exceptions, e.g.
-- waṭiʔa~yaṭaʔu "to tread, to trample"; wasiʕa~yasaʕu "to be spacious; to be well-off"; waṯiʔa~yaṯaʔu "to get bruised,
-- to be sprained". Also beware of waniya~yawnā "to be faint; to languish", which is sound in the first radical and
-- final-weak in the last radical. Nonetheless, the regularity of the patterns mentioned above suggest we should provide
-- them as defaults.
-- Note that there are other cases of unexpectedly sound verbs, e.g. izdawaja~yazdawiju "to be in pairs", layisa~yalyasu
-- "to be valiant, to be brave", ʔaḥwaja~yuḥwiju "to need", istahwana~yastahwinu "to consider easy", sawisa~yaswasu "to
-- be or become moth-eaten or worm-eaten" (vs. sāsa~yasūsu "to govern, to rule" from the same radicals), ʕawira~yaʕwaru
-- "to be one-eyed", istajwaba~yastajwibu "to interrogate", etc. But in these cases there is no need for explicit user
-- specification as the lemma itself specifies the unexpected soundness.
for _, form_with_weakness in ipairs { "I-sound", "I-assimilated", "none-sound", "none-hollow", "none-geminate",
"none-final-weak" } do
table.insert(allowed_vforms_with_weakness, form_with_weakness)
end
local allowed_vforms_with_weakness_set = m_table.listToSet(allowed_vforms_with_weakness)
local function vform_supports_final_weak(vform)
return vform ~= "XI" and vform ~= "XV" and vform ~= "IVq"
end
local function vform_supports_geminate(vform)
return vform == "I" or vform == "III" or vform == "IV" or vform == "VI" or vform == "VII" or vform == "VIII" or
vform == "X"
end
local function vform_supports_hollow(vform)
return vform == "I" or vform == "IV" or vform == "VII" or vform == "VIII" or vform == "X"
end
local function vform_probably_impersonal_passive(vform, weakness, past_vowel, nonpast_vowel)
return vform == "I" and req(past_vowel, I) or vform == "V" or vform == "VI" or vform == "X" or vform == "IIq"
end
local function vform_probably_full_passive(vform)
return vform == "II" or vform == "III" or vform == "IV" or vform == "Iq"
end
local function vform_probably_no_passive(vform, weakness, past_vowel, nonpast_vowel)
return vform == "I" and req(past_vowel, U) or vform == "VII" or vform == "IX" or
vform == "XI" or vform == "XII" or vform == "XIII" or vform == "XIV" or vform == "XV" or
vform == "IIIq" or vform == "IVq"
end
-- Active vforms II, III, IV, Iq use non-past prefixes in -u- instead of -a-.
local function prefix_vowel_from_vform(vform)
if vform == "II" or vform == "III" or vform == "IV" or vform == "Iq" then
return "u"
else
return "a"
end
end
-- True if the active non-past takes a-vocalization rather than i-vocalization in its last syllable.
local function vform_nonpast_a_vowel(vform)
return vform == "V" or vform == "VI" or vform == "XV" or vform == "IIq"
end
-- True if the `passive` spec indicates a passive-only verb.
local function is_passive_only(passive)
return passive == "onlypass" or passive == "onlypass-impers"
end
export.is_passive_only = is_passive_only -- for use in [[Module:ar-headword]]
-------------------------------------------------------------------------------
-- Properties of specific sounds --
-------------------------------------------------------------------------------
-- Is radical wāw (و) or yāʾ (ي)?
local function is_waw_ya(rad)
return req(rad, W) or req(rad, Y)
end
-- Check that radical is wāw (و) or yāʾ (ي), error if not
local function check_waw_ya(rad)
if not is_waw_ya(rad) then
error("Expecting weak radical: '" .. rget(rad) .. "' should be " .. W .. " or " .. Y)
end
end
-- Form-I verb حيّ or حيي and form-X verb استحيا or استحى
local function hayy_radicals(rad1, rad2, rad3)
return req(rad1, "ح") and req(rad2, Y) and is_waw_ya(rad3)
end
-- FUCK ME HARD. "Lua error at line 1514: main function has more than 200 local variables".
local function create_conjugations()
-------------------------------------------------------------------------------
-- Radicals associated with various irregular verbs --
-------------------------------------------------------------------------------
-- Form-I verb أخذ or form-VIII verb اتخذ
local function axadh_radicals(rad1, rad2, rad3)
return req(rad1, HAMZA) and req(rad2, "خ") and req(rad3, "ذ")
end
-- Form-I verb whose imperative has a reduced form: أكل and أخذ and أمر. Return "shortonly" if only
-- short-form imperatives exist (أكل and أخذ) or "shortlong" if long-form imperatives also exist (أمر);
-- they are used after a clitic like فَ and وَ.
local function reduced_imperative_verb(rad1, rad2, rad3)
return axadh_radicals(rad1, rad2, rad3) and "shortonly" or
req(rad1, HAMZA) and req(rad2, "ك") and req(rad3, "ل") and "shortonly" or
req(rad1, HAMZA) and req(rad2, "م") and req(rad3, "ر") and "shortlong"
end
-- Form-I verb رأى and form-IV verb أرى
local function raa_radicals(rad1, rad2, rad3)
return req(rad1, "ر") and req(rad2, HAMZA) and is_waw_ya(rad3)
end
-- Form-I verb سأل
local function saal_radicals(rad1, rad2, rad3)
return req(rad1, "س") and req(rad2, HAMZA) and req(rad3, "ل")
end
-- Form-I verb كان
local function kaan_radicals(rad1, rad2, rad3)
return req(rad1, "ك") and req(rad2, W) and req(rad3, N)
end
-------------------------------------------------------------------------------
-- Sets of past endings --
-------------------------------------------------------------------------------
-- The 13 endings of the sound/hollow/geminate past tense.
local past_endings = {
-- singular
SK .. TU, SK .. TA, SK .. "تِ", A, A .. "تْ",
--dual
SK .. "تُمَا", AA, A .. "تَا",
-- plural
SK .. "نَا", SK .. "تُمْ",
-- shadda + vowel diacritic ends up in the wrong order due to Unicode
-- bug, so keep them separate to avoid this
SK .. "تُن" .. SH .. A, UU .. ALIF, SK .. "نَ"
}
-- Make endings for final-weak past in -aytu or -awtu. AYAW is AY or AW as appropriate. Note that AA and AW are
-- global variables.
local function make_past_endings_ay_aw(ayaw, third_sg_masc)
return {
-- singular
ayaw .. SK .. TU, ayaw .. SK .. TA, ayaw .. SK .. "تِ",
third_sg_masc, A .. "تْ",
--dual
ayaw .. SK .. "تُمَا", ayaw .. AA, A .. "تَا",
-- plural
ayaw .. SK .. "نَا", ayaw .. SK .. "تُمْ",
-- shadda + vowel diacritic ends up in the wrong order due to Unicode
-- bug, so keep them separate to avoid this
ayaw .. SK .. "تُن" .. SH .. A, AW .. SK .. ALIF, ayaw .. SK .. "نَ"
}
end
-- past final-weak -aytu endings
local past_endings_ay = make_past_endings_ay_aw(AY, AAMAQ)
-- past final-weak -awtu endings
local past_endings_aw = make_past_endings_ay_aw(AW, AA)
-- used for alternative endings for form-X geminate verbs like اِسْتَمَرَّ
local past_endings_ay_12_person_only = {
-- singular
AY .. SK .. TU, AY .. SK .. TA, AY .. SK .. "تِ",
{}, {},
--dual
AY .. SK .. "تُمَا", {}, {},
-- plural
AY .. SK .. "نَا", AY .. SK .. "تُمْ",
-- shadda + vowel diacritic ends up in the wrong order due to Unicode
-- bug, so keep them separate to avoid this
AY .. SK .. "تُن" .. SH .. A, {}, {},
}
-- Make endings for final-weak past in -ītu or -ūtu. IIUU is ī or ū as appropriate. Note that AA and UU are global
-- variables.
local function make_past_endings_ii_uu(iiuu)
return {
-- singular
iiuu .. TU, iiuu .. TA, iiuu .. "تِ", iiuu .. A, iiuu .. A .. "تْ",
--dual
iiuu .. "تُمَا", iiuu .. AA, iiuu .. A .. "تَا",
-- plural
iiuu .. "نَا", iiuu .. "تُمْ",
-- shadda + vowel diacritic ends up in the wrong order due to Unicode
-- bug, so keep them separate to avoid this
iiuu .. "تُن" .. SH .. A, UU .. ALIF, iiuu .. "نَ"
}
end
-- past final-weak -ītu endings
local past_endings_ii = make_past_endings_ii_uu(II)
-- past final-weak -ūtu endings
local past_endings_uu = make_past_endings_ii_uu(UU)
-------------------------------------------------------------------------------
-- Sets of non-past prefixes and endings --
-------------------------------------------------------------------------------
local nonpast_prefix_consonants = {
-- singular
HAMZA, T, T, Y, T,
-- dual
T, Y, T,
-- plural
N, T, T, Y, Y
}
-- There are only five distinct endings in all non-past verbs. Make any set of non-past endings given these five
-- distinct endings.
local function make_nonpast_endings(null, fem, dual, pl, fempl)
return {
-- singular
null, null, fem, null, null,
-- dual
dual, dual, dual,
-- plural
null, pl, fempl, pl, fempl
}
end
-- endings for non-past indicative
local ind_endings = make_nonpast_endings(
U,
II .. NA,
AANI,
UU .. NA,
SK .. NA
)
-- Make the endings for non-past subjunctive/jussive, given the vowel diacritic used in "null" endings
-- (1s/2ms/3ms/3fs/1p).
local function make_sub_juss_endings(dia_null)
return make_nonpast_endings(
dia_null,
II,
AA,
UU .. ALIF,
SK .. NA
)
end
-- endings for non-past subjunctive
local sub_endings = make_sub_juss_endings(A)
-- endings for non-past jussive
local juss_endings = make_sub_juss_endings(SK)
-- endings for alternative geminate non-past jussive in -a; same as subjunctive
local juss_endings_alt_a = sub_endings
-- endings for alternative geminate non-past jussive in -i
local juss_endings_alt_i = make_sub_juss_endings(I)
-- Endings for final-weak non-past indicative in -ā. Note that AY, AW and AAMAQ are global variables.
local ind_endings_aa = make_nonpast_endings(
AAMAQ,
AYSK .. NA,
AY .. AANI,
AWSK .. NA,
AYSK .. NA
)
-- Make endings for final-weak non-past indicative in -ī or -ū; IIUU is ī or ū as appropriate. Note that II and UU
-- are global variables.
local function make_ind_endings_ii_uu(iiuu)
return make_nonpast_endings(
iiuu,
II .. NA,
iiuu .. AANI,
UU .. NA,
iiuu .. NA
)
end
-- endings for final-weak non-past indicative in -ī
local ind_endings_ii = make_ind_endings_ii_uu(II)
-- endings for final-weak non-past indicative in -ū
local ind_endings_uu = make_ind_endings_ii_uu(UU)
-- Endings for final-weak non-past subjunctive in -ā. Note that AY, AW, ALIF, AAMAQ are global variables.
local sub_endings_aa = make_nonpast_endings(
AAMAQ,
AYSK,
AY .. AA,
AWSK .. ALIF,
AYSK .. NA
)
-- Make endings for final-weak non-past subjunctive in -ī or -ū. IIUU is ī or ū as appropriate. Note that AA, II,
-- UU, ALIF are global variables.
local function make_sub_endings_ii_uu(iiuu)
return make_nonpast_endings(
iiuu .. A,
II,
iiuu .. AA,
UU .. ALIF,
iiuu .. NA
)
end
-- endings for final-weak non-past subjunctive in -ī
local sub_endings_ii = make_sub_endings_ii_uu(II)
-- endings for final-weak non-past subjunctive in -ū
local sub_endings_uu = make_sub_endings_ii_uu(UU)
-- endings for final-weak non-past jussive in -ā
local juss_endings_aa = make_nonpast_endings(
A,
AYSK,
AY .. AA,
AWSK .. ALIF,
AYSK .. NA
)
-- Make endings for final-weak non-past jussive in -ī or -ū. IU is short i or u, IIUU is long ī or ū as appropriate.
-- Note that AA, II, UU, ALIF are global variables.
local function make_juss_endings_ii_uu(iu, iiuu)
return make_nonpast_endings(
iu,
II,
iiuu .. AA,
UU .. ALIF,
iiuu .. NA
)
end
-- endings for final-weak non-past jussive in -ī
local juss_endings_ii = make_juss_endings_ii_uu(I, II)
-- endings for final-weak non-past jussive in -ū
local juss_endings_uu = make_juss_endings_ii_uu(U, UU)
-------------------------------------------------------------------------------
-- Sets of imperative endings --
-------------------------------------------------------------------------------
-- Extract the second person jussive endings to get corresponding imperative endings.
local function imperative_endings_from_jussive(endings)
return {endings[2], endings[3], endings[6], endings[10], endings[11]}
end
-- normal imperative endings
local imp_endings = imperative_endings_from_jussive(juss_endings)
-- alternative geminate imperative endings in -a
local imp_endings_alt_a = imperative_endings_from_jussive(juss_endings_alt_a)
-- alternative geminate imperative endings in -i
local imp_endings_alt_i = imperative_endings_from_jussive(juss_endings_alt_i)
-- final-weak imperative endings in -ā
local imp_endings_aa = imperative_endings_from_jussive(juss_endings_aa)
-- final-weak imperative endings in -ī
local imp_endings_ii = imperative_endings_from_jussive(juss_endings_ii)
-- final-weak imperative endings in -ū
local imp_endings_uu = imperative_endings_from_jussive(juss_endings_uu)
-------------------------------------------------------------------------------
-- Basic functions to inflect tenses --
-------------------------------------------------------------------------------
-- Add to `base` the inflections for the tense indicated by `tense` (the prefix in the slot names, e.g. 'past'
-- or 'juss_pass'), formed by combining the `prefixes`, `stems` and `endings`. Each of `prefixes`, `stems` and
-- `endings` is either a sequence of 5 (for the imperative) or 13 (for other tenses) abbreviated form lists (each of
-- which is either a string, a form object, or a list of strings and/or form objects; see
-- [[Module:inflection utilities]] for more info). Alternatively, any of `prefixes`, `stems` or `endings` can be a
-- single-element list containing an abbreviated form list, with an additional key `all_same` set to true, or (as a
-- special case) a single string; in the latter cases, the same value is used for all 5 or 13 slots. If existing
-- inflections already exist, they will be added to, not overridden. `pnums` is the list of person/number slot name
-- suffixes, which must match up with the elements in `prefixes`, `stems` and `endings` (i.e. 5 for imperative, 13
-- otherwise).
local function inflect_tense_1(base, tense, prefixes, stems, endings, pnums)
if not prefixes or not stems or not endings then
return
end
local function verify_affixes(affixname, affixes)
local function interr(msg)
error(("Internal error: For tense '%s', '%s' %s: %s"):format(tense, affixname, msg, dump(affixes)))
end
if type(affixes) == "string" then
-- do nothing
elseif type(affixes) ~= "table" then
interr("is not a table or string")
elseif affixes.all_same then
if #affixes ~= 1 then
interr(("with all_same = true should have length 1 but has length %s"):format(#affixes))
end
else
if #affixes ~= #pnums then
interr(("should have length %s but has length %s"):format(#pnums, #affixes))
end
end
end
verify_affixes("prefixes", prefixes)
verify_affixes("stems", stems)
verify_affixes("endings", endings)
local function get_affix(affixes, i)
if type(affixes) == "string" then
return affixes
elseif affixes.all_same then
return affixes[1]
else
return affixes[i]
end
end
for i, pnum in ipairs(pnums) do
local prefix = get_affix(prefixes, i)
local stem = get_affix(stems, i)
local ending = get_affix(endings, i)
local slot = tense .. "_" .. pnum
add3(base, slot, prefix, stem, ending)
end
end
-- Add to `base` the inflections for the tense indicated by `tense` (the prefix in the slot names, e.g. 'past'
-- or 'juss_pass'), formed by combining the `prefixes`, `stems` and `endings`. This is a simple wrapper around
-- inflect_tense_1() that applies to all tenses other than the imperative; see inflect_tense_1() for more
-- information about the parameters.
local function inflect_tense(base, tense, prefixes, stems, endings)
inflect_tense_1(base, tense, prefixes, stems, endings, all_person_number_list)
end
-- Like inflect_tense() but for the imperative, which has only five parts instead of 13 and no prefixes.
local function inflect_tense_imp(base, stems, endings)
inflect_tense_1(base, "imp", "", stems, endings, imp_person_number_list)
end
-------------------------------------------------------------------------------
-- Functions to inflect the past tense --
-------------------------------------------------------------------------------
-- Generate past verbs using specified vowel and consonant stems; works for sound, assimilated, hollow, and geminate
-- verbs, active and passive.
local function past_2stem_conj(base, tense, v_stem, c_stem, footnote_12)
local passive = tense:find("_pass") and "_pass" or ""
-- Override stems with user-specified stems if available.
v_stem = override_stem_if_needed(base, "past" .. passive .. "_v", v_stem)
local c_stem_12 = c_stem
if footnote_12 then
c_stem_12 = iut.combine_form_and_footnotes(c_stem_12, footnote_12)
end
c_stem_12 = override_stem_if_needed(base, "past" .. passive .. "_c", c_stem_12)
local c_stem_3 = override_stem_if_needed(base, "past" .. passive .. "_c", c_stem)
inflect_tense(base, tense, "", {
-- singular
c_stem_12, c_stem_12, c_stem_12, v_stem, v_stem,
--dual
c_stem_12, v_stem, v_stem,
-- plural
c_stem_12, c_stem_12, c_stem_12, v_stem, c_stem_3
}, past_endings)
end
-- Generate past verbs using single specified stem; works for sound and assimilated verbs, active and passive.
local function past_1stem_conj(base, tense, stem)
past_2stem_conj(base, tense, stem, stem)
end
-------------------------------------------------------------------------------
-- Functions to inflect non-past tenses --
-------------------------------------------------------------------------------
-- Generate non-past conjugation, with two stems, for vowel-initial and consonant-initial endings, respectively.
-- Useful for active and passive; for all forms; for all weaknesses (sound, assimilated, hollow, final-weak and
-- geminate) and for all types of non-past (indicative, subjunctive, jussive) except for the imperative. (There is a
-- separate wrapper function below for geminate jussives because they have three alternants.) Both stems may be the
-- same, e.g. for sound verbs.
-- `prefix_vowel` will be either "a" or "u". `endings` should be an array of 13 items. If `endings` is nil or
-- omitted, infer the endings from the tense. If `jussive` is true, or `endings` is nil and `tense` indicatives
-- jussive, use the jussive pattern of vowel/consonant stems (different from the normal ones).
local function nonpast_2stem_conj(base, tense, prefix_vowel, v_stem, c_stem, endings, jussive)
local passive = tense:find("_pass") and "_pass" or ""
-- Override stems with user-specified stems if available.
v_stem = override_stem_if_needed(base, "nonpast" .. passive .. "_v",
v_stem and q(dia[prefix_vowel], v_stem) or nil)
c_stem = override_stem_if_needed(base, "nonpast" .. passive .. "_c",
c_stem and q(dia[prefix_vowel], c_stem) or nil)
if not endings then
if tense:find("^ind") then
endings = ind_endings
elseif tense:find("^sub") then
endings = sub_endings
elseif tense:find("^juss") then
jussive = true
endings = juss_endings
else
error("Internal error: Unrecognized tense '" .. tense .."'")
end
end
if not jussive then
inflect_tense(base, tense, nonpast_prefix_consonants, {
-- singular
v_stem, v_stem, v_stem, v_stem, v_stem,
--dual
v_stem, v_stem, v_stem,
-- plural
v_stem, v_stem, c_stem, v_stem, c_stem
}, endings)
else
inflect_tense(base, tense, nonpast_prefix_consonants, {
-- singular
-- 'adlul, tadlul, tadullī, yadlul, tadlul
c_stem, c_stem, v_stem, c_stem, c_stem,
--dual
-- tadullā, yadullā, tadullā
v_stem, v_stem, v_stem,
-- plural
-- nadlul, tadullū, tadlulna, yadullū, yadlulna
c_stem, v_stem, c_stem, v_stem, c_stem
}, endings)
end
end
-- Generate non-past conjugation with one stem (no distinct stems for vowel-initial and consonant-initial endings).
-- See nonpast_2stem_conj().
local function nonpast_1stem_conj(base, tense, prefix_vowel, stem, endings, jussive)
nonpast_2stem_conj(base, tense, prefix_vowel, stem, stem, endings, jussive)
end
-- Generate active/passive jussive geminative. There are three alternants, two with terminations -a and -i and one
-- in a null termination with a distinct pattern of vowel/consonant stem usage. See nonpast_2stem_conj() for a
-- description of the arguments.
local function jussive_gem_conj(base, tense, prefix_vowel, v_stem, c_stem)
-- alternative in -a
nonpast_2stem_conj(base, tense, prefix_vowel, v_stem, c_stem, juss_endings_alt_a)
-- alternative in -i
nonpast_2stem_conj(base, tense, prefix_vowel, v_stem, c_stem, juss_endings_alt_i)
-- alternative in -null; requires different combination of v_stem and
-- c_stem since the null endings require the c_stem (e.g. "tadlul" here)
-- whereas the corresponding endings above in -a or -i require the v_stem
-- (e.g. "tadulla, tadulli" above)
nonpast_2stem_conj(base, tense, prefix_vowel, v_stem, c_stem, juss_endings, "jussive")
end
-------------------------------------------------------------------------------
-- Functions to inflect the imperative --
-------------------------------------------------------------------------------
-- Generate imperative conjugation, with two stems, for vowel-initial and consonant-initial endings, respectively.
-- Useful for all forms, and for all weaknesses other than final-weak. Note that the two stems may be the same
-- (specifically for sound and assimilated verbs). If `endings` is nil or omitted, use `imp_endings`. If `alt_gem`
-- is specified, use the pattern of vowel and consonant stems appropriate for the alternative geminate imperatives
-- that use a null ending of -a or -i instead of an empty ending.
local function make_2stem_imperative(base, v_stem, c_stem, endings, alt_gem)
endings = endings or imp_endings
-- Override stems with user-specified stems if available.
v_stem = override_stem_if_needed(base, "imp_v", v_stem)
c_stem = override_stem_if_needed(base, "imp_c", c_stem)
if alt_gem then
inflect_tense_imp(base, {v_stem, v_stem, v_stem, v_stem, c_stem}, endings)
else
inflect_tense_imp(base, {c_stem, v_stem, v_stem, v_stem, c_stem}, endings)
end
end
-- Generate imperative parts for sound or assimilated verbs.
local function make_1stem_imperative(base, stem)
make_2stem_imperative(base, stem, stem)
end
-- Generate imperative parts for geminate verbs form I (also IV, VII, VIII, X).
local function make_gem_imperative(base, v_stem, c_stem)
make_2stem_imperative(base, v_stem, c_stem, imp_endings_alt_a, "alt gem")
make_2stem_imperative(base, v_stem, c_stem, imp_endings_alt_i, "alt gem")
make_2stem_imperative(base, v_stem, c_stem)
end
-------------------------------------------------------------------------------
-- Functions to inflect entire verbs --
-------------------------------------------------------------------------------
-- Generate finite parts of a sound verb (also works for assimilated verbs) from five stems (past and non-past,
-- active and passive, plus imperative) plus the prefix vowel in the active non-past ("a" or "u").
local function make_sound_verb(base, past_stem, past_pass_stem, nonpast_stem, nonpast_pass_stem, imp_stem,
prefix_vowel)
past_1stem_conj(base, "past", past_stem)
past_1stem_conj(base, "past_pass", past_pass_stem)
nonpast_1stem_conj(base, "ind", prefix_vowel, nonpast_stem)
nonpast_1stem_conj(base, "sub", prefix_vowel, nonpast_stem)
nonpast_1stem_conj(base, "juss", prefix_vowel, nonpast_stem)
nonpast_1stem_conj(base, "ind_pass", "u", nonpast_pass_stem)
nonpast_1stem_conj(base, "sub_pass", "u", nonpast_pass_stem)
nonpast_1stem_conj(base, "juss_pass", "u", nonpast_pass_stem)
make_1stem_imperative(base, imp_stem)
end
local function past_final_weak_endings_from_vowel(vowel)
if vowel == "ay" then
return past_endings_ay
elseif vowel == "aw" then
return past_endings_aw
elseif vowel == "ī" then
return past_endings_ii
elseif vowel == "ū" then
return past_endings_uu
elseif not vowel then
return nil
else
error(("Internal error: Unrecognized past final-weak vowel spec '%s'"):format(vowel))
end
end
local function nonpast_final_weak_endings_from_vowel(vowel)
if vowel == "ā" then
return ind_endings_aa, sub_endings_aa, juss_endings_aa, imp_endings_aa
elseif vowel == "ī" then
return ind_endings_ii, sub_endings_ii, juss_endings_ii, imp_endings_ii
elseif vowel == "ū" then
return ind_endings_uu, sub_endings_uu, juss_endings_uu, imp_endings_uu
elseif not vowel then
return nil
else
error(("Internal error: Unrecognized non-past final-weak vowel spec '%s'"):format(vowel))
end
end
-- Generate finite parts of a final-weak verb from five stems (past and non-past, active and passive, plus
-- imperative), the past active ending vowel (ay, aw, ī or ū), the non-past active ending vowel (ā, ī or ū) and the
-- prefix vowel in the active non-past (a or u).
local function make_final_weak_verb(base, past_stem, past_pass_stem, nonpast_stem, nonpast_pass_stem, imp_stem,
past_ending_vowel, nonpast_ending_vowel, prefix_vowel)
past_stem = override_stem_if_needed(base, "past", past_stem)
past_pass_stem = override_stem_if_needed(base, "past_pass", past_pass_stem)
-- Don't call override_stem_if_needed() here for non-past stems; it's called in nonpast_2stem_conj().
imp_stem = override_stem_if_needed(base, "imp", imp_stem)
-- + not supported for ending vowel overrides
past_ending_vowel = base.stem_overrides.past_final_weak_vowel or past_ending_vowel
local past_pass_ending_vowel = base.stem_overrides.past_pass_final_weak_vowel or "ī"
nonpast_ending_vowel = base.stem_overrides.nonpast_final_weak_vowel or nonpast_ending_vowel
local nonpast_pass_ending_vowel = base.stem_overrides.nonpast_pass_final_weak_vowel or "ā"
local past_endings = past_final_weak_endings_from_vowel(past_ending_vowel)
local past_pass_endings = past_final_weak_endings_from_vowel(past_pass_ending_vowel)
local ind_endings, sub_endings, juss_endings, imp_endings =
nonpast_final_weak_endings_from_vowel(nonpast_ending_vowel)
local ind_pass_endings, sub_pass_endings, juss_pass_endings =
nonpast_final_weak_endings_from_vowel(nonpast_pass_ending_vowel)
inflect_tense(base, "past", "", {past_stem, all_same = 1}, past_endings)
inflect_tense(base, "past_pass", "", {past_pass_stem, all_same = 1}, past_pass_endings)
nonpast_1stem_conj(base, "ind", prefix_vowel, nonpast_stem, ind_endings)
nonpast_1stem_conj(base, "sub", prefix_vowel, nonpast_stem, sub_endings)
nonpast_1stem_conj(base, "juss", prefix_vowel, nonpast_stem, juss_endings)
nonpast_1stem_conj(base, "ind_pass", "u", nonpast_pass_stem, ind_pass_endings)
nonpast_1stem_conj(base, "sub_pass", "u", nonpast_pass_stem, sub_pass_endings)
nonpast_1stem_conj(base, "juss_pass", "u", nonpast_pass_stem, juss_pass_endings)
inflect_tense_imp(base, {imp_stem, all_same = 1}, imp_endings)
end
-- Generate finite parts of an augmented (form II+) final-weak verb from five stems (past and non-past, active and
-- passive, plus imperative) plus the prefix vowel in the active non-past ("a" or "u") and a flag indicating if it
-- behaves like a form V/VI verb in taking non-past endings in -ā instead of -ī.
local function make_augmented_final_weak_verb(base, past_stem, past_pass_stem, nonpast_stem, nonpast_pass_stem,
imp_stem, prefix_vowel, form56)
make_final_weak_verb(base, past_stem, past_pass_stem, nonpast_stem, nonpast_pass_stem, imp_stem, "ay",
form56 and "ā" or "ī", prefix_vowel)
end
-- Generate finite parts of an augmented (form II+) sound or final-weak verb, given:
-- * `base` (conjugation data structure);
-- * `vowel_spec` (radicals, weakness);
-- * `past_stem_base` (active past stem minus last syllable (= -al or -ā));
-- * `nonpast_stem_base` (non-past stem minus last syllable (= -al/-il or -ā/-ī);
-- * `past_pass_stem_base` (passive past stem minus last syllable (= -il or -ī));
-- * `vn` (verbal noun).
local function make_augmented_sound_final_weak_verb(base, vowel_spec, past_stem_base, nonpast_stem_base,
past_pass_stem_base, vn)
insert_form_or_forms(base, "vn", vn)
local lastrad = base.quadlit and vowel_spec.rad4 or vowel_spec.rad3
local final_weak = is_final_weak(base, vowel_spec)
local prefix_vowel = prefix_vowel_from_vform(base.verb_form)
local form56 = vform_nonpast_a_vowel(base.verb_form)
local a_base_suffix = final_weak and "" or q(A, lastrad)
local i_base_suffix = final_weak and "" or q(I, lastrad)
-- past and non-past stems, active and passive
local past_stem = q(past_stem_base, a_base_suffix)
-- In forms 5 and 6, non-past has /a/ as last stem vowel in the non-past
-- in both active and passive, but /i/ in the active participle and /a/
-- in the passive participle. Elsewhere, consistent /i/ in active non-past
-- and participle, consistent /a/ in passive non-past and participle.
-- Hence, forms 5 and 6 differ only in the non-past active (but not
-- active participle), so we have to split the finite non-past stem and
-- active participle stem.
local nonpast_stem = q(nonpast_stem_base, form56 and a_base_suffix or i_base_suffix)
local ap_stem = q(nonpast_stem_base, i_base_suffix)
local past_pass_stem = q(past_pass_stem_base, i_base_suffix)
local nonpast_pass_stem = q(nonpast_stem_base, a_base_suffix)
-- imperative stem
local imp_stem = q(past_stem_base, form56 and a_base_suffix or i_base_suffix)
-- make parts
if final_weak then
make_augmented_final_weak_verb(base, past_stem, past_pass_stem, nonpast_stem, nonpast_pass_stem, imp_stem,
prefix_vowel, form56)
else
make_sound_verb(base, past_stem, past_pass_stem, nonpast_stem, nonpast_pass_stem, imp_stem, prefix_vowel)
end
-- active and passive participle
if final_weak then
insert_form_or_forms(base, "ap", q(MU, ap_stem, IN))
insert_form_or_forms(base, "pp", q(MU, nonpast_pass_stem, AN, AMAQ))
else
insert_form_or_forms(base, "ap", q(MU, ap_stem))
insert_form_or_forms(base, "pp", q(MU, nonpast_pass_stem))
end
end
-- Generate finite parts of a hollow or geminate verb from ten stems (vowel and consonant stems for each of past and
-- non-past, active and passive, plus imperative) plus the prefix vowel in the active non-past ("a" or "u"), plus a
-- flag indicating if we are a geminate verb.
local function make_hollow_geminate_verb(base, geminate, past_v_stem, past_c_stem, past_pass_v_stem,
past_pass_c_stem, nonpast_v_stem, nonpast_c_stem, nonpast_pass_v_stem, nonpast_pass_c_stem, imp_v_stem,
imp_c_stem, prefix_vowel, altgem_note)
past_2stem_conj(base, "past", past_v_stem, past_c_stem, altgem_note)
past_2stem_conj(base, "past_pass", past_pass_v_stem, past_pass_c_stem)
nonpast_2stem_conj(base, "ind", prefix_vowel, nonpast_v_stem, nonpast_c_stem)
nonpast_2stem_conj(base, "sub", prefix_vowel, nonpast_v_stem, nonpast_c_stem)
nonpast_2stem_conj(base, "ind_pass", "u", nonpast_pass_v_stem, nonpast_pass_c_stem)
nonpast_2stem_conj(base, "sub_pass", "u", nonpast_pass_v_stem, nonpast_pass_c_stem)
if geminate then
jussive_gem_conj(base, "juss", prefix_vowel, nonpast_v_stem, nonpast_c_stem)
jussive_gem_conj(base, "juss_pass", "u", nonpast_pass_v_stem, nonpast_pass_c_stem)
make_gem_imperative(base, imp_v_stem, imp_c_stem)
else
nonpast_2stem_conj(base, "juss", prefix_vowel, nonpast_v_stem, nonpast_c_stem)
nonpast_2stem_conj(base, "juss_pass", "u", nonpast_pass_v_stem, nonpast_pass_c_stem)
make_2stem_imperative(base, imp_v_stem, imp_c_stem)
end
end
-- Generate finite parts of an augmented (form II+) hollow verb, given:
-- * `base` (conjugation data structure);
-- * `vowel_spec` (radicals, weakness);
-- * `past_stem_base` (invariable part of active past stem);
-- * `nonpast_stem_base` (invariable part of nonpast stem);
-- * `past_pass_stem_base` (invariable part of passive past stem);
-- * `vn` (verbal noun).
local function make_augmented_hollow_verb(base, vowel_spec, past_stem_base, nonpast_stem_base, past_pass_stem_base,
vn)
insert_form_or_forms(base, "vn", vn)
local lastrad = base.quadlit and vowel_spec.rad4 or vowel_spec.rad3
local form410 = base.verb_form == "IV" or base.verb_form == "X"
local prefix_vowel = prefix_vowel_from_vform(base.verb_form)
local a_base_suffix_v, a_base_suffix_c
local i_base_suffix_v, i_base_suffix_c
a_base_suffix_v = q(AA, lastrad) -- 'af-āl-a, inf-āl-a
a_base_suffix_c = q(A, lastrad) -- 'af-al-tu, inf-al-tu
i_base_suffix_v = q(II, lastrad) -- 'uf-īl-a, unf-īl-a
i_base_suffix_c = q(I, lastrad) -- 'uf-il-tu, unf-il-tu
-- past and non-past stems, active and passive, for vowel-initial and
-- consonant-initial endings
local past_v_stem = q(past_stem_base, a_base_suffix_v)
local past_c_stem = q(past_stem_base, a_base_suffix_c)
-- yu-f-īl-u, ya-staf-īl-u but yanf-āl-u, yaft-āl-u
local nonpast_v_stem = q(nonpast_stem_base, form410 and i_base_suffix_v or a_base_suffix_v)
local nonpast_c_stem = q(nonpast_stem_base, form410 and i_base_suffix_c or a_base_suffix_c)
local past_pass_v_stem = q(past_pass_stem_base, i_base_suffix_v)
local past_pass_c_stem = q(past_pass_stem_base, i_base_suffix_c)
local nonpast_pass_v_stem = q(nonpast_stem_base, a_base_suffix_v)
local nonpast_pass_c_stem = q(nonpast_stem_base, a_base_suffix_c)
-- imperative stem
local imp_v_stem = q(past_stem_base, form410 and i_base_suffix_v or a_base_suffix_v)
local imp_c_stem = q(past_stem_base, form410 and i_base_suffix_c or a_base_suffix_c)
-- make parts
make_hollow_geminate_verb(base, false, past_v_stem, past_c_stem, past_pass_v_stem,
past_pass_c_stem, nonpast_v_stem, nonpast_c_stem, nonpast_pass_v_stem,
nonpast_pass_c_stem, imp_v_stem, imp_c_stem, prefix_vowel)
-- active participle
insert_form_or_forms(base, "ap", q(MU, nonpast_v_stem))
-- passive participle
insert_form_or_forms(base, "pp", q(MU, nonpast_pass_v_stem))
end
-- Generate finite parts of an augmented (form II+) geminate verb, given:
-- * `base` (conjugation data structure);
-- * `vowel_spec` (radicals, weakness);
-- * `past_stem_base` (invariable part of active past stem; this and the stem bases below will end with a consonant
-- for forms IV, X, IVq, and a short vowel for the others);
-- * `nonpast_stem_base` (invariable part of nonpast stem);
-- * `past_pass_stem_base` (invariable part of passive past stem);
-- * `vn` (verbal noun);
-- * `altgem_note` (footnote to add to active past 1/2-person forms, when alternative forms are supplied [form X]).
local function make_augmented_geminate_verb(base, vowel_spec, past_stem_base, nonpast_stem_base,
past_pass_stem_base, vn, altgem_note)
insert_form_or_forms(base, "vn", vn)
local vform = base.verb_form
local lastrad = base.quadlit and vowel_spec.rad4 or vowel_spec.rad3
local prefix_vowel = prefix_vowel_from_vform(vform)
local a_base_suffix_v, a_base_suffix_c
local i_base_suffix_v, i_base_suffix_c
if vform == "IV" or vform == "X" or vform == "IVq" then
a_base_suffix_v = q(A, lastrad, SH) -- 'af-all
a_base_suffix_c = q(SK, lastrad, A, lastrad) -- 'af-lal
i_base_suffix_v = q(I, lastrad, SH) -- yuf-ill
i_base_suffix_c = q(SK, lastrad, I, lastrad) -- yuf-lil
else
a_base_suffix_v = q(lastrad, SH) -- fā-ll, infa-ll
a_base_suffix_c = q(lastrad, A, lastrad) -- fā-lal, infa-lal
i_base_suffix_v = q(lastrad, SH) -- yufā-ll, yanfa-ll
i_base_suffix_c = q(lastrad, I, lastrad) -- yufā-lil, yanfa-lil
end
-- past and non-past stems, active and passive, for vowel-initial and
-- consonant-initial endings
local past_v_stem = q(past_stem_base, a_base_suffix_v)
local past_c_stem = q(past_stem_base, a_base_suffix_c)
local nonpast_v_stem = q(nonpast_stem_base, vform_nonpast_a_vowel(vform) and a_base_suffix_v or i_base_suffix_v)
local nonpast_c_stem = q(nonpast_stem_base, vform_nonpast_a_vowel(vform) and a_base_suffix_c or i_base_suffix_c)
-- NOTE: Formerly had a comment that "vform III and VI passive past do not have contracted parts, only
-- uncontracted parts, which are added separately by those functions". This is based on Mace
-- "Arabic Verbs and Essential Grammar" (1999) entry 63 (continued), which shows passive ḥūjija but no ḥūjja;
-- but that is apparently a mistake, as (1) verb tables in other books do show contracted passive parts for
-- these forms; (2) there is no mention of such an exception on p. 99, which explains how geminate ("doubled")
-- verbs work (on the contrary, it says "The contracted and uncontracted pairs (see above) are found all
-- over Forms III and VI of the doubled verbs").
local past_pass_v_stem = q(past_pass_stem_base, i_base_suffix_v)
local past_pass_c_stem = q(past_pass_stem_base, i_base_suffix_c)
local nonpast_pass_v_stem = q(nonpast_stem_base, a_base_suffix_v)
local nonpast_pass_c_stem = q(nonpast_stem_base, a_base_suffix_c)
-- imperative stem
local imp_v_stem = q(past_stem_base, vform_nonpast_a_vowel(vform) and a_base_suffix_v or i_base_suffix_v)
local imp_c_stem = q(past_stem_base, vform_nonpast_a_vowel(vform) and a_base_suffix_c or i_base_suffix_c)
-- make parts
make_hollow_geminate_verb(base, "geminate", past_v_stem, past_c_stem, past_pass_v_stem,
past_pass_c_stem, nonpast_v_stem, nonpast_c_stem, nonpast_pass_v_stem,
nonpast_pass_c_stem, imp_v_stem, imp_c_stem, prefix_vowel, altgem_note)
-- active participle
insert_form_or_forms(base, "ap", q(MU, nonpast_v_stem))
-- passive participle
insert_form_or_forms(base, "pp", q(MU, nonpast_pass_v_stem))
end
-------------------------------------------------------------------------------
-- Conjugation functions for specific conjugation types --
-------------------------------------------------------------------------------
local function form_i_imp_stem_through_rad1(base, nonpast_vowel, rad1)
local imp_vowel = map_vowel(nonpast_vowel, function(vow)
if vow == A or vow == I then
return I
elseif vow == U then
return U
elseif not skip_slot(base, "imp_2ms") then
error(("Internal error: Non-past vowel %s isn't a, i, or u, should have been caught earlier"):format(
dump(nonpast_vowel)))
else
-- Passive-only; imperative won't ever be displayed so it doesn't matter.
return I
end
end)
-- Mace ("Arabic Verbs and Essentials of Grammar" p. 63: [https://archive.org/details/arabicverbsessen00john/page/62/mode/2up])
-- claims that initial hamza is assimilated/elided into a long vowel in the form-I imperative, but apparently
-- this isn't corrrect.
local vowel_on_alif = map_vowel(imp_vowel, function(vow)
return ALIF .. vow
end)
return q(vowel_on_alif, rad1, SK)
end
-- Implement form-I sound or assimilated verb. ASSIMILATED is true for assimilated verbs.
local function make_form_i_sound_assimilated_verb(base, vowel_spec, assimilated)
local rad1, rad2, rad3, past_vowel, nonpast_vowel = get_radicals_3(vowel_spec)
-- Verbal nouns (maṣādir) for form I are unpredictable and have to be supplied
-- past and non-past stems, active and passive
local past_stem = q(rad1, A, rad2, past_vowel, rad3)
local nonpast_stem = assimilated and q(rad2, nonpast_vowel, rad3) or
q(rad1, SK, rad2, nonpast_vowel, rad3)
local past_pass_stem = q(rad1, U, rad2, I, rad3)
local nonpast_pass_stem = q(rad1, SK, rad2, A, rad3)
-- imperative stem
-- check for irregular verb with reduced imperative (أَخَذَ or أَكَلَ or أَمَرَ)
local reducedimp = reduced_imperative_verb(rad1, rad2, rad3)
if reducedimp then
base.irregular = true
end
local imp_stem_suffix = q(rad2, nonpast_vowel, rad3)
local long_imp_stem_base = form_i_imp_stem_through_rad1(base, nonpast_vowel, rad1)
local short_imp_stem_base = ""
local imp_stem = q((assimilated or reducedimp) and "" or long_imp_stem_base, imp_stem_suffix)
-- make parts
make_sound_verb(base, past_stem, past_pass_stem, nonpast_stem, nonpast_pass_stem, imp_stem, "a")
if reducedimp == "shortlong" then
make_1stem_imperative(base, iut.combine_form_and_footnotes(q(long_imp_stem_base, imp_stem_suffix),
mw.getCurrentFrame():preprocess("[used especially with a clitic such as {{m|ar|فَ}} or {{m|ar|وَ}}]")))
end
-- Check for irregular verb سَأَلَ with alternative jussive and imperative. Calling this after make_sound_verb()
-- adds additional entries to the paradigm parts.
if saal_radicals(rad1, rad2, rad3) then
base.irregular = true
nonpast_1stem_conj(base, "juss", "a", "سَل")
nonpast_1stem_conj(base, "juss_pass", "u", "سَل")
make_1stem_imperative(base, "سَل")
end
-- Active participle.
insert_form_or_forms(base, "ap1", q(rad1, AA, rad2, I, rad3))
-- Insert alternative active participle (stative type I) فَعِيل. Since not all verbs have this, we require that
-- verbs that do have it specify it explicitly; a shortcut ++ is provided to make this easier (e.g. <ap:++> to
-- indicate that the alternative form should be used for the active participle, <ap:+,++> to indicate that both
-- forms can be used, and <ap:-> to indicate that there is no active participle). The same form is used for
-- secondary default passive participle.
insert_ap2_pp2(base, q(rad1, A, rad2, II, rad3))
-- Active participle, stative type II فَعِل (+++).
insert_form_or_forms(base, "ap3", q(rad1, A, rad2, I, rad3))
-- Active participle, color/defect أَفْعَل (+cd).
insert_form_or_forms(base, "apcd", q(HAMZA, A, rad1, SK, rad2, A, rad3))
-- Active participle, -ān فَعْلَان (+an).
insert_form_or_forms(base, "apan", q(rad1, A, rad2, SK, rad3, AAN))
-- Passive participle.
insert_form_or_forms(base, "pp", q(MA, rad1, SK, rad2, UU, rad3))
end
conjugations["I-sound"] = function(base, vowel_spec)
make_form_i_sound_assimilated_verb(base, vowel_spec, false)
end
conjugations["none-sound"] = function(base, vowel_spec)
-- All default stems are nil.
make_sound_verb(base)
end
conjugations["none-hollow"] = function(base, vowel_spec)
-- All default stems are nil.
make_hollow_geminate_verb(base, false)
end
conjugations["none-geminate"] = function(base, vowel_spec)
-- All default stems are nil.
make_hollow_geminate_verb(base, "geminate")
end
conjugations["none-final-weak"] = function(base, vowel_spec)
-- All default stems are nil.
make_final_weak_verb(base)
end
conjugations["I-assimilated"] = function(base, vowel_spec)
make_form_i_sound_assimilated_verb(base, vowel_spec, "assimilated")
end
local function make_form_i_hayy_verb(base, vowel_spec)
-- Verbal nouns (maṣādir) for form I are unpredictable and have to be supplied
base.irregular = true
-- past and non-past stems, active and passive, and imperative stem
local past_c_stem = "حَيِي"
local past_v_stem_long = past_c_stem
local past_v_stem_short = "حَيّ"
local past_pass_c_stem = "حُيِي"
local past_pass_v_stem_long = past_pass_c_stem
local past_pass_v_stem_short = "حُيّ"
local nonpast_stem = "حْي"
local nonpast_pass_stem = nonpast_stem
local imp_stem = _I .. nonpast_stem
-- make parts
past_2stem_conj(base, "past", {}, past_c_stem)
past_2stem_conj(base, "past_pass", {}, past_pass_c_stem)
local variant = vowel_spec.variant or "both"
if variant == "short" or variant == "both" then
past_2stem_conj(base, "past", past_v_stem_short, {})
past_2stem_conj(base, "past_pass", past_pass_v_stem_short, {})
end
function inflect_long_variant(tense, long_stem, short_stem)
inflect_tense_1(base, tense, "",
{long_stem, long_stem, long_stem, long_stem, short_stem},
{past_endings[4], past_endings[5], past_endings[7], past_endings[8],
past_endings[12]},
{"3ms", "3fs", "3md", "3fd", "3mp"})
end
if variant == "long" or variant == "both" then
inflect_long_variant("past", past_v_stem_long, past_v_stem_short)
inflect_long_variant("past_pass", past_pass_v_stem_long, past_pass_v_stem_short)
end
nonpast_1stem_conj(base, "ind", "a", nonpast_stem, ind_endings_aa)
nonpast_1stem_conj(base, "sub", "a", nonpast_stem, sub_endings_aa)
nonpast_1stem_conj(base, "juss", "a", nonpast_stem, juss_endings_aa)
nonpast_1stem_conj(base, "ind_pass", "u", nonpast_pass_stem, ind_endings_aa)
nonpast_1stem_conj(base, "sub_pass", "u", nonpast_pass_stem, sub_endings_aa)
nonpast_1stem_conj(base, "juss_pass", "u", nonpast_pass_stem, juss_endings_aa)
inflect_tense_imp(base, {imp_stem, all_same = 1}, imp_endings_aa)
-- active and passive participles apparently do not exist for this verb
end
-- Implement form-I final-weak assimilated+final-weak verb. ASSIMILATED is true for assimilated verbs.
local function make_form_i_final_weak_verb(base, vowel_spec, assimilated)
local rad1, rad2, rad3, past_vowel, nonpast_vowel = get_radicals_3(vowel_spec)
-- حَيَّ or حَيِيَ is weird enough that we handle it as a separate function.
if hayy_radicals(rad1, rad2, rad3) then
make_form_i_hayy_verb(base, vowel_spec)
return
end
-- Verbal nouns (maṣādir) for form I are unpredictable and have to be supplied.
-- Past and non-past stems, active and passive, and imperative stem.
local past_stem = q(rad1, A, rad2)
local past_pass_stem = q(rad1, U, rad2)
local nonpast_stem, nonpast_pass_stem, imp_stem
if raa_radicals(rad1, rad2, rad3) then
base.irregular = true
nonpast_stem = rad1
nonpast_pass_stem = rad1
imp_stem = rad1
else
nonpast_pass_stem = q(rad1, SK, rad2)
if assimilated then
nonpast_stem = rad2
imp_stem = rad2
else
nonpast_stem = nonpast_pass_stem
imp_stem = q(form_i_imp_stem_through_rad1(base, nonpast_vowel, rad1), rad2)
end
end
-- Make parts.
local past_ending_vowel =
req(rad3, Y) and req(past_vowel, A) and "ay" or
req(rad3, W) and req(past_vowel, A) and "aw" or
req(past_vowel, I) and "ī" or "ū"
-- Try to preserve footnotes attached to the third radical and/or past and/or non-past vowels.
local past_footnotes = iut.combine_footnotes(rget_footnotes(rad3), rget_footnotes(past_vowel))
local nonpast_ending_vowel = req(nonpast_vowel, A) and "ā" or req(nonpast_vowel, I) and "ī" or "ū"
local nonpast_footnotes = iut.combine_footnotes(rget_footnotes(rad3), rget_footnotes(nonpast_vowel))
make_final_weak_verb(base,
iut.combine_form_and_footnotes(past_stem, past_footnotes),
iut.combine_form_and_footnotes(past_pass_stem, past_footnotes),
iut.combine_form_and_footnotes(nonpast_stem, nonpast_footnotes),
iut.combine_form_and_footnotes(nonpast_pass_stem, nonpast_footnotes),
iut.combine_form_and_footnotes(imp_stem, nonpast_footnotes),
past_ending_vowel, nonpast_ending_vowel, "a")
-- Active participle.
insert_form_or_forms(base, "ap1", q(rad1, AA, rad2, IN))
-- Active participle, stative type I فَعِيّ (++). FIXME: Is this correct when rad3 is W?
insert_ap2_pp2(base, q(rad1, A, rad2, II, SH))
-- Active participle, stative type II فَعٍ (+++). FIXME: Any examples of this to verify it's correct?
insert_form_or_forms(base, "ap3", q(rad1, A, rad2, IN))
-- Active participle, color/defect أَفْعَى (+cd).
insert_form_or_forms(base, "apcd", q(HAMZA, A, rad1, SK, rad2, AAMAQ))
-- Active participle, -ān فَعْيَان or فَعْوَان (+an).
-- FIXME: Any examples of this for both rad3 = W and y to verify it's correct?
insert_form_or_forms(base, "apan", q(rad1, A, rad2, SK, rad3, AAN))
-- Passive participle.
insert_form_or_forms(base, "pp", q(MA, rad1, SK, rad2, req(rad3, Y) and II or UU, SH))
end
conjugations["I-final-weak"] = function(base, vowel_spec)
make_form_i_final_weak_verb(base, vowel_spec, false)
end
conjugations["I-assimilated+final-weak"] = function(base, vowel_spec)
make_form_i_final_weak_verb(base, vowel_spec, "assimilated")
end
conjugations["I-hollow"] = function(base, vowel_spec)
local rad1, rad2, rad3, past_vowel, nonpast_vowel = get_radicals_3(vowel_spec)
-- In some sense, hollow vowels i~i and u~u are more "correct" than a~i and a~u, but the latter follow the
-- pattern of other form-I verbs, so we map i~i to a~i and u~u to a~u in infer_radicals(). Now however we have
-- to undo this to get the actual past vowel based on the non-past vowel.
if req(past_vowel, A) then
past_vowel = map_vowel(past_vowel, function(vow)
return req(nonpast_vowel, A) and I or rget(nonpast_vowel)
end)
end
local lengthened_nonpast = map_vowel(nonpast_vowel, function(vow)
return vow == U and UU or vow == I and II or AA
end)
-- Verbal nouns (maṣādir) for form I are unpredictable and have to be supplied.
-- active past stems - vowel (v) and consonant (c)
local past_v_stem = q(rad1, AA, rad3)
local past_c_stem = q(rad1, past_vowel, rad3)
-- active non-past stems - vowel (v) and consonant (c)
local nonpast_v_stem = q(rad1, lengthened_nonpast, rad3)
local nonpast_c_stem = q(rad1, nonpast_vowel, rad3)
-- passive past stems - vowel (v) and consonant (c)
-- 'ufīla, 'ufiltu
local past_pass_v_stem = q(rad1, II, rad3)
local past_pass_c_stem = q(rad1, I, rad3)
-- passive non-past stems - vowel (v) and consonant (c)
-- yufāla/yufalna
-- stem is built differently but conjugation is identical to sound verbs
local nonpast_pass_v_stem = q(rad1, AA, rad3)
local nonpast_pass_c_stem = q(rad1, A, rad3)
-- imperative stem
local imp_v_stem = nonpast_v_stem
local imp_c_stem = nonpast_c_stem
-- make parts
make_hollow_geminate_verb(base, false, past_v_stem, past_c_stem, past_pass_v_stem,
past_pass_c_stem, nonpast_v_stem, nonpast_c_stem, nonpast_pass_v_stem,
nonpast_pass_c_stem, imp_v_stem, imp_c_stem, "a")
if kaan_radicals(rad1, rad2, rad3) then
local endings = make_nonpast_endings(U, {}, {}, {}, {})
inflect_tense(base, "juss", nonpast_prefix_consonants, q(A, rad1), endings)
base.irregular = true
end
-- Active participle.
insert_form_or_forms(base, "ap1", req(rad3, HAMZA) and q(rad1, AA, HAMZA, IN) or
q(rad1, AA, HAMZA, I, rad3))
-- Active participle, stative type I فَيِّد (++). FIXME: Any examples of this to verify it's correct?
insert_ap2_pp2(base, q(rad1, A, Y, SH, I, rad3))
-- Active participle, stative type II فَيِد (+++). FIXME: Any examples of this to verify it's correct?
insert_form_or_forms(base, "ap3", q(rad1, A, Y, I, rad3))
-- Active participle, color/defect أَفّيَد or أَفّوَد (+cd). FIXME: Any examples of this to verify it's correct?
insert_form_or_forms(base, "apcd", q(HAMZA, A, rad1, SK, rad2, A, rad3))
-- Active participle, -ān فَيْدَان or فَوْدَان (+an). Example: جَاعَ "to be hungry", act part جَوْعَان
insert_form_or_forms(base, "apan", q(rad1, A, rad2, SK, rad3, AAN))
-- Passive participle.
insert_form_or_forms(base, "pp", q(MA, rad1, req(rad2, Y) and II or UU, rad3))
end
conjugations["I-geminate"] = function(base, vowel_spec)
local rad1, rad2, rad3, past_vowel, nonpast_vowel = get_radicals_3(vowel_spec)
-- Verbal nouns (maṣādir) for form I are unpredictable and have to be supplied.
-- active past stems - vowel (v) and consonant (c)
local past_v_stem = q(rad1, A, rad2, SH)
local past_c_stem = q(rad1, A, rad2, past_vowel, rad2)
-- active non-past stems - vowel (v) and consonant (c)
local nonpast_v_stem = q(rad1, nonpast_vowel, rad2, SH)
local nonpast_c_stem = q(rad1, SK, rad2, nonpast_vowel, rad2)
-- passive past stems - vowel (v) and consonant (c)
-- dulla/dulilta
local past_pass_v_stem = q(rad1, U, rad2, SH)
local past_pass_c_stem = q(rad1, U, rad2, I, rad2)
-- passive non-past stems - vowel (v) and consonant (c)
--yudallu/yudlalna
-- stem is built differently but conjugation is identical to sound verbs
local nonpast_pass_v_stem = q(rad1, A, rad2, SH)
local nonpast_pass_c_stem = q(rad1, SK, rad2, A, rad2)
-- imperative stem
local imp_v_stem = q(rad1, nonpast_vowel, rad2, SH)
local imp_c_stem = q(form_i_imp_stem_through_rad1(base, nonpast_vowel, rad1), rad2, nonpast_vowel, rad2)
-- make parts
make_hollow_geminate_verb(base, "geminate", past_v_stem, past_c_stem, past_pass_v_stem,
past_pass_c_stem, nonpast_v_stem, nonpast_c_stem, nonpast_pass_v_stem,
nonpast_pass_c_stem, imp_v_stem, imp_c_stem, "a")
-- Active participle.
insert_form_or_forms(base, "ap1", q(rad1, AA, rad2, SH))
-- Active participle, stative type I فَعِيع (++). FIXME: Any examples of this to verify it's correct?
insert_ap2_pp2(base, q(rad1, A, rad2, II, rad2))
-- Active participle, stative type II فَعّ (+++). Example: بَرَّ "to be pious", active participle بَرّ
insert_form_or_forms(base, "ap3", q(rad1, A, rad2, SH))
-- Active participle, color/defect أَفَعّ (+cd).
-- Example: لَصَّ "to be thievish, to steal repeatedly", active participle أَلَصّ.
insert_form_or_forms(base, "apcd", q(HAMZA, A, rad1, A, rad2, SH))
-- Active participle, -ān فَعَّان (+an). FIXME: Any examples of this to verify it's correct?
insert_form_or_forms(base, "apan", q(rad1, A, rad2, SH, AAN))
-- Passive participle.
insert_form_or_forms(base, "pp", q(MA, rad1, SK, rad2, UU, rad2))
end
-- Return the ta- (active, past and non-past) and tu- (passive past) prefixes for a form II/III/V/VI verb.
-- Form V and VI verbs normally use ta- and tu-, but reduced (base.reduced) verbs use different prefixes. Form II
-- and III verbs have no prefix.
local function form_ii_iii_v_vi_ta_tu_prefix(base, rad1)
local vform = base.verb_form
if vform == "V" or vform == "VI" then
if base.reduced then
-- To simplify the code, we generate two rad1's with a sukūn between them, which is cleaned up in
-- postprocessing.
return q(_I, rad1, SK), q(rad1, SK), q(_U, rad1, SK)
else
return TA, TA, TU
end
else
return "", "", ""
end
end
-- Make form II or V sound or final-weak verb.
local function make_form_ii_v_sound_final_weak_verb(base, vowel_spec)
local rad1, rad2, rad3 = get_radicals_3(vowel_spec)
local final_weak = is_final_weak(base, vowel_spec)
local vform = base.verb_form
local ta_past_prefix, ta_nonpast_prefix, tu_past_prefix = form_ii_iii_v_vi_ta_tu_prefix(base, rad1)
local vn = vform == "V" and
q(ta_past_prefix, rad1, A, rad2, SH, final_weak and IN or q(U, rad3)) or
q(TA, rad1, SK, rad2, II, final_weak and AH or rad3)
-- various stem bases
local past_stem_base = q(ta_past_prefix, rad1, A, rad2, SH)
local nonpast_stem_base = q(ta_nonpast_prefix, rad1, A, rad2, SH)
local past_pass_stem_base = q(tu_past_prefix, rad1, U, rad2, SH)
-- make parts
make_augmented_sound_final_weak_verb(base, vowel_spec, past_stem_base, nonpast_stem_base, past_pass_stem_base,
vn)
end
conjugations["II-sound"] = function(base, vowel_spec)
make_form_ii_v_sound_final_weak_verb(base, vowel_spec)
end
conjugations["II-final-weak"] = function(base, vowel_spec)
make_form_ii_v_sound_final_weak_verb(base, vowel_spec)
end
local function make_form_iii_alt_vn(base, vowel_spec)
local rad1, rad2, rad3 = get_radicals_3(vowel_spec)
local final_weak = is_final_weak(base, vowel_spec)
-- Insert alternative verbal noun فِعَال. Since not all verbs have this, we require that verbs that do have it
-- specify it explicitly; a shortcut ++ is provided to make this easier (e.g. <vn:+,++> to indicate that
-- both the normal verbal noun مُفَاعَلَة and secondary verbal noun فِعَال are available).
insert_form_or_forms(base, "vn2", q(rad1, I, rad2, AA, final_weak and HAMZA or rad3))
end
-- Make form III or VI sound or final-weak verb.
local function make_form_iii_vi_sound_final_weak_verb(base, vowel_spec)
local rad1, rad2, rad3 = get_radicals_3(vowel_spec)
local final_weak = is_final_weak(base, vowel_spec)
local vform = base.verb_form
local ta_past_prefix, ta_nonpast_prefix, tu_past_prefix = form_ii_iii_v_vi_ta_tu_prefix(base, rad1)
local vn = vform == "VI" and
q(ta_past_prefix, rad1, AA, rad2, final_weak and IN or q(U, rad3)) or
q(MU, rad1, AA, rad2, final_weak and AAH or q(A, rad3, AH))
-- various stem bases
local past_stem_base = q(ta_past_prefix, rad1, AA, rad2)
local nonpast_stem_base = q(ta_nonpast_prefix, rad1, AA, rad2)
local past_pass_stem_base = q(tu_past_prefix, rad1, UU, rad2)
-- make parts
make_augmented_sound_final_weak_verb(base, vowel_spec, past_stem_base, nonpast_stem_base, past_pass_stem_base,
vn)
if vform == "III" then
make_form_iii_alt_vn(base, vowel_spec)
end
end
conjugations["III-sound"] = function(base, vowel_spec)
make_form_iii_vi_sound_final_weak_verb(base, vowel_spec)
end
conjugations["III-final-weak"] = function(base, vowel_spec)
make_form_iii_vi_sound_final_weak_verb(base, vowel_spec)
end
-- Make form III or VI geminate verb.
local function make_form_iii_vi_geminate_verb(base, vowel_spec)
local rad1, rad2, rad3 = get_radicals_3(vowel_spec)
local vform = base.verb_form
local ta_past_prefix, ta_nonpast_prefix, tu_past_prefix = form_ii_iii_v_vi_ta_tu_prefix(base, rad1)
-- Alternative verbal noun فِعَال will be inserted when we add sound parts below.
local vn = vform == "VI" and q(ta_past_prefix, rad1, AA, rad2, SH) or q(MU, rad1, AA, rad2, SH, AH)
-- Various stem bases.
local past_stem_base = q(ta_past_prefix, rad1, AA)
local nonpast_stem_base = q(ta_nonpast_prefix, rad1, AA)
local past_pass_stem_base = q(tu_past_prefix, rad1, UU)
-- Make parts.
local variant = vowel_spec.variant or "short"
if variant == "short" or variant == "both" then
make_augmented_geminate_verb(base, vowel_spec, past_stem_base, nonpast_stem_base, past_pass_stem_base, vn)
end
-- Also add alternative sound (non-compressed) parts. This will lead to some duplicate entries, but they are
-- removed during addition.
if variant == "long" or variant == "both" then
make_form_iii_vi_sound_final_weak_verb(base, vowel_spec)
elseif vform == "III" then
-- Still need to add the alternative form-III verbal noun.
make_form_iii_alt_vn(base, vowel_spec)
end
end
conjugations["III-geminate"] = function(base, vowel_spec)
make_form_iii_vi_geminate_verb(base, vowel_spec)
end
-- Make form IV sound or final-weak verb.
local function make_form_iv_sound_final_weak_verb(base, vowel_spec)
local rad1, rad2, rad3 = get_radicals_3(vowel_spec)
local final_weak = is_final_weak(base, vowel_spec)
-- core of stem base, minus stem prefixes
local stem_core
-- check for irregular verb أَرَى
local is_raa = raa_radicals(rad1, rad2, rad3)
if is_raa then
base.irregular = true
stem_core = rad1
else
stem_core = q(rad1, SK, rad2)
end
-- verbal noun
local vn = is_raa and
q(HAMZA, I, stem_core, AA, HAMZA, AH) or
q(HAMZA, I, stem_core, AA, final_weak and HAMZA or rad3)
-- various stem bases
local past_stem_base = q(HAMZA, A, stem_core)
local nonpast_stem_base = stem_core
local past_pass_stem_base = q(HAMZA, U, stem_core)
-- make parts
make_augmented_sound_final_weak_verb(base, vowel_spec, past_stem_base, nonpast_stem_base, past_pass_stem_base,
vn)
end
conjugations["IV-sound"] = function(base, vowel_spec)
make_form_iv_sound_final_weak_verb(base, vowel_spec)
end
conjugations["IV-final-weak"] = function(base, vowel_spec)
make_form_iv_sound_final_weak_verb(base, vowel_spec)
end
conjugations["IV-hollow"] = function(base, vowel_spec)
local rad1, rad2, rad3 = get_radicals_3(vowel_spec)
-- verbal noun
local vn = q(HAMZA, I, rad1, AA, rad3, AH)
-- various stem bases
local past_stem_base = q(HAMZA, A, rad1)
local nonpast_stem_base = rad1
local past_pass_stem_base = q(HAMZA, U, rad1)
-- make parts
make_augmented_hollow_verb(base, vowel_spec, past_stem_base, nonpast_stem_base, past_pass_stem_base, vn)
end
conjugations["IV-geminate"] = function(base, vowel_spec)
local rad1, rad2, rad3 = get_radicals_3(vowel_spec)
local vn = q(HAMZA, I, rad1, SK, rad2, AA, rad2)
-- various stem bases
local past_stem_base = q(HAMZA, A, rad1)
local nonpast_stem_base = rad1
local past_pass_stem_base = q(HAMZA, U, rad1)
-- make parts
make_augmented_geminate_verb(base, vowel_spec, past_stem_base, nonpast_stem_base, past_pass_stem_base, vn)
end
conjugations["V-sound"] = function(base, vowel_spec)
make_form_ii_v_sound_final_weak_verb(base, vowel_spec)
end
conjugations["V-final-weak"] = function(base, vowel_spec)
make_form_ii_v_sound_final_weak_verb(base, vowel_spec)
end
conjugations["VI-sound"] = function(base, vowel_spec)
make_form_iii_vi_sound_final_weak_verb(base, vowel_spec)
end
conjugations["VI-final-weak"] = function(base, vowel_spec)
make_form_iii_vi_sound_final_weak_verb(base, vowel_spec)
end
conjugations["VI-geminate"] = function(base, vowel_spec)
make_form_iii_vi_geminate_verb(base, vowel_spec)
end
-- Make a verbal noun of the general form that applies to forms VII and above. RAD12 is the first consonant cluster
-- (after initial اِ) and RAD34 is the second consonant cluster. RAD5 is the final consonant.
local function high_form_verbal_noun(rad12, rad34, rad5)
return q(_I, rad12, I, rad34, AA, rad5)
end
-- Populate a sound or final-weak verb for any of the various high-numbered augmented forms (form VII and up) that
-- have up to 5 consonants in two clusters in the stem and the same pattern of vowels between. Some of these
-- consonants in certain verb parts are w's, which leads to apparent anomalies in certain stems of these parts, but
-- these anomalies are handled automatically in postprocessing, where we resolve sequences of iwC -> īC, uwC -> ūC,
-- w + sukūn + w -> w + shadda.
-- RAD12 is the first consonant cluster (after initial اِ) and RAD34 is the second consonant cluster. RAD5 is the
-- final consonant.
local function make_high_form_sound_final_weak_verb(base, vowel_spec, rad12, rad34, rad5)
local final_weak = is_final_weak(base, vowel_spec)
local vn = high_form_verbal_noun(rad12, rad34, final_weak and HAMZA or rad5)
-- various stem bases
local nonpast_stem_base = q(rad12, A, rad34)
local past_stem_base = q(_I, nonpast_stem_base)
local past_pass_stem_base = q(_U, rad12, U, rad34)
-- make parts
make_augmented_sound_final_weak_verb(base, vowel_spec, past_stem_base, nonpast_stem_base, past_pass_stem_base,
vn)
end
local function form_vii_nrad1(base, rad1)
if base.reduced then
if not req(rad1, M) then
error(("Internal error: Form VII first radical %s is not م but .reduced specified; should have been caught earlier"):
format(rget(rad1)))
end
return M .. SH
else
return q("نْ", rad1)
end
end
-- Make form VII sound or final-weak verb.
local function make_form_vii_sound_final_weak_verb(base, vowel_spec)
local rad1, rad2, rad3 = get_radicals_3(vowel_spec)
make_high_form_sound_final_weak_verb(base, vowel_spec, form_vii_nrad1(base, rad1), rad2, rad3)
end
conjugations["VII-sound"] = function(base, vowel_spec)
make_form_vii_sound_final_weak_verb(base, vowel_spec)
end
conjugations["VII-final-weak"] = function(base, vowel_spec)
make_form_vii_sound_final_weak_verb(base, vowel_spec)
end
conjugations["VII-hollow"] = function(base, vowel_spec)
local rad1, rad2, rad3 = get_radicals_3(vowel_spec)
local nrad1 = form_vii_nrad1(base, rad1)
local vn = high_form_verbal_noun(nrad1, Y, rad3)
-- various stem bases
local nonpast_stem_base = nrad1
local past_stem_base = q(_I, nonpast_stem_base)
local past_pass_stem_base = q(_U, nrad1)
-- make parts
make_augmented_hollow_verb(base, vowel_spec, past_stem_base, nonpast_stem_base, past_pass_stem_base, vn)
end
conjugations["VII-geminate"] = function(base, vowel_spec)
local rad1, rad2, rad3 = get_radicals_3(vowel_spec)
local nrad1 = form_vii_nrad1(base, rad1)
local vn = high_form_verbal_noun(nrad1, rad2, rad2)
-- various stem bases
local nonpast_stem_base = q(nrad1, A)
local past_stem_base = q(_I, nonpast_stem_base)
local past_pass_stem_base = q(_U, nrad1, U)
-- make parts
make_augmented_geminate_verb(base, vowel_spec, past_stem_base, nonpast_stem_base, past_pass_stem_base, vn)
end
-- Return Form VIII verbal noun.
local function form_viii_verbal_noun(base, vowel_spec, rad1, rad2, rad3)
local final_weak = is_final_weak(base, vowel_spec)
rad3 = final_weak and HAMZA or rad3
return {high_form_verbal_noun(vowel_spec.form_viii_assim, rad2, rad3)}
end
-- Make form VIII sound or final-weak verb.
local function make_form_viii_sound_final_weak_verb(base, vowel_spec)
local rad1, rad2, rad3 = get_radicals_3(vowel_spec)
-- check for irregular verb اِتَّخَذَ
if axadh_radicals(rad1, rad2, rad3) then
base.irregular = true
rad1 = T
end
make_high_form_sound_final_weak_verb(base, vowel_spec, vowel_spec.form_viii_assim, rad2, rad3)
end
conjugations["VIII-sound"] = function(base, vowel_spec)
make_form_viii_sound_final_weak_verb(base, vowel_spec)
end
conjugations["VIII-final-weak"] = function(base, vowel_spec)
make_form_viii_sound_final_weak_verb(base, vowel_spec)
end
conjugations["VIII-hollow"] = function(base, vowel_spec)
local rad1, rad2, rad3 = get_radicals_3(vowel_spec)
local vn = form_viii_verbal_noun(base, vowel_spec, rad1, Y, rad3)
-- various stem bases
local nonpast_stem_base = vowel_spec.form_viii_assim
local past_stem_base = q(_I, nonpast_stem_base)
local past_pass_stem_base = q(_U, nonpast_stem_base)
-- make parts
make_augmented_hollow_verb(base, vowel_spec, past_stem_base, nonpast_stem_base, past_pass_stem_base, vn)
end
conjugations["VIII-geminate"] = function(base, vowel_spec)
local rad1, rad2, rad3 = get_radicals_3(vowel_spec)
local vn = form_viii_verbal_noun(base, vowel_spec, rad1, rad2, rad2)
-- various stem bases
local nonpast_stem_base = q(vowel_spec.form_viii_assim, A)
local past_stem_base = q(_I, nonpast_stem_base)
local past_pass_stem_base = q(_U, vowel_spec.form_viii_assim, U)
-- make parts
make_augmented_geminate_verb(base, vowel_spec, past_stem_base, nonpast_stem_base, past_pass_stem_base, vn)
end
conjugations["IX-sound"] = function(base, vowel_spec)
local rad1, rad2, rad3 = get_radicals_3(vowel_spec)
local vn = q(_I, rad1, SK, rad2, I, rad3, AA, rad3)
-- various stem bases
local nonpast_stem_base = q(rad1, SK, rad2, A)
local past_stem_base = q(_I, nonpast_stem_base)
local past_pass_stem_base = q(_U, rad1, SK, rad2, U)
-- make parts
make_augmented_geminate_verb(base, vowel_spec, past_stem_base, nonpast_stem_base, past_pass_stem_base, vn)
end
conjugations["IX-final-weak"] = function(base, vowel_spec)
local rad1, rad2, rad3 = get_radicals_3(vowel_spec)
make_high_form_sound_final_weak_verb(base, vowel_spec, q(rad1, SK, rad2), rad3, rad3)
end
-- Populate a sound or final-weak verb for any of the various high-numbered
-- augmented forms that have 5 consonants in the stem and the same pattern of
-- vowels. Some of these consonants in certain verb parts are w's, which leads to
-- apparent anomalies in certain stems of these parts, but these anomalies
-- are handled automatically in postprocessing, where we resolve sequences of
-- iwC -> īC, uwC -> ūC, w + sukūn + w -> w + shadda.
local function make_high5_form_sound_final_weak_verb(base, vowel_spec, rad1, rad2, rad3, rad4, rad5)
make_high_form_sound_final_weak_verb(base, vowel_spec, q(rad1, SK, rad2), q(rad3, SK, rad4), rad5)
end
-- Make form X sound or final-weak verb.
local function make_form_x_sound_final_weak_verb(base, vowel_spec)
local rad1, rad2, rad3 = get_radicals_3(vowel_spec)
-- check for irregular verb اِسْتَحْيَا (also اِسْتَحَى)
local is_hayy = hayy_radicals(rad1, rad2, rad3)
local variant = vowel_spec.variant or "both"
if not is_hayy or variant == "long" or variant == "both" then
make_high5_form_sound_final_weak_verb(base, vowel_spec, S, T, rad1, rad2, rad3)
end
if is_hayy and (variant == "short" or variant == "both") then
base.irregular = true
-- Add alternative entries to the verbal paradigms. Any duplicates are removed during addition.
make_high_form_sound_final_weak_verb(base, vowel_spec, S .. SK .. T, rad1, rad3)
end
end
conjugations["X-sound"] = function(base, vowel_spec)
make_form_x_sound_final_weak_verb(base, vowel_spec)
end
conjugations["X-final-weak"] = function(base, vowel_spec)
make_form_x_sound_final_weak_verb(base, vowel_spec)
end
conjugations["X-hollow"] = function(base, vowel_spec)
local rad1, rad2, rad3 = get_radicals_3(vowel_spec)
local vn = q(base.reduced and "اِسْ" or "اِسْتِ", rad1, AA, rad3, AH)
-- various stem bases
local past_stem_base = q(base.reduced and "اِسْ" or "اِسْتَ", rad1)
local nonpast_stem_base = q(base.reduced and "سْ" or "سْتَ", rad1)
local past_pass_stem_base = q(base.reduced and "اُسْ" or "اُسْتُ", rad1)
-- make parts
make_augmented_hollow_verb(base, vowel_spec, past_stem_base, nonpast_stem_base, past_pass_stem_base, vn)
end
conjugations["X-geminate"] = function(base, vowel_spec)
local rad1, rad2, rad3 = get_radicals_3(vowel_spec)
local vn = q("اِسْتِ", rad1, SK, rad2, AA, rad2)
-- various stem bases
local past_stem_base = q("اِسْتَ", rad1)
local nonpast_stem_base = q("سْتَ", rad1)
local past_pass_stem_base = q("اُسْتُ", rad1)
-- make parts
if base.altgem then
inflect_tense(base, "past", "", {q(past_stem_base, A, rad2, SH), all_same = 1},
past_endings_ay_12_person_only)
end
make_augmented_geminate_verb(base, vowel_spec, past_stem_base, nonpast_stem_base, past_pass_stem_base, vn,
base.altgem and "[uncommon]" or nil)
end
conjugations["XI-sound"] = function(base, vowel_spec)
local rad1, rad2, rad3 = get_radicals_3(vowel_spec)
local vn = q(_I, rad1, SK, rad2, II, rad3, AA, rad3)
-- various stem bases
local nonpast_stem_base = q(rad1, SK, rad2, AA)
local past_stem_base = q(_I, nonpast_stem_base)
local past_pass_stem_base = q(_U, rad1, SK, rad2, UU)
-- make parts
make_augmented_geminate_verb(base, vowel_spec, past_stem_base, nonpast_stem_base, past_pass_stem_base, vn)
end
-- Probably no form XI final-weak, since already geminate in form; would behave as XI-sound.
-- Make form XII sound or final-weak verb.
local function make_form_xii_sound_final_weak_verb(base, vowel_spec)
local rad1, rad2, rad3 = get_radicals_3(vowel_spec)
make_high5_form_sound_final_weak_verb(base, vowel_spec, rad1, rad2, W, rad2, rad3)
end
conjugations["XII-sound"] = function(base, vowel_spec)
make_form_xii_sound_final_weak_verb(base, vowel_spec)
end
conjugations["XII-final-weak"] = function(base, vowel_spec)
make_form_xii_sound_final_weak_verb(base, vowel_spec)
end
-- Make form XIII sound or final-weak verb.
local function make_form_xiii_sound_final_weak_verb(base, vowel_spec)
local rad1, rad2, rad3 = get_radicals_3(vowel_spec)
make_high5_form_sound_final_weak_verb(base, vowel_spec, rad1, rad2, W, W, rad3)
end
conjugations["XIII-sound"] = function(base, vowel_spec)
make_form_xiii_sound_final_weak_verb(base, vowel_spec)
end
conjugations["XIII-final-weak"] = function(base, vowel_spec)
make_form_xiii_sound_final_weak_verb(base, vowel_spec)
end
-- Make a form XIV or XV sound or final-weak verb. Last radical appears twice (if`anlala / yaf`anlilu) so if it were
-- w or y you'd get if`anwā / yaf`anwī or if`anyā / yaf`anyī, i.e. unlike for most augmented verbs, the identity of
-- the radical matters.
local function make_form_xiv_xv_sound_final_weak_verb(base, vowel_spec)
local rad1, rad2, rad3 = get_radicals_3(vowel_spec)
local lastrad = base.verb_form == "XV" and Y or rad3
make_high5_form_sound_final_weak_verb(base, vowel_spec, rad1, rad2, N, rad3, lastrad)
end
conjugations["XIV-sound"] = function(base, vowel_spec)
make_form_xiv_xv_sound_final_weak_verb(base, vowel_spec)
end
conjugations["XIV-final-weak"] = function(base, vowel_spec)
make_form_xiv_xv_sound_final_weak_verb(base, vowel_spec)
end
conjugations["XV-sound"] = function(base, vowel_spec)
make_form_xiv_xv_sound_final_weak_verb(base, vowel_spec)
end
-- Probably no form XV final-weak, since already final-weak in form; would behave as XV-sound.
-- Make form Iq or IIq sound or final-weak verb.
local function make_form_iq_iiq_sound_final_weak_verb(base, vowel_spec)
local rad1, rad2, rad3, rad4 = get_radicals_4(vowel_spec)
local final_weak = is_final_weak(base, vowel_spec)
local vform = base.verb_form
local vn = vform == "IIq" and
q(TA, rad1, A, rad2, SK, rad3, (final_weak and IN or q(U, rad4))) or
q(rad1, A, rad2, SK, rad3, (final_weak and AAH or q(A, rad4, AH)))
local ta_pref = vform == "IIq" and TA or ""
local tu_pref = vform == "IIq" and TU or ""
-- various stem bases
local past_stem_base = q(ta_pref, rad1, A, rad2, SK, rad3)
local nonpast_stem_base = past_stem_base
local past_pass_stem_base = q(tu_pref, rad1, U, rad2, SK, rad3)
-- make parts
make_augmented_sound_final_weak_verb(base, vowel_spec, past_stem_base, nonpast_stem_base, past_pass_stem_base,
vn)
end
conjugations["Iq-sound"] = function(base, vowel_spec)
make_form_iq_iiq_sound_final_weak_verb(base, vowel_spec)
end
conjugations["Iq-final-weak"] = function(base, vowel_spec)
make_form_iq_iiq_sound_final_weak_verb(base, vowel_spec)
end
conjugations["IIq-sound"] = function(base, vowel_spec)
make_form_iq_iiq_sound_final_weak_verb(base, vowel_spec)
end
conjugations["IIq-final-weak"] = function(base, vowel_spec)
make_form_iq_iiq_sound_final_weak_verb(base, vowel_spec)
end
-- Make form IIIq sound or final-weak verb.
local function make_form_iiiq_sound_final_weak_verb(base, vowel_spec)
local rad1, rad2, rad3, rad4 = get_radicals_4(vowel_spec)
make_high5_form_sound_final_weak_verb(base, vowel_spec, rad1, rad2, N, rad3, rad4)
end
conjugations["IIIq-sound"] = function(base, vowel_spec)
make_form_iiiq_sound_final_weak_verb(base, vowel_spec)
end
conjugations["IIIq-final-weak"] = function(base, vowel_spec)
make_form_iiiq_sound_final_weak_verb(base, vowel_spec)
end
conjugations["IVq-sound"] = function(base, vowel_spec)
local rad1, rad2, rad3, rad4 = get_radicals_4(vowel_spec)
local vn = q(_I, rad1, SK, rad2, I, rad3, SK, rad4, AA, rad4)
-- various stem bases
local past_stem_base = q(_I, rad1, SK, rad2, A, rad3)
local nonpast_stem_base = q(rad1, SK, rad2, A, rad3)
local past_pass_stem_base = q(_U, rad1, SK, rad2, U, rad3)
-- make parts
make_augmented_geminate_verb(base, vowel_spec, past_stem_base, nonpast_stem_base, past_pass_stem_base, vn)
end
-- Probably no form IVq final-weak, since already geminate in form; would behave as IVq-sound.
end
create_conjugations()
-------------------------------------------------------------------------------
-- Guts of main conjugation function --
-------------------------------------------------------------------------------
-- Given form, weakness and radicals, check to make sure the radicals present are allowable for the weakness. Hamzas on
-- alif/wāw/yāʾ seats are never allowed (should always appear as hamza-on-the-line), and various weaknesses have various
-- strictures on allowable consonants.
local function check_radicals(form, weakness, rad1, rad2, rad3, rad4)
local function hamza_check(index, rad)
if rad == HAMZA_ON_ALIF or rad == HAMZA_UNDER_ALIF or
rad == HAMZA_ON_W or rad == HAMZA_ON_Y then
error("Radical " .. index .. " is " .. rad .. " but should be ء (hamza on the line)")
end
end
local function check_waw_ya(index, rad)
if not is_waw_ya(rad) then
error("Radical " .. index .. " is " .. rad .. " but should be و or ي")
end
end
local function check_not_waw_ya(index, rad)
if is_waw_ya(rad) then
error("In a sound verb, radical " .. index .. " should not be و or ي")
end
end
hamza_check(rad1)
hamza_check(rad2)
hamza_check(rad3)
hamza_check(rad4)
if weakness == "assimilated" or weakness == "assimilated+final-weak" then
if rad1 ~= W then
error("Radical 1 is " .. rad1 .. " but should be و")
end
-- don't check that non-assimilated form I verbs don't have wāw as their
-- first radical because some form-I verbs exist where a first-radical wāw
-- behaves as sound, e.g. wajuha yawjuhu "to be distinguished".
end
if weakness == "final-weak" or weakness == "assimilated+final-weak" then
if rad4 then
check_waw_ya(4, rad4)
else
check_waw_ya(3, rad3)
end
elseif vform_supports_final_weak(form) then
-- non-final-weak verbs cannot have weak final radical if there's a corresponding
-- final-weak verb category. I think this is safe. We may have problems with
-- ḥayya/ḥayiya yaḥyā if we treat it as a geminate verb.
if rad4 then
check_not_waw_ya(4, rad4)
else
check_not_waw_ya(3, rad3)
end
end
if weakness == "hollow" then
check_waw_ya(2, rad2)
-- don't check that non-hollow verbs in forms that support hollow verbs
-- don't have wāw or yāʾ as their second radical because some verbs exist
-- where a middle-radical wāw/yāʾ behaves as sound, e.g. form-VIII izdawaja
-- "to be in pairs".
end
if weakness == "geminate" then
if rad4 then
error("Internal error: No geminate quadrilaterals, should not be seen")
end
if rad2 ~= rad3 then
error("Weakness is geminate; radical 3 is " .. rad3 .. " but should be same as radical 2 " .. rad2)
end
elseif vform_supports_geminate(form) then
-- non-geminate verbs cannot have second and third radical same if there's
-- a corresponding geminate verb category. I think this is safe. We
-- don't fuss over double wāw or double yāʾ because this could legitimately
-- be a final-weak verb with middle wāw/yāʾ, treated as sound.
if rad4 then
error("Internal error: No quadrilaterals should support geminate verbs")
end
if rad2 == rad3 and not is_waw_ya(rad2) then
error("Weakness is '" .. weakness .. "'; radical 2 and 3 are same at " .. rad2 .. " but should not be; consider making weakness 'geminate'")
end
end
end
-- array of substitutions; each element is a 2-entry array FROM, TO; do it
-- this way so the concatenations only get evaluated once
local postprocess_subs = {
-- reorder short-vowel + shadda -> shadda + short-vowel for easier processing
{"(" .. AIU .. ")" .. SH, SH .. "%1"},
----------same letter separated by sukūn should instead use shadda---------
------------happens e.g. in kun-nā "we were".-----------------
{"(.)" .. SK .. "%1", "%1" .. SH},
---------------------------- assimilated verbs ----------------------------
-- iw, iy -> ī (assimilated verbs)
{I .. W .. SK, II},
{I .. Y .. SK, II},
-- uw, uy -> ū (assimilated verbs)
{U .. W .. SK, UU},
{U .. Y .. SK, UU},
-------------- final -yā uses tall alif not alif maqṣūra ------------------
{"(" .. Y .. SH .. "?" .. A .. ")" .. AMAQ, "%1" .. ALIF},
----------------------- handle hamza assimilation -------------------------
-- initial hamza + short-vowel + hamza + sukūn -> hamza + long vowel
{HAMZA .. A .. HAMZA .. SK, HAMZA .. A .. ALIF},
{HAMZA .. I .. HAMZA .. SK, HAMZA .. I .. Y},
{HAMZA .. U .. HAMZA .. SK, HAMZA .. U .. W}
}
local postprocess_tr_subs = {
{"ī([" .. vowels .. "y*])", "iy%1"},
{"ū([" .. vowels .. "w*])", "uw%1"},
{"(.)%*", "%1%1"}, -- implement shadda
---------------------------- assimilated verbs ----------------------------
-- iw, iy -> ī (assimilated verbs)
{"iw([^" .. vowels .. "w])", "ī%1"},
{"iy([^" .. vowels .. "y])", "ī%1"},
-- uw, uy -> ū (assimilated verbs)
{"uw([^" .. vowels .. "w])", "ū%1"},
{"uy([^" .. vowels .. "y])", "ū%1"},
----------------------- handle hamza assimilation -------------------------
-- initial hamza + short-vowel + hamza + sukūn -> hamza + long vowel
{"ʔaʔ(" .. NV .. ")", "ʔā%1"},
{"ʔiʔ(" .. NV .. ")", "ʔī%1"},
{"ʔuʔ(" .. NV .. ")", "ʔū%1"},
}
-- Post-process verb parts to eliminate phonological anomalies. Many of the changes, particularly the tricky ones,
-- involve converting hamza to have the proper seat. The rules for this are complicated and are documented on the
-- [[w:Hamza]] Wikipedia page. In some cases there are alternatives allowed, and we handle them below by returning
-- multiple possibilities.
local function postprocess_term(term)
if term == "?" then
return "?"
end
-- Add BORDER at text boundaries.
term = BORDER .. term .. BORDER
-- Do the main post-processing, based on the pattern substitutions in postprocess_subs.
for _, sub in ipairs(postprocess_subs) do
term = rsub(term, sub[1], sub[2])
end
term = term:gsub(BORDER, "")
if not rfind(term, HAMZA) then
return term
end
term = term:gsub(HAMZA, HAMZA_PH)
term = ar_utilities.process_hamza(term)
if #term == 1 then
term = term[1]
end
return term
end
local function postprocess_translit(translit)
if translit == "?" then
return "?"
end
-- Add BORDER at text boundaries.
translit = BORDER .. translit .. BORDER
-- Do the main post-processing, based on the pattern substitutions in postprocess_tr_subs.
for _, sub in ipairs(postprocess_tr_subs) do
translit = rsub(translit, sub[1], sub[2])
end
translit = translit:gsub(BORDER, "")
return translit
end
local function postprocess_forms(base)
local converted_values = {}
for slot, forms in pairs(base.forms) do
local need_dedup = false
for i, form in ipairs(forms) do
local term = postprocess_term(form.form)
local translit = form.translit and postprocess_translit(form.translit) or nil
if term ~= form.form or translit ~= form.translit then
need_dedup = true
end
converted_values[i] = {term, translit}
end
if need_dedup then
local temp_dedup = {}
for i = 1, #forms do
local new_term, new_translit = unpack(converted_values[i])
if type(new_term) == "table" then
for _, nt in ipairs(new_term) do
local new_formobj = {
form = nt,
translit = new_translit,
footnotes = forms[i].footnotes,
}
iut.insert_form(temp_dedup, "temp", new_formobj)
end
else
local new_formobj = {
form = new_term,
translit = new_translit,
footnotes = forms[i].footnotes,
}
iut.insert_form(temp_dedup, "temp", new_formobj)
end
end
base.forms[slot] = temp_dedup.temp
end
end
end
local function process_slot_overrides(base)
for slot, forms in pairs(base.slot_overrides) do
local existing_values = base.forms[slot]
base.forms[slot] = nil
for _, form in ipairs(forms) do
-- + in active participle for form I requests slot ap1
if form.form == "+" and (base.verb_form ~= "I" or slot ~= "ap") then
if not existing_values then
error(("Slot '%s' requested the default value but no such value available"):format(slot))
end
-- We maintain an invariant that no two slots share a form object (although they may share the footnote
-- lists inside the form objects). However, there is no need to copy the form objects here because there
-- is a one-to-one correspondence between slots and slot overrides, i.e. you can't have a default value
-- go into two slots.
insert_form_or_forms(base, slot, existing_values, "allow overrides", form.uncertain)
elseif default_indicator_to_active_participle_slot[form.form] then
if form.form == "++" then
if slot ~= "vn" and slot ~= "ap" and slot ~= "pp" then
error(("Secondary default value request '++' only applicable to verbal nouns and pariciples, but found in slot '%s'"):
format(slot))
end
else
if slot ~= "ap" then
error(("Secondary default value request '%s' only applicable to active pariciples, but found in slot '%s'"):
format(form.form, slot))
end
end
local secondary_default_slot =
slot == "vn" and "vn2" or slot == "pp" and "pp2" or
default_indicator_to_active_participle_slot[form.form]
local existing_values = base.forms[secondary_default_slot]
if not existing_values then
error(("Slot '%s' requested a secondary default value using '%s' but no such value available"):
format(slot, form.form))
end
-- See comment above about the lack of need to copy the form objects.
insert_form_or_forms(base, slot, existing_values, "allow overrides", form.uncertain)
-- To make sure there aren't shared form objects.
base.forms[secondary_default_slot] = nil
else
insert_form_or_forms(base, slot, form, "allow overrides", form.uncertain)
end
end
end
-- Now, for non-stative form-I verbs, fill the active participle slot from ap1 unless it should be missing (e.g.
-- passive-only or user specified 'ap:-').
if base.verb_form == "I" and not base.forms.ap and base.forms.ap1 and not skip_slot(base, "ap") then
local saw_non_stative = false
for _, vowel_spec in ipairs(base.conj_vowels) do
if req(vowel_spec.past, A) then
saw_non_stative = true
break
end
end
if saw_non_stative then
base.forms.ap = base.forms.ap1
-- To make sure there aren't shared form objects.
base.forms.ap1 = nil
end
end
end
local function handle_lemma_linked(base)
-- Compute linked versions of potential lemma slots, for use in {{ar-verb}}. We substitute the original lemma
-- (before removing links) for forms that are the same as the lemma, if the original lemma has links.
for _, slot in ipairs(export.potential_lemma_slots) do
if base.forms[slot] then
insert_form_or_forms(base, slot .. "_linked", iut.map_forms(base.forms[slot], function(form)
if form == base.lemma and rfind(base.linked_lemma, "%[%[") then
return base.linked_lemma
else
return form
end
end))
end
end
end
-- Process specs given by the user using 'addnote[SLOTSPEC][FOOTNOTE][FOOTNOTE][...]'.
local function process_addnote_specs(base)
for _, spec in ipairs(base.addnote_specs) do
for _, slot_spec in ipairs(spec.slot_specs) do
slot_spec = "^" .. slot_spec .. "$"
for slot, forms in pairs(base.forms) do
if rfind(slot, slot_spec) then
-- To save on memory, side-effect the existing forms.
for _, form in ipairs(forms) do
form.footnotes = iut.combine_footnotes(form.footnotes, spec.footnotes)
end
end
end
end
end
end
local function add_missing_links_to_forms(base)
-- Any forms without links should get them now. Redundant ones will be stripped later.
for slot, forms in pairs(base.forms) do
for _, form in ipairs(forms) do
if not form.form:find("%[%[") then
form.form = "[[" .. form.form .. "]]"
end
end
end
end
local function conjugate_verb(base)
construct_stems(base)
for _, vowel_spec in ipairs(base.conj_vowels) do
-- Reconstruct conjugation type from verb form and (possibly inferred) weakness.
conj_type = base.verb_form .. "-" .. vowel_spec.weakness
-- Check that the conjugation type is recognized.
if not conjugations[conj_type] then
error("Unknown conjugation type '" .. conj_type .. "'")
end
-- The way the conjugation functions work is they always add entries to the appropriate parts of the paradigm
-- (each of which is an array), rather than setting the values. This makes it possible to call more than one
-- conjugation function and essentially get a paradigm of the "either A or B" kind. Doing this may insert
-- duplicate entries into a particular paradigm part, but this is not a problem because we check for duplicate
-- entries when adding them, and don't insert in that case.
conjugations[conj_type](base, vowel_spec)
end
postprocess_forms(base)
process_slot_overrides(base)
-- This should happen before add_missing_links_to_forms() so that the comparison `form == base.lemma` in
-- handle_lemma_linked() works correctly and compares unlinked forms to unlinked forms.
handle_lemma_linked(base)
process_addnote_specs(base)
if not base.alternant_multiword_spec.args.noautolinkverb then
add_missing_links_to_forms(base)
end
end
local function parse_indicator_spec(angle_bracket_spec)
-- Store the original angle bracket spec so we can reconstruct the overall conj spec with the lemma(s) in them.
local base = {
angle_bracket_spec = angle_bracket_spec,
conj_vowels = {},
root_consonants = {},
user_stem_overrides = {},
user_slot_overrides = {},
slot_explicitly_missing = {},
slot_uncertain = {},
slot_override_uses_default = {},
addnote_specs = {},
}
local function parse_err(msg)
error(msg .. ": " .. angle_bracket_spec)
end
local function fetch_footnotes(separated_group)
local footnotes
for j = 2, #separated_group - 1, 2 do
if separated_group[j + 1] ~= "" then
parse_err("Extraneous text after bracketed footnotes: '" .. table.concat(separated_group) .. "'")
end
if not footnotes then
footnotes = {}
end
table.insert(footnotes, separated_group[j])
end
return footnotes
end
local inside = angle_bracket_spec:match("^<(.*)>$")
assert(inside)
local segments = put.parse_multi_delimiter_balanced_segment_run(inside, {{"[", "]"}, {"<", ">"}})
local dot_separated_groups = put.split_alternating_runs_and_strip_spaces(segments, "%.")
-- The first dot-separated element must specify the verb form, e.g. IV or IIq. If the form is I, it needs to include
-- the the past and non-past vowels, e.g. I/a~u for kataba ~ yaktubu. More than one vowel can be given,
-- comma-separated, and more than one past~non-past pair can be given, slash-separated, e.g. I/a,u~u/i~a for form I
-- كمل, which can be conjugated as kamala/kamula ~ yakmulu or kamila ~ yakmalu. An individual vowel spec must be one
-- of a, i or u and in general (a) at least one past~non-past pair most be given, and (b) both past and non-past
-- vowels must be given even though sometimes the vowel can be determined from the unvocalized form. An exception is
-- passive-only verbs, where the vowels can't in general be determined (except indirectly in some cases by looking
-- at an associated non-passive verb); in that case, the vowel~vowel spec can left out.
local slash_separated_groups = put.split_alternating_runs_and_strip_spaces(dot_separated_groups[1], "/")
local form_spec = slash_separated_groups[1]
base.form_footnotes = fetch_footnotes(form_spec)
if form_spec[1] == "" then
parse_err("Missing verb form")
end
if not allowed_vforms_with_weakness_set[form_spec[1]] then
parse_err(("Unrecognized verb form '%s', should be one of %s"):format(
form_spec[1], list_to_text(allowed_vforms, nil, " or ")))
end
if form_spec[1]:find("%-") then
base.verb_form, base.explicit_weakness = form_spec[1]:match("^(.-)%-(.*)$")
else
base.verb_form = form_spec[1]
end
if #slash_separated_groups > 1 then
if base.verb_form ~= "I" then
parse_err(("Past~non-past vowels can only be specified when verb form is I, but saw form '%s'"):format(
base.verb_form))
end
for i = 2, #slash_separated_groups do
local slash_separated_group = slash_separated_groups[i]
local tilde_separated_groups = put.split_alternating_runs_and_strip_spaces(slash_separated_group, "~")
if #tilde_separated_groups ~= 2 then
parse_err(("Expected two tilde-separated vowel specs: %s"):format(table.concat(slash_separated_group)))
end
local function parse_conj_vowels(tilde_separated_group, vtype)
local conj_vowel_objects = {}
local comma_separated_groups = put.split_alternating_runs_and_strip_spaces(tilde_separated_group, ",")
for _, comma_separated_group in ipairs(comma_separated_groups) do
local conj_vowel = comma_separated_group[1]
if conj_vowel ~= "a" and conj_vowel ~= "i" and conj_vowel ~= "u" then
parse_err(("Expected %s conjugation vowel '%s' to be one of a, i or u in %s"):format(
vtype, conj_vowel, table.concat(slash_separated_group)))
end
conj_vowel = dia[conj_vowel]
local conj_vowel_footnotes = fetch_footnotes(comma_separated_group)
-- Try to use strings when possible as it makes q() significantly more efficient.
if conj_vowel_footnotes then
table.insert(conj_vowel_objects, {form = conj_vowel, footnotes = conj_vowel_footnotes})
else
table.insert(conj_vowel_objects, conj_vowel)
end
end
return conj_vowel_objects
end
local conj_vowel_spec = {
past = parse_conj_vowels(tilde_separated_groups[1], "past"),
nonpast = parse_conj_vowels(tilde_separated_groups[2], "non-past"),
}
table.insert(base.conj_vowels, conj_vowel_spec)
end
end
for i = 2, #dot_separated_groups do
local dot_separated_group = dot_separated_groups[i]
local first_element = dot_separated_group[1]
if first_element == "addnote" then
local spec_and_footnotes = fetch_footnotes(dot_separated_group)
if #spec_and_footnotes < 2 then
parse_err("Spec with 'addnote' should be of the form 'addnote[SLOTSPEC][FOOTNOTE][FOOTNOTE][...]'")
end
local slot_spec = table.remove(spec_and_footnotes, 1)
local slot_spec_inside = rmatch(slot_spec, "^%[(.*)%]$")
if not slot_spec_inside then
parse_err("Internal error: slot_spec " .. slot_spec .. " should be surrounded with brackets")
end
local slot_specs = rsplit(slot_spec_inside, ",")
-- FIXME: Here, [[Module:it-verb]] called strip_spaces(). Generally we don't do this. Should we?
table.insert(base.addnote_specs, {slot_specs = slot_specs, footnotes = spec_and_footnotes})
elseif first_element:find("^var:") then
if #dot_separated_group > 1 then
parse_err(("Can't attach footnotes to 'var:' spec '%s'"):format(first_element))
end
base.variant = first_element:match("^var:(.*)$")
elseif first_element:find("^I+V?:") then
local root_cons, root_cons_value = first_element:match("^(I+V?):(.*)$")
local root_index
if root_cons == "I" then
root_index = 1
elseif root_cons == "II" then
root_index = 2
elseif root_cons == "III" then
root_index = 3
elseif root_cons == "IV" then
root_index = 4
if not base.verb_form:find("q$") then
parse_err(("Can't specify root consonant IV for non-quadriliteral verb form '%s': %s"):format(
base.verb_form, first_element))
end
end
local cons, translit = root_cons_value:match("^(.*)//(.*)$")
if not cons then
cons = root_cons_value
end
local root_footnotes = fetch_footnotes(dot_separated_group)
if not translit and not root_footnotes then
base.root_consonants[root_index] = cons
else
base.root_consonants[root_index] = {form = cons, translit = translit, footnotes = root_footnotes}
end
elseif first_element:find("^[a-z][a-z0-9_]*:") then
local slot_or_stem, remainder = first_element:match("^(.-):(.*)$")
dot_separated_group[1] = remainder
local comma_separated_groups = put.split_alternating_runs_and_strip_spaces(dot_separated_group, "[,،]")
if overridable_stems[slot_or_stem] then
if base.user_stem_overrides[slot_or_stem] then
parse_err("Overridable stem '" .. slot_or_stem .. "' specified twice")
end
base.user_stem_overrides[slot_or_stem] = overridable_stems[slot_or_stem](comma_separated_groups,
{prefix = slot_or_stem, base = base, parse_err = parse_err, fetch_footnotes = fetch_footnotes})
else -- assume a form override; we validate further later when the possible slots are available
if base.user_slot_overrides[slot_or_stem] then
parse_err("Form override '" .. slot_or_stem .. "' specified twice")
end
base.user_slot_overrides[slot_or_stem] = allow_multiple_values_for_override(comma_separated_groups,
{prefix = slot_or_stem, base = base, parse_err = parse_err, fetch_footnotes = fetch_footnotes},
"is form override")
end
elseif indicator_flags[first_element] then
if #dot_separated_group > 1 then
parse_err("No footnotes allowed with '" .. first_element .. "' spec")
end
if base[first_element] then
parse_err("Spec '" .. first_element .. "' specified twice")
end
base[first_element] = true
else
local passive, uncertain = first_element:match("^(.*)(%?)$")
passive = passive or first_element
uncertain = not not uncertain
if passive_types[passive] then
if #dot_separated_group > 1 then
parse_err("No footnotes allowed with '" .. passive .. "' spec")
end
if base.passive then
parse_err("Value for passive type specified twice")
end
base.passive = passive
base.passive_uncertain = uncertain
else
parse_err("Unrecognized spec '" .. first_element .. "'")
end
end
end
return base
end
-- Normalize all lemmas, substituting the pagename for blank lemmas and adding links to multiword lemmas.
local function normalize_all_lemmas(alternant_multiword_spec, head)
-- (1) Add links to all before and after text. Remember the original text so we can reconstruct the verb spec later.
if not alternant_multiword_spec.args.noautolinktext then
iut.add_links_to_before_and_after_text(alternant_multiword_spec, "remember original")
end
-- (2) Remove any links from the lemma, but remember the original form so we can use it below in the 'lemma_linked'
-- form.
iut.map_word_specs(alternant_multiword_spec, function(base)
if base.lemma == "" then
base.lemma = head
end
base.user_specified_lemma = base.lemma
base.lemma = m_links.remove_links(base.lemma)
base.user_specified_verb = base.lemma
base.verb = base.user_specified_verb
local linked_lemma
if alternant_multiword_spec.args.noautolinkverb or base.user_specified_lemma:find("%[%[") then
linked_lemma = base.user_specified_lemma
else
-- Add links to the lemma so the user doesn't specifically need to, since we preserve
-- links in multiword lemmas and include links in non-lemma forms rather than allowing
-- the entire form to be a link.
linked_lemma = iut.add_links(base.user_specified_lemma)
end
base.linked_lemma = linked_lemma
end)
end
-- Determine weakness from radicals. Used when root given in place of lemma (e.g. for {{ar-verb forms}}).
local function weakness_from_radicals(form, rad1, rad2, rad3, rad4)
local weakness = nil
local quadlit = form:find("q$")
-- If weakness unspecified, derive from radicals.
if not quadlit then
if is_waw_ya(rad3) and rad1 == W and form == "I" then
weakness = "assimilated+final-weak"
elseif is_waw_ya(rad3) and vform_supports_final_weak(form) then
weakness = "final-weak"
elseif rad2 == rad3 and vform_supports_geminate(form) then
weakness = "geminate"
elseif is_waw_ya(rad2) and vform_supports_hollow(form) then
weakness = "hollow"
elseif rad1 == W and form == "I" then
weakness = "assimilated"
else
weakness = "sound"
end
else
if is_waw_ya(rad4) then
weakness = "final-weak"
else
weakness = "sound"
end
end
return weakness
end
-- Join the infixed tāʔ (ت) to the first radical in form VIII verbs. This may cause assimilation of the tāʔ to the
-- radical or in some cases the radical to the tāʔ. Used when a root is supplied instead of a lemma (which already has
-- the appropriate assimilation in it).
local function form_viii_join_ta(rad)
if rad == W or rad == Y or rad == "ت" then return "تّ"
elseif rad == "د" then return "دّ"
elseif rad == "ث" then return "ثّ"
elseif rad == "ذ" then return "ذّ"
elseif rad == "ز" then return "زْد"
elseif rad == "ص" then return "صْط"
elseif rad == "ض" then return "ضْط"
elseif rad == "ط" then return "طّ"
elseif rad == "ظ" then return "ظّ"
else return rad .. SK .. "ت"
end
end
local function detect_indicator_spec(base)
base.forms = {}
base.stem_overrides = {}
base.slot_overrides = {}
if not base.conj_vowels[1] then
-- These may be converted to inferred vowels. If not, we throw an error if form I and not passive-only.
base.conj_vowels = {{
past = "-",
nonpast = "-",
}}
else
-- If multiple vowels specified for a given vowel type (e.g. a,u~u), expand so that each spec in
local expansion = {}
for _, spec in ipairs(base.conj_vowels) do
for _, past in ipairs(spec.past) do
for _, nonpast in ipairs(spec.nonpast) do
table.insert(expansion, {past = past, nonpast = nonpast})
end
end
end
base.conj_vowels = expansion
end
local vform = base.verb_form
-- check for quadriliteral form (Iq, IIq, IIIq, IVq)
base.quadlit = not not vform:find("q$")
-- Infer radicals as necessary. We infer a separate set of radicals for each past~non-past vowel combination because
-- they may be different (particularly with form-I hollow verbs).
for _, vowel_spec in ipairs(base.conj_vowels) do
-- NOTE: rad1, rad2, etc. refer to user-specified radicals, which are formobj tables that optionally specify an
-- explicit manual translit, whereas ir1, ir2, etc. refer to inferred radicals, which are either strings or
-- lists of possible radicals.
local rads = base.root_consonants
local rad1, rad2, rad3, rad4 = rads[1], rads[2], rads[3], rads[4]
-- Default any unspecified radicals to radicals determined from the headword. The returned radicals may be
-- lists of possible radicals, where the first radical should be chosen if the user didn't explicitly specify a
-- radical but all are allowed. If `ambig = true` is set in the table, the radical is considered ambiguous and
-- categories won't be created for weak radicals.
local weakness, ir1, ir2, ir3, ir4
if vform ~= "none" then
ir1, ir2, ir3 = rmatch(base.lemma, "^([^_])_([^_])_([^_])$")
if not ir1 then
ir1, ir2, ir3, ir4 = rmatch(base.lemma, "^([^_])_([^_])_([^_])_([^_])$")
end
if ir1 then
-- root given instead of lemma
weakness = weakness_from_radicals(vform, ir1, ir2, ir3, ir4)
if vform == "VIII" then
vowel_spec.form_viii_assim = form_viii_join_ta(ir1)
end
else
local ret = export.infer_radicals {
headword = base.lemma,
vform = vform,
passive = base.passive,
past_vowel = vowel_spec.past,
nonpast_vowel = vowel_spec.nonpast,
is_reduced = base.reduced,
}
weakness, ir1, ir2, ir3, ir4 = ret.weakness, ret.rad1, ret.rad2, ret.rad3, ret.rad4
vowel_spec.form_viii_assim = ret.form_viii_assim
vowel_spec.past = ret.past_vowel
vowel_spec.nonpast = ret.nonpast_vowel
vowel_spec.variant = base.variant or ret.variant
end
end
-- For most ambiguous radicals, the choice of radical doesn't matter because it doesn't affect the conjugation
-- one way or another. For form I hollow verbs, however, it definitely does. In fact, the choice of radical is
-- critical even beyond the past and non-past vowels because it affects the form of the passive participle. So,
-- check for this and signal an error if the radical could not be inferred and is not given explicitly.
if vform == "I" and type(ir2) == "table" and ir2.need_radical and not rad2 then
error("Unable to guess middle radical of hollow form I verb; need to specify radical explicitly")
end
if vform == "I" and not is_passive_only(base.passive) and (
rget(vowel_spec.past) == "-" or rget(vowel_spec.nonpast) == "-") then
error("Form I verb that isn't passive-only or final-weak must have past~non-past vowels specified")
end
-- Convert ambiguous radicals.
local function regularize_inferred_radical(rad)
if type(rad) == "table" then
if rad.ambig then
return {form = rad[1], ambig = true}
else
return rad[1]
end
else
return rad
end
end
-- Return the appropriate radical at index `index` (1 through 4), based either on the user-specified radical
-- `user_radical` or (if unspecified) `inferred_radical`, inferred from the unvocalized lemma. Two values are
-- returned, the "regularized" version of the radical (where ambiguous inferred radicals are converted to their
-- most likely actual radical) and the non-regularized version. The returned values are form objects rather than
-- strings.
local function fetch_radical(user_radical, inferred_radical, index)
if not user_radical then
return regularize_inferred_radical(inferred_radical), inferred_radical
else
local rad_formval = rget(user_radical)
if type(inferred_radical) == "table" then
local allowed_radical_set = m_table.listToSet(inferred_radical)
if not allowed_radical_set[rad_formval] then
error(("For lemma %s, radical %s ambiguously inferred as %s but user radical incompatibly given as %s"):
format(base.lemma, index,
list_to_text(inferred_radical, nil, " or "), rad_formval))
end
elseif rad_formval ~= inferred_radical then
error(("For lemma %s, radical %s inferred as %s but user radical incompatibly given as %s"):
format(base.lemma, index, inferred_radical, rad_formval))
end
return user_radical, user_radical
end
end
if vform ~= "none" then
vowel_spec.rad1, vowel_spec.unreg_rad1 = fetch_radical(rad1, ir1, 1)
vowel_spec.rad2, vowel_spec.unreg_rad2 = fetch_radical(rad2, ir2, 2)
vowel_spec.rad3, vowel_spec.unreg_rad3 = fetch_radical(rad3, ir3, 3)
if base.quadlit then
vowel_spec.rad4, vowel_spec.unreg_rad4 = fetch_radical(rad4, ir4, 4)
end
end
if vform == "I" then
-- If explicit weakness given using 'I-sound' or 'I-assimilated', we may need to adjust the inferred weakness.
if base.explicit_weakness == "sound" then
if weakness == "assimilated" then
weakness = "sound"
elseif weakness == "assimilated+final-weak" then
-- Verbs like waniya~yawnā "to be faint; to languish" (although the defaults should handle this
-- correctly)
weakness = "final-weak"
else
error(("Can't specify form 'I-sound' when inferred weakness is '%s' for lemma %s"):format(
weakness, base.lemma))
end
elseif base.explicit_weakness == "assimilated" then
if weakness == "sound" then
-- i~a verbs like waṭiʔa~yaṭaʔu "to tread, to trample"; wasiʕa~yasaʕu "to be spacious; to be well-off";
-- waṯiʔa~yaṯaʔu "to get bruised, to be sprained", which would default to sound.
weakness = "assimilated"
elseif weakness == "final-weak" then
-- For completeness; not clear if any verbs occur where this is needed. (There are plenty of
-- assimilated+final-weak verbs but the defaults should take care of them.)
weakness = "assimilated+final-weak"
else
error(("Can't specify form 'I-assimilated' when inferred weakness is '%s' for lemma %s"):format(
weakness, base.lemma))
end
elseif base.explicit_weakness then
error(("Internal error: Unrecognized value '%s' for base.explicit_weakness"):format(base.explicit_weakness))
end
elseif vform == "none" then
weakness = base.explicit_weakness
elseif base.explicit_weakness then
error(("Internal error: Explicit weakness should not be specifiable except with forms I and none, but saw explicit weakness '%s' with verb form '%s'"):
format(base.explicit_weakness, vform))
end
vowel_spec.weakness = weakness
if vform ~= "none" then
-- Error if radicals are wrong given the weakness. More likely to happen if the weakness is explicitly given
-- rather than inferred. Will also happen if certain incorrect letters are included as radicals e.g. hamza on
-- top of various letters, alif maqṣūra, tā' marbūṭa.
check_radicals(vform, weakness, rget(vowel_spec.rad1), rget(vowel_spec.rad2), rget(vowel_spec.rad3),
base.quadlit and rget(vowel_spec.rad4) or nil)
end
-- Check the variant value.
local form_iii_vi_geminate = (vform == "III" or vform == "VI") and rget(vowel_spec.rad2) == rget(vowel_spec.rad3) and
not req(vowel_spec.rad2, Y)
local hayy_i_x = hayy_radicals(vowel_spec.rad1, vowel_spec.rad2, vowel_spec.rad3) and (vform == "I" or vform == "X")
if form_iii_vi_geminate or hayy_i_x then
if vowel_spec.variant and vowel_spec.variant ~= "long" and vowel_spec.variant ~= "short" and vowel_spec.variant ~= "both" then
error(("For form-III/VI geminate verb or form-I/X verb with ح-ي-ي radicals, saw unrecognized 'var:%s' value; should be 'var:long', 'var:short' or 'var:both'"):format(
vowel_spec.variant))
end
elseif vowel_spec.variant then
error(("Variant value 'var:%s' not allowed in this context"):format(vowel_spec.variant))
end
end
-- If form I, regroup expanded vowels for display purposes.
if vform == "I" then
local group_by_past = {}
for _, vowel_spec in ipairs(base.conj_vowels) do
m_table.insertIfNot(group_by_past, {
past = undia[rget(vowel_spec.past)],
nonpasts = {undia[rget(vowel_spec.nonpast)]},
}, {
key = function(obj) return obj.past end,
combine = function(obj1, obj2)
for _, nonpast in ipairs(obj2.nonpasts) do
m_table.insertIfNot(obj1.nonpasts, nonpast)
end
end,
})
end
local group_by_nonpast = {}
for _, vowel_spec in ipairs(group_by_past) do
m_table.insertIfNot(group_by_nonpast, {
pasts = {vowel_spec.past},
nonpasts = vowel_spec.nonpasts,
}, {
key = function(obj) return obj.nonpasts end,
combine = function(obj1, obj2)
for _, past in ipairs(obj2.pasts) do
m_table.insertIfNot(obj1.pasts, past)
end
end,
})
end
base.grouped_conj_vowels = group_by_nonpast
end
-- Set value of passive. If not specified, default is yes for forms II, III, IV and Iq; no but uncertainly for
-- forms VII, IX, XI - XV and IIIq - IVq, as well as form I with past vowel u; impersonal but uncertainly for form
-- V, VI, X and IIq, as well as form I with past vowel i; and yes but uncertainly for the remainder (form I with
-- past vowel only a and form VIII).
if not base.passive then
base.passive_defaulted = true
-- Temporary tracking for defaulted passives by verb form, weakness and (for form I) past/non-past vowels.
track_if_ar_conj(base, "passive-defaulted/" .. vform)
for _, vowel_spec in ipairs(base.conj_vowels) do
track_if_ar_conj(base, "passive-defaulted/" .. vform.. "/" .. vowel_spec.weakness)
if vform == "I" then
local past_nonpast = ("%s~%s"):format(undia[vowel_spec.past], undia[vowel_spec.nonpast])
track_if_ar_conj(base, "passive-defaulted/I/" .. past_nonpast)
track_if_ar_conj(base, "passive-defaulted/I/" .. vowel_spec.weakness .. "/" .. past_nonpast)
end
end
if vform_probably_full_passive(vform) then
base.passive = "pass"
else
base.passive_uncertain = true
for _, vowel_spec in ipairs(base.conj_vowels) do
if vform_probably_no_passive(vform, vowel_spec.weakness, vowel_spec.past, vowel_spec.nonpast) then
base.passive = "nopass"
break
elseif vform_probably_impersonal_passive(vform, vowel_spec.weakness, vowel_spec.past,
vowel_spec.nonpast) then
base.passive = "ipass"
break
end
end
base.passive = base.passive or "pass"
end
end
-- NOTE: Currently there are no built-in stems or form overrides for Arabic; this code is inherited from
-- [[Module:ca-verb]], where such things do exist, and is kept for generality in case we decide in the future to
-- implement such things.
-- Override built-in verb stems and overrides with user-specified ones.
for stem, values in pairs(base.user_stem_overrides) do
base.stem_overrides[stem] = values
end
for slot, values in pairs(base.user_slot_overrides) do
if not base.alternant_multiword_spec.verb_slots_map[slot] then
error("Unrecognized override slot '" .. slot .. "': " .. base.angle_bracket_spec)
end
if export.unsettable_slots_set[slot] then
error("Slot '" .. slot .. "' cannot be set using an override: " .. base.angle_bracket_spec)
end
if skip_slot(base, slot, "allow overrides") then
error("Override slot '" .. slot ..
"' would be skipped based on the passive, 'noimp' and/or 'no_nonpast' settings: " ..
base.angle_bracket_spec)
end
base.slot_overrides[slot] = values
end
if base.verb_form == "none-final-weak" then
for _, stem_type in ipairs { "past", "past_pass", "nonpast", "nonpast_pass" } do
if base.stem_overrides[stem_type .. "_c"] or base.stem_overrides[stem_type .. "_v"] then
error(("Specify past stem for verb type 'none-final-weak' using '%s:...' not '%s_c:...' or '%s_v:...'"):
format(stem_type, stem_type, stem_type))
end
end
for _, stem_type in ipairs { "past", "nonpast" } do
if base.stem_overrides[stem_type] or not base.stem_overrides[stem_type .. "_final_weak_vowel"] then
error(("For verb type 'none-final-weak', if '%s:...' specified, so must '%s_final_weak_vowel:...'"):
format(stem_type, stem_type))
end
end
end
end
local function detect_all_indicator_specs(alternant_multiword_spec)
add_slots(alternant_multiword_spec)
alternant_multiword_spec.verb_forms = {}
-- This means at least one individual base had the slot marked as explicitly missing. Another base (e.g. when
-- there are multiple alternants) might have a value for the slot. In practice, we only respect this when there are
-- no overall values in the slot and `slot_uncertain` isn't set; in this case, we display "no ..." for the slot
-- instead of simply not displaying anything for the slot.
alternant_multiword_spec.slot_explicitly_missing = {}
-- This means at least one individual base had no values for the slot and the slot marked as explicitly uncertain.
-- Note that this is different from a value being present but marked as uncertain (e.g. if an override was given
-- with a ? after it); this causes the form object for the value to have `uncertain = true` set. If there are no
-- overall values in the slot and `slot_uncertain` is set, we display this in the headword.
alternant_multiword_spec.slot_uncertain = {}
iut.map_word_specs(alternant_multiword_spec, function(base)
-- So arguments, etc. can be accessed. WARNING: Creates circular reference.
base.alternant_multiword_spec = alternant_multiword_spec
detect_indicator_spec(base)
if not base.nocat then
m_table.insertIfNot(alternant_multiword_spec.verb_forms, base.verb_form)
end
if base.passive_uncertain then
alternant_multiword_spec.passive_uncertain = true
end
for slot, _ in pairs(base.slot_explicitly_missing) do
alternant_multiword_spec.slot_explicitly_missing[slot] = true
end
end)
end
local function determine_slot_uncertainty_from_forms(alternant_multiword_spec)
iut.map_word_specs(alternant_multiword_spec, function(base)
-- If no verbal noun and verb form is not 'none' (manually-specified stems) — which currently only happens for
-- form I — and the verbal noun wasn't explicitly indicated as missing using <vn:->, we assume it's just
-- unknown/unspecified rather than missing. Same with active participles.
for uncertain_slot, _ in pairs(slots_that_may_be_uncertain) do
if not base.forms[uncertain_slot] and vform ~= "none" and not skip_slot(base, uncertain_slot) then
base.slot_uncertain[uncertain_slot] = true
end
end
-- Propagate slot uncertainty up. Currently only the verbal noun can have this set but we write the code
-- generally.
for slot, _ in pairs(base.slot_uncertain) do
alternant_multiword_spec.slot_uncertain[slot] = true
end
end)
-- If slot is uncertain and has no value, explicitly set its value to "?".
for uncertain_slot, _ in pairs(slots_that_may_be_uncertain) do
if not alternant_multiword_spec.forms[uncertain_slot] and
alternant_multiword_spec.slot_uncertain[uncertain_slot] then
alternant_multiword_spec.forms[uncertain_slot] = {{form = "?"}}
end
end
end
-- Determine certain properties of the verb from the overall forms, such as whether the verb is active-only or
-- passive-only, is impersonal, lacks an imperative, etc.
local function determine_verb_properties_from_forms(alternant_multiword_spec)
alternant_multiword_spec.has_active = false
alternant_multiword_spec.has_passive = false
alternant_multiword_spec.has_non_impers_active = false
alternant_multiword_spec.has_non_impers_passive = false
alternant_multiword_spec.has_imp = false
alternant_multiword_spec.has_past = false
alternant_multiword_spec.has_nonpast = false
for slot, _ in pairs(alternant_multiword_spec.forms) do
if slot == "ap" or slot:find("[123]") and not slot:find("_pass") then
alternant_multiword_spec.has_active = true
end
if slot == "pp" or slot:find("[123]") and slot:find("_pass") then
alternant_multiword_spec.has_passive = true
end
if slot:find("[123]") and not slot:find("pass_[123]") and not slot:find("3ms") then
alternant_multiword_spec.has_non_impers_active = true
end
if slot:find("pass_[123]") and not slot:find("3ms") then
alternant_multiword_spec.has_non_impers_passive = true
end
if slot:find("^imp_") then
alternant_multiword_spec.has_imp = true
end
if slot:find("^past_") then
alternant_multiword_spec.has_past = true
end
if slot:find("^ind_") or slot:find("^sub_") or slot:find("^juss_") then
alternant_multiword_spec.has_nonpast = true
end
end
end
local function add_categories_and_annotation(alternant_multiword_spec, base, multiword_lemma, insert_ann, insert_cat)
-- Useful e.g. in constructing suppletive verbs out of parts. For a verb like جاء or أتى whose imperative comes
-- from the unrelated verb تعالى, we don't want the latter verb showing up in categories or annotations.
if base.nocat then
return
end
local vform = base.verb_form
if vform ~= "none" then
insert_ann("form", vform)
insert_cat("form-" .. vform .. " verbs")
end
if base.reduced then
insert_ann("reduced", "reduced")
if vform ~= "none" then
insert_cat("form-" .. vform .. " reduced verbs")
end
end
if base.quadlit then
insert_cat("verbs with quadriliteral roots")
end
if base.passive_defaulted then
insert_cat("verbs with defaulted passive")
end
for _, vowel_spec in ipairs(base.conj_vowels) do
local rad1, rad2, rad3, rad4 = get_radicals_4(vowel_spec)
local final_weak = is_final_weak(base, vowel_spec)
local weakness = vowel_spec.weakness
-- We have to distinguish weakness by form and weakness by conjugation. Weakness by form merely indicates the
-- presence of weak letters in certain positions in the radicals. Weakness by conjugation is related to how the
-- verbs are conjugated. For example, form-II verbs that are "hollow by form" (middle radical is wāw or yāʾ) are
-- conjugated as sound verbs. Another example: form-I verbs with initial wāw are "assimilated by form" and most
-- are assimilated by conjugation as well, but a few are sound by conjugation, e.g. wajuha yawjuhu "to be
-- distinguished" (rather than wajuha yajuhu); similarly for some hollow-by-form verbs in various forms, e.g.
-- form VIII izdawaja yazdawiju "to be in pairs" (rather than izdāja yazdāju). Categories referring to weakness
-- always refer to weakness by conjugation; weakness by form is distinguished only by categories such as
-- [[:Category:Arabic form-III verbs with و as second radical]].
insert_ann("weakness", weakness)
if vform ~= "none" then
insert_cat(("%s form-%s verbs"):format(weakness, vform))
end
local function radical_is_ambiguous(rad)
return type(rad) == "table" and rad.ambig
end
local function radical_is_unambiguous_weak(rad)
return not radical_is_ambiguous(rad) and (is_waw_ya(rad) or req(rad, HAMZA))
end
if vform ~= "none" then
local ur1, ur2, ur3, ur4 =
vowel_spec.unreg_rad1, vowel_spec.unreg_rad2, vowel_spec.unreg_rad3, vowel_spec.unreg_rad4
-- Create headword categories based on the radicals. Do the following before
-- converting the Latin radicals into Arabic ones so we distinguish
-- between ambiguous and non-ambiguous radicals.
if radical_is_ambiguous(ur1) or radical_is_ambiguous(ur2) or radical_is_ambiguous(ur3) or
ur4 and radical_is_ambiguous(ur4) then
insert_cat("verbs with ambiguous radicals")
end
if radical_is_unambiguous_weak(ur1) then
insert_cat("form-" .. vform .. " verbs with " .. rget(ur1) .. " as first radical")
end
if radical_is_unambiguous_weak(ur2) then
insert_cat("form-" .. vform .. " verbs with " .. rget(ur2) .. " as second radical")
end
if radical_is_unambiguous_weak(ur3) then
insert_cat("form-" .. vform .. " verbs with " .. rget(ur3) .. " as third radical")
end
if ur4 and radical_is_unambiguous_weak(ur4) then
insert_cat("form-" .. vform .. " verbs with " .. rget(ur4) .. " as fourth radical")
end
end
end
if vform == "I" and not is_passive_only(base.passive) then
for _, vowel_spec in ipairs(base.grouped_conj_vowels) do
insert_ann("vowels",
("%s ~ %s"):format(table.concat(vowel_spec.pasts, "/"), table.concat(vowel_spec.nonpasts, "/")))
for _, past in ipairs(vowel_spec.pasts) do
for _, nonpast in ipairs(vowel_spec.nonpasts) do
if past == "-" or nonpast == "-" then
error("Internal error: Saw form I past vowel %s and non-past vowel %s but - in place of vowel should have triggered an error earlier")
end
insert_cat(("form-I verbs with past vowel %s and non-past vowel %s"):format(past, nonpast))
end
end
end
end
for slot, name in pairs(slots_that_may_be_uncertain) do
if base.slot_uncertain[slot] then
-- An unspecified and non-defaulted verbal noun (form I) is considered uncertain rather than explicitly
-- missing. Use <vn:-> to explicitly indicate the lack of verbal noun. Same for form-I stative active
-- participles.
insert_cat(("verbs with unknown or uncertain %ss"):format(name))
end
end
if base.irregular then
insert_ann("irreg", "irregular")
insert_cat("irregular verbs")
end
end
-- Compute the categories to add the verb to, as well as the annotation to display in the conjugation title bar. We
-- combine the code to do these functions as both categories and title bar contain similar information.
local function compute_categories_and_annotation(alternant_multiword_spec)
alternant_multiword_spec.categories = {}
local ann = {}
alternant_multiword_spec.annotation = ann
ann.form = {}
ann.weakness = {}
ann.vowels = {}
ann.passive = nil
ann.reduced = {}
ann.irreg = {}
ann.defective = {}
local multiword_lemma = false
for _, slot in ipairs(export.potential_lemma_slots) do
if alternant_multiword_spec.forms[slot] then
for _, formobj in ipairs(alternant_multiword_spec.forms[slot]) do
if formobj.form:find(" ") then
multiword_lemma = true
break
end
end
break
end
end
local function insert_ann(anntype, value)
m_table.insertIfNot(alternant_multiword_spec.annotation[anntype], value)
end
local function insert_cat(cat, also_when_multiword)
-- Don't place multiword terms in categories like 'Arabic form-II verbs' to avoid spamming the categories with
-- such terms.
if also_when_multiword or not multiword_lemma then
m_table.insertIfNot(alternant_multiword_spec.categories, "Arabic " .. cat)
end
end
iut.map_word_specs(alternant_multiword_spec, function(base)
add_categories_and_annotation(alternant_multiword_spec, base, multiword_lemma, insert_ann, insert_cat)
end)
for slot, name in pairs(slots_that_may_be_uncertain) do
if alternant_multiword_spec.forms[slot] then
for _, form in ipairs(alternant_multiword_spec.forms[slot]) do
if form.uncertain then
if form.form == "?" then
insert_cat(("verbs with explicitly unknown %ss"):format(name))
else
insert_cat(("verbs needing %s checked"):format(name))
end
break
end
end
end
end
if alternant_multiword_spec.has_active then
if alternant_multiword_spec.has_passive and alternant_multiword_spec.has_non_impers_passive then
insert_cat("verbs with full passive")
ann.passive = "full passive"
elseif alternant_multiword_spec.has_passive then
insert_cat("verbs with impersonal passive")
ann.passive = "impersonal passive"
else
insert_cat("verbs lacking passive forms")
ann.passive = "no passive"
end
else
if alternant_multiword_spec.has_non_impers_passive then
insert_cat("passive verbs")
insert_cat("verbs with full passive")
ann.passive = "passive-only"
else
insert_cat("passive verbs")
insert_cat("impersonal verbs")
insert_cat("verbs with impersonal passive")
ann.passive = "impersonal (passive-only)"
end
end
if alternant_multiword_spec.passive_uncertain then
insert_cat("verbs needing passive checked")
ann.passive = ann.passive .. ' <abbr title="passive status uncertain">(?)</abbr>'
end
if alternant_multiword_spec.has_active and not alternant_multiword_spec.has_imp then
insert_ann("defective", "no imperative")
insert_cat("verbs lacking imperative forms")
end
if not alternant_multiword_spec.has_past then
insert_ann("defective", "no past")
insert_cat("verbs lacking past forms")
end
if not alternant_multiword_spec.has_nonpast then
insert_ann("defective", "no non-past")
insert_cat("verbs lacking non-past forms")
end
local ann_parts = {}
local function insert_ann_part(part, conj)
local val = table.concat(ann[part], conj or " or ")
if val ~= "" and val ~= "regular" then
table.insert(ann_parts, val)
end
end
insert_ann_part("form")
insert_ann_part("weakness")
insert_ann_part("reduced")
insert_ann_part("vowels")
if ann.passive then
table.insert(ann_parts, ann.passive)
end
insert_ann_part("irreg")
insert_ann_part("defective", ", ")
alternant_multiword_spec.annotation = table.concat(ann_parts, ", ")
end
local function show_forms(alternant_multiword_spec)
local lemmas = {}
for _, slot in ipairs(export.potential_lemma_slots) do
if alternant_multiword_spec.forms[slot] then
for _, formobj in ipairs(alternant_multiword_spec.forms[slot]) do
table.insert(lemmas, formobj)
end
break
end
end
alternant_multiword_spec.lemmas = lemmas -- save for later use in make_table()
alternant_multiword_spec.vn = alternant_multiword_spec.forms.vn -- save for later use in make_table()
-- Reconstruct the original verb spec without overrides for verbal nouns and participles, since those specific slots
-- are ignored by {{ar-verb form}}. Compute this once beforehand; `transform_accel_obj` is called repeatedly on each
-- form and we don't want to compute this repeatedly.
local reconstructed_verb_spec = iut.reconstruct_original_spec(alternant_multiword_spec, {
preprocess_angle_bracket_spec = function(spec)
spec = spec:match("^<(.*)>$")
assert(spec)
local segments = put.parse_multi_delimiter_balanced_segment_run(spec, {{"[", "]"}, {"<", ">"}})
local dot_separated_groups = put.split_alternating_runs_and_strip_spaces(segments, "%.")
-- Rejoin each dot-separated group into a single string, since we aren't actually going to do any parsing
-- of bracket-bounded textual runs; then filter out overrides for verbal nouns and participles.
local filtered_indicators = {}
for _, dot_separated_group in ipairs(dot_separated_groups) do
local indicator = table.concat(dot_separated_group)
-- FIXME: Do we want to filter out any other indicators?
if not (indicator:find("^vn:") or indicator:find("^[ap]p:")) then
table.insert(filtered_indicators, indicator)
end
end
return ("<%s>"):format(table.concat(filtered_indicators, "."))
end,
})
-- If we're dealing with a single word, no alternants and a single verb form, use the auto-conjugation-fetching
-- variant.
local reconstructed_lemma, inside = reconstructed_verb_spec:match("^([^ <>()]+)(%b<>)$")
if inside and alternant_multiword_spec.verb_forms[1] and not alternant_multiword_spec.verb_forms[2] then
reconstructed_verb_spec = ("+%s<%s>"):format(reconstructed_lemma, alternant_multiword_spec.verb_forms[1])
end
local function transform_accel_obj(slot, formobj, accel_obj)
if not accel_obj then
return accel_obj
end
if slot == "ap" or slot == "pp" or slot == "vn" then
-- FIXME: [[Module:accel]] can't correctly handle more than one verb form for participles and verbal nouns
accel_obj.form = slot .. "-" .. table.concat(alternant_multiword_spec.verb_forms, ",")
else
accel_obj.form = "verb-form-" .. reconstructed_verb_spec
end
return accel_obj
end
local function generate_link(data)
local form = data.form
local term = form.formval_for_link
local alt = form.alt
if term == "?" then
term = nil
alt = "?"
end
local link = m_links.full_link {
lang = lang, term = term, tr = "-", accel = form.accel_obj,
alt = alt, gloss = form.gloss, genders = form.genders, pos = form.pos, lit = form.lit, id = form.id,
} .. iut.get_footnote_text(form.footnotes, data.footnote_obj)
if form.q and form.q[1] or form.qq and form.qq[1] or form.l and form.l[1] or form.ll and form.ll[1] then
link = require(pron_qualifier_module).format_qualifiers {
lang = lang,
text = link,
q = form.q,
qq = form.qq,
l = form.l,
ll = form.ll,
}
end
return link
end
local props = {
lang = lang,
lemmas = lemmas,
transform_accel_obj = transform_accel_obj,
generate_link = generate_link,
slot_list = alternant_multiword_spec.verb_slots,
include_translit = true,
}
iut.show_forms(alternant_multiword_spec.forms, props)
end
-------------------------------------------------------------------------------
-- Functions to create inflection tables --
-------------------------------------------------------------------------------
-- Make the conjugation table. Called from export.show().
local function make_table(alternant_multiword_spec)
local text = mw.getCurrentFrame():expandTemplate{
title = 'inflection-table-top',
args = {
title = 'Conjugation of {title}',
tall = 'yes',
palette = "green",
category = 'conjugation',
class = 'tr-alongside', -- temp hack to prevent extra line break
}
}
text = text .. [=[
! colspan="6" | verbal noun<br /><<الْمَصْدَر>>
| colspan="7" | {vn}
]=]
if alternant_multiword_spec.has_active then
text = text .. [=[
|-
! colspan="6" | active participle<br /><<اِسْم الْفَاعِل>>
| colspan="7" | {ap}
]=]
end
if alternant_multiword_spec.has_passive then
text = text .. [=[
|-
! colspan="6" | passive participle<br /><<اِسْم الْمَفْعُول>>
| colspan="7" | {pp}
]=]
end
text = text .. [=[
|-
! colspan="999" class="separator" |
]=]
if alternant_multiword_spec.has_active then
text = text .. [=[
|-
! colspan="12" class="outer" | active voice<br /><<الْفِعْل الْمَعْلُوم>>
|-
! colspan="2" |
! colspan="3" | singular<br /><<الْمُفْرَد>>
! rowspan="12" class="separator" |
! colspan="2" | dual<br /><<الْمُثَنَّى>>
! rowspan="12" class="separator" |
! colspan="3"| plural<br /><<الْجَمْع>>
|-
! colspan="2"|
! 1<sup>st</sup> person<br /><<الْمُتَكَلِّم>>
! 2<sup>nd</sup> person<br /><<الْمُخَاطَب>>
! 3<sup>rd</sup> person<br /><<الْغَائِب>>
! 2<sup>nd</sup> person<br /><<الْمُخَاطَب>>
! 3<sup>rd</sup> person<br /><<الْغَائِب>>
! 1<sup>st</sup> person<br /><<الْمُتَكَلِّم>>
! 2<sup>nd</sup> person<br /><<الْمُخَاطَب>>
! 3<sup>rd</sup> person<br /><<الْغَائِب>>
|-
! rowspan="2" | past (perfect) indicative<br /><<الْمَاضِي>>
! class="secondary" | m
| rowspan="2" | {past_1s}
| {past_2ms}
| {past_3ms}
| rowspan="2" | {past_2d}
| {past_3md}
| rowspan="2" | {past_1p}
| {past_2mp}
| {past_3mp}
|-
! class="secondary" | f
| {past_2fs}
| {past_3fs}
| {past_3fd}
| {past_2fp}
| {past_3fp}
|-
! rowspan="2" | non-past (imperfect) indicative<br /><<الْمُضَارِع الْمَرْفُوع>>
! class="secondary" | m
| rowspan="2" | {ind_1s}
| {ind_2ms}
| {ind_3ms}
| rowspan="2" | {ind_2d}
| {ind_3md}
| rowspan="2" | {ind_1p}
| {ind_2mp}
| {ind_3mp}
|-
! class="secondary" | f
| {ind_2fs}
| {ind_3fs}
| {ind_3fd}
| {ind_2fp}
| {ind_3fp}
|-
! rowspan="2" | subjunctive<br /><<الْمُضَارِع الْمَنْصُوب>>
! class="secondary" | m
| rowspan="2" | {sub_1s}
| {sub_2ms}
| {sub_3ms}
| rowspan="2" | {sub_2d}
| {sub_3md}
| rowspan="2" | {sub_1p}
| {sub_2mp}
| {sub_3mp}
|-
! class="secondary" | f
| {sub_2fs}
| {sub_3fs}
| {sub_3fd}
| {sub_2fp}
| {sub_3fp}
|-
! rowspan="2" | jussive<br /><<الْمُضَارِع الْمَجْزُوم>>
! class="secondary" | m
| rowspan="2" | {juss_1s}
| {juss_2ms}
| {juss_3ms}
| rowspan="2" | {juss_2d}
| {juss_3md}
| rowspan="2" | {juss_1p}
| {juss_2mp}
| {juss_3mp}
|-
! class="secondary" | f
| {juss_2fs}
| {juss_3fs}
| {juss_3fd}
| {juss_2fp}
| {juss_3fp}
|-
! rowspan="2" | imperative<br /><<الْأَمْر>>
! class="secondary" | m
| rowspan="2" |
| {imp_2ms}
| rowspan="2" |
| rowspan="2" | {imp_2d}
| rowspan="2" |
| rowspan="2" |
| {imp_2mp}
| rowspan="2" |
|-
! class="secondary" | f
| {imp_2fs}
| {imp_2fp}
]=]
end
if alternant_multiword_spec.has_passive then
text = text .. [=[
|-
! colspan="999" class="separator" |
|-
! colspan="12" class="outer" | passive voice<br /><<الْفِعْل الْمَجْهُول>>
|-
! colspan="2" |
! colspan="3" | singular<br /><<الْمُفْرَد>>
! rowspan="10" class="separator" |
! colspan="2" | dual<br /><<الْمُثَنَّى>>
! rowspan="10" class="separator" |
! colspan="3" | plural<br /><<الْجَمْع>>
|-
! colspan="2" |
! 1<sup>st</sup> person<br /><<الْمُتَكَلِّم>>
! 2<sup>nd</sup> person<br /><<الْمُخَاطَب>>
! 3<sup>rd</sup> person<br /><<الْغَائِب>>
! 2<sup>nd</sup> person<br /><<الْمُخَاطَب>>
! 3<sup>rd</sup> person<br /><<الْغَائِب>>
! 1<sup>st</sup> person<br /><<الْمُتَكَلِّم>>
! 2<sup>nd</sup> person<br /><<الْمُخَاطَب>>
! 3<sup>rd</sup> person<br /><<الْغَائِب>>
|-
! rowspan="2" | past (perfect) indicative<br /><<الْمَاضِي>>
! class="secondary" | m
| rowspan="2" | {past_pass_1s}
| {past_pass_2ms}
| {past_pass_3ms}
| rowspan="2" | {past_pass_2d}
| {past_pass_3md}
| rowspan="2" | {past_pass_1p}
| {past_pass_2mp}
| {past_pass_3mp}
|-
! class="secondary" | f
| {past_pass_2fs}
| {past_pass_3fs}
| {past_pass_3fd}
| {past_pass_2fp}
| {past_pass_3fp}
|-
! rowspan="2" | non-past (imperfect) indicative<br /><<الْمُضَارِع الْمَرْفُوع>>
! class="secondary" | m
| rowspan="2" | {ind_pass_1s}
| {ind_pass_2ms}
| {ind_pass_3ms}
| rowspan="2" | {ind_pass_2d}
| {ind_pass_3md}
| rowspan="2" | {ind_pass_1p}
| {ind_pass_2mp}
| {ind_pass_3mp}
|-
! class="secondary" | f
| {ind_pass_2fs}
| {ind_pass_3fs}
| {ind_pass_3fd}
| {ind_pass_2fp}
| {ind_pass_3fp}
|-
! rowspan="2" | subjunctive<br /><<الْمُضَارِع الْمَنْصُوب>>
! class="secondary" | m
| rowspan="2" | {sub_pass_1s}
| {sub_pass_2ms}
| {sub_pass_3ms}
| rowspan="2" | {sub_pass_2d}
| {sub_pass_3md}
| rowspan="2" | {sub_pass_1p}
| {sub_pass_2mp}
| {sub_pass_3mp}
|-
! class="secondary" | f
| {sub_pass_2fs}
| {sub_pass_3fs}
| {sub_pass_3fd}
| {sub_pass_2fp}
| {sub_pass_3fp}
|-
! rowspan="2" | jussive<br /><<الْمُضَارِع الْمَجْزُوم>>
! class="secondary" | m
| rowspan="2" | {juss_pass_1s}
| {juss_pass_2ms}
| {juss_pass_3ms}
| rowspan="2" | {juss_pass_2d}
| {juss_pass_3md}
| rowspan="2" | {juss_pass_1p}
| {juss_pass_2mp}
| {juss_pass_3mp}
|-
! class="secondary" | f
| {juss_pass_2fs}
| {juss_pass_3fs}
| {juss_pass_3fd}
| {juss_pass_2fp}
| {juss_pass_3fp}
]=]
end
text = text .. mw.getCurrentFrame():expandTemplate{
title = 'inflection-table-bottom',
args = {
notes = '{footnote}',
}
}
local forms = alternant_multiword_spec.forms
if not alternant_multiword_spec.lemmas then
forms.title = "—"
else
local linked_lemmas = {}
for _, form in ipairs(alternant_multiword_spec.lemmas) do
table.insert(linked_lemmas, link_term(form.form, "term"))
end
forms.title = table.concat(linked_lemmas, ", ")
end
local ann_parts = {}
if alternant_multiword_spec.annotation ~= "" then
table.insert(ann_parts, alternant_multiword_spec.annotation)
end
if alternant_multiword_spec.vn then
local linked_vns = {}
for _, form in ipairs(alternant_multiword_spec.vn) do
table.insert(linked_vns, link_term(form.form, "term"))
end
table.insert(ann_parts, (#linked_vns > 1 and "verbal nouns" or "verbal noun") .. " " ..
table.concat(linked_vns, ", "))
end
local annotation = table.concat(ann_parts, ", ")
if annotation ~= "" then
forms.title = forms.title .. " (" .. annotation .. ")"
end
-- Format the table.
local tagged_table = rsub(text, "<<(.-)>>", tag_text)
return m_string_utilities.format(tagged_table, forms)
end
-------------------------------------------------------------------------------
-- External entry points --
-------------------------------------------------------------------------------
-- Append two lists `l1` and `l2`, removing duplicates. If either is {nil}, just return the other.
local function combine_lists(l1, l2)
-- combine_footnotes() does exactly what we want.
return iut.combine_footnotes(l1, l2)
end
local function combine_metadata(data)
local src1 = data.form1
local src2 = data.form2
local dest = data.dest_form
dest.uncertain = src1.uncertain or src2.uncertain
if src1.genders and src2.genders and not m_table.deepEquals(src1.genders, src2.genders) then
-- do nothing
else
dest.genders = src1.genders or src2.genders
end
if src1.pos and src2.pos and src1.pos ~= src2.pos then
-- do nothing
else
dest.pos = src1.pos or src2.pos
end
-- Don't copy .alt, .gloss, .lit, .id, which describe a single term and don't extend to multiword terms.
dest.q = combine_lists(src1.q, src2.q)
dest.qq = combine_lists(src1.qq, src2.qq)
dest.l = combine_lists(src1.l, src2.l)
dest.ll = combine_lists(src1.ll, src2.ll)
end
-- Externally callable function to parse and conjugate a verb given user-specified arguments.
-- Return value is WORD_SPEC, an object where the conjugated forms are in `WORD_SPEC.forms`
-- for each slot. If there are no values for a slot, the slot key will be missing. The value
-- for a given slot is a list of objects {form=FORM, footnotes=FOOTNOTES}.
function export.do_generate_forms(args, source_template, headword_head)
local PAGENAME = mw.loadData("Module:headword/data").pagename
local function in_template_space()
return mw.title.getCurrentTitle().nsText == "Template"
end
-- Determine the verb spec we're being asked to generate the conjugation of. This may be taken from the current page
-- title or the value of |pagename=; but not when called from {{ar-verb form}}, where the page title is a
-- non-lemma form. Note that the verb spec may omit the lemma; e.g. it may be "<II>". For this reason, we use the
-- value of `pagename` computed here down below, when calling normalize_all_lemmas().
local pagename = source_template ~= "ar-verb form" and args.pagename or PAGENAME
local head = headword_head or pagename
local arg1 = args[1]
if not arg1 then
if (pagename == "ar-conj" or pagename == "ar-verb" or pagename == "ar-verb form") and in_template_space() then
arg1 = "كتب<I/a~u.pass>"
else
arg1 = "<>"
end
end
-- When called from {{ar-verb form}}, determine the non-lemma form whose inflections we're being asked to
-- determine. This normally comes from the page title or the value of |pagename=.
local verb_form_of_form
if source_template == "ar-verb form" then
verb_form_of_form = args.pagename
if not verb_form_of_form then
if PAGENAME == "ar-verb form" and in_template_space() then
verb_form_of_form = "كتبت"
else
verb_form_of_form = PAGENAME
end
end
end
local incorporated_headword_head_into_lemma = false
if arg1:find("^<.*>$") then -- missing lemma
if head:find(" ") then
-- If multiword lemma, try to add arg spec after the first word.
-- Try to preserve the brackets in the part after the verb, but don't do it
-- if there aren't the same number of left and right brackets in the verb
-- (which means the verb was linked as part of a larger expression).
local first_word, post = rmatch(head, "^(.-)( .*)$")
local left_brackets = rsub(first_word, "[^%[]", "")
local right_brackets = rsub(first_word, "[^%]]", "")
if #left_brackets == #right_brackets then
arg1 = iut.remove_redundant_links(first_word) .. arg1 .. post
incorporated_headword_head_into_lemma = true
else
-- Try again using the form without links.
local linkless_head = m_links.remove_links(head)
if linkless_head:find(" ") then
first_word, post = rmatch(linkless_head, "^(.-)( .*)$")
arg1 = first_word .. arg1 .. post
else
error("Unable to incorporate <...> spec into explicit head due to a multiword linked verb or " ..
"unbalanced brackets; please include <> explicitly: " .. arg1)
end
end
else
-- Will be incorporated through `head` below in the call to normalize_all_lemmas().
incorporated_headword_head_into_lemma = true
end
end
local parse_props = {
parse_indicator_spec = parse_indicator_spec,
angle_brackets_omittable = true,
allow_blank_lemma = true,
}
local alternant_multiword_spec = iut.parse_inflected_text(arg1, parse_props)
alternant_multiword_spec.pos = pos or "verbs"
alternant_multiword_spec.args = args
alternant_multiword_spec.source_template = source_template
alternant_multiword_spec.verb_form_of_form = verb_form_of_form
alternant_multiword_spec.incorporated_headword_head_into_lemma = incorporated_headword_head_into_lemma
normalize_all_lemmas(alternant_multiword_spec, head)
detect_all_indicator_specs(alternant_multiword_spec)
local inflect_props = {
lang = lang,
slot_list = alternant_multiword_spec.verb_slots,
inflect_word_spec = conjugate_verb,
combine_metadata = combine_metadata,
-- We add links around the generated verbal forms rather than allow the entire multiword
-- expression to be a link, so ensure that user-specified links get included as well.
include_user_specified_links = true,
}
iut.inflect_multiword_or_alternant_multiword_spec(alternant_multiword_spec, inflect_props)
if debug_translit then
for slot, forms in pairs(alternant_multiword_spec.forms) do
for _, form in ipairs(forms) do
if form.translit then
local full_form_translit = (lang:transliterate(m_links.remove_links(form.form)))
if full_form_translit ~= form.translit then
error(("Internal error: For slot '%s', form '%s' incremental translit '%s' not same as full translit '%s'"):
format(slot, form.form, form.translit, full_form_translit))
end
end
form.form = iut.remove_redundant_links(form.form)
end
end
end
-- Remove redundant brackets around entire forms.
for slot, forms in pairs(alternant_multiword_spec.forms) do
for _, form in ipairs(forms) do
form.form = iut.remove_redundant_links(form.form)
end
end
determine_slot_uncertainty_from_forms(alternant_multiword_spec)
determine_verb_properties_from_forms(alternant_multiword_spec)
compute_categories_and_annotation(alternant_multiword_spec)
if args.json and source_template == "ar-conj" then
-- There is a circular reference in `base.alternant_multiword_spec`, which points back to top level.
iut.map_word_specs(alternant_multiword_spec, function(base)
base.alternant_multiword_spec = nil
end)
return require("Module:JSON").toJSON(alternant_multiword_spec)
end
return alternant_multiword_spec
end
-- Entry point for {{ar-conj}}. Template-callable function to parse and conjugate a verb given
-- user-specified arguments and generate a displayable table of the conjugated forms.
function export.show(frame)
local parent_args = frame:getParent().args
local params = {
[1] = {},
["noautolinktext"] = {type = "boolean"},
["noautolinkverb"] = {type = "boolean"},
["t"] = {}, -- for use by {{ar-verb form}}; otherwise ignored
["id"] = {}, -- for use by {{ar-verb form}}; otherwise ignored
["pagename"] = {}, -- for testing/documentation pages
["json"] = {type = "boolean"}, -- for bot use
}
local args = require("Module:parameters").process(parent_args, params)
local alternant_multiword_spec = export.do_generate_forms(args, "ar-conj")
if type(alternant_multiword_spec) == "string" then
-- JSON return value
return alternant_multiword_spec
end
show_forms(alternant_multiword_spec)
return make_table(alternant_multiword_spec) ..
require("Module:utilities").format_categories(alternant_multiword_spec.categories, lang, nil, nil, force_cat)
end
function export.verb_forms(frame)
local parargs = frame:getParent().args
local params = {
[1] = {},
[2] = {},
[3] = {},
[4] = {},
[5] = {},
pagename = {},
}
for _, form in ipairs(allowed_vforms) do
-- FIXME: We go up to 5 here. The code supports unlimited variants but it's unlikely we will ever see more than
-- 2.
for index = 1, 5 do
local prefix = index == 1 and form or form .. index
params[prefix .. "-pv"] = {}
for _, extn in ipairs { "", "-vn", "-ap", "-pp" } do
params[prefix .. extn] = {}
params[prefix .. extn .. "-head"] = {}
-- FIXME: No -tr?
params[prefix .. extn .. "-gloss"] = {}
end
end
end
local args = require("Module:parameters").process(parargs, params)
local i = 1
local past_vowel_re = "^[aui,]*$"
local combined_root = nil
if not args[i] or rfind(args[i], past_vowel_re) then
combined_root = args.pagename or mw.loadData("Module:headword/data").pagename
if not rfind(combined_root, "^([^ ]) ([^ ]) ([^ ])$") and not
rfind(combined_root, "^([^ ]) ([^ ]) ([^ ]) ([^ ])$") then
error("When inferring roots from page title, need three or four space-separated radicals: " .. combined_root)
end
elseif rfind(args[i], " ") then
combined_root = args[i]
i = i + 1
else
local separate_roots = {}
while args[i] and not rfind(args[i], past_vowel_re) do
table.insert(separate_roots, args[i])
i = i + 1
end
combined_root = table.concat(separate_roots, " ")
end
local past_vowel = args[i]
i = i + 1
if past_vowel and not rfind(past_vowel, past_vowel_re) then
error("Unrecognized past vowel, should be 'a', 'i', 'u', 'a,u', etc. or empty: " .. past_vowel)
end
-- Spaces interfere with parsing as a unit in [[Module:inflection utilities]], so replace with underscore.
combined_root = combined_root:gsub(" ", "_")
local split_root = rsplit(combined_root, "_")
-- Map from verb forms (I, II, etc.) to a table of verb properties,
-- which has entries e.g. for "verb" (either true to autogenerate the verb
-- head, or an explicitly specified verb head using e.g. argument "I-head"),
-- and for "verb-gloss" (which comes from e.g. the argument "I" or "I-gloss"),
-- and for "vn" and "vn-gloss", "ap" and "ap-gloss", "pp" and "pp-gloss".
local verb_properties = {}
for _, form in ipairs(allowed_vforms) do
local formpropslist = {}
local derivs = {{"verb", ""}, {"vn", "-vn"}, {"ap", "-ap"}, {"pp", "-pp"}}
local index = 1
while true do
local formprops = {}
local prefix = index == 1 and form or form .. index
if prefix == "I" then
formprops.pv = past_vowel
end
if args[prefix .. "-pv"] then
formprops.pv = args[prefix .. "-pv"]
end
for _, deriv in ipairs(derivs) do
local prop = deriv[1]
local extn = deriv[2]
if args[prefix .. extn] == "+" then
formprops[prop] = true
elseif args[prefix .. extn] == "-" then
formprops[prop] = false
elseif args[prefix .. extn] then
formprops[prop] = true
formprops[prop .. "-gloss"] = args[prefix .. extn]
end
if args[prefix .. extn .. "-head"] then
if formprops[prop] == nil then
formprops[prop] = true
end
formprops[prop] = args[prefix .. extn .. "-head"]
end
if args[prefix .. extn .. "-gloss"] then
if formprops[prop] == nil then
formprops[prop] = true
end
formprops[prop .. "-gloss"] = args[prefix .. extn .. "-gloss"]
end
end
if formprops.verb then
-- If a verb form specified, also turn on vn (unless form I, with
-- unpredictable vn) and ap, and maybe pp, according to form,
-- weakness and past vowel. But don't turn these on if there's
-- an explicit on/off specification for them (e.g. I-pp=-).
if form ~= "I" and formprops.vn == nil then
formprops.vn = true
end
if formprops.ap == nil then
formprops.ap = true
end
local weakness = weakness_from_radicals(form, split_root[1], split_root[2], split_root[3],
split_root[4])
if formprops.pp == nil and not vform_probably_no_passive(form,
weakness, rsplit(formprops.pv or "", ","), {}) then
formprops.pp = true
end
if formprops.verb == true or formprops.vn == true or formprops.ap == true or formprops.pp == true then
formprops.need_autogen = true
end
table.insert(formpropslist, formprops)
index = index + 1
else
break
end
end
table.insert(verb_properties, {form, formpropslist})
end
-- Go through and create the verb form derivations as necessary, when they haven't been explicitly given.
for _, vplist in ipairs(verb_properties) do
local vform = vplist[1]
for _, props in ipairs(vplist[2]) do
if props.need_autogen then
local form_with_vowels
if vform == "I" then
local pv = props.pv
if not pv then
-- Make up likely past vowels based on weakness and actual radical.
if split_root[3] == W then -- final-weak
form_with_vowels = "I/a~u"
elseif split_root[3] == Y then
form_with_vowels = "I/a~i"
elseif split_root[2] == W then --hollow
form_with_vowels = "I/u~u"
elseif split_root[2] == Y then
form_with_vowels = "I/i~i"
else
-- most common; doesn't matter so much since we're not displaying the non-past
form_with_vowels = "I/a~u"
end
else
local pvs = rsplit(pv, ",")
local vowel_sufs = {}
for _, pv in ipairs(pvs) do
local vowel_spec
if pv == "a" then
-- Make up likely past vowels based on weakness and actual radical.
if split_root[3] == W then -- final-weak
vowel_spec = "a~u"
elseif split_root[3] == Y then
vowel_spec = "a~i"
elseif split_root[2] == W then --hollow
vowel_spec = "a~u"
elseif split_root[2] == Y then
vowel_spec = "a~i"
else
-- most common; doesn't matter so much since we're not displaying the non-past
vowel_spec = "a~u"
end
elseif pv == "i" then
-- most common; doesn't matter so much since we're not displaying the non-past
vowel_spec = "i~a"
elseif pv == "u" then
-- most common; doesn't matter so much since we're not displaying the non-past
vowel_spec = "u~u"
else
error(("Internal error: Bad past vowel '%s' in {{ar-verb forms}}"):format(pv))
end
table.insert(vowel_sufs, vowel_spec)
end
form_with_vowels = "I/" .. table.concat(vowel_sufs, "/")
end
else
form_with_vowels = vform
end
local angle_bracket_spec = ("%s<%s.pass>"):format(combined_root, form_with_vowels)
local alternant_multiword_spec = export.do_generate_forms({angle_bracket_spec}, "ar-verb forms")
local function format_forms(forms)
if not forms then
return "-" -- FIXME: Throw an error?
end
local formatted = {}
for _, form in ipairs(forms) do
if form.translit then
table.insert(formatted, ("%s//%s"):format(form.form, form.translit))
else
table.insert(formatted, form.form)
end
end
return table.concat(formatted, ",")
end
if props.verb == true then
props.verb = format_forms(alternant_multiword_spec.forms.past_3ms)
end
for _, deriv in ipairs({"vn", "ap", "pp"}) do
if props[deriv] == true then
props[deriv] = format_forms(alternant_multiword_spec.forms[deriv])
end
end
end
end
end
-- Go through and output the result
local formtextarr = {}
for _, vplist in ipairs(verb_properties) do
local form = vplist[1]
for _, props in ipairs(vplist[2]) do
local textarr = {}
if props.verb then
local text = "* '''[[Appendix:Arabic verbs#Form " .. form .. "|Form " .. form .. "]]''': "
local linktext = {}
local splitheads = rsplit(props.verb, "[,،]")
for _, head in ipairs(splitheads) do
table.insert(linktext, m_links.full_link({lang = lang, term = head, gloss = props["verb-gloss"]}))
end
text = text .. table.concat(linktext, ", ")
table.insert(textarr, text)
for _, derivengl in ipairs({{"vn", "Verbal noun"}, {"ap", "Active participle"}, {"pp", "Passive participle"}}) do
local deriv = derivengl[1]
local engl = derivengl[2]
if props[deriv] then
local text = "** " .. engl .. ": "
local linktext = {}
local splitheads = rsplit(props[deriv], "[,،]")
for _, head in ipairs(splitheads) do
local ar, translit = head:match("^(.*)//(.-)$")
if not ar then
ar = head
end
table.insert(linktext, m_links.full_link {lang = lang, term = ar, tr = translit,
gloss = props[deriv .. "-gloss"]} )
end
text = text .. table.concat(linktext, ", ")
table.insert(textarr, text)
end
end
table.insert(formtextarr, table.concat(textarr, "\n"))
end
end
end
return table.concat(formtextarr, "\n")
end
-- Infer radicals from lemma headword (i.e. 3rd masculine singular past) and verb form (I, II, etc.). Throw an error if
-- headword is malformed. A given returned radical may be actually be a list of possible radicals, where the first one
-- should be used if the user didn't explicitly give the radical. If the list contains a field `ambig = true`, the
-- radical is considered ambiguous and should not be categorized. `is_reduced` indicates that the user specified
-- `.reduced` to indicate that the verb form is reduced by assimilation and/or haplology (typically archaic Koranic
-- forms such as اِدَّارَأَ instead of تَدَارَأَ; or اِسْطَاعَ instead of اِسْتِطَاعَ; etc.
function export.infer_radicals(data)
local headword, vform, passive, past_vowel, nonpast_vowel, is_reduced =
data.headword, data.vform, data.passive, data.past_vowel, data.nonpast_vowel, data.is_reduced
past_vowel = past_vowel or "-"
nonpast_vowel = nonpast_vowel or "-"
local function verify_vowel(vowel, param)
if vowel ~= A and vowel ~= I and vowel ~= U and vowel ~= "-" then
error(("Internal error: Bad value for %s: %s (should be Arabic diacritic vowel or '-')"):format(
param, vowel))
end
end
verify_vowel(past_vowel, "past_vowel")
verify_vowel(nonpast_vowel, "nonpast_vowel")
local ch = {}
local form_viii_assim, variant
-- sub out alif-madda for easier processing
headword = rsub(headword, AMAD, HAMZA .. ALIF)
local function infer_err(msg, noann)
local anns = {}
local nohead, novform
if noann == "nohead" then
nohead = true
elseif noann == "novform" then
novform = true
elseif noann == "nohead-vform" then
nohead = true
novform = true
elseif noann then
error(("Internal error: Unrecognized value for 'noann': %s"):format(dump(noann)))
end
if not nohead then
table.insert(anns, ("headword=%s"):format(data.headword))
end
if not novform then
table.insert(anns, ("verb form=%s"):format(data.vform))
end
anns = table.concat(anns, ", ")
if anns ~= "" then
anns = ": " .. anns
end
error(msg .. anns)
end
local len = ulen(headword)
local expected_length
-- extract the headword letters into an array
for i = 1, len do
table.insert(ch, usub(headword, i, i))
end
-- check that the letter at the given index is the given string, or
-- is one of the members of the given array
local function check(index, must)
local letter = ch[index]
if type(must) == "string" then
if not letter then
infer_err("Letter " .. index .. " is nil")
end
if letter ~= must then
infer_err(("For verb form %s, letter %s must be %s, not %s"):format(vform, index, must, letter),
"novform")
end
elseif not m_table.contains(must, letter) then
infer_err("For verb form " .. vform .. ", radical " .. index ..
" must be one of " .. table.concat(must, " ") .. ", not " .. letter, "novform")
end
end
-- Check that length of headword is within [min, max]
local function check_len(min, max)
if min and len < min then
infer_err(("Not enough letters for verb form %s, expected at least %s"):format(vform, min), "novform")
end
if max and len > max then
infer_err(("Too many letters for verb form %s, expected at most %s"):format(vform, max), "novform")
end
end
-- If the vowels are i~a or u~u, a form I verb beginning with w- normally keeps the w in the non-past. Otherwise it
-- loses it (i.e. it is "assimilated").
local function form_I_w_non_assimilated()
return req(past_vowel, I) and req(nonpast_vowel, A) or req(past_vowel, U) and req(nonpast_vowel, U)
end
-- Convert radicals to canonical form (handle various hamza varieties and check for misplaced alif or alif maqṣūra;
-- legitimate cases of these letters are handled above).
local function convert(rad, index)
if type(rad) == "table" then
for i, r in ipairs(rad) do
rad[i] = convert(r, index)
end
return rad
elseif rad == HAMZA_ON_ALIF or rad == HAMZA_UNDER_ALIF or
rad == HAMZA_ON_W or rad == HAMZA_ON_Y then
return HAMZA
elseif rad == AMAQ then
infer_err("Radical " .. index .. " must not be alif maqṣūra")
elseif rad == ALIF then
infer_err("Radical " .. index .. " must not be alif")
else
return rad
end
end
local quadlit = vform:find("q$")
-- find first radical, start of second/third radicals, check for
-- required letters
local radstart, rad1, rad2, rad3, rad4
local weakness
if vform == "I" or vform == "II" then
rad1 = ch[1]
radstart = 2
elseif vform == "III" then
rad1 = ch[1]
check(2, {ALIF, W}) -- W occurs in passive-only verbs
radstart = 3
elseif vform == "IV" then
-- this would be alif-madda but we replaced it with hamza-alif above.
if ch[1] == HAMZA and ch[2] == ALIF then
rad1 = HAMZA
else
check(1, HAMZA_ON_ALIF)
rad1 = ch[2]
end
radstart = 3
elseif vform == "V" then
check(1, is_reduced and ALIF or T)
rad1 = ch[2]
radstart = 3
elseif vform == "VI" then
check(1, is_reduced and ALIF or T)
if ch[2] == AMAD then
rad1 = HAMZA
radstart = 3
else
rad1 = ch[2]
check(3, {ALIF, W}) -- W occurs in passive-only verbs
radstart = 4
end
elseif vform == "VII" then
check(1, ALIF)
if is_reduced then
check(2, M)
rad1 = M
radstart = 3
else
check(2, N)
rad1 = ch[3]
radstart = 4
end
elseif vform == "VIII" then
check(1, ALIF)
rad1 = ch[2]
if rad1 == "د" then
rad1 = {"د", "ذ"} -- not considered ambiguous since it's usually د
radstart = 3
form_viii_assim = "دّ"
elseif rad1 == "ظ" and ch[3] == "ط" and len >= 5 then
-- [[اظطلم]], variant of [[اظلم]]
radstart = 4
form_viii_assim = "ظْط"
elseif rad1 == "ذ" and ch[3] == "د" and len >= 5 then
-- [[اذدكر]], variant of [[اذكر]]
radstart = 4
form_viii_assim = "ذْد"
elseif rad1 == T or rad1 == "ث" or rad1 == "ذ" or rad1 == "ط" or rad1 == "ظ" then
radstart = 3
form_viii_assim = rad1 .. SH
elseif rad1 == "ز" then
check(3, "د")
radstart = 4
form_viii_assim = "زْد"
elseif rad1 == "ص" or rad1 == "ض" then
check(3, "ط")
radstart = 4
form_viii_assim = rad1 .. SK .. "ط"
else
check(3, T)
radstart = 4
rad1 = convert(rad1, 1)
form_viii_assim = rad1 .. SK .. "ت"
end
if rad1 == T then
-- Radical is ambiguous, might be ت or و or ي but doesn't affect conjugation. Note that there are no
-- form-VIII verbs with initial radical ي given in Hans Wehr but Lane mentions at least:
-- - (page 2973) اِتَّأَسَ, with assimilation of the ي to ت, from root ي ء س;
-- - (page 2975) اِتَّبَسَ non-past يَتَّبِسُ and alternative اِيتَبَسَ non-past يَاتَبِسُ from the root ي ب س;
-- - (page 2976) اِتَّسَرَ non-past يَتَّسِرُ or alternatively يَأْتَسِرُ with hamza preserved from the root ي س ر.
-- These alternative forms seem very rare and probably not worth worrying about, but if we want to handle
-- them, we can do it when the time comes.
rad1 = {T, W, Y, ambig = true}
-- اِتَّخَذَ irregularly has hamza as the radical but assimilates like و
if ch[3] == "خ" and ch[4] == "ذ" then
rad1[4] = HAMZA
end
end
elseif vform == "IX" then
check(1, ALIF)
rad1 = ch[2]
radstart = 3
elseif vform == "X" then
check(1, ALIF)
check(2, S)
if is_reduced then
rad1 = ch[3]
radstart = 4
else
check(3, T)
rad1 = ch[4]
radstart = 5
end
elseif vform == "Iq" then
rad1 = ch[1]
rad2 = ch[2]
radstart = 3
elseif vform == "IIq" then
check(1, T)
rad1 = ch[2]
rad2 = ch[3]
radstart = 4
elseif vform == "IIIq" then
check(1, ALIF)
rad1 = ch[2]
rad2 = ch[3]
check(4, N)
radstart = 5
elseif vform == "IVq" then
check(1, ALIF)
rad1 = ch[2]
rad2 = ch[3]
radstart = 4
elseif vform == "XI" then
check_len(5, 5)
check(1, ALIF)
rad1 = ch[2]
rad2 = ch[3]
check(4, ALIF)
rad3 = ch[5]
weakness = "sound"
elseif vform == "XII" then
check(1, ALIF)
rad1 = ch[2]
if ch[3] ~= ch[5] then
infer_err("For verb form XII, letters 3 and 5 should be the same", "novform")
end
check(4, W)
radstart = 5
elseif vform == "XIII" then
check_len(5, 5)
check(1, ALIF)
rad1 = ch[2]
rad2 = ch[3]
check(4, W)
rad3 = ch[5]
if rad3 == AMAQ then
weakness = "final-weak"
else
weakness = "sound"
end
elseif vform == "XIV" then
check_len(6, 6)
check(1, ALIF)
rad1 = ch[2]
rad2 = ch[3]
check(4, N)
rad3 = ch[5]
if ch[6] == AMAQ then
check_waw_ya(rad3)
weakness = "final-weak"
else
if ch[5] ~= ch[6] then
infer_err("For verb form XIV, letters 5 and 6 should be the same", "novform")
end
weakness = "sound"
end
elseif vform == "XV" then
check_len(6, 6)
check(1, ALIF)
rad1 = ch[2]
rad2 = ch[3]
check(4, N)
rad3 = ch[5]
if rad3 == Y then
check(6, ALIF)
else
check(6, AMAQ)
end
weakness = "sound"
else
error("Internal error: Unrecognized verb form " .. vform)
end
-- Process the last two radicals. RADSTART is the index of the first of the two. If it's nil then all radicals have
-- already been processed above, and we don't do anything.
if radstart then
-- There must (normally) be one or two letters left.
if len == radstart then
if vform == "I" and ch[len] == Y then
-- short form حَيَّ
weakness = "final-weak"
rad2 = Y
rad3 = Y
variant = "short"
elseif vform == "IV" and rad1 == "ر" and ch[len] == AMAQ then
-- irregular verb أَرَى
weakness = "final-weak"
rad2 = HAMZA
rad3 = Y
elseif vform == "X" and rad1 == "ح" and ch[len] == AMAQ then
-- irregular verb اِسْتَحَى
weakness = "final-weak"
rad2 = Y
rad3 = Y
variant = "short"
else
-- If one letter left, then it's a geminate verb. If the letter is alif or alif maqṣūra, it will trigger
-- an error down the line.
if vform_supports_geminate(vform) then
weakness = "geminate"
rad2 = ch[len]
rad3 = ch[len]
if vform == "III" or vform == "VI" then
variant = "short"
end
else
infer_err("Apparent geminate verb, but geminate verbs not allowed for this verb form")
end
end
elseif quadlit then
-- Process last two radicals of a quadriliteral verb form.
rad3 = ch[radstart]
rad4 = ch[radstart + 1]
expected_length = radstart + 1
check_len(expected_length)
if rad4 == AMAQ or rad4 == ALIF and rad3 == Y or rad4 == Y then
-- rad4 can be Y in passive-only verbs.
if vform_supports_final_weak(vform) then
weakness = "final-weak"
-- Ambiguous radical; randomly pick wāw as radical (but avoid two wāws in a row); it could be wāw or
-- yāʾ, but doesn't affect the conjugation.
rad4 = rad3 == W and {Y, W, ambig = true} or {W, Y, ambig = true}
else
infer_err("Last radical is " .. rad4 .. " but verb form " .. vform ..
" doesn't support final-weak verbs", "novform")
end
else
weakness = "sound"
end
else
-- Process last two radicals of a triliteral verb form.
rad2 = ch[radstart]
rad3 = ch[radstart + 1]
expected_length = radstart + 1
check_len(expected_length)
if vform == "I" and (is_waw_ya(rad3) or rad3 == ALIF or rad3 == AMAQ) then
local inferred_past_vowel, inferred_nonpast_vowel
-- Check for final-weak form I verb. It can end in tall alif (rad3 = wāw) or alif maqṣūra (rad3 = yāʾ)
-- or a wāw or yāʾ (with a past vowel of i or u, e.g. nasiya/yansā "forget" or with a passive-only
-- verb).
if rad1 == W and not form_I_w_non_assimilated() then
weakness = "assimilated+final-weak"
else
weakness = "final-weak"
end
if rad3 == ALIF then
rad3 = W
inferred_past_vowel = A
inferred_nonpast_vowel = U
if is_passive_only(passive) then
infer_err("Final-weak form-I passive verbs should end in yāʔ (ي), not tall alif (ا)", "novform")
end
elseif rad3 == AMAQ then
rad3 = Y
inferred_past_vowel = A
inferred_nonpast_vowel = I
if is_passive_only(passive) then
infer_err("Final-weak form-I passive verbs should end in yāʔ (ي), not alif maqṣūra (ى)",
"novform")
end
elseif rad1 == "ح" and rad2 == Y and rad3 == Y then
-- Long variant حَيِيَ.
inferred_past_vowel = I
inferred_nonpast_vowel = A
variant = "long"
else
if not is_passive_only(passive) then
-- does a non-passive final-weak verb in -uwa ever happen? (YES: e.g. [[رجو]] "to be slack")
inferred_past_vowel = rad3 == Y and I or U
inferred_nonpast_vowel = A
end
-- Ambiguous radical; randomly pick wāw as radical (but avoid two wāws); it could be wāw or yāʾ, but
-- doesn't affect the conjugation.
rad3 = (rad1 == W or rad2 == W) and {Y, W, ambig = true} or {W, Y, ambig = true} -- ambiguous
end
if inferred_past_vowel then
local raw_past_vowel = rget(past_vowel)
local raw_nonpast_vowel = rget(nonpast_vowel)
if raw_past_vowel ~= "-" then
if raw_past_vowel ~= inferred_past_vowel then
infer_err(("Final-weak form-I verb inferred past vowel %s, which disagrees with " ..
"explicitly specified %s"):format(undia[inferred_past_vowel], undia[raw_past_vowel]), "novform")
else
-- in case of footnote in past_vowel
inferred_past_vowel = past_vowel
end
end
if raw_nonpast_vowel ~= "-" and raw_nonpast_vowel ~= A and inferred_nonpast_vowel == U then
-- if inferred as I or A, the reality can be the reverse; form-I final-weak verbs with a~a and
-- i~i exist, e.g. سَعَى/يَسْعَى, وَلِيَ/يَلِي. Weird verb [[صها]] (also written [[صهى]]) has non-past
-- يصهى so we can't throw an error in this situation.
if raw_nonpast_vowel ~= inferred_nonpast_vowel then
infer_err(("Final-weak form-I verb inferred non-past vowel %s, which disagrees with " ..
"explicitly specified %s"):format(undia[inferred_nonpast_vowel], undia[raw_nonpast_vowel]), "novform")
else
-- in case of footnote in nonpast_vowel
inferred_nonpast_vowel = nonpast_vowel
end
end
end
if not is_passive_only(passive) then
if rget(past_vowel) == "-" then
past_vowel = inferred_past_vowel
end
if rget(nonpast_vowel) == "-" then
nonpast_vowel = inferred_nonpast_vowel
end
end
elseif vform == "IX" and is_waw_ya(rad3) and len == radstart + 2 and ch[len] == AMAQ then
-- Final-weak form IX verbs like اِرْعَوَى "to desist, to repent, to see the light".
weakness = "final-weak"
expected_length = radstart + 2
elseif vform == "X" and rad1 == "ح" and rad2 == Y and rad3 == ALIF then
-- Long variant اِسْتَحْيَا.
weakness = "final-weak"
rad3 = Y
variant = "long"
elseif rad3 == AMAQ or rad2 == Y and rad3 == ALIF or rad3 == Y then
-- rad3 == Y happens in passive-only verbs.
if vform_supports_final_weak(vform) then
weakness = "final-weak"
else
infer_err("Last radical is " .. rad3 .. " but verb form doesn't support final-weak verbs")
end
-- Ambiguous radical; randomly pick wāw as radical (but avoid two wāws); it could be wāw or yāʾ, but
-- doesn't affect the conjugation.
rad3 = (rad1 == W or rad2 == W) and {Y, W, ambig = true} or {W, Y, ambig = true}
elseif rad2 == ALIF then
if vform_supports_hollow(vform) then
weakness = "hollow"
local function set_past_to_a()
if req(past_vowel, A) then
-- already set
elseif req(past_vowel, "-") or req(past_vowel, rget(nonpast_vowel)) then
past_vowel = A
else
infer_err(("Form I hollow verb with nonpast vowel set to '%s' must have past vowel set to 'a' or the same value, not %s"):
format(undia[rget(nonpast_vowel)], undia[rget(past_vowel)]), "novform")
end
end
if vform == "I" and req(nonpast_vowel, U) then
rad2 = W
set_past_to_a()
elseif vform == "I" and req(nonpast_vowel, I) then
rad2 = Y
set_past_to_a()
else
if req(nonpast_vowel, A) and not req(past_vowel, I) then
infer_err(("Form I hollow verb with nonpast vowel set to 'a' must have past vowel set to 'i', not %s"):
format(undia[rget(past_vowel)]), "novform")
end
-- Ambiguous radical; could be wāw or yāʾ; if verb form I, it's critical to get this right, and
-- the caller checks for this situation and throws an error if non-past vowel is "a" and second
-- radical isn't explicitly given.
rad2 = {W, Y, ambig = true, need_radical = true}
end
else
infer_err("Second radical is alif but verb form doesn't support hollow verbs")
end
elseif vform == "I" and rad1 == W and not form_I_w_non_assimilated() then
weakness = "assimilated"
elseif rad2 == rad3 and (vform == "III" or vform == "VI") then
weakness = "geminate"
variant = "long"
else
weakness = "sound"
end
end
if expected_length then
check_len(expected_length, expected_length)
end
end
rad1 = convert(rad1, 1)
rad2 = convert(rad2, 2)
rad3 = convert(rad3, 3)
rad4 = convert(rad4, 4)
if not weakness then
error("Internal error: Returned weakness from infer_radicals() is nil")
end
return {
weakness = weakness,
rad1 = rad1,
rad2 = rad2,
rad3 = rad3,
rad4 = rad4,
past_vowel = past_vowel,
nonpast_vowel = nonpast_vowel,
form_viii_assim = form_viii_assim,
variant = variant,
}
end
-- bot interface to infer_radicals()
function export.infer_radicals_json(frame)
local iparams = {
headword = {},
vform = {},
passive = {},
past_vowel = {},
nonpast_vowel = {},
is_reduced = {type = "boolean"},
}
local iargs = require("Module:parameters").process(frame.args, iparams)
return require("Module:JSON").toJSON(export.infer_radicals(iargs))
end
-- Infer vocalization from participle headword (active or passive), verb form (I, II, etc.) and whether the headword is
-- active or passive. Throw an error if headword is malformed. Returned radicals may contain Latin letters "t", "w" or "y"
-- indicating ambiguous radicals guessed to be tāʾ, wāw or yāʾ respectively.
function export.infer_participle_vocalization(headword, vform, weakness, is_active)
local chars = {}
local orig_headword = headword
-- Sub out alif-madda for easier processing.
headword = rsub(headword, AMAD, HAMZA .. ALIF)
local len = ulen(headword)
-- Extract the headword letters into an array.
for i = 1, len do
table.insert(chars, usub(headword, i, i))
end
local function form_intro_error_msg()
return ("For verb form %s %s%s participle %s, "):format(vform, orig_headword ~= headword and "normalized " or
"", is_active and "active" or "passive", headword)
end
local function err(msg)
error(form_intro_error_msg() .. msg, 1)
end
-- Check that length of headword is within [min, max].
local function check_len(min, max)
if min and len < min then
err(("expected at least %s letters but saw %s"):format(min, len))
elseif max and len > max then
err(("expected at most %s letters but saw %s"):format(max, len))
end
end
-- Get the character at `ind`, making sure it exists.
local function c(ind)
check_len(ind)
return chars[ind]
end
-- Check that the letter at the given index is the given string, or is one of the members of the given array
local function check(index, must)
local letter = chars[index]
local function make_possible_values()
if type(must) == "string" then
return must
else
return list_to_text(must, nil, " or ")
end
end
if not letter then
err(("expected a letter (specifically %s) at position %s, but participle is too short"):format(
make_possible_values(), index))
end
local matches
if type(must) == "string" then
matches = letter == must
else
matches = m_table.contains(must, letter)
end
if not matches then
err(("letter %s at index %s must be %s"):format(letter, index, make_possible_values()))
end
end
local function check_weakness(values, allow_missing, invert_condition)
local function make_possible_weaknesses()
for i, val in ipairs(values) do
values[i] = "'" .. val .. "'"
end
return list_to_text(values, nil, " or ")
end
if allow_missing and invert_condition then
error("Internal error: Can't specify both allow_missing and invert_condition")
end
if not weakness then
if allow_missing or invert_condition then
return
else
err(("weakness is unspecified but must be %s"):format(make_possible_weaknesses()))
end
else
local matches = m_table.contains(values, weakness)
if invert_condition and matches then
err(("weakness '%s' must not be %s"):format(weakness, make_possible_weaknesses()))
elseif not invert_condition and not matches then
err(("weakness '%s' must be %s"):format(weakness, make_possible_weaknesses()))
end
end
end
local vocalized
local function handle_possibly_final_weak(sound_prefix, expected_length)
check_len(expected_length, expected_length)
if c(expected_length) == AMAQ then
-- passive final-weak
if is_active then
err("participle in -ِى only allowed for passive participles")
end
check_weakness({"final-weak", "assimilated+final-weak"}, "allow missing")
vocalized = sound_prefix .. AN .. AMAQ
else
-- all others behave as if sound
check_weakness({"final-weak", "assimilated+final-weak"}, nil, "invert condition")
vocalized = sound_prefix .. (is_active and I or A) .. c(expected_length)
end
end
if not (vform == "I" and is_active) then
-- all participles except verb form I active begin in م-.
check(1, M)
end
if vform == "I" then
if is_active then
check(2, ALIF)
local sound_prefix = c(1) .. AA .. c(3)
if len == 3 then
if c(3) == HAMZA then
-- Either hollow with hamzated third radical, e.g. [[شاء]] active participle 'شَاءٍ', or final-weak
-- with hamzated second radical, e.g. [[رأى]] active participle 'رَاءٍ'. Theoretically (?), also
-- geminate with hamzated second/third radical, but I don't know if any such verbs exist.
if weakness == "geminate" then
vocalized = sound_prefix .. SH
else
check_weakness({"hollow", "final-weak"}, "allow missing")
vocalized = sound_prefix .. IN
end
else
check_weakness({"final-weak", "geminate"})
if weakness == "geminate" then
vocalized = sound_prefix .. SH
else
vocalized = sound_prefix .. IN
end
end
else
check_len(4, 4)
-- we will convert back to alif maqṣūra below as needed
vocalized = sound_prefix .. I .. c(4)
end
else
-- assimilated verbs: regular, e.g. مَوْزُون "weighed"
-- geminate verbs: regular, e.g. مَبْلُول "moistened"
-- third-hamzated verbs: مَبْرُوء
-- hollow verbs: مَقُود "led, driven"; مَزِيد "added, increased"
-- hollow first-hamzated verbs: مَئِيض "returned, reverted"; مَأْيُوس "despaired" (NOTE: formation is sound);
-- مَأُود or مَؤُود "bent; depleted"
-- hollow third-hamzated verbs: مَشِيء "willed, intended", مَضُوء "glittered?"
-- final-weak: مَلْقِيّ "found, encountered"; مَصْغُوّ "inclined"
-- hollow + final-weak: مَشْوِيّ "fried, grilled", مَهْوِيّ "loved"
-- first-hamzated + hollow + final-weak: مَأْوِيّ "received hospitably"
local sound_prefix = MA .. c(2) .. SK .. c(3)
if len == 5 then
-- sound, assimilated or geminate
check(4, W)
vocalized = sound_prefix .. UU .. c(5)
else
check_len(4, 4)
if c(4) == W then
-- final-weak third-wāw
vocalized = sound_prefix .. U .. W .. SH
elseif c(4) == Y then
-- final-weak third-yāʾ
vocalized = sound_prefix .. I .. Y .. SH
else
-- hollow
check(3, {W, Y})
if c(3) == W then
vocalized = MA .. c(2) .. UU .. c(4)
else
vocalized = MA .. c(2) .. II .. c(4)
end
end
end
end
elseif vform == "II" or vform == "V" or vform == "XII" or vform == "XIII" or vform == "Iq" or vform == "IIq" or
vform == "IIIq" then
local sound_prefix, expected_length
if vform == "II" then
sound_prefix = MU .. c(2) .. A .. c(3) .. SH
expected_length = 4
elseif vform == "V" then
check(2, T)
sound_prefix = MU .. T .. A .. c(3) .. A .. c(4) .. SH
expected_length = 5
elseif vform == "XII" then
-- e.g. [[احدودب]] "to be or become convex or humpbacked", مُحْدَوْدِب (active);
-- [[اثنونى]] "to be bent; to be doubled up", مُثْنَوْنٍ (active)
check(4, W)
if c(3) ~= c(5) then
err(("third letter %s should be the same as the fifth letter %s"):format(c(3), c(5)))
end
sound_prefix = MU .. c(2) .. SK .. c(3) .. A .. W .. SK .. c(5)
expected_length = 6
elseif vform == "XIII" then
-- e.g. [[اخروط]] "to get entangled; to extend", مُخْرَوِّط (active), مُخْرَوَّط (passive)
check(4, W)
sound_prefix = MU .. c(2) .. SK .. c(3) .. A .. W .. SH
expected_length = 5
elseif vform == "Iq" then
sound_prefix = MU .. c(2) .. A .. c(3) .. SK .. c(4)
expected_length = 5
elseif vform == "IIq" then
check(2, T)
sound_prefix = MU .. T .. A .. c(3) .. A .. c(4) .. SK .. c(5)
expected_length = 6
elseif vform == "IIIq" then
-- e.g. [[اخرنطم]] "to be proud and angry"
check(4, T)
sound_prefix = MU .. c(2) .. SK .. c(3) .. A .. N .. SK .. c(5)
expected_length = 6
else
error("Internal error: Unhandled verb form " .. vform)
end
if len == expected_length - 1 then
-- active final-weak
if not is_active then
err(("length-%s participle only allowed for active participles"):format(len))
end
check_weakness({"final-weak", "assimilated+final-weak"}, "allow missing")
vocalized = sound_prefix .. IN
else
handle_possibly_final_weak(sound_prefix, expected_length)
end
elseif vform == "III" or vform == "VI" then
local sound_prefix, expected_length
if vform == "VI" then
check(2, T)
check(4, ALIF)
sound_prefix = MU .. T .. A .. c(3) .. AA .. c(5)
expected_length = 6
else
sound_prefix = MU .. c(2) .. AA .. c(4)
expected_length = 5
end
if len == expected_length - 1 then
-- active final-weak or active or passive geminate
if is_active then
check_weakness({"geminate", "final-weak", "assimilated+final-weak"})
if weakness == "geminate" then
vocalized = sound_prefix .. SH
else
vocalized = sound_prefix .. IN
end
else
check_weakness({"geminate"}, "allow missing")
vocalized = sound_prefix .. SH
end
else
handle_possibly_final_weak(sound_prefix, expected_length)
end
elseif vform == "IV" or vform == "X" then
-- form IV:
-- sound: مُرْسِخ (active, "entrenching"), مُرْسَخ (passive, "entrenched")
-- first-hamzated (like sound): مُؤْيِس (active, "causing to despair"), مُؤْيَس (passive, "caused to despair")
-- final-weak: مُكْرٍ (active, "renting out"), مُكْرًى (passive, "rented out")
-- assimilated: مُورِد (active, "transferring"), مُورَد (passive, "transferred"); same when first-Y, e.g.
-- أَيْقَنَ "to be certain of": مُوقِن (active), مُوقَن (passive)
-- assimilated + final-weak: مُورٍ (active, "setting fire, kindling"), مُورًى (passive, "set fire, kindled")
-- geminate: مُمِدّ (active, "granting, helping"), مُمَدّ (passive, "granted, helped")
-- hollow: مُزِيل (active, "eliminating"), مُزَال (passive, "eliminated")
-- hollow + final-weak: مُعْيٍ (active, "tiring"), مُعْيًى (passive, "tired")
local sound_prefix, expected_length
if vform == "X" then
check(2, S)
check(3, T)
sound_prefix = MU .. S .. SK .. T .. A .. c(4)
expected_length = 6
else
sound_prefix = MU .. c(2)
expected_length = 4
end
if len == expected_length and c(len - 1) == Y and c(len) ~= AMAQ then
-- active hollow
if not is_active then
err("this shape only allowed for active participles")
end
check_weakness({"hollow"}, "allow missing")
vocalized = sound_prefix .. II .. c(len)
elseif len == expected_length and c(len - 1) == ALIF then
-- passive hollow
if is_active then
err("this shape only allowed for passive participles")
end
check_weakness({"hollow"}, "allow missing")
vocalized = sound_prefix .. AA .. c(len)
elseif len == expected_length - 1 then
-- active final-weak or active or passive geminate
if is_active then
check_weakness({"geminate", "final-weak", "assimilated+final-weak"})
if weakness == "geminate" then
vocalized = sound_prefix .. I .. c(len) .. SH
elseif vform == "IV" and c(2) == W then
-- assimilated final-weak
vocalized = sound_prefix .. c(len) .. IN
else
vocalized = sound_prefix .. SK .. c(len) .. IN
end
else
check_weakness({"geminate"}, "allow missing")
vocalized = sound_prefix .. A .. c(len) .. SH
end
else
if vform == "IV" and c(2) == W then
-- assimilated, possibly final-weak
sound_prefix = sound_prefix .. c(expected_length - 1)
else
sound_prefix = sound_prefix .. SK .. c(expected_length - 1)
end
handle_possibly_final_weak(sound_prefix, expected_length)
end
elseif vform == "VII" or vform == "VIII" then
-- form VII (passive participles are fairly rare but do exist):
-- sound: مُنْكَتِب (active "subscribing"), مُنْكَتَب (passive "subscribed")
-- geminate: مُنْضَمّ (both active "joining, containing" and passive "joined, contained")
-- final-weak: مُنْطَلٍ (active "fooling (someone)"), مُنْطَلًى (passive "fooled")
-- final-weak with medial wāw: مُنْطَوٍ (active "involving"), مُنْطَوًى (passive "involved")
-- hollow: مُنْقَاد (both active "complying with" and passive "complied with")
--
-- for form VIII, the same variants exist but things are complicated by assimilations involving the template T.
-- sound third-hamzated no assimilation: مُبْتَدِئ (active "beginning"), مُبْتَدَأ (passive "begun")
-- geminate no assimilation: مُبْتَزّ (both active "robbing" and passive "robbed")
-- final-weak no assimilation: مُبْتَنٍ (active "building"), مُبْتَنًى (passive "built")
-- final-weak with medial wāw no assimilation: مُحْتَوٍ (active "containing"), مُحْتَوًى (passive "contained")
-- hollow no assimilation: مُخْتَار (both active "choosing" and passive "chosen")
--
-- sound with total assimilation: مُتَّبِع (active "following"), مُتَّبَع (passive "followed")
-- sound with total assimilation, assimilating wāw: مُتَّعِد (active "threatening"), مُتَّعَد (passive "threatened")
-- sound with total assimilation, irregularly assimilating hamza: مُتَّخِذ (active "taking"), مُتَّخَذ (passive "taken")
-- sound with total assimilation (to ḏāl, producing dāl): مُدَّخِر (active "reserving"), مُدَّخَر (passive "reserved")
-- sound with total assimilation (to ḏāl): مُذَّكِر (active "remembering"), مُذَّكَر (passive "remembered")
-- sound with total assimilation (to ṭāʔ): مُطَّرِح (active "discarding"), مُطَّرَح (passive "discarded")
-- sound with total assimilation (to ẓāʔ): مُظَّلِم (active "tolerating"), مُظَّلَم (passive "tolerated")
-- final-weak with total assimilation, assimilating wāw: مُتَّقٍ (active "guarding against"), مُتَّقًى (passive "guarded against")
-- final-weak with total assimilation (to ṯāʔ): مُثَّنٍ (active "undulating"), مُثَّنًى (passive "undulated")
-- final-weak with total assimilation (to dāl): مُدَّعٍ (active "claiming"), مُدَّعًى (passive "claimed")
-- sound with partial assimilation (to zayn): مُزْدَهِر (active "thriving"), مُزْدَهَر (passive "thrived")
-- sound with medial wāw with partial assimilation (to zayn): مُزْدَوِج (active "appearing twice")
-- sound with partial assimilation (to ṣād): مُصْطَبِح (active "illuminating"), مُصْطَبَح (passive, "illuminated")
-- sound with partial assimilation (to ḍād): مُضْطَرِب (active "to be disturbed"; no passive)
-- geminate with partial assimilation (to ṣād): مُصْطَبّ (both active "effusing" and passive "effused")
-- geminate with partial assimilation (to ḍād): مُضْطَرّ (both active "forcing" and passive "forced")
-- final-weak with partial assimilation (to ṣād): مُصْطَلٍ (active "warming"), مُصْطَلًى (passive "warmed")
-- hollow with partial assimilation (to zayn): مُزْدَاد (both active "increasing" and passive "increased")
-- hollow with partial assimilation (to ṣad): مُصْطَاد (both active "hunting" and passive "hunted")
local sound_prefix, sufind
if vform == "VII" then
check(2, N)
sound_prefix = MU .. N .. SK .. c(3)
sufind = 4
else
local c2 = c(2)
if c2 == T or c2 == "د" or c2 == "ث" or c2 == "ذ" or c2 == "ط" or c2 == "ظ" then
-- full assimilation
sound_prefix = MU .. c2 .. SH
sufind = 3
else
-- partial or no assimilation
if c2 == "ز" then
check(3, "د")
elseif c2 == "ص" or c2 == "ض" then
check(3, "ط")
else
check(3, T)
end
sound_prefix = MU .. c2 .. SK .. c(3)
sufind = 4
end
end
if c(sufind) == ALIF then
-- hollow, active or passive
check_len(sufind + 1, sufind + 1)
check_weakness({"hollow"}, "allow missing")
vocalized = sound_prefix .. AA .. c(sufind + 1)
elseif len == sufind then
-- active final-weak or active or passive geminate
if is_active then
check_weakness({"geminate", "final-weak", "assimilated+final-weak"})
if weakness == "geminate" then
vocalized = sound_prefix .. A .. c(len) .. SH
else
vocalized = sound_prefix .. A .. c(len) .. IN
end
else
check_weakness({"geminate"}, "allow missing")
vocalized = sound_prefix .. A .. c(len) .. SH
end
else
sound_prefix = sound_prefix .. A .. c(sufind)
handle_possibly_final_weak(sound_prefix, sufind + 1)
end
elseif vform == "IX" then
check_len(4, 4)
vocalized = MU .. c(2) .. SK .. c(3) .. A .. c(4) .. SH
elseif vform == "IVq" then
-- e.g. [[اذلعب]] "to scamper away", مُذْلَعِبّ (active), مُذْلَعَبّ (passive);
-- [[اطمأن]] "to remain quietly; to be certain", مُطْمَئِنّ (active), مُطْمَأَنّ (passive)
check_len(5, 5)
local sound_prefix = MU .. c(2) .. SK .. c(3) .. A .. c(4)
if is_active then
vocalized = sound_prefix .. I .. c(5) .. SH
else
vocalized = sound_prefix .. A .. c(5) .. SH
end
elseif vform == "XI" then
check_len(5, 5)
check(4, ALIF)
vocalized = MU .. c(2) .. SK .. c(3) .. AA .. c(5) .. SH
-- e.g. [[احمار]] "to turn red, to blush", مُحْمَارّ (active)
elseif vform == "XIV" or vform == "XV" then
-- FIXME: Implement. No examples in Wiktionary currently; need to look up in a grammar.
error("Support for verb form " .. vform .. " not implemented yet")
else
error("Don't recognize verb form " .. vform)
end
vocalized = rsub(vocalized, HAMZA .. AA, AMAD)
local reconstructed_headword = lang:stripDiacritics(vocalized)
if reconstructed_headword ~= orig_headword then
error(("Internal error: Vocalized participle %s doesn't match original participle %s"):format(
vocalized, orig_headword))
end
return vocalized
end
function export.infer_participle_vocalization_json(frame)
local iparams = {
[1] = {required = true},
[2] = {required = true},
["weakness"] = {},
["passive"] = {type = "boolean"}
}
local iargs = require("Module:parameters").process(frame.args, iparams)
return export.infer_participle_vocalization(iargs[1], iargs[2], iargs.weakness, not iargs.passive)
end
return export
gopkz51p5nvpnjqfkifs9ek7o5xi1ai
মডিউল:inflection utilities
828
52289
509637
323491
2026-06-03T12:50:13Z
Redmin
6857
[[en:Module:inflection utilities|ইংরেজি উইকিঅভিধান]] থেকে হালনাগাদ করলাম
509637
Scribunto
text/plain
local export = {}
local m_links = require("Module:links")
local m_str_utils = require("Module:string utilities")
local m_table = require("Module:table")
local put = require("Module:parse utilities")
local headword_data_module = "Module:headword/data"
local script_utilities_module = "Module:script utilities"
local table_tools_module = "Module:table tools"
local is_callable = require("Module:fun").is_callable
local split = m_str_utils.split
local rfind = mw.ustring.find
local rmatch = mw.ustring.match
local rsubn = mw.ustring.gsub
local ucfirst = m_str_utils.ucfirst
local unpack = unpack or table.unpack -- Lua 5.2 compatibility
local dump = mw.dumpObject
-- version of rsubn() that discards all but the first return value
local function rsub(term, foo, bar)
local retval = rsubn(term, foo, bar)
return retval
end
local function track(page)
require("Module:debug/track")("inflection utilities/" .. page)
return true
end
local footnote_abbrevs = {
["a"] = "archaic",
["c"] = "colloquial",
["d"] = "dialectal",
["fp"] = "folk-poetic",
["l"] = "literary",
["lc"] = "low colloquial",
["p"] = "poetic",
["pej"] = "pejorative",
["r"] = "rare",
}
--[==[ intro:
The following code is used in building up the inflection of terms in inflected languages, where a term can potentially
consist of several inflected words, each surrounded by fixed text, and a given slot (e.g. accusative singular) of a
given word can potentially consist of multiple possible inflected forms. In addition, each form may be associated with
a manual transliteration and/or a list of footnotes (or qualifiers, in the case of headword lines). The following
terminology is helpful to understand:
* A '''term''' is a word or multiword expression that can be inflected. A multiword term may in turn consist of several
single-word inflected terms with surrounding fixed text. A term belongs to a particular '''part of speech''' (e.g.
noun, verb, adjective, etc.).
* An '''inflection dimension''' is a particular dimension over which a term may be inflected, such as case, number,
gender, person, tense, mood, voice, aspect, etc.
* The '''lemma''' is the particular form of a term under which the term is entered into a dictionary. For example, for
verbs, it is most commonly the infinitive, but this differs for some languages: e.g. Latin, Greek and Bulgarian use
the first-person singular present indicative (active voice in the case of Latin and Greek); Sanskrit and Macedonian
use the third-person singular present indicative (active voice in the case of Sanskrit); Hebrew and Arabic use the
third-person singular masculine past (aka "perfect"); etc. For nouns, the lemma form is most commonly the nominative
singular, but e.g. for Old French it is the objective singular and for Sanskrit it is the root.
* A '''slot''' is a particular combination of inflection dimensions. An example might be "accusative plural" for a noun,
or "first-person singular present indicative" for a verb. Slots are named in a language-specific fashion. For
example, the slot "accusative plural" might have a name `accpl`, while "first-person singular present indicative"
might be variously named `pres1s`, `pres_ind_1_sg`, etc. Each slot is filled with zero or more '''forms'''.
* A '''form''' is a particular inflection of a slot for a particular term. Note that a given slot may (and often does)
have more than one associated form; these different forms are termed '''variants'''. An example is
{{m+|de|Bug||bow (of a ship)}}, which has two genitive singular forms ''Buges'' and ''Bugs''; two plural forms in all
cases, e.g. nominative plural ''Buge'' and ''Büge''; and two dative singular forms ''Bug'' and rare/archaic ''Buge''.
The form variants for a given slot are ordered, and generally should have the more common and/or preferred variants
first, along with rare, archaic or obsolete variants last (if they are included at all).
* Forms are described using '''form objects''', which are Lua objects taking the form
`{form="``form_value``", translit="``manual_translit``", footnotes={"``footnote``", "``footnote``", ...}}`.
(Additional '''metadata''' may be present in a form object, although the support for preserving such metadata when
transformations are applied to form objects isn't yet complete.) ``form_value`` is a '''form value''' specifying the
value of the form itself in the term's script. ``manual_translit`` specifies optional manual transliteration for the
form, in case (a) the form value is in a different script; and (b) either the form's automatic transliteration is
incorrect and needs to be overridden, or the language of the term has no automatic transliteration (e.g. in the case
of Persian and Hebrew). ``footnote`` is a footnote to be attached to the form in question, and should be e.g.
{"[archaic]"} or {"[only in the meaning 'to succeed (an officeholder)']"}, i.e. the string must be surrounded by
brackets and should begin with a lowercase letter and not end in a period/full stop. When such footnotes are converted
to actual footnotes in a table of inflected forms, the brackets will be removed, the first letter will be capitalized
and a period/full stop will be added to the end. (However, when such footnotes are used as qualifiers in headword
lines, only the brackets will be removed, with no capitalization or final period.) Note that only ``form_value`` is
mandatory.
* A list of zero or more form objects is termed a '''form object list''', or usually just a '''form list'''. Such lists
are ordered and go into form tables (see below).
* A '''form table''' is a Lua table (i.e. a dictionary) describing all the possible inflections of a given term. The
keys in such a table are slots (strings) and the values are form lists. '''NOTE:''' All inflection code assumes and
maintains the invariant that no two slots, and no two forms in a single slot, share the same form object (by
reference, i.e. the Lua object describing a form object should never be shared in two places). This allows for safely
side-effecting form objects in certain sorts of operations. This same invariant necessarily applies to the Lua list
objects containing the form objects, but does '''NOT''' apply to metadata inside of form objects. In particular, a
list of footnotes may well be shared among different form objects. This means it is '''NOT''' safe to side-effect
such lists, and in fact no code in this module that manipulates footnote lists will ever side-effect such lists; they
are treated as immutable.
* Some functions, to save memory, accept and work with abbreviated forms of form objects and/or form lists.
Specifically, an '''abbreviated form object''' is either a form object or a string, the latter corresponding to a form
object whose form value is the string and all other properties are nil. Similarly, an '''abbreviated form list''' is
either a single abbreviated form object or a list of such objects, i.e. any of a string, form object or list of
strings and/or form objects. Functions that do not accept such abbreviated structures may be said to insist on being
passed form objects in '''general form''', or form lists in '''general list form'''.
* Each slot is associated with an '''accelerator tag set''', which is a list of inflection tags that are used when
generating an accelerator entry for the forms in the slot (see [[WT:ACCEL]]). For example, the first singular present
indicative of a verb might have slot name `pres_1sg` and corresponding accelerator tag set `1|s|pres|ind`. As shown,
the accelerator tag set is a string consisting of inflection tags (as used in {{tl|inflection of}}) separated by `|`.
Despite the terminology ''tag set'', the tags in a tag set are ordered, although the same tag should never occur
twice.
* Some inflected terms are '''multiword''', i.e. they consist of multiple '''words''', where each word is generally
separated by spaces or sometimes hyphens. In such a term, some of the words inflect, while others remain fixed. Words
that inflect are termed '''inflecting words''' (or more correctly '''inflecting parts''', since in some circumstances,
parts of a word can inflect). The '''fixed text''' is all the parts of a multiword term that do not inflect.
* The descriptor that describes how a given term inflects is called an '''inflection spec''', and consists of the lemma
form of the term itself, annotated with an '''angle bracket spec''' after each inflecting word. As the name implies,
an angle bracket spec is surrounded by angle brackets (`<...>`). A simple example is {{m+|de|Feder||feather}}, whose
inflection spec looks like `Feder<f>`, where `f` specifies the feminine gender. In this case, although there are
several properties that could be specified between angle brackets, all except the gender are optional and have been
left out, indicating that defaults should be used. Another example is {{m+|de|Baske|Basque person}}, whose inflection
spec looks like `Baske<m.weak>`, where `m` specifies the masculine gender and `weak` specifies the weak inflection.
Note that individual components of an angle bracket spec like `m` and `weak` are termed '''indicators''' and are
separated by periods/full stops. A slightly more complex example is {{m+|de|Zeitgeist||zeitgeist}}, whose inflection
spec looks like `Zeitgeist<m,es:s,er>` and which specifies three things in a single '''compound indicator''': `m` (the
masculine gender); `es:s` (the genitive singular, which can end in either ''-es'' or ''-s''); and `er` (the
nomininative plural, which ends in ''-er'').
* If there are several inflecting words in a term, each one will be followed by its own angle bracket spec. An example
is {{m+|de|schwarzes Loch||black hole}}, whose inflection spec looks like `schwarzes<+> Loch<n,es:s,^er>`. Here,
the adjective ''schwarzes'' (the nominative neuter singular of {{m|de|schwarz||black}}) is followed by the angle
bracket spec `<+>` specifying that it inflects as an adjective, and the noun ''Loch'' has the angle bracket spec
`<n,es:s,^er>`, indicating (similarly to the above example) that it is neuter, has a genitive singular in either
''-es'' or ''-s'', and has a nominative plural in ''-er'' with umlaut, hence ''Löcher'' (the `^` specifies that the
form requires umlaut).
* Sometimes a given term has multiple ways of inflecting that differ in ways that can't be specified using a single
angle bracket spec. This is supported using '''alternants''', which are specified using double parentheses. (This is
so that terms that themselves contain parentheses can be specified without interference.) An example is
{{m+|uk|русин||Rusyn}}, which can be stressed either as ''ру́син'' (stress on the first syllable and following accent
paradigm ''a'', hence genitive singular ''ру́сина'') or ''руси́н'' (stress on the second syllable and following accent
paradigm ''b'', hence genitive singular ''русина́''; note how the stress moves onto the ending, in accordance with the
accent paradigm). This is specified using `((ру́син<pr>,руси́н<b.pr>))`, i.e. each separate the alternants with a comma
and surround them with double parentheses. (Here, `pr` means that the terms belong to the personal animacy class, and
`b` specifies the accent paradigm; paradigm ''a'' is the default and hence is omitted.)
* Note that occasionally, parts of a single space-delimited word can inflect separately. An example is
{{m+|la|rōsmarīnus||rosemary}}, which is a compound of {{m+|la|rōs||dew}} and {{m|la|marīnus||marine, of the sea}}.
In this compound, both parts of the compound can inflect separately; hence genitive singular ''rōrismarīnī'',
accusative singular ''rōremmarīnum'', etc. Alternatively, only the second part inflects; hence genitive singular
''rōsmarīnī'', accusative singular ''rōsmarīnum'', etc. This is specified as
`((rōs/rōr<3.M>marīnus<2>,rōsmarīnus<2>))`. Here, the term {{m|la|rōs}} by itself would have inflection spec
`rōs/rōr<3.M>` (indicating that it is third declension masculine with a non-nominative-singular stem ''rōr-'') and
the term {{m|la|marīnus}} would have inflection spec `<2>` (indicating that it is second declension; the masculine
gender is inferred from the ''-us'' ending). When combined in a single inflection spec, the doubly-inflecting
alternant is written `rōs/rōr<3.M>marīnus<2>`, with each inflecting part followed by its corresponding angle bracket
spec, and the singly-inflecting alternant is written `rōsmarīnus<2>`. As this example shows, the two alternants
need not correspond in how many inflecting parts there are. It should also be noted that fixed text can surround
an alternant and it is even possible to supply multiple alternants in a single inflection spec (e.g. if the term
has two words in it and each word requires an alternant to inflect).
* The result of parsing a single angle bracket spec is stored into a '''word spec'''. The structure of a word spec is
fairly arbitrary and is determined by the user-written `parse_indicator_spec` function, but always contains a form
table under the `forms` key that is populated during inflection (see below). A parameter or local variable that holds
a word spec is conventionally named `base` for historical reasons. Word specs are grouped together into a structure
termed a '''multiword spec''', which describes one or more word specs along with the fixed text in between and around
the inflected words. Multiword specs are in turn grouped into structures termed '''alternant specs''', indicating
the distinct alternants and the words in each alternant. Finally, multiword specs and alternant specs are grouped into
an '''alternant multiword spec''', which is the top-level object describing an inflection spec. Each of these
different specs has a form table in it stored in the `forms` key that is populated during the inflection process and
contains the form objects that specify the inflections of this part of the full multiword term. (It should be noted
that the term '''spec''' is overloaded to mean two different things: the user-specified descriptor that specifies the
lemma form of the term and associated inflection, and the associated internal Lua object that encapsulates all
information derived from the descriptor, along with later-generated information on how to inflect the term(s) being
described.)
* Among these various "spec" structures, the two most important are the top-level alternant multiword spec and the
bottom-level word spec or "base". You will rarely find it necessary to manipulate the intermediate structures or
concern yourself with the details of their formation.
* The term ''form'' is unfortunatately overloaded in various modules to mean several things. In particular, for
historical reasons, the form value inside of a form object is stored using the key `form`; the form table inside of an
alternant multiword spec, a word spec (or "base") and the intermediate structures is stored using the key `forms`; and
the accelerator tag set is internally referred to in [[WT:ACCEL]] as a "form". To avoid confusion, the following
conventions are followed in code in this module, and should be followed for code in invoking modules as well:
*# Functions that accept form objects often name the relevant parameter `form` (if a single form object is required) or
`forms` (if a list of form objects, aka form list, is required).
*# Functions that accept abbreviated form objects should (but don't always) indicate this by naming the parameter
`abform` (for a single abbreviated form object) or `abforms` (for an abbreviated form list).
*# Functions that accept a form value (the native-script string portion of a form object, stored for historical reasons
in the `.form` property) should '''not''' call such a parameter `form`, but instead use something that makes clear
that a form value is required, such as `formval` or sometimes just `val`.
*# Similarly, functions that accept a form table should '''not''' call such a parameter `forms` (although for historical
reasons the form table in an alternant multiword spec is stored in the field `forms`). Instead, use `formtable` or
`formtab`, or similar name that makes clear that the value is a form table (i.e. a map from slot to form list).
====Footnote handling====
Each form can have one or more attached footnotes. The form of a footnote as specified by the user and stored in form
values is e.g. {"[archaic]"} or {"[only in the meaning 'to succeed (an officeholder)']"}, i.e. the string must be
surrounded by brackets and should begin with a lowercase letter and not end in a period/full stop. When such footnotes
are converted to actual footnotes in a table of inflected forms, the brackets will be removed, the first letter will be
capitalized and a period/full stop will be added to the end. (However, when such footnotes are used as qualifiers in
headword lines, only the brackets will be removed, with no capitalization or final period.)
When merging two forms into one, such as when concatenating the form objects of two inflected words in a multiword term
or deduplicating form objects sharing the same form value during `show_forms()`, the footnotes are generally combined as
well. This means that if one form object has footnotes and the other doesn't, the resulting form object inherits the
footnotes of the object that has them, and if both form objects have footnotes, the resulting form object gets all
footnotes from both source form objects, with duplicates removed. However, when inserting a form into a form table slot
that already has a form whose form value and translit are identical to the new form, the behavior is different. In
under normal circumstances the footnotes of the new form are ''not'' incorporated into those of the existing form (if
any), but are simply dropped. To understand why this makes sense, consider a term that has two possible forms of its
lemma (e.g. two forms differing in stress or in vowel length), where the second form is archaic, rare, colloquial or the
like, and has an attached footnote indicating this. An example of this is {{m+|ru|кожух||sheepskin coat; bullet shell}},
where the form ''кожу́х'' with accent pattern ''b'' is more common overall but the form ''ко́жух'' with accent pattern
''c(1)'' is more common among professionals. On first glance, this could be indicated using
`((кожу́х<b>,ко́жух<c(1).[professional usage only]>))`. But some forms of these two declensions are the same (in
particular, the genitive, dative, instrumental and prepositional plural). If for these slots, the footnotes of the
duplicate forms were combined (i.e. the footnotes of the second declension pattern were added to the already-existing
form taken from the first declension pattern), these forms would wrongly be labeled as ''professional usage only''.
For this reason, it makes more sense to drop the footnotes of the second form when deduplicating.
The same sort of behavior makes sense when a single lemma can have two different declensions, the second of which
requires a footnote and where some forms in the two declensions are shared. An example of this is
{{m+|uk|окови́та||strong, high-quality liquor}}, which can be inflected adjectivally or (rarely) nominally. This would be
indicated as `((окови́та<sg.+>,окови́та<sg.[rare]>))` where the `+` indicates adjectival declension and the `sg` indicates
that this term only exists in the singular. Here, the two declensions differ in the genitive, dative/locative and
vocative (respectively, adjectival ''окови́тої'', ''окови́тій'', ''окови́та'' vs. nominal ''окови́ти'', ''окови́ті'',
''окови́то'') but are the same in the accusative (''окови́ту'') and instrumental (''окови́тою''). Again, dropping the
footnotes of the second form when deduplicating is correct and including them would be wrong.
This behavior can be changed by attaching a '''footnote modifier''' to the footnote associated the second form. A
footnote modifier is a symbol attached to the beginning of a footnote, directly following the opening bracket. The
following modifiers are currently recognized:
* `!` or `+`: If placed on a footnote of the second form, combine that footnote with those of the first form (if any)
rather than dropping it.
* `*`: If placed on a footnote of the first form, drop that footnote when merging a second form with any footnotes.
An example where the `*` modifier makes sense is a modification of the above example with {{m+|ru|кожух}}. If we
notated it as `((кожу́х<b.[more common among laymen]>,ко́жух<c(1).[more common among professionals]>))`, the shared forms
would wrongly have the footnote ''more common among laymen'' when in fact they are the only possible forms. If instead
we used `((кожу́х<b.[*more common among laymen]>,ко́жух<c(1).[more common among professionals]>))`, the shared forms
would correctly have no footnote.
Finally, be aware of '''old-style footnote symbols'''. For compatibility reasons, some inflection implementations
support a system whereby footnote symbols (consisting of numbers; certain ASCII symbols such as `*`, `~`, `@`, `#`,
`+`, etc.; and a large number of Unicode symbols) are directly attached to form values and the footnotes themselves
specified manually using the `footnotes` property passed to `show_forms()`. This is allowed only when
`allow_footnote_symbols` is set and is highly deprecated. All uses of such symbols should be converted to standard
footnotes and the support for such symbols removed.
]==]
local function extract_footnote_modifiers(footnote)
local footnote_mods, footnote_without_mods = rmatch(footnote, "^%[([!*+]?)(.*)%]$")
if not footnote_mods then
error("Saw footnote '" .. footnote .. "' not surrounded by brackets")
end
return footnote_mods, footnote_without_mods
end
--[==[
Insert a form object (see above) into a list of such objects. If the form is already present (i.e. both the form
value and translit, if any, match), the footnotes of the existing and new form might be combined (specifically,
footnotes in the new form beginning with `!` will be combined).
]==]
function export.insert_form_into_list(list, form)
-- Don't do anything if the form object or the form inside it is nil. This simplifies
-- form insertion in the presence of inflection generating functions that may return nil,
-- such as generate_noun_vocative() and generate_noun_count_form().
if not form or not form.form then
return
end
for _, listform in ipairs(list) do
if listform.form == form.form and listform.translit == form.translit then
-- Form already present; maybe combine footnotes.
if form.footnotes then
-- Check to see if there are existing footnotes with *; if so, remove them.
if listform.footnotes then
local any_footnotes_with_asterisk = false
for _, footnote in ipairs(listform.footnotes) do
local footnote_mods, _ = extract_footnote_modifiers(footnote)
if rfind(footnote_mods, "%*") then
any_footnotes_with_asterisk = true
break
end
end
if any_footnotes_with_asterisk then
local filtered_footnotes = {}
for _, footnote in ipairs(listform.footnotes) do
local footnote_mods, _ = extract_footnote_modifiers(footnote)
if not rfind(footnote_mods, "%*") then
table.insert(filtered_footnotes, footnote)
end
end
if #filtered_footnotes > 0 then
listform.footnotes = filtered_footnotes
else
listform.footnotes = nil
end
end
end
-- The behavior here has changed; track cases where the old behavior might
-- be needed by adding ! to the footnote.
track("combining-footnotes")
local any_footnotes_with_bang = false
for _, footnote in ipairs(form.footnotes) do
local footnote_mods, _ = extract_footnote_modifiers(footnote)
if rfind(footnote_mods, "[!+]") then
any_footnotes_with_bang = true
break
end
end
if any_footnotes_with_bang then
if not listform.footnotes then
listform.footnotes = {}
else
listform.footnotes = m_table.shallowCopy(listform.footnotes)
end
for _, footnote in ipairs(form.footnotes) do
local already_seen = false
local footnote_mods, footnote_without_mods = extract_footnote_modifiers(footnote)
if rfind(footnote_nods, "[!+]") then
for _, existing_footnote in ipairs(listform.footnotes) do
local existing_footnote_mods, existing_footnote_without_mods =
extract_footnote_modifiers(existing_footnote)
if existing_footnote_without_mods == footnote_without_mods then
already_seen = true
break
end
end
if not already_seen then
table.insert(listform.footnotes, footnote)
end
end
end
end
end
return
end
end
-- Form not found.
table.insert(list, form)
end
--[==[
Insert a form object (see above) into the given slot in the given form table. ``form`` can be {nil}, in which case
nothing happens.
]==]
function export.insert_form(formtable, slot, form)
-- Don't do anything if the form object or the form inside it is nil. This simplifies
-- form insertion in the presence of inflection generating functions that may return nil,
-- such as generate_noun_vocative() and generate_noun_count_form().
if not form or not form.form then
return
end
if not formtable[slot] then
formtable[slot] = {}
end
export.insert_form_into_list(formtable[slot], form)
end
--[==[
Insert a list of form objects (see above) into the given slot in the given form table. ``forms`` can be {nil},
in which case nothing happens.
]==]
function export.insert_forms(formtable, slot, forms)
if not forms then
return
end
for _, form in ipairs(forms) do
export.insert_form(formtable, slot, form)
end
end
--[==[
Identity mapping function.
]==]
function export.identity(formval, translit)
return formval, translit
end
local function form_value_transliterable(formval)
return formval ~= "?" and formval ~= "—"
end
local function call_map_function_str(str, fun)
if str == "?" then
return "?"
end
local newformval, newtranslit = fun(str)
if newtranslit then
return {form=newformval, translit=newtranslit}
else
return newformval
end
end
-- FIXME: This doesn't correctly handle metadata.
local function call_map_function_obj(form, fun)
if form.form == "?" then
return {form = "?", footnotes = form.footnotes}
end
local newformval, newtranslit = fun(form.form, form.translit)
return {form = newformval, translit = newtranslit, footnotes = form.footnotes}
end
--[==[
Map a function over the form values in ``forms`` (a list of form objects in "general list form; see above). If an
input form value is {"?"}, it is preserved on output and the function is not called. Otherwise, the function is
called with two arguments, the original form and manual translit; if manual translit isn't relevant, it's fine to
declare the function with only one argument. The return value is either a single value (the new form) or two values
(the new form and new manual translit). The footnotes (if any) from the input form objects are preserved on output. Uses
`insert_form_into_list()` to insert the resulting form objects into the returned list in case two different forms map
to the same thing.
FIXME: Expand this to correctly handle metadata, or create a variant that correctly handles metadata.
]==]
function export.map_forms(forms, fun)
if not forms then
return nil
end
local retval = {}
for _, form in ipairs(forms) do
export.insert_form_into_list(retval, call_map_function_obj(form, fun))
end
return retval
end
--[==[
Map a list-returning function over the form values in ``forms`` (a list of form objects in "general list form"; see
above). If an input form value is {"?"}, it is preserved on output and the function is not called. Otherwise, the
function is called with two arguments, the original form and manual translit; if manual translit isn't relevant, it's
fine to declare the function with only one argument. The return value of the function can be {nil} or an abbreviated
form list (i.e. anything that is convertible into a general list form, such as a single form value, a list of form
values, a form object or a list of form objects). For each form object in the return value, the footnotes of that form
object (if any) are combined with any footnotes from the input form object, and the result inserted into the returned
list using `insert_form_into_list()` in case two different forms map to the same thing.
FIXME: Expand this to correctly handle metadata, or create a variant that correctly handles metadata.
]==]
function export.flatmap_forms(forms, fun)
if not forms then
return nil
end
local retval = {}
for _, form in ipairs(forms) do
local funret = form.form == "?" and {"?"} or fun(form.form, form.translit)
if funret then
funret = export.convert_to_general_list_form(funret)
for _, fr in ipairs(funret) do
local newform = {
form = fr.form,
translit = fr.translit,
footnotes = export.combine_footnotes(form.footnotes, fr.footnotes)
}
export.insert_form_into_list(retval, newform)
end
end
end
return retval
end
--[==[
Map a function over the form values in ``abforms`` (an abbreviated form list). If the input form value is {"?"}, it is
preserved on output and the function is not called. If ``first_only`` is given and ``abforms`` is a list, only map over
the first element. Return value is of the same form as ``abforms``, unless ``abforms`` is a string and the function
returns both form value and manual translit (in which case the return value is a form object). The function is called
with two arguments, the original form value and manual translit; if manual translit isn't relevant, it's fine to declare
the function with only one argument. The return value is either a single value (the new form value) or two values (the
new form value and new manual translit). The footnotes (if any) from the input form objects are preserved on output.
FIXME: This function is used only in [[Module:bg-verb]] and should be moved into that module.
]==]
function export.map_form_or_forms(abforms, fun, first_only)
if not abforms then
return nil
elseif type(abforms) == "string" then
return call_map_function_str(abforms, fun)
elseif abforms.form then
return call_map_function_obj(abforms, fun)
else
local retval = {}
for i, abform in ipairs(abforms) do
if first_only then
return export.map_form_or_forms(abform, fun)
end
table.insert(retval, export.map_form_or_forms(abform, fun))
end
return retval
end
end
--[==[
Combine two sets of footnotes. If either is {nil}, just return the other, and if both are {nil}, return {nil}.
]==]
function export.combine_footnotes(notes1, notes2)
if not notes1 and not notes2 then
return nil
end
if not notes1 then
return notes2
end
if not notes2 then
return notes1
end
local combined = m_table.shallowCopy(notes1)
for _, note in ipairs(notes2) do
m_table.insertIfNot(combined, note)
end
return combined
end
--[==[
Expand a given footnote (as specified by the user, including the surrounding brackets) into the form to be inserted
into the final generated table. If ``no_parse_refs`` is not given and the footnote is a reference (of the form
{"[ref:...]"}), parse and return the specified reference(s). Two values are returned, `footnote_string` (the expanded
footnote, or nil if the second value is present) and `references` (a list of objects of the form
`{text = ``text``, name = ``name``, group = ``group``}` if the footnote is a reference and ``no_parse_refs`` is not
given, otherwise {nil}). Unless ``return_raw`` is given, the returned footnote string is capitalized and has a final
period added.
]==]
function export.expand_footnote_or_references(note, return_raw, no_parse_refs)
local _, notetext = extract_footnote_modifiers(note)
if not no_parse_refs and notetext:find("^ref:") then
-- a reference
notetext = rsub(notetext, "^ref:", "")
local parsed_refs = require("Module:references").parse_references(notetext)
for i, ref in ipairs(parsed_refs) do
if type(ref) == "string" then
parsed_refs[i] = {text = ref}
end
end
return nil, parsed_refs
end
if footnote_abbrevs[notetext] then
notetext = footnote_abbrevs[notetext]
track("footnote-whole-abbrev")
else
local split_notes = split(notetext, "<(.-)>")
for i, split_note in ipairs(split_notes) do
if i % 2 == 0 then
split_notes[i] = footnote_abbrevs[split_note]
track("footnote-angle-bracket-abbrev")
if not split_notes[i] then
-- Don't error for now, because HTML might be in the footnote.
-- Instead we should switch the syntax here to e.g. <<a>> to avoid
-- conflicting with HTML.
split_notes[i] = "<" .. split_note .. ">"
track("footnote-unrecognized-angle-bracket-abbrev")
--error("Unrecognized footnote abbrev: <" .. split_note .. ">")
else
track("footnote-recognized-angle-bracket-abbrev")
end
end
end
notetext = table.concat(split_notes)
end
return return_raw and notetext or ucfirst(notetext) .. "."
end
--[==[
Convert a list of foonotes to qualifiers and references for use in [[Module:headword]] or similar. Returns two values,
a list of qualifiers (possibly {nil}) and a list of reference structures (possibly {nil}), following the structure
defined in [[Module:references]]).
]==]
function export.convert_footnotes_to_qualifiers_and_references(footnotes)
if not footnotes then
return nil
end
local quals, refs
for _, qualifier in ipairs(footnotes) do
local this_footnote, this_refs = export.expand_footnote_or_references(qualifier, "return raw")
if this_refs then
if not refs then
refs = this_refs
else
for _, ref in ipairs(this_refs) do
table.insert(refs, ref)
end
end
else
if not quals then
quals = {this_footnote}
else
table.insert(quals, this_footnote)
end
end
end
return quals, refs
end
--[==[
Combine an abbreviated form object (either a string or a table) with additional footnotes, possibly replacing the form
value and/or translit in the process. Normally called in one of two ways:
(1) `combine_form_and_footnotes(``form_obj``, ``addl_footnotes``, ``new_form``, ``new_translit``)` where ``form_obj``
is an existing abbreviated form object; ``addl_footnotes`` is either {nil}, a single string (a footnote) or a list
of footnotes; ``new_formval`` is either {nil} or the new form value to substitute; and ``new_translit`` is either
{nil} or the new translit string to substitute.
(2) `combine_form_and_footnotes(``form_value``, ``footnotes``)`, where ``form_value`` is a form value (a string) and
``footnotes`` is either {nil}, a single string (a footnote) or a list of footnotes.
In either case, a form object is returned, preserving as many properties as possible from any existing form object in
``abform``. Do the minimal amount of work; e.g. if ``abform`` is a form object and ``addl_footnotes``, ``new_formval``
and ``new_translit`` are all {nil}, the same object as passed in is returned. Under no circumstances is the existing
form object side-effected.
'''FIXME:''' This does not correctly preserve metadata.
]==]
function export.combine_form_and_footnotes(abform, addl_footnotes, new_formval, new_translit)
if type(addl_footnotes) == "string" then
addl_footnotes = {addl_footnotes}
end
if not addl_footnotes and not new_formval and not new_translit then
return abform
end
if type(abform) == "string" then
new_formval = new_formval or abform
return {form = new_formval, translit = new_translit, footnotes = addl_footnotes}
end
abform = m_table.shallowCopy(abform)
if new_formval then
abform.form = new_formval
end
if new_translit then
abform.translit = new_translit
end
if addl_footnotes then
abform.footnotes = export.combine_footnotes(abform.footnotes, addl_footnotes)
end
return abform
end
--[==[
Convert an abbreviated form list (either a string, form object, or list of either) into general list form. If
``footnotes`` is supplied, then for each form in the form list, combine the form's footnotes with ``footnotes``.
This function does not side-effect any of the objects passed into ``abforms``, but will return ``abforms``
unchanged if already in general list form and ``footnotes`` is {nil}.
'''FIXME:''' This does not correctly preserve metadata.
]==]
function export.convert_to_general_list_form(abforms, footnotes)
if type(footnotes) == "string" then
footnotes = {footnotes}
end
if type(abforms) == "string" then
return {{form = abforms, footnotes = footnotes}}
elseif abforms.form then
return {export.combine_form_and_footnotes(abforms, footnotes)}
elseif not footnotes then
-- Check if already in general list form and return directly if so.
local must_convert = false
for _, form in ipairs(abforms) do
if type(form) == "string" then
must_convert = true
break
end
end
if not must_convert then
return abforms
end
end
local retval = {}
for _, form in ipairs(abforms) do
if type(form) == "string" then
table.insert(retval, {form = form, footnotes = footnotes})
else
table.insert(retval, export.combine_form_and_footnotes(form, footnotes))
end
end
return retval
end
local function is_table_of_strings(forms)
for k, v in pairs(forms) do
if type(k) ~= "number" or type(v) ~= "string" then
return false
end
end
return true
end
local function lang_or_func_transliterate(func, lang, text)
local retval
if func then
retval = func(text)
else
retval = (lang:transliterate(text))
end
-- FIXME! Hack to work around bug in ...:transliterate(). Remove me as soon as this bug is fixed.
if not retval and (text == " " or text == "-" or text == "?") then
retval = text
end
if not retval then
error(("Unable to transliterate text '%s'"):format(text))
end
return retval
end
--[==[
Combine ``stems`` and ``endings`` and store into slot ``slot`` of form table ``formtable``. Either of ``stems`` and
``endings`` can be {nil} or an abbreviated form list. The combination of a given stem and ending happens using
``combine_stem_ending``, which takes two parameters (stem and ending, each a string) and returns one value (a string).
If manual transliteration is present in either ``stems`` or ``endings``, ``lang`` (a language object or a function of
one argument to transliterate a string) along with ``combine_stem_ending_tr`` (a function for combining manual
transliterations that works much like ``combine_stem_ending``) must be given. ``footnotes``, if specified, is a list of
additional footnotes to attach to the resulting inflections (stem+ending combinations). The resulting inflections are
inserted into the form table using `insert_form()`, in case of duplication.
]==]
function export.add_forms(formtable, slot, stems, endings, combine_stem_ending, lang, combine_stem_ending_tr, footnotes)
if stems == nil or endings == nil then
return
end
local function combine(stem, ending)
if stem == "?" or ending == "?" then
return "?"
end
return combine_stem_ending(stem, ending)
end
local function transliterate(text)
return lang_or_func_transliterate(is_callable(lang) and lang or nil, lang, text)
end
if type(stems) == "string" and type(endings) == "string" then
export.insert_form(formtable, slot, {form = combine(stems, endings), footnotes = footnotes})
elseif type(stems) == "string" and is_table_of_strings(endings) then
for _, ending in ipairs(endings) do
export.insert_form(formtable, slot, {form = combine(stems, ending), footnotes = footnotes})
end
else
stems = export.convert_to_general_list_form(stems)
endings = export.convert_to_general_list_form(endings, footnotes)
for _, stem in ipairs(stems) do
for _, ending in ipairs(endings) do
local footnotes = nil
if stem.footnotes and ending.footnotes then
footnotes = m_table.shallowCopy(stem.footnotes)
for _, footnote in ipairs(ending.footnotes) do
m_table.insertIfNot(footnotes, footnote)
end
elseif stem.footnotes then
footnotes = stem.footnotes
elseif ending.footnotes then
footnotes = ending.footnotes
end
local new_form = combine(stem.form, ending.form)
local new_translit
if new_form ~= "?" and (stem.translit or ending.translit) then
if not lang or not combine_stem_ending_tr then
error("Internal error: With manual translit, 'lang' and 'combine_stem_ending_tr' must be passed to 'add_forms'")
end
local stem_tr = stem.translit or transliterate(m_links.remove_links(stem.form))
local ending_tr = ending.translit or transliterate(m_links.remove_links(ending.form))
new_translit = combine_stem_ending_tr(stem_tr, ending_tr)
end
export.insert_form(formtable, slot, {form = new_form, translit = new_translit, footnotes = footnotes})
end
end
end
end
--[==[
Combine any number of form components and store into slot ``slot`` of form table ``formtable``. ``components`` is a list of abbreviated form
lists which should be concatenated similarly to how `add_forms()` does it, and stored in ``slot`` along with any footnotes in ``footnotes``.
More specifically:
# If there are no components, nothing happens.
# If there is one component, it is converted to general list form and `insert_forms()` called.
# If there are two components, they are treated as stems and endings respectively and `add_forms()` is called.
# If there are three or more components, they are concatenated left-to-right in the manner of a `reduce()` operation: the first two components
are combined using `add_forms()` and stored into a temporary table, then the next component is combined with the result of the previous
operation, etc. In the last combination, footnotes in `footnotes` are combined in, and the result stored into `formtable`.
This should generally be used when you are likely to have three or more components, as in [[Module:ar-verb]] (prefixes, stems and endings)
and [[Module:de-verb]] (which in some situations has five components combined together). ``combine_stem_ending``, ``lang``,
``combine_stem_ending_tr`` and ``footnotes`` are as in `add_forms()`.
]==]
function export.add_multiple_forms(formtable, slot, components, combine_stem_ending, lang, combine_stem_ending_tr, footnotes)
if #components == 0 then
return
elseif #components == 1 then
local forms = export.convert_to_general_list_form(components[1], footnotes)
export.insert_forms(formtable, slot, forms)
elseif #components == 2 then
local stems = components[1]
local endings = components[2]
export.add_forms(formtable, slot, stems, endings, combine_stem_ending, lang, combine_stem_ending_tr, footnotes)
else
local prev = components[1]
for i=2, #components do
local temptable = {}
export.add_forms(temptable, slot, prev, components[i], combine_stem_ending, lang, combine_stem_ending_tr,
i == #components and footnotes or nil)
prev = temptable[slot]
end
export.insert_forms(formtable, slot, prev)
end
end
local function iterate_slot_list_or_table(props, do_slot)
if props.slot_list then
for _, slot_and_accel_tag_set in ipairs(props.slot_list) do
local slot, accel_tag_set = unpack(slot_and_accel_tag_set)
do_slot(slot, accel_tag_set)
end
else
for slot, accel_tag_set in pairs(props.slot_table) do
do_slot(slot, accel_tag_set)
end
end
end
function export.default_split_bracketed_runs_into_words(bracketed_runs, data)
-- If the text begins with a hyphen, include the hyphen in the set of allowed characters
-- for an inflected segment. This way, e.g. conjugating "-ir" is treated as a regular
-- -ir verb rather than a hyphen + irregular [[ir]].
local is_suffix = (not data or data.text_index == 1) and rfind(bracketed_runs[1], "^%-")
local split_pattern = is_suffix and " " or "[ %-]"
return put.split_alternating_runs(bracketed_runs, split_pattern, "preserve splitchar")
end
local function props_transliterate(props, text)
return lang_or_func_transliterate(props.transliterate, props.lang, text)
end
local function parse_before_or_post_text(data)
local props, text, text_index, segments, lemma_is_last = data.props, data.text, data.text_index, data.segments,
data.lemma_is_last
-- Call parse_balanced_segment_run() to keep multiword links together.
local bracketed_runs = put.parse_balanced_segment_run(text, "[", "]")
-- Split normally on space or hyphen (but customizable). Use preserve_splitchar so we know whether the separator was
-- a space or hyphen.
local space_separated_groups
if props.split_bracketed_runs_into_words then
space_separated_groups = props.split_bracketed_runs_into_words(bracketed_runs)
end
if not space_separated_groups then
space_separated_groups = export.default_split_bracketed_runs_into_words(bracketed_runs, data)
end
local parsed_components = {}
local parsed_components_translit = {}
local saw_manual_translit = false
local lemma
for j, space_separated_group in ipairs(space_separated_groups) do
local component = table.concat(space_separated_group)
if lemma_is_last and j == #space_separated_groups then
lemma = component
if lemma == "" and not props.allow_blank_lemma then
error("Word is blank: '" .. table.concat(segments) .. "'")
end
elseif rfind(component, "//") then
-- Manual translit or respelling specified.
if not props.lang then
error("Manual translit not allowed for this language; if this is incorrect, 'props.lang' must be set internally")
end
saw_manual_translit = true
local split = split(component, "//", "plain")
if #split ~= 2 then
error("Term with translit or respelling should have only one // in it: " .. component)
end
local translit
component, translit = unpack(split)
if props.transliterate_respelling then
translit = props.transliterate_respelling(translit)
end
table.insert(parsed_components, component)
table.insert(parsed_components_translit, translit)
else
table.insert(parsed_components, component)
table.insert(parsed_components_translit, false) -- signal that it may need later transliteration
end
end
if saw_manual_translit then
for j, parsed_component in ipairs(parsed_components) do
if not parsed_components_translit[j] then
parsed_components_translit[j] = props_transliterate(props, m_links.remove_links(parsed_component))
end
end
end
text = table.concat(parsed_components)
local translit
if saw_manual_translit then
translit = table.concat(parsed_components_translit)
end
return text, translit, lemma
end
--[=[
Parse a segmented multiword spec such as "[[медичний|меди́чна]]<+> [[сестра́]]<*,*#.pr>" (in Ukrainian).
"Segmented" here means it is broken up on <...> segments using parse_balanced_segment_run(text, "<", ">"),
e.g. the above text would be passed in as {"[[медичний|меди́чна]]", "<+>", " [[сестра́]]", "<*,*#.pr>", ""}.
The return value is a table of the form
{
word_specs = {``word_spec``, ``word_spec``, ...},
post_text = "``text-at-end``",
post_text_no_links = "``text-at-end-no-links``",
post_text_translit = "``manual-translit-of-text-at-end``" or nil (if no manual translit or respelling was specified in the post-text)
}
where ``word_spec`` describes an individual inflected word and "``text-at-end``" is any raw text that may occur
after all inflected words. Individual words or linked text (including multiword text) may be given manual
transliteration or respelling in languages that support this using ``text``//``translit`` or ``text``//``respelling``.
Each ``word_spec`` is of the form returned by parse_indicator_spec():
{
lemma = "``lemma``",
before_text = "``text-before-word``",
before_text_no_links = "``text-before-word-no-links``",
before_text_translit = "``manual-translit-of-text-before-word``" or nil (if no manual translit or respelling was specified in the before-text)
-- Fields as described in parse_indicator_spec()
...
}
For example, the return value for "[[медичний|меди́чна]]<+> [[сестра́]]<*,*#.pr>" is
{
word_specs = {
{
lemma = "[[медичний|меди́чна]]",
overrides = {},
adj = true,
before_text = "",
before_text_no_links = "",
forms = {},
},
{
lemma = "[[сестра́]]",
overrides = {},
stresses = {
{
reducible = true,
genpl_reversed = false,
},
{
reducible = true,
genpl_reversed = true,
},
},
animacy = "pr",
before_text = " ",
before_text_no_links = " ",
forms = {},
},
},
post_text = "",
post_text_no_links = "",
}
]=]
local function parse_multiword_spec(segments, props, disable_allow_default_indicator)
local multiword_spec = {
word_specs = {}
}
if not disable_allow_default_indicator then
if #segments == 1 then
if props.allow_default_indicator then
table.insert(segments, "<>")
table.insert(segments, "")
elseif props.angle_brackets_omittable then
segments[1] = "<" .. segments[1] .. ">"
table.insert(segments, 1, "")
table.insert(segments, "")
end
end
end
-- Loop over every other segment. The even-numbered segments are angle-bracket specs while
-- the odd-numbered segments are the text between them.
for i = 2, #segments - 1, 2 do
local before_text, before_text_translit, lemma =
parse_before_or_post_text {
props = props,
text = segments[i - 1],
text_index = i - 1,
segments = segments,
lemma_is_last = true
}
local base = props.parse_indicator_spec(segments[i], lemma)
base.before_text = before_text
base.before_text_no_links = m_links.remove_links(base.before_text)
base.before_text_translit = before_text_translit
base.lemma = base.lemma or lemma
table.insert(multiword_spec.word_specs, base)
end
multiword_spec.post_text, multiword_spec.post_text_translit =
parse_before_or_post_text {
props = props,
text = segments[#segments],
text_index = #segments,
segments = segments,
lemma_is_last = false
}
multiword_spec.post_text_no_links = m_links.remove_links(multiword_spec.post_text)
return multiword_spec
end
--[=[
Parse an alternant, e.g. "((родо́вий,родови́й))" or "((ру́син<pr>,руси́н<b.pr>))" (both in Ukrainian).
The return value is a table of the form
{
alternants = {``multiword_spec``, ``multiword_spec``, ...}
}
where ``multiword_spec`` describes a given alternant and is as returned by parse_multiword_spec().
]=]
local function parse_alternant(alternant, props)
local parsed_alternants = {}
local alternant_text = rmatch(alternant, "^%(%((.*)%)%)$")
local segments = put.parse_balanced_segment_run(alternant_text, "<", ">")
local comma_separated_groups = put.split_alternating_runs(segments, "%s*,%s*")
local alternant_spec = {alternants = {}}
for _, comma_separated_group in ipairs(comma_separated_groups) do
table.insert(alternant_spec.alternants, parse_multiword_spec(comma_separated_group, props))
end
return alternant_spec
end
--[==[
Top-level parsing function. Parse text describing one or more inflected words. `text` is the inflected text to parse,
which generally has `<...>` specs following words to be inflected, and may have alternants indicated using double
parens. Examples:
* {"[[медичний|меди́чна]]<+> [[сестра́]]<*,*#.pr>"} (Ukrainian, for {{m|uk|меди́чна сестра́||nurse|lit=medical sister}});
* {"((ру́син<pr>,руси́н<b.pr>))"} (Ukrainian, for {{m|uk|русин||Rusyn}}, with two possible stress patterns);
* {"पंचायती//पंचाय*ती राज<M>"} (Hindi, for {{m|hi|पंचायती राज||village council}}, with phonetic respelling in the
before-text component);
* {"((<M>,<M.plstem:फ़तूह.dirpl:फ़तूह>))"} (Hindi, for {{m|hi|फ़तह||win, victory}} when used on that page, where the lemma
is omitted and taken from the pagename);
* {""} (for any number of Hindi adjectives, where the lemma is omitted and taken from the pagename, and the angle
bracket spec <> is assumed);
* {"काला<+>धन<M>"} (Hindi, for {{m|hi|कालाधन||black money}}, showing that closed compounds where each part is declined
can be correctly handled).
`props` is an object specifying properties used during parsing, as follows:
```{
parse_indicator_spec = __function__(``angle_bracket_spec``, ``lemma``) `''(required)''`,
lang = __lang object__,
transliterate_respelling = __function__(``respelling_or_translit``) `''(optional)''`,
split_bracketed_runs_into_words = __function__(``bracket_split_runs``) `''(optional)''`,
allow_default_indicator = __boolean__,
angle_brackets_omittable = __boolean__,
allow_blank_lemma = __boolean__,
}```
`parse_indicator_spec` is a required function that takes two arguments, a string surrounded by angle brackets and the
lemma, and should return an arbitrary object containing properties describing the indicators inside of the angle
brackets). This object is often called a '''base''' and given the argument name `base` in inflection code.
`lang` is the language object for the language in question; only needed if manual translit or respelling may be present
using `//`.
`transliterate_respelling` is a function that is only needed if respelling is allowed in place of manual translit after
`//`. It takes one argument, the respelling or translit, and should return the transliteration of any respelling but
return any translit unchanged.
`split_bracketed_runs_into_words` is an optional function to split the passed-in text into words. It is used, for
example, to determine what text constitutes a word when followed by an angle-bracket spec, i.e. what the lemma to be
inflected is vs. surrounding fixed text. It takes one argument, the result of splitting the original text on brackets,
and should return alternating runs of words and split characters, or nil to apply the default algorithm. Specifically,
the value passed in is the result of calling `parse_balanced_segment_run(``text``, "[", "]")` from
[[Module:parse utilities]] on the original text, and the default version of this function calls
`split_alternating_runs(``bracketed_runs``, ``pattern``, "preserve splitchar")`, where ``bracketed_runs`` is the value
passed in and ``pattern`` splits on either spaces or hyphens (unless the text begins with a hyphen, in which case
splitting is only on spaces, so that suffixes can be inflected).
`allow_default_indicator` should be {true} if an empty indicator in angle brackets `<>` can be omitted and should be
automatically added at the end of the multiword text (if no alternants) or at the end of each alternant (if alternants
present).
`angle_brackets_omittable` should be {true} if angle brackets can be omitted around a non-empty indicator in the
presence of a blank lemma. In this case, if the combined indicator spec has no angle brackets, they will be added around
the indicator (or around all indicators, if alternants are present). This only makes sense when `allow_blank_lemma` is
specified.
`allow_blank_lemma` should be {true} of if a blank lemma is allowed; in such a case, the calling function should
substitute a default lemma, typically taken from the pagename.
The return value is a table referred to as an '''alternant multiword spec''', and is of the form
```{
alternant_or_word_specs = {``alternant_or_word_spec``, ``alternant_or_word_spec``, ...},
post_text = "``text_at_end``",
post_text_no_links = "``text_at_end_no_links``",
post_text_translit = "``translit_of_text_at_end``" `(or nil)`,
}```
where `alternant_or_word_spec` is either an '''alternant spec''' as returned by `parse_alternant()` or a
'''multiword spec''' as described in the comment above `parse_multiword_spec()`. An alternant spec looks as follows:
```{
alternants = {``multiword_spec``, ``multiword_spec``, ...},
before_text = "``text_before_alternant``",
before_text_no_links = "``text_before_alternant``",
before_text_translit = "``translit_of_text_before_alternant``" `(or nil)`,
}```
i.e. it is like what is returned by `parse_alternant()` but has extra `before_text` and `before_text_no_links` fields.
]==]
function export.parse_inflected_text(text, props)
if props.angle_brackets_omittable and not props.allow_blank_lemma then
error("If 'angle_brackets_omittable' is specified, so should 'allow_blank_lemma'")
end
local alternant_multiword_spec = {alternant_or_word_specs = {}}
local alternant_segments = split(text, "(%(%(.-%)%))")
local last_post_text, last_post_text_no_links, last_post_text_translit
for i = 1, #alternant_segments do
if i % 2 == 1 then
local segments = put.parse_balanced_segment_run(alternant_segments[i], "<", ">")
-- Disable allow_default_indicator if alternants are present and we're processing
-- the non-alternant text. Otherwise we will try to treat the non-alternant text
-- surrounding the alternants as an inflected word rather than as raw text.
local multiword_spec = parse_multiword_spec(segments, props, #alternant_segments ~= 1)
for _, word_spec in ipairs(multiword_spec.word_specs) do
table.insert(alternant_multiword_spec.alternant_or_word_specs, word_spec)
end
last_post_text = multiword_spec.post_text
last_post_text_no_links = multiword_spec.post_text_no_links
last_post_text_translit = multiword_spec.post_text_translit
else
local alternant_spec = parse_alternant(alternant_segments[i], props)
alternant_spec.before_text = last_post_text
alternant_spec.before_text_no_links = last_post_text_no_links
alternant_spec.before_text_translit = last_post_text_translit
table.insert(alternant_multiword_spec.alternant_or_word_specs, alternant_spec)
end
end
alternant_multiword_spec.post_text = last_post_text
alternant_multiword_spec.post_text_no_links = last_post_text_no_links
alternant_multiword_spec.post_text_translit = last_post_text_translit
-- Save boolean properties from `props`. We need at least `allow_default_indicator` when implementing
-- `reconstruct_original_spec()`.
alternant_multiword_spec.allow_default_indicator = props.allow_default_indicator
alternant_multiword_spec.angle_brackets_omittable = props.angle_brackets_omittable
alternant_multiword_spec.allow_blank_lemma = props.allow_blank_lemma
return alternant_multiword_spec
end
-- Inflect alternants in ``alternant_spec`` (an object as returned by parse_alternant()).
-- This sets the form values in ```alternant_spec``.forms` for all slots.
-- (If a given slot has no values, it will not be present in ```alternant_spec``.forms`).
local function inflect_alternants(alternant_spec, props)
alternant_spec.forms = {}
for _, multiword_spec in ipairs(alternant_spec.alternants) do
export.inflect_multiword_or_alternant_multiword_spec(multiword_spec, props)
iterate_slot_list_or_table(props, function(slot)
if not props.skip_slot or not props.skip_slot(slot) then
export.insert_forms(alternant_spec.forms, slot, multiword_spec.forms[slot])
end
end)
end
end
--[=[
Subfunction of `inflect_multiword_or_alternant_multiword_spec()`. This is used in building up the inflections of
multiword expressions. The basic purpose of this function is to append a set of forms representing the inflections of
a given inflected term in a given slot onto the existing forms for that slot. Given a multiword expression potentially
consisting of several inflected terms along with fixed text in between, we work iteratively from left to right, adding
the new forms onto the existing ones. Normally, all combinations of new and existing forms are created, meaning if
there are M existing forms and N new ones, we will end up with M*N forms. However, some of these combinations can be
rejected using the variant mechanism (see the description of get_variants below).
Specifically, `formtable` is a table of per-slot forms, where the key is a slot and the value is a list of form objects
(objects of the form {form=``form``, translit=``manual_translit``, footnotes=``footnotes``}). `slot` is the slot in question.
`forms` specifies the forms to be appended onto the existing forms, and is likewise a list of form objects. `props`
is the same as in `inflect_multiword_or_alternant_multiword_spec()`. `before_text` is the fixed text that goes before
the forms to be added. `before_text_no_links` is the same as `before_text` but with any links (i.e. hyperlinks of the
form [[``term``]] or [[``term``|``display``]]) converted into raw terms using remove_links() in [[Module:links]], and
`before_text_translit` is optional manual translit of `before_text_no_links`.
Note that the value "?" in a form is "infectious" in that if either the existing or new form has the value "?", the
resulting combination will also be "?". This allows "?" to be used to mean "unknown".
]=]
local function append_forms(props, formtable, slot, forms, before_text, before_text_no_links, before_text_translit)
if not forms then
return
end
local old_forms = formtable[slot] or {{form = ""}}
local ret_forms = {}
for _, old_form in ipairs(old_forms) do
for _, form in ipairs(forms) do
local old_form_vars = props.get_variants and props.get_variants(old_form.form) or ""
local form_vars = props.get_variants and props.get_variants(form.form) or ""
if old_form_vars ~= "" and form_vars ~= "" and old_form_vars ~= form_vars then
-- Reject combination due to non-matching variant codes.
else
local new_formval
local new_translit
if old_form.form == "?" or form.from == "?" then
new_formval = "?"
else
new_formval = old_form.form .. before_text .. form.form
if old_form.translit or before_text_translit or form.translit then
if not props.lang then
error("Internal error: If manual translit is given, 'props.lang' must be set")
end
if not before_text_translit then
before_text_translit = props_transliterate(props, before_text_no_links) or ""
end
local old_translit =
old_form.translit or props_transliterate(props, m_links.remove_links(old_form.form)) or ""
local translit =
form.translit or props_transliterate(props, m_links.remove_links(form.form)) or ""
new_translit = old_translit .. before_text_translit .. translit
end
end
local new_formobj
local new_footnotes = export.combine_footnotes(old_form.footnotes, form.footnotes)
if new_formval == form.form and new_translit == form.translit then
-- Automatically preserve metadata when possible.
new_formobj = m_table.shallowCopy(form)
new_formobj.footnotes = new_footnotes
else
local new_footnotes = export.combine_footnotes(old_form.footnotes, form.footnotes)
new_formobj = {form=new_formval, translit=new_translit, footnotes=new_footnotes}
if props.combine_metadata then
props.combine_metadata {
slot = slot,
dest_form = new_formobj,
form1 = old_form,
form2 = form,
between_text = before_text,
between_text_no_links = before_text_no_links,
between_text_translit = before_text_translit,
}
end
end
table.insert(ret_forms, new_formobj)
end
end
end
formtable[slot] = ret_forms
end
--[==[
Top-level inflection function. Create the inflections of a noun, verb, adjective or similar. `alternant_multiword_spec`
is as
returned by `parse_inflected_text` and describes the properties of the term to be inflected, including all the
user-provided inflection specifications (e.g. the number, gender, conjugation/declension/etc. of each word) and the
surrounding text. `props` indicates how to do the actual inflection (see below). The resulting inflected forms are
stored into the `.forms` property of `multiword_spec`. This property holds a table whose keys are slots (i.e. ID's
of individual inflected forms, such as "pres_1sg" for the first-person singular present indicative tense of a verb)
and whose values are lists of the form `{ form = ``form``, translit = ``manual_translit_or_nil``, footnotes = ``footnote_list_or_nil``}`,
where ``form`` is a string specifying the value of the form (e.g. "ouço" for the first-person singular present indicative
of the Portuguese verb [[ouvir]]); ``manual_translit_or_nil`` is the corresponding manual transliteration if needed (i.e.
if the form is in a non-Latin script and the automatic transliteration is incorrect or unavailable), otherwise nil;
and ``footnote_list_or_nil`` is a list of footnotes to be attached to the form, or nil for no footnotes. Note that
currently footnotes must be surrounded by brackets, e.g "[archaic]", and should not begin with a capital letter or end
with a period. (Conversion from "[archaic]" to "Archaic." happens automatically.)
This function has no return value, but modifies `multiword_spec` in-place, adding the `forms` table as described above.
After calling this function, call show_forms() on the `forms` table to convert the forms and footnotes given in this
table to strings suitable for display.
`props` is an object specifying properties used during inflection, as follows:
```{
slot_list = {{"``slot``", "``accel``"}, {"``slot``", "``accel``"}, ...},
slot_table = {``slot`` = "``accel``", ``slot`` = "``accel``", ...},
skip_slot = nil `or` __function__(slot),
lang = nil `or` __lang_object__,
inflect_word_spec = __function__(base),
get_variants = nil 'or` __function__(formval),
combine_metadata = nil `or` __function__(data),
include_user_specified_links = __boolean__,
}```
`slot_list` is a list of two-element lists of slots and associated accelerator tags. ``slot`` is arbitrary but should
correspond with slot names as generated by `inflect_word_spec`. ``accel`` is the corresponding accelerator tags; e.g. if
``slot`` is "pres_1sg", ``accel`` might be "1|s|pres|ind". ``accel`` is actually unused during inflection, but is used
during `show_forms()`, which takes the same `slot_list` as a property upon input.
`slot_table` is a table mapping slots to associated accelerator tags and serves the same function as `slot_list`. Only
one of `slot_list` or `slot_table` must be given. For new code it is preferable to use `slot_list` because this allows
you to control the order of processing slots, which may occasionally be important.
`skip_slot` is a function of one argument, a slot name, and should return a boolean indicating whether to skip the
given slot during inflection. It can be used, for example, to skip singular slots if the overall term being inflected
is plural-only, and vice-versa.
`lang` is a language object. This is only used to generate manual transliteration. If the language is written in the
Latin script or manual transliteration cannot be specified in the input to parse_inflected_text(), this can be omitted.
(Manual transliteration is allowed if the `lang` object is set in the `props` passed to parse_inflected_text().)
`inflect_word_spec` is the function to do the actual inflection. It is passed a single argument, which is a ``word_spec``
object describing the word to be inflected and the user-provided inflection specifications. It is exactly the same as
was returned by the `parse_indicator_spec` function provided in the `props` sent on input to `parse_inflected_text`, but
has additional fields describing the word to be inflected and the surrounding text, as follows:
```{
lemma = "``lemma``",
before_text = "``text-before-word``",
before_text_no_links = "``text-before-word-no-links``",
before_text_translit = "``manual-translit-of-text-before-word``" or nil (if no manual translit or respelling was specified in the before-text)
-- Fields as described in parse_indicator_spec()
...
}```
Here ``lemma`` is the word to be inflected as specified by the user (including any links if so given), and the
`before_text*` fields describe the raw text preceding the word to be inflected. Any other fields in this object are as
set by `parse_inflected_text`, and describe things like the gender, number, conjugation/declension, etc. as specified
by the user in the <...> spec following the word to be inflected.
`inflect_word_spec` should initialize the `.forms` property of the passed-in ``word_spec`` object to the inflected forms of
the word in question. The value of this property is a table of the same format as the `.forms` property that is
ultimately generated by inflect_multiword_or_alternant_multiword_spec() and described above near the top of this
documentation: i.e. a table whose keys are slots and whose values are lists of the form
`{ form = ``form``, translit = ``manual_translit_or_nil``, footnotes = ``footnote_list_or_nil``}`.
`get_variants` is either {nil} or a function of one argument (a string, a form value). The purpose of
this function is to ensure that in a multiword term where a given slot has more than one possible variant, the final
output has only parallel variants in it. For example, feminine nouns and adjectives in Russian have two possible
endings, one typically in -ой (-oj) and the other in -ою (-oju). If we have a feminine adjective-noun combination (or
a hyphenated feminine noun-noun combination, or similar), and we don't specify `get_variants`, we'll end up with four
values for the instrumental singular: one where both adjective and noun end in -ой, one where both end in -ою, and
two where one of the words ends in -ой and the other in -ою. In general if we have N words each with K variants, we'll
end up with an explosion of N^K possibilities. `get_variants` avoids this by returning a variant code (an arbitary
string) for each variant. If two words each have a non-empty variant code, and the variant codes disagree, the
combination will be rejected. If `get_variants` is not provided, or either variant code is an empty string, or the
variant codes agree, the combination is allowed.
The recommended way to use `get_variants` is as follows:
1. During inflection in `inflect_word_spec`, add a special character or string to each of the variants generated for a
given slot when there is more than one. (As an optimization, do this only when there is more than one word being
inflected.) Special Unicode characters can be used for this purpose, e.g. U+FFF0, U+FFF1, ..., U+FFFD, which have
no meaning in Unicode.
2. Specify `get_variants` as a function that pulls out and returns the special character(s) or string included in the
variant forms.
3. When calling show_forms(), specify a `canonicalize` function that removes the variant code character(s) or string
from each form before converting to the display form.
See [[Module:hi-verb]] and [[Module:hi-common]] for an example of doing this in a generalized fashion. (Look for
add_variant_codes(), get_variants() and remove_variant_codes().)
`combine_metadata` is a function that is invoked when combining two form objects along along with in-between text and
storing into a destination form object. When this happens, if the the form value and translit in the first form object
is empty and the in-between text is likewise empty (which regularly happens when appending the form object describing
the first word in a multiword expression to empty base text), the second form object is simply shallow-copied along with
all of its metadata, and any footnotes are combined appropriately (normally the first form object is such a case won't
have footnotes). Otherwise, a new form object is constructed by combining the form values, translit and footnotes from
the two objects and in-between text, and calling `combine_metadata` to combine any other metadata. Leave this
unspecified if there is no additional metadata or if you don't want any metadata carried over. (Examples of metadata
that should generally not be carried over are glosses of individual words, sense ID's and similar word-level properties
that can't easily be combined to generate a multiword equivalent. Examples of metadata that should be carried over and
combined are qualifiers, labels and certain boolean properties such as an uncertainty flag indicating that a given form
is uncertain. For some metadata, it is more complex; for example, if both source words have the same gender or part of
speech, the destination should keep that value, but if they differ, it may be safest to leave the field blank.) This
function, if specified, is called with a single argument as follows:
```{
slot = "__string__",
dest_form = __formobj__,
form1 = __formobj__,
form2 = __formobj__,
between_text = "__string__",
between_text_no_links = "__string__",
between_text_translit = "__string__" `or` nil
}```
Here, `slot` is the slot whose forms are being constructed. `dest_form` is the destination form object into which the
combined metadata should be written, and is pre-populated with appropriate `form`, `translit` and `footnotes` fields.
`form1` and `form2` are the two source forms being combined, and `between_text` is the text to be inserted between the
two source forms. `between_text_no_links` is the same as `between_text` but with double-bracket links removed, and
`between_text_translit` is the manual transliteration of `between_text_no_links`, if specified. The function should
return nothing, but should side-effect `dest_form` as appropriate.
`include_user_specified_links`, if given, ensures that user-specified links in the raw text surrounding a given word
are preserved in the output. If omitted or set to false, such links will be removed and the whole multiword expression
will be linked.
]==]
function export.inflect_multiword_or_alternant_multiword_spec(multiword_spec, props)
multiword_spec.forms = {}
local is_alternant_multiword = not not multiword_spec.alternant_or_word_specs
for _, word_spec in ipairs(is_alternant_multiword and multiword_spec.alternant_or_word_specs or multiword_spec.word_specs) do
if word_spec.alternants then
inflect_alternants(word_spec, props)
else
props.inflect_word_spec(word_spec)
end
iterate_slot_list_or_table(props, function(slot)
if not props.skip_slot or not props.skip_slot(slot) then
append_forms(props, multiword_spec.forms, slot, word_spec.forms[slot],
(rfind(slot, "linked") or props.include_user_specified_links) and
word_spec.before_text or word_spec.before_text_no_links,
word_spec.before_text_no_links, word_spec.before_text_translit
)
end
end)
end
if multiword_spec.post_text ~= "" then
local pseudoform = {{form=""}}
iterate_slot_list_or_table(props, function(slot)
-- If slot is empty or should be skipped, don't try to append post-text.
if (not props.skip_slot or not props.skip_slot(slot)) and multiword_spec.forms[slot] then
append_forms(props, multiword_spec.forms, slot, pseudoform,
(rfind(slot, "linked") or props.include_user_specified_links) and
multiword_spec.post_text or multiword_spec.post_text_no_links,
multiword_spec.post_text_no_links, multiword_spec.post_text_translit
)
end
end)
end
end
function export.map_word_specs(alternant_multiword_spec, fun)
for _, alternant_or_word_spec in ipairs(alternant_multiword_spec.alternant_or_word_specs) do
if alternant_or_word_spec.alternants then
for _, multiword_spec in ipairs(alternant_or_word_spec.alternants) do
for _, word_spec in ipairs(multiword_spec.word_specs) do
fun(word_spec)
end
end
else
fun(alternant_or_word_spec)
end
end
end
function export.create_footnote_obj()
return {
notes = {},
seen_notes = {},
noteindex = 1,
seen_refs = {},
}
end
function export.get_footnote_text(footnotes, footnote_obj)
if not footnotes then
return ""
end
local link_indices = {}
local all_refs = {}
for _, footnote in ipairs(footnotes) do
local refs
footnote, refs = export.expand_footnote_or_references(footnote)
if footnote then
local this_noteindex = footnote_obj.seen_notes[footnote]
if not this_noteindex then
-- Generate a footnote index.
this_noteindex = footnote_obj.noteindex
footnote_obj.noteindex = footnote_obj.noteindex + 1
table.insert(footnote_obj.notes, '<sup style="color: var(--wikt-palette-red, red)">' .. this_noteindex .. '</sup>' .. footnote)
footnote_obj.seen_notes[footnote] = this_noteindex
end
m_table.insertIfNot(link_indices, this_noteindex)
end
if refs then
for _, ref in ipairs(refs) do
if not ref.name then
local this_refhash = footnote_obj.seen_refs[ref.text]
if not this_refhash then
-- Different text needs to have different auto-generated names, globally across the entire page,
-- including across different invocations of {{it-verb}} or {{it-conj}}. The easiest way to accomplish
-- this is to use a message-digest hashing function. It does not have to be cryptographically secure
-- (MD5 is insecure); it just needs to have low probability of collisions.
this_refhash = mw.hash.hashValue("md5", ref.text)
footnote_obj.seen_refs[ref.text] = this_refhash
end
ref.autoname = this_refhash
end
-- I considered using "n" as the default group rather than nothing, to more clearly distinguish regular
-- footnotes from references, but this requires referencing group "n" as <references group="n"> below,
-- which is non-obvious.
m_table.insertIfNot(all_refs, ref)
end
end
end
table.sort(link_indices)
local function sort_refs(r1, r2)
-- FIXME, we are now sorting on an arbitrary hash. Should we keep track of the order we
-- saw the autonamed references and sort on that?
if r1.autoname and r2.name then
return true
elseif r1.name and r2.autoname then
return false
elseif r1.name and r2.name then
return r1.name < r2.name
else
return r1.autoname < r2.autoname
end
end
table.sort(all_refs, sort_refs)
for i, ref in ipairs(all_refs) do
local refargs = {name = ref.name or ref.autoname, group = ref.group}
all_refs[i] = mw.getCurrentFrame():extensionTag("ref", ref.text, refargs)
end
local link_text
if #link_indices > 0 then
link_text = '<sup style="color: var(--wikt-palette-red, red)">' .. table.concat(link_indices, ",") .. '</sup>'
else
link_text = ""
end
local ref_text = table.concat(all_refs)
if link_text ~= "" and ref_text ~= "" then
return link_text .. "<sup>,</sup>" .. ref_text
else
return link_text .. ref_text
end
end
--[==[
Add links around words in a term. If multiword_only, do it only in multiword terms.
]==]
function export.add_links(form, multiword_only)
if form == "" or form == " " then
return form
end
if not form:find("%[%[") then
if rfind(form, "[%s%p]") then --optimization to avoid loading [[Module:headword]] on single-word forms
local m_headword = require("Module:headword")
if m_headword.head_is_multiword(form) then
form = m_headword.add_multiword_links(form)
end
end
if not multiword_only and not form:find("%[%[") then
form = "[[" .. form .. "]]"
end
end
return form
end
--[==[
Remove redundant link surrounding entire term.
]==]
function export.remove_redundant_links(term)
return rsub(term, "^%[%[([^%[%]|]*)%]%]$", "%1")
end
--[==[
Add links to all before and after text; for use in inflection modules that preserve links in multiword lemmas and
include links in non-lemma forms rather than allowing the entire form to be a link. If `remember_original`, remember
the original user-specified before/after text so we can reconstruct the original spec later. `add_links` is a
function of one argument to add links to a given piece of text; if unspecified, it defaults to `export.add_links`.
]==]
function export.add_links_to_before_and_after_text(alternant_multiword_spec, remember_original, add_links)
add_links = add_links or export.add_links
local function add_links_remember_original(object, field)
if remember_original then
object["user_specified_" .. field] = object[field]
end
object[field] = add_links(object[field])
end
for _, alternant_or_word_spec in ipairs(alternant_multiword_spec.alternant_or_word_specs) do
add_links_remember_original(alternant_or_word_spec, "before_text")
if alternant_or_word_spec.alternants then
for _, multiword_spec in ipairs(alternant_or_word_spec.alternants) do
for _, word_spec in ipairs(multiword_spec.word_specs) do
add_links_remember_original(word_spec, "before_text")
end
add_links_remember_original(multiword_spec, "post_text")
end
end
end
add_links_remember_original(alternant_multiword_spec, "post_text")
end
--[==[
Reconstruct the original overall spec from the output of parse_inflected_text(), so we can use it in the
language-specific acceleration module in the implementation of {{tl|pt-verb form of}} and the like. `props` is an
optional table of properties. Currently only `preprocess_angle_bracket_spec` is recognized, and is an optional function
of one argument that is called to process an angle-bracket spec before inserting into the reconstructed spec.
]==]
function export.reconstruct_original_spec(alternant_multiword_spec, props)
local parts = {}
props = props or {}
local function ins(txt)
table.insert(parts, txt)
end
local function insert_angle_bracket_spec(spec)
if props.preprocess_angle_bracket_spec then
spec = props.preprocess_angle_bracket_spec(spec)
end
ins(spec)
end
for _, alternant_or_word_spec in ipairs(alternant_multiword_spec.alternant_or_word_specs) do
ins(alternant_or_word_spec.user_specified_before_text)
if alternant_or_word_spec.alternants then
ins("((")
for i, multiword_spec in ipairs(alternant_or_word_spec.alternants) do
if i > 1 then
ins(",")
end
for _, word_spec in ipairs(multiword_spec.word_specs) do
ins(word_spec.user_specified_before_text)
ins(word_spec.user_specified_lemma)
insert_angle_bracket_spec(word_spec.angle_bracket_spec)
end
ins(multiword_spec.user_specified_post_text)
end
ins("))")
else
ins(alternant_or_word_spec.user_specified_lemma)
insert_angle_bracket_spec(alternant_or_word_spec.angle_bracket_spec)
end
end
ins(alternant_multiword_spec.user_specified_post_text)
local retval = table.concat(parts)
if alternant_multiword_spec.allow_default_indicator then
-- As a special case, if we see e.g. "amar<>", remove the <>. Don't do this if there are spaces or alternants.
if not retval:find(" ") and not retval:find("%(%(") then
local retval_no_angle_brackets = retval:match("^(.*)<>$")
if retval_no_angle_brackets then
return retval_no_angle_brackets
end
end
end
return retval
end
--[==[
Convert the forms in ``formtable`` (a form table, whose keys are slots and whose values are lists of form objects, each
of which is a table of the form `form = ``form``, translit = ``manual_translit_or_nil``, footnotes = ``footnote_list_or_nil``, no_accel = ``true_to_suppress_accelerators``, ... `)
into strings. The form table is side-effected. Each form list turns into a string consisting of a comma-separated list
of linked forms, with accelerators (unless `no_accel` is set in a given form object). If `include_translit` is
specified, each string consists of a comma-separated list of form values (each formatted as a link), an HTML
`<br/>`, and a comma-separated list of transliterations. `props` is a table used in generating the strings, as
follows:
```{
lang = __lang_object__,
lemmas = {"``lemma``", "``lemma``", ...},
slot_list = {{"``slot``", "``accel``"}, {"``slot``", "``accel``"}, ...},
slot_table = {``slot`` = "``accel``", ``slot`` = "``accel``", ...},
include_translit = __boolean__,
create_footnote_obj = nil `or` __function__(),
canonicalize = nil or __function__(formval),
preprocess_forms = nil `or` __function__(data),
no_deduplicate_forms = __boolean__,
combine_metadata_during_dedup = nil `or` __function__(data),
transform_accel_obj = nil `or` __function__(slot, form, accel_obj),
format_forms = nil `or` __function__(data),
generate_link = nil `or` __function__(data),
format_tr = nil `or` __function__(data),
join_spans = nil `or` __function__(data),
allow_footnote_symbols = __boolean__,
footnotes = nil or {"``extra_footnote``", "``extra_footnote``", ...},
}```
`lemmas` is the list of lemmas, used in the accelerators.
`slot_list` is a list of two-element lists of slots and associated accelerator tag sets. ``slot`` should correspond
to slots generated during `inflect_multiword_or_alternant_multiword_spec()`. ``accel`` is the corresponding accelerator
tag set; e.g. if ``slot`` is "pres_1sg", ``accel`` might be "1|s|pres|ind". ``accel`` is used in generating entries for
accelerator support (see [[WT:ACCEL]]).
`slot_table` is a table mapping slots to associated accelerator tag sets and serves the same function as `slot_list`.
Only one of `slot_list` or `slot_table` must be given. For new code it is preferable to use `slot_list` because this
allows you to control the order of processing slots, which may occasionally be important.
`include_translit`, if given, causes transliteration to be included in the generated strings.
The function works as follows:
# Create an object to hold footnotes (customizable using `create_footnote_obj`).
# Generate the comma-separated lemma form values and store in `.lemma` in the form table.
# Loop over the slots specified using `slot_list` or `slot_table`. For each slot:
## Canonicalize the form values (customizable using `canonicalize`; by default does nothing).
## Preprocess the forms (customizable using `preprocess_forms`; by default does nothing).
## Unless `no_deduplicate_forms` is set, deduplicate forms in a slot sharing the same form value but possibly different
transliteration. (This happens e.g. in Russian, where it is relatively common for a given form to have two possible
transliterations, one reflecting a more nativized pronunciation where Cyrillic е triggers palatalization of the
preceding consonant, and one reflecting a more "foreign" pronunciation where this palatalization does not happen. In
such a case, the automatic transliteration would normally suffice for the more nativized pronunciation but the more
"foreign" pronunciation will need manual transliteration.) As part of deduplication, footnotes will be combined using
`combine_footnotes`; distinct manual transliterations will be combined into a list (meaning the `translit` field of
form objects in some subsequent `props` functions may hold a list; this will be noted when possible); and any
remaining metadata will be combined using the `combine_metadata_during_dedup` method, if provided.
## Add acceleration to all forms. The acceleration tag set associated with a given form comes from `slot_list` or
`slot_table`, i.e. all forms in a given slot have the same tag set. However, different forms will have different
associated transliterations stored into the accelerator object associated with the form, as well as possibly
different lemmas. In particular, when there are multiple lemma forms, this is often due to alternative ways to
pronounce the lemma (e.g. alternative stress positions or vowel lengths), and there are often associated non-lemma
forms that match each lemma. An example given in the introduction is {{m+|uk|русин||Rusyn}}, stressed in the lemma
as ''ру́син'' or ''руси́н'' with associated genitive singulars ''ру́сина'' and ''русина́''. We would like the
auto-generated accelerator entry for {{m|uk|русина}} to show the variant ''ру́сина'' as having lemma ''ру́син'' and
the variant ''русина́'' as having the lemma ''руси́н'', rather than showing both variants as having both lemmas,
which is less accurate. As a result, the code that generates acceleration objects for forms matches up forms and
lemmas one-to-one if possible. If this is not possible, the matching is usually one lemma to many forms, as in
{{m+|uk|міст||bridge}} with genitive singular ''мо́сту'' or ''моста́'' (in which case all forms get the same lemma), or
many lemmas to one form, as in {{m+|uk|черга||turn, queue}} stressed either ''че́рга'' or ''черга́'' with nominative
singular only ''че́рги'' (in which case the single form gets assicated all lemmas). If there are multiple lemmas and
multiple forms, the algorithm attempts to align them as evenly as possible (e.g. two lemma variants to four forms
means the first two forms get assigned the first lemma variant and the last two forms get assigned the second lemma
variant); this is often going to be incorrect, but (a) there's unlikely to be a single algorithm that works in all
such circumstances, and (b) these cases are very rare. Finally, note the following:
##* No acceleration is assigned to a form if any of the following apply: (a) there are no lemmas given in
`props.lemmas`; (b) the `no_accel` key in the form object has a non-falsy value; (c) the form value of the form is
{"?"} or an em-dash ({"—"}); (d) the accelerator tag set is given as a hyphen {"-"}); or (e) the form value contains
an internal link.
##* The accelerator code sets the `formval_for_link` key in each form object to the version of the form value that
should be passed to `full_link()` in [[Module:links]]. This is usually the same as the passed-in form value, but
differs when `props.allow_footnote_symbols` is specified and an old-style footnote symbol is attached to the form
(the removed footnote symbol is stored in the `formval_old_style_footnote_symbol` key), and also differs when the
entire form value is surrounded with a redundant internal link (which is removed).
##* The resulting accelerator object can be modified (or replaced entirely) by the `transform_accel_obj` function. This
is used, for example, in [[Module:es-verb]], [[Module:pt-verb]] and other Romance-language verb conjugation modules
(likewise [[Module:ar-verb]]) to replace the tag set with the original verb spec used to generate the verb, so that
the accelerator code can generate the appropriate call to {{tl|es-verb form of}}, {{tl|pt-verb form of}} or the
like, which computes the inflections, instead of directly listing the inflections.
## Format the forms into strings. The entire default process can be replaced using `format_forms`; otherwise the default
algorithm works as follows:
### Generate the '''form value spans''', with one entry (a linked HTML-ized version of the form value) per form. This
can be customized using `generate_link`. (Various modules do this. For example, the Arabic verb module includes
qualifiers, labels, ID's and the like that can be specified by the user; the Portuguese and reintegrated Galician
verb modules italicize certain superseded or otherwise less-desirable forms instead of linking them normally; the
German verb module adds {{m|de|dass}} to subjunctive forms and optional pronouns to imperative forms; and the German
adjective module adds articles to adjective forms normally accompanied by articles and the equivalent of "he/she is"
etc. to predicate forms.) The default uses `full_link()` in [[Module:links]] (with transliteration generation
disabled) concatenated with the appropriate footnote symbol(s) (if any).
### Generate the '''transliteration spans''', with one entry per distinct translit, auto-generated if manual translit
isn't available. Note that, due to the earlier form value deduplication step, there may be multiple translits per
form object. These translits are themselves deduplicated to get the list of spans. (Such duplication can happen, for
example, in Arabic with terms containing a glottal stop in them; there may be multiple ways of spelling the glottal
stop or ''hamza'' in Arabic, but only one way of transliterating it.) Each span consists of an object specifying
the translit minus any attached old-style footnote symbols (which are only allowed if
`props.allow_footnote_symbols` is set); the attached old-style footnote symbol, which is always an empty string when
`props.allow_footnote_symbols` is not set; and the list of (new-style) footnotes. These objects are then converted
to formatted strings, either using `format_tr` if supplied or else calling `tag_translit()` in
[[Module:script utilities]] and concatenating the appropriate footnote symbol(s) (if any).
### Combine the form value and transliteration spans. If `join_spans` is supplied, use it; otherwise, concatenate the
form value spans (comma-separated) and (if available) transliteration spans (comma-separated), and (if appropriate)
combine them using {<br />}.
`create_footnote_obj` is an optional function of no arguments to create the footnote object used to track footnotes;
see `create_footnote_obj()`. Customizing it is useful to prepopulate the footnote table using `get_footnote_text()`.
`canonicalize` is an optional function of one argument (a form value) to canonicalize each form before processing; it
can return nil for no change. The most common purpose of this function is to remove variant codes from the form value.
See the documentation for `inflect_multiword_or_alternant_multiword_spec()` for a description of variant codes and their
purpose.
`preprocess_forms` is an optional function of one argument (a table of properties) to preprocess the form objects as
a whole. It runs after `canonicalize` (meaning that the form values passed in are canonicalized) and before
deduplication and the addition of acceleration info. The property table passed in has the following properties:
* `slot`: The slot being processed.
* `forms`: The list of form objects for this slot.
* `accel_tag_set`: The accelerator tag set for this slot, taken from `slot_list` or `slot_table`.
* `footnote_obj`: The footnote object returned by the `create_footnote_obj` property or the default
`create_footnote_obj()` function.
`preprocess_forms` should return a list of preprocessed form objects, or {nil} to use the passed-in `forms`. If this
function does deduplication, you should set `no_deduplicate_forms` to disable the default deduplication process.
`no_deduplicate_forms`, if set, disables the deduplication step (see above).
`combine_metadata_during_dedup` is an optional function of one argument (a table of properties) to combine the metadata
of deduplicated form objects. The property table passed in has the following properties:
* `slot`: The slot being processed.
* `existing_form`: The existing form object into which a duplicated form is being combined.
* `dup_form`: The duplicated form being combined into `existing_form`.
* `existing_form_pos`: The one-based position of the existing form in the deduplicated form list (not necessarily its
original position).
* `dup_form_pos`: The one-based position of the duplicated form in its original list.
The following should be noted about the form objects passed in:
# The form values in `.form` have been canonicalized using `.canonicalize`, if provided.
# The form values in `existing_form` and `dup_form` are always the same.
# The footnotes in `existing_form` have already been combined with those in `dup_form`.
# If there was manual translit either in `existing_form` (prior to deduplication) or in `dup_form`, there will be
manual translit in `existing_form.translit` that is a list and combines any previous accumulated translits in
`existing_form` as well as the translit in `dup_form` (even if one of them was specified as {nil} indicating an
automatic translit). This means that the translit in `existing_form.translit` is always either {nil} or a list of
strings (and the same applies to `dup_form.translit`).
`transform_accel_obj` is an optional function of three arguments (``slot``, ``formobj``, ``accel_obj``) to transform the
default constructed accelerator object in ``accel_obj`` into an object that should be passed to `full_link()` in
[[Module:links]]. It should return the new accelerator object, or {nil} for no acceleration. (If {nil} is returned,
the corresponding form has no acceleration; this is unlike most customization functions, where returning {nil} causes
the default algorithm to be invoked.) The function can destructively modify the accelerator object passed in.
'''NOTE''': This is called even when the passed-in ``accel_obj`` is {nil} (see the (a) through (e) reasons above why no
acceleration may be assigned to a form). Thus, your code needs to do something sensible in this case. The description
above of how `show_forms()` works inclues various examples of modules that supply a `transform_accel_obj` function and
the reasons for doing so.
`format_forms`, if supplied, is a function that entirely replaces the formatting portion of `show_forms()`. An example
of why you might want to do this is to get a different layout than the default, e.g. one where translit is displayed
next to each form value instead of the form values and translits grouped and displayed on separate lines. Under normal
circumstances, you should not do this, but instead customize the functions that replace specific parts of the default
formatting algorithm (see below). This function is passed one argument (a table of properties) and should return a
string (the formatted forms, ready to store into the slot in the form table) or {nil} to proceed with the default
algorithm (see above). The property table passed in has the following properties:
* `slot`: The slot being processed.
* `forms`: The list of form objects, deduplicated and with accelerator info added.
* `footnote_obj`: The footnote object returned by the `create_footnote_obj` property or the default
`create_footnote_obj()` function.
The following should be noted about the form objects in `forms`:
# There are extra fields `formval_for_link`, `formval_old_style_footnote_symbol` and `accel_obj`. The first two are as
described above under the paragraph beginning "Add acceleration to all forms" under "The function works as follows".
The third one is the accelerator object in the format expected by [[Module:links]].
# The `translit` field, if non-{nil}, is a list of transliterations rather than a single transliteration; this is due to
the form value deduplication step.
`generate_link` is an optional function to generate the link text for a given form value. It is passed a single argument
(a table of properties) and should return a string, the formatted link. If it returns {nil}, the default algorithm (see
above) is invoked. The property table passed in has the following properties:
* `slot`: The slot being processed.
* `form`: The form to be converted to a formatted link. As with the `format_forms` function described above, the form
objects passed in contain extra fields `formval_for_link`, `formval_old_style_footnote_symbol` and `accel_obj` (all
of which will normally be used), and the `translit` field, if non-{nil}, is a list.
* `pos`: The one-based position of the form being processed, in the list of form value spans. Rarely used.
* `footnote_obj`: The footnote object returned by the `create_footnote_obj` property or the default
`create_footnote_obj()` function. Normally used in order to get the (new-style) footnote symbol associated with any
footnotes in `footnotes`.
The description above of how `show_forms()` works inclues various examples of modules that supply a `generate_link`
function and the reasons for doing so.
`format_tr` is an optional function to generate the formatted text for a given transliteration. It is passed a single
argument (a table of properties) and should return a string, the formatted transliteration text. If it returns {nil},
the default algorithm (see above) is invoked. The property table passed in has the following properties:
* `slot`: The slot being processed.
* `tr_for_tag`: The transliteration to process, where old-style footnote symbols have been removed.
* `old_style_footnote_symbol`: The removed old-style footnote symbol, or a blank string if no symbol was removed.
* `pos`: The one-based position of the transliteration being processed, in the list of transliteration spans. Rarely
used.
* `footnotes`: The list of footnotes associated with all form objects with this transliteration. (If there were multiple
form objects with the same transliteration, the list of footnotes will have been generated using
`combine_footnotes()`.)
* `footnote_obj`: The footnote object returned by the `create_footnote_obj` property or the default
`create_footnote_obj()` function. Normally used in order to get the (new-style) footnote symbol associated with any
footnotes in `footnotes`.
`join_spans` is an optional function to join the processed form value and transliteration spans into a formatted string.
It is passed a single argument (a table of properties) and should return the final string to store into the form table
slot. If it returns {nil}, the default algorithm (see above) is invoked. The property table passed in has the following
properties:
* `slot`: The slot being processed.
* `formval_spans`: A list of strings, the formatted form value spans.
* `tr_spans`: A list of strings, the formatted transliteration spans. If there is no transliteration, this will be an
empty list.
A custom `join_spans` is provided by [[Module:de-verb]], which concatenates the form value spans vertically (using
{"<br />"}) instead of horizontally using a comma, as is normal; this is because there is no translit and the form
values are often long, containing extra words attached during `generate_link()`. The only exception is the `aux` slot
holding the auxiliaries, which is concatenated horizontally using {" or "}. [[Module:de-adjective]] similarly provides
a custom `join_spans` function that concatenates the form value spans vertically.
`allow_footnote_symbols`, if given, causes any old-style footnote symbols attached to forms (e.g. numbers, asterisk) to
be separated off, placed outside the links, and superscripted. In this case, `footnotes` should be a list of footnotes
(preceded by footnote symbols, which are superscripted). These footnotes are combined with any footnotes found in the
forms and placed into `forms.footnotes`. This mechanism of specifying footnotes is provided for backward compatibility
with certain existing inflection modules and should not be used for new modules. Instead, use the regular footnote
mechanism specified using the `footnotes` property attached to each form object.
]==]
function export.show_forms(formtable, props)
local footnote_obj = props.create_footnote_obj and props.create_footnote_obj() or export.create_footnote_obj()
local function fetch_formval_and_translit(entry, remove_links)
local formval, translit
if type(entry) == "table" then
formval, translit = entry.form, entry.translit
else
formval = entry
end
if remove_links then
formval = m_links.remove_links(formval)
end
return formval, translit
end
local lemma_formvals = {}
for _, lemma in ipairs(props.lemmas) do
local lemma_formval, _ = fetch_formval_and_translit(lemma)
m_table.insertIfNot(lemma_formvals, lemma_formval)
end
formtable.lemma = #lemma_formvals > 0 and table.concat(lemma_formvals, ", ") or
mw.loadData(headword_data_module).pagename
-- For safety, since we in-place modify `lemmas` usually before processing a given slot, make a copy.
local props_lemmas = m_table.shallowCopy(props.lemmas)
for i, lemma in ipairs(props_lemmas) do
props_lemmas[i] = m_table.shallowCopy(lemma)
end
local function do_slot(slot, accel_tag_set)
local formobjs = formtable[slot]
if formobjs then
if type(formobjs) ~= "table" then
error("Internal error: For slot '" .. slot .. "', expected table but saw " .. dump(formobjs))
end
-- Maybe canonicalize the form values (e.g. remove variant codes and monosyllabic accents).
if props.canonicalize then
for _, form in ipairs(formobjs) do
form.form = props.canonicalize(form.form) or form.form
end
end
-- Preprocess the forms as a whole if called for.
if props.preprocess_forms then
formobjs = props.preprocess_forms {
slot = slot,
forms = formobjs,
accel_tag_set = accel_tag_set,
footnote_obj = footnote_obj,
} or formobjs
end
-- Maybe deduplicate form values (happens e.g. in Russian with two terms with the same Russian form but
-- different translits).
if not props.no_deduplicate_forms then
local deduped_formobjs = {}
for i, form in ipairs(formobjs) do
local function combine_forms(existing_form, dup_form, pos)
assert(existing_form.form == dup_form.form)
-- Combine footnotes.
existing_form.footnotes = export.combine_footnotes(existing_form.footnotes, dup_form.footnotes)
-- If translit is being generated, and there's manual translit associated with either form, we
-- need to generate any missing translits and combine them, taking into account the fact that a
-- translit value may actually be a list of translits (particularly with the existing form if we
-- already combined an item with manual translit into it).
if props.include_translit and form_value_transliterable(existing_form.form) and (
existing_form.translit or dup_form.translit) then
local combined_translit
if not existing_form.translit then
combined_translit = {
props_transliterate(props, m_links.remove_links(existing_form.form))
}
elseif type(existing_form.translit) == "string" then
combined_translit = {existing_form.translit}
else
combined_translit = existing_form.translit
end
local dup_form_translit = dup_form.translit
if not dup_form_translit then
-- dup_form.form is the same as existing_form.form (see assert above), but this is
-- defensive programming in case that changes
dup_form_translit = {props_transliterate(props, m_links.remove_links(dup_form.form))}
elseif type(dup_form_translit) == "string" then
dup_form_translit = {dup_form_translit}
end
for _, translit in ipairs(dup_form_translit) do
m_table.insertIfNot(combined_translit, translit)
end
existing_form.translit = combined_translit
end
if props.combine_metadata_during_dedup then
props.combine_metadata_during_dedup {
slot = slot,
existing_form = existing_form,
existing_form_pos = pos,
dup_form = dup_form,
dup_form_pos = i,
}
end
end
m_table.insertIfNot(deduped_formobjs, form, {
key = function(form) return form.form end,
combine = combine_forms,
})
end
formobjs = deduped_formobjs
end
-- Add acceleration info to form objects.
for i, form in ipairs(formobjs) do
local formval = form.form
if not form_value_transliterable(formval) then
form.formval_for_link = formval
form.formval_old_style_footnote_symbol = ""
else
local formval_for_link, formval_old_style_footnote_symbol
if props.allow_footnote_symbols then
formval_for_link, formval_old_style_footnote_symbol =
require(table_tools_module).get_notes(formval)
if formval_old_style_footnote_symbol ~= "" then
track("old-style-footnote-symbol")
end
else
formval_for_link = formval
formval_old_style_footnote_symbol = ""
end
-- remove redundant link surrounding entire form
formval_for_link = export.remove_redundant_links(formval_for_link)
form.formval_for_link = formval_for_link
form.formval_old_style_footnote_symbol = formval_old_style_footnote_symbol
-------------------- Compute the accelerator object. -----------------
local accel_obj
-- Check if form still has links; if so, don't add accelerators because the resulting entries will
-- be wrong.
if props_lemmas[1] and not form.no_accel and accel_tag_set ~= "-" and
not rfind(formval_for_link, "%[%[") then
-- If there is more than one form or more than one lemma, things get tricky. Often, there are
-- the same number of forms as lemmas, e.g. for Ukrainian [[зимовий]] "wintry; winter (rel.)",
-- which can be stressed зимо́вий or зимови́й with corresponding masculine/neuter genitive
-- singulars зимо́вого or зимово́го etc. In this case, usually the forms and lemmas match up so
-- we do this. If there are different numbers of forms than lemmas, it's usually one lemma
-- against several forms e.g. Ukrainian [[міст]] "bridge" with genitive singular мо́сту or моста́
-- (accent patterns b or c) or [[ложка|ло́жка]] "spoon" with nominative plural ло́жки or ложки́
-- (accent patterns a or c). Here, we should assign the same lemma to both forms. The opposite
-- can happen, e.g. [[черга]] "turn, queue" stressed че́рга or черга́ with nominative plural only
-- че́рги (accent patterns a or d). Here we should assign both lemmas to the same form. In more
-- complicated cases, with more than one lemma and form and different numbers of each, we try
-- to align them as much as possible, e.g. if there are somehow eight forms and three lemmas,
-- we assign lemma 1 to forms 1-3, lemma 2 to forms 4-6 and lemma 3 to forms 7 and 8, and
-- conversely if there are somehow three forms and eight lemmas. This is likely to be wrong, but
-- (a) there's unlikely to be a single algorithm that works in all such circumstances, and (b)
-- these cases are vanishingly rare or nonexistent. Properly we should try to remember which
-- form was generated by which lemma, but that is significant extra work for little gain.
local first_lemma, last_lemma
if #formobjs >= #props_lemmas then
-- More forms than lemmas. Try to even out the forms assigned per lemma.
local forms_per_lemma = math.ceil(#formobjs / #props_lemmas)
first_lemma = math.floor((i - 1) / forms_per_lemma) + 1
last_lemma = first_lemma
else
-- More lemmas than forms. Try to even out the lemmas assigned per form.
local lemmas_per_form = math.ceil(#props_lemmas / #formobjs)
first_lemma = (i - 1) * lemmas_per_form + 1
last_lemma = math.min(first_lemma + lemmas_per_form - 1, #props_lemmas)
end
local accel_lemma, accel_lemma_translit
if first_lemma == last_lemma then
accel_lemma, accel_lemma_translit =
fetch_formval_and_translit(props_lemmas[first_lemma], "remove links")
else
accel_lemma = {}
accel_lemma_translit = {}
for j=first_lemma, last_lemma do
local this_lemma = props_lemmas[j]
local this_accel_lemma, this_accel_lemma_translit =
fetch_formval_and_translit(props_lemmas[j], "remove links")
-- Do not use table.insert() especially for the translit because it may be nil and in
-- that case we want gaps in the array.
accel_lemma[j - first_lemma + 1] = this_accel_lemma
accel_lemma_translit[j - first_lemma + 1] = this_accel_lemma_translit
end
end
local accel_translit
if props.include_translit and form.translit then
if type(form.translit) == "table" then
accel_translit = table.concat(form.translit, ", ")
elseif type(form.translit) == "string" then
accel_translit = form.translit
else
error(("Internal error: For slot '%s', form translit is not a table or string: %s"):
format(slot, dump(accel_translit)))
end
end
accel_obj = {
form = accel_tag_set,
translit = accel_translit,
lemma = accel_lemma,
lemma_translit = props.include_translit and accel_lemma_translit or nil,
}
end
-- Postprocess if requested.
if props.transform_accel_obj then
accel_obj = props.transform_accel_obj(slot, form, accel_obj)
end
form.accel_obj = accel_obj
end
end
-- Format the form objects into a string for insertion into the table.
local formatted_forms
if props.format_forms then
formatted_forms = props.format_forms {
slot = slot,
forms = forms,
footnote_obj = footnote_obj,
}
end
if not formatted_forms then
-- Default algorithm: Separate form values and translits and concatenate on separate lines.
-- Form values have already been deduplicated but we may need to deduplicate translits (this happens
-- e.g. in Arabic where there may be multiple ways of spelling a hamza in the Arabic script but only
-- one way in transliteration).
local formval_spans = {}
local tr_spans = {}
for i, form in ipairs(formobjs) do
local link
if props.generate_link then
link = props.generate_link {
slot = slot,
pos = i,
form = form,
footnote_obj = footnote_obj,
}
end
if not link then
link = m_links.full_link {
lang = props.lang, term = form.formval_for_link, tr = "-", accel = form.accel_obj
} .. form.formval_old_style_footnote_symbol ..
export.get_footnote_text(form.footnotes, footnote_obj)
end
formval_spans[i] = link
if props.include_translit then
-- Note that if there is an attached old-style footnote symbol, we transliterate it.
local translits = form.translit or props_transliterate(props, m_links.remove_links(form.form))
if type(translits) == "string" then
translits = {translits}
end
for _, tr in ipairs(translits) do
local tr_for_tag, tr_old_style_footnote_symbol
if props.allow_footnote_symbols then
tr_for_tag, tr_old_style_footnote_symbol = require(table_tools_module).get_notes(tr)
if tr_old_style_footnote_symbol ~= "" then
track("old-style-footnote-symbol")
end
else
tr_for_tag = tr
tr_old_style_footnote_symbol = ""
end
m_table.insertIfNot(tr_spans, {
tr_for_tag = tr_for_tag,
old_style_footnote_symbol = tr_old_style_footnote_symbol,
footnotes = form.footnotes,
}, {
key = function(trobj) return trobj.tr_for_tag end,
combine = function(tr, newtr)
-- Combine footnotes.
tr.footnotes = export.combine_footnotes(tr.footnotes, newtr.footnotes)
tr.old_style_footnote_symbol = tr.old_style_footnote_symbol ..
newtr.old_style_footnote_symbol
end,
})
end
end
end
for i, tr_span in ipairs(tr_spans) do
local formatted_tr
if props.format_tr then
formatted_tr = props.format_tr {
slot = slot,
pos = i,
tr_for_tag = tr_span.tr_for_tag,
old_style_footnote_symbol = tr_span.old_style_footnote_symbol,
footnotes = tr_span.footnotes,
footnote_obj = footnote_obj,
}
end
if not formatted_tr then
formatted_tr = require(script_utilities_module).tag_translit(tr_span.tr_for_tag, props.lang,
"default", " style=\"color: var(--wikt-palette-grey-8,#888);\"") .. tr_span.old_style_footnote_symbol ..
export.get_footnote_text(tr_span.footnotes, footnote_obj)
end
tr_spans[i] = formatted_tr
end
if props.join_spans then
formatted_forms = props.join_spans {
slot = slot,
formval_spans = formval_spans,
tr_spans = tr_spans,
}
end
if not formatted_forms then
local formval_span = table.concat(formval_spans, ", ")
local tr_span
if #tr_spans > 0 then
tr_span = table.concat(tr_spans, ", ")
end
if tr_span then
formatted_forms = formval_span .. "<br />" .. tr_span
else
formatted_forms = formval_span
end
end
end
formtable[slot] = formatted_forms
else
formtable[slot] = "—"
end
end
iterate_slot_list_or_table(props, do_slot)
local all_notes = footnote_obj.notes
if props.footnotes then
for _, note in ipairs(props.footnotes) do
track("old-style-footnote-symbol")
local symbol, entry = require(table_tools_module).get_initial_notes(note)
table.insert(all_notes, symbol .. entry)
end
end
formtable.footnote = table.concat(all_notes, "<br />")
end
return export
sq49yezgupz8m0onxhq1n2fc9pc6vg7
মডিউল:ar-pronunciation
828
52319
509632
278924
2026-06-03T12:23:55Z
Redmin
6857
[[en:Module:ar-pronunciation|ইংরেজি উইকিঅভিধান]] থেকে হালনাগাদ করলাম
509632
Scribunto
text/plain
local export = {}
local m_str_utils = require("Module:string utilities")
local m_table = require("Module:table")
local audio_module = "Module:audio"
local parse_utilities_module = "Module:parse utilities"
local rfind = m_str_utils.find
local rsplit = m_str_utils.split
local ugsub = m_str_utils.gsub
local ulen = m_str_utils.len
local ulower = m_str_utils.lower
local usub = m_str_utils.sub
local concat = table.concat
local insert = table.insert
local lang = require("Module:languages").getByCode("ar")
local sc = require("Module:scripts").getByCode("Arab")
local correspondences = {
["ʾ"] = "ʔ",
["ṯ"] = "θ",
["j"] = "d͡ʒ",
["ḥ"] = "ħ",
["ḵ"] = "x",
["ḏ"] = "ð",
["š"] = "ʃ",
["ṣ"] = "sˤ",
["ḍ"] = "dˤ",
["ṭ"] = "tˤ",
["ẓ"] = "ðˤ",
["ž"] = "ʒ",
["ʿ"] = "ʕ",
["ḡ"] = "ɣ",
["ḷ"] = "lˤ",
["ū"] = "uː",
["ī"] = "iː",
["ā"] = "aː",
["y"] = "j",
["g"] = "ɡ",
["ē"] = "eː",
["ō"] = "oː",
[""] = "",
}
local vowels = "aāeēiīoōuū"
local vowel = "[" .. vowels .. "]"
local long_vowels = "āēīōū"
local long_vowel = "[" .. long_vowels .. "]"
local consonant = "[^" .. vowels .. ". -]"
local syllabify_pattern = "(" .. vowel .. ")(" .. consonant .. "?)(" .. consonant .. "?)(" .. vowel .. ")"
local tie = "‿"
local closed_syllable_shortening_pattern = "(" .. long_vowel .. ")(" .. tie .. ")" .. "(" .. consonant .. ")"
local function rsub(term, foo, bar)
local retval = ugsub(term, foo, bar)
return retval
end
local function generate_obj(respelling)
return { respelling = respelling }
end
local function combine_qualifiers(qual1, qual2)
if not qual1 then
return qual2
end
if not qual2 then
return qual1
end
local qualifiers = m_table.deepCopy(qual1)
for _, qual in ipairs(qual2) do
m_table.insertIfNot(qualifiers, qual)
end
return qualifiers
end
local function split_on_comma(term)
if not term then
return nil
end
if term:find(",%s") or term:find("\\") then
return require(parse_utilities_module).split_on_comma(term)
else
return rsplit(term, ",")
end
end
local function parse_respellings_with_modifiers(respelling, paramname)
if respelling:find("[<%[]") then
local put = require(parse_utilities_module)
local segments = put.parse_multi_delimiter_balanced_segment_run(respelling, { { "<", ">" }, { "[", "]" } })
local comma_separated_groups = put.split_alternating_runs_on_comma(segments)
local retval = {}
for _, group in ipairs(comma_separated_groups) do
local j = 2
while j <= #group do
if not group[j]:find("^<.*>$") then
group[j - 1] = group[j - 1] .. group[j] .. group[j + 1]
table.remove(group, j)
table.remove(group, j)
else
j = j + 2
end
end
local param_mods = {
q = { type = "qualifier" },
qq = { type = "qualifier" },
a = { type = "labels" },
aa = { type = "labels" },
ref = { item_dest = "refs", type = "references" },
}
table.insert(retval, put.parse_inline_modifiers_from_segments {
group = group,
arg = respelling,
props = {
paramname = paramname,
param_mods = param_mods,
generate_obj = generate_obj,
},
})
end
return retval
else
local retval = {}
for _, item in ipairs(split_on_comma(respelling)) do
table.insert(retval, generate_obj(item))
end
return retval
end
end
local function parse_pron_modifier(arg, paramname, generate_obj, param_mods, splitchar)
splitchar = splitchar or ","
if arg:find("<") then
param_mods.q = { type = "qualifier" }
param_mods.qq = { type = "qualifier" }
param_mods.a = { type = "labels" }
param_mods.aa = { type = "labels" }
param_mods.ref = { item_dest = "refs", type = "references" }
return require(parse_utilities_module).parse_inline_modifiers(arg, {
param_mods = param_mods,
generate_obj = generate_obj,
paramname = paramname,
splitchar = splitchar,
})
else
local retval = {}
local split_arg = splitchar == "," and split_on_comma(arg) or rsplit(arg, splitchar)
for _, term in ipairs(split_arg) do
table.insert(retval, generate_obj(term))
end
return retval
end
end
local function parse_audio(lang, arg, pagename, paramname)
local param_mods = {
IPA = { sublist = true },
text = {},
t = { item_dest = "gloss" },
gloss = {},
pos = {},
lit = {},
g = { item_dest = "genders", sublist = true },
bad = {},
cap = { item_dest = "caption" },
}
local function process_special_chars(val)
if not val then
return val
end
return (val:gsub("#", pagename))
end
local function generate_audio_obj(arg)
return { file = process_special_chars(arg) }
end
local retvals = parse_pron_modifier(arg, paramname, generate_audio_obj, param_mods, "%s*;%s*")
for _, retval in ipairs(retvals) do
retval.lang = lang
retval.text = process_special_chars(retval.text)
retval.caption = process_special_chars(retval.caption)
local textobj = require(audio_module).construct_audio_textobj(retval)
retval.text = textobj
retval.gloss = nil
retval.pos = nil
retval.lit = nil
retval.genders = nil
end
return retvals
end
local function parse_regional_phonetics(ph_arg, pagename)
if not ph_arg or ph_arg == "" then
return {}
end
local regionals = {}
for _, item in ipairs(rsplit(ph_arg, "%s*;%s*")) do
local audio = nil
local item_no_mod = item:gsub("<a:([^>]+)>", function(a)
audio = a:gsub("#", pagename)
return ""
end)
local region, ipa = item_no_mod:match("^([^:]+):(.+)$")
if region and ipa then
local regions = rsplit(region, "%s*,%s*")
table.insert(regionals, { regions = regions, ipa = ipa, audio = audio })
end
end
return regionals
end
local function syllabify(text)
text = ugsub(text, "%-(" .. consonant .. ")%-(" .. consonant .. ")", "%1.%2")
text = ugsub(text, "%-", ".")
for _ = 1, 2 do
text = ugsub(
text,
syllabify_pattern,
function(a, b, c, d)
if c == "" and b ~= "" then
c, b = b, ""
end
return a .. b .. "." .. c .. d
end
)
end
text = ugsub(text, "(" .. vowel .. ") (" .. consonant .. ")%.?(" ..
consonant .. ")", "%1" .. tie .. "%2.%3")
return text
end
local function closed_syllable_shortening(text)
local shorten = {
["ā"] = "a",
["ē"] = "e",
["ī"] = "i",
["ō"] = "o",
["ū"] = "u",
}
text = ugsub(text,
closed_syllable_shortening_pattern,
function(vowel, tie, consonant)
return shorten[vowel] .. tie .. consonant
end)
return text
end
function export.link(term)
return require("Module:links").full_link { term = term, lang = lang, sc = sc }
end
function export.toIPA(list, silent_error)
local translit
if list.tr then
translit = list.tr
elseif list.term then
require("Module:script utilities").checkScript(list.term, "Arab")
translit = lang:transliterate(list.term)
if not translit then
if silent_error then
return ''
else
error('Module:ar-translit failed to generate a transliteration from "' .. list.term .. '".')
end
end
else
if silent_error then
return ''
else
error('No Arabic text or transliteration was provided to the function "toIPA".')
end
end
translit = ugsub(translit, "llāh", "ḷḷāh")
translit = ugsub(translit, "([iī] ?)ḷḷ", "%1ll")
translit = ugsub(translit, "%(t%)", "")
translit = ugsub(translit, "(" .. vowel .. ") " .. vowel, "%1 ")
translit = ugsub(translit, "%-?l%-?", "l")
translit = syllabify(translit)
translit = closed_syllable_shortening(translit)
local output = ugsub(translit, ".", correspondences)
output = ugsub(output, "%-", "")
return output
end
function export.get_pron_info(terms, pagename, paramname)
if #terms == 1 and terms[1].respelling == "-" then
return { pron_list = nil }
end
local pron_list = {}
local brackets = "/%s/"
for _, term in ipairs(terms) do
local respelling = term.respelling
local ar_term, tr
if not respelling or respelling == "" or respelling == "#" then
ar_term = pagename
elseif rfind(respelling, "[a-zA-Z]") then
tr = respelling
elseif respelling:find("[ء-ي]") then
ar_term = respelling
else
tr = respelling
end
local pron = export.toIPA({ term = ar_term, tr = tr }, false)
if pron and pron ~= "" then
local bracketed_pron = brackets:format(pron)
table.insert(pron_list, {
pron = bracketed_pron,
q = term.q,
qq = term.qq,
a = term.a,
aa = term.aa,
refs = term.refs,
})
end
end
return { pron_list = pron_list }
end
function export.show_old(frame)
local params = {
[1] = { list = true, allow_holes = true },
["tr"] = { list = true, allow_holes = true },
["qual"] = { list = true, allow_holes = true },
["nl"] = { type = "boolean" },
["ann"] = {},
}
local args = require("Module:parameters").process(frame:getParent().args, params)
local ar_terms = args[1]
local transliterations = args.tr
local qualifiers = args.qual
local nl = args.nl
if not (ar_terms.maxindex > 0 or transliterations.maxindex > 0) then
if mw.title.getCurrentTitle().nsText == "Template" then
ar_terms[1] = "كَلِمَة"
ar_terms.maxindex = 1
else
error(
'Please provide vocalized Arabic in the first parameter of {{[[Template:ar-IPA|ar-IPA]]}}, or transliteration in the "tr" parameter.')
end
end
local pronunciations = {}
for i = 1, math.max(ar_terms.maxindex, transliterations.maxindex) do
local ar_term = ar_terms[i]
local tr = transliterations[i]
local qual = qualifiers[i]
if not (ar_term or tr) then
error("There is a gap in the parameters. Provide either |" .. i .. "= or |tr" .. i .. "=.")
elseif ar_term and tr then
mw.logObject("Duplicate parameters |" .. i .. "= and |tr" .. i .. "= in {{ar-IPA}},")
end
local pron = export.toIPA { term = ar_term, tr = tr }
table.insert(pronunciations, { pron = "/" .. pron .. "/", qualifiers = qual and { qual } or nil })
end
local anntext = ""
if args.ann then
anntext = args.ann
if args.ann:find("%+") then
local anndefs = {}
for i = 1, ar_terms.maxindex do
local ar_term = ar_terms[i]
if ar_term then
table.insert(anndefs, "'''" .. ar_term .. "'''")
end
end
if anndefs[1] then
anndefs = table.concat(anndefs, ", ")
anntext = anntext:gsub("%+", require("Module:string utilities").replacement_escape(anndefs))
end
end
anntext = require("Module:qualifier").format_qualifier(anntext, "", "") .. ": "
end
if nl then
return anntext .. require("Module:IPA").format_IPA_multiple(lang, pronunciations)
else
return anntext .. require("Module:IPA").format_IPA_full { lang = lang, items = pronunciations }
end
end
function export.show(frame)
local parent_args = frame:getParent().args
local process = require("Module:parameters").process
local params = {
[1] = {},
["audios"] = {},
["a"] = { alias_of = "audios" },
["ph"] = {},
["pagename"] = {},
["indent"] = {},
["ann"] = {},
}
local args = process(parent_args, params)
local pagename = args.pagename or mw.loadData("Module:headword/data").pagename
local indent = args.indent or "*"
local termspec = args[1] or "#"
local terms = parse_respellings_with_modifiers(termspec, 1)
local pronobj = export.get_pron_info(terms, pagename, 1)
local regional_phonetics = parse_regional_phonetics(args.ph, pagename)
local parts = {}
local function ins(text)
table.insert(parts, text)
end
local anntext = ""
if args.ann then
anntext = args.ann
if args.ann:find("%+") then
local anndefs = {}
for _, term in ipairs(terms) do
local respelling = term.respelling
if respelling and respelling:find("[ء-ي]") then
table.insert(anndefs, "'''" .. respelling .. "'''")
end
end
if anndefs[1] then
anndefs = table.concat(anndefs, ", ")
anntext = anntext:gsub("%+", require("Module:string utilities").replacement_escape(anndefs))
end
end
anntext = require("Module:qualifier").format_qualifier(anntext, "", "") .. ": "
end
if pronobj.pron_list and #pronobj.pron_list > 0 then
local formatted = require("Module:IPA").format_IPA_full { lang = lang, items = pronobj.pron_list }
ins(indent .. anntext .. mw.ustring.toNFC(formatted))
end
if args.audios then
local format_audio = require("Module:audio").format_audio
local audio_objs = parse_audio(lang, args.audios, pagename, "audios")
for i, audio_obj in ipairs(audio_objs) do
if #audio_objs > 1 and not audio_obj.caption then
audio_obj.caption = "Audio " .. i
end
ins("\n" .. indent .. " " .. format_audio(audio_obj))
end
end
if #regional_phonetics > 0 then
local m_IPA = require("Module:IPA")
local m_accent = require("Module:accent qualifier")
for _, regional in ipairs(regional_phonetics) do
local regions = regional.regions
local ipa = regional.ipa
local pron_item = { pron = "[" .. ipa .. "]" }
local formatted_ipa = m_IPA.format_IPA_full { lang = lang, items = { pron_item } }
local formatted_region = m_accent.format_qualifiers(lang, regions)
local line = "\n" .. indent .. indent .. " " .. formatted_region .. " " .. mw.ustring.toNFC(formatted_ipa)
if regional.audio then
local audio_obj = {
lang = lang,
file = regional.audio,
}
local textobj = require(audio_module).construct_audio_textobj(audio_obj)
audio_obj.text = textobj
line = line .. " " .. require("Module:audio").format_audio(audio_obj)
end
ins(line)
end
end
return concat(parts)
end
return export
fufezqvhcygwotxk2txtm6ulorript3
509633
509632
2026-06-03T12:24:54Z
Redmin
6857
509633
Scribunto
text/plain
local export = {}
local m_str_utils = require("Module:string utilities")
local m_table = require("Module:table")
local audio_module = "Module:audio"
local parse_utilities_module = "Module:parse utilities"
local rfind = m_str_utils.find
local rsplit = m_str_utils.split
local ugsub = m_str_utils.gsub
local ulen = m_str_utils.len
local ulower = m_str_utils.lower
local usub = m_str_utils.sub
local concat = table.concat
local insert = table.insert
local lang = require("Module:languages").getByCode("ar")
local sc = require("Module:scripts").getByCode("Arab")
local correspondences = {
["ʾ"] = "ʔ",
["ṯ"] = "θ",
["j"] = "d͡ʒ",
["ḥ"] = "ħ",
["ḵ"] = "x",
["ḏ"] = "ð",
["š"] = "ʃ",
["ṣ"] = "sˤ",
["ḍ"] = "dˤ",
["ṭ"] = "tˤ",
["ẓ"] = "ðˤ",
["ž"] = "ʒ",
["ʿ"] = "ʕ",
["ḡ"] = "ɣ",
["ḷ"] = "lˤ",
["ū"] = "uː",
["ī"] = "iː",
["ā"] = "aː",
["y"] = "j",
["g"] = "ɡ",
["ē"] = "eː",
["ō"] = "oː",
[""] = "",
}
local vowels = "aāeēiīoōuū"
local vowel = "[" .. vowels .. "]"
local long_vowels = "āēīōū"
local long_vowel = "[" .. long_vowels .. "]"
local consonant = "[^" .. vowels .. ". -]"
local syllabify_pattern = "(" .. vowel .. ")(" .. consonant .. "?)(" .. consonant .. "?)(" .. vowel .. ")"
local tie = "‿"
local closed_syllable_shortening_pattern = "(" .. long_vowel .. ")(" .. tie .. ")" .. "(" .. consonant .. ")"
local function rsub(term, foo, bar)
local retval = ugsub(term, foo, bar)
return retval
end
local function generate_obj(respelling)
return { respelling = respelling }
end
local function combine_qualifiers(qual1, qual2)
if not qual1 then
return qual2
end
if not qual2 then
return qual1
end
local qualifiers = m_table.deepCopy(qual1)
for _, qual in ipairs(qual2) do
m_table.insertIfNot(qualifiers, qual)
end
return qualifiers
end
local function split_on_comma(term)
if not term then
return nil
end
if term:find(",%s") or term:find("\\") then
return require(parse_utilities_module).split_on_comma(term)
else
return rsplit(term, ",")
end
end
local function parse_respellings_with_modifiers(respelling, paramname)
if respelling:find("[<%[]") then
local put = require(parse_utilities_module)
local segments = put.parse_multi_delimiter_balanced_segment_run(respelling, { { "<", ">" }, { "[", "]" } })
local comma_separated_groups = put.split_alternating_runs_on_comma(segments)
local retval = {}
for _, group in ipairs(comma_separated_groups) do
local j = 2
while j <= #group do
if not group[j]:find("^<.*>$") then
group[j - 1] = group[j - 1] .. group[j] .. group[j + 1]
table.remove(group, j)
table.remove(group, j)
else
j = j + 2
end
end
local param_mods = {
q = { type = "qualifier" },
qq = { type = "qualifier" },
a = { type = "labels" },
aa = { type = "labels" },
ref = { item_dest = "refs", type = "references" },
}
table.insert(retval, put.parse_inline_modifiers_from_segments {
group = group,
arg = respelling,
props = {
paramname = paramname,
param_mods = param_mods,
generate_obj = generate_obj,
},
})
end
return retval
else
local retval = {}
for _, item in ipairs(split_on_comma(respelling)) do
table.insert(retval, generate_obj(item))
end
return retval
end
end
local function parse_pron_modifier(arg, paramname, generate_obj, param_mods, splitchar)
splitchar = splitchar or ","
if arg:find("<") then
param_mods.q = { type = "qualifier" }
param_mods.qq = { type = "qualifier" }
param_mods.a = { type = "labels" }
param_mods.aa = { type = "labels" }
param_mods.ref = { item_dest = "refs", type = "references" }
return require(parse_utilities_module).parse_inline_modifiers(arg, {
param_mods = param_mods,
generate_obj = generate_obj,
paramname = paramname,
splitchar = splitchar,
})
else
local retval = {}
local split_arg = splitchar == "," and split_on_comma(arg) or rsplit(arg, splitchar)
for _, term in ipairs(split_arg) do
table.insert(retval, generate_obj(term))
end
return retval
end
end
local function parse_audio(lang, arg, pagename, paramname)
local param_mods = {
IPA = { sublist = true },
text = {},
t = { item_dest = "gloss" },
gloss = {},
pos = {},
lit = {},
g = { item_dest = "genders", sublist = true },
bad = {},
cap = { item_dest = "caption" },
}
local function process_special_chars(val)
if not val then
return val
end
return (val:gsub("#", pagename))
end
local function generate_audio_obj(arg)
return { file = process_special_chars(arg) }
end
local retvals = parse_pron_modifier(arg, paramname, generate_audio_obj, param_mods, "%s*;%s*")
for _, retval in ipairs(retvals) do
retval.lang = lang
retval.text = process_special_chars(retval.text)
retval.caption = process_special_chars(retval.caption)
local textobj = require(audio_module).construct_audio_textobj(retval)
retval.text = textobj
retval.gloss = nil
retval.pos = nil
retval.lit = nil
retval.genders = nil
end
return retvals
end
local function parse_regional_phonetics(ph_arg, pagename)
if not ph_arg or ph_arg == "" then
return {}
end
local regionals = {}
for _, item in ipairs(rsplit(ph_arg, "%s*;%s*")) do
local audio = nil
local item_no_mod = item:gsub("<a:([^>]+)>", function(a)
audio = a:gsub("#", pagename)
return ""
end)
local region, ipa = item_no_mod:match("^([^:]+):(.+)$")
if region and ipa then
local regions = rsplit(region, "%s*,%s*")
table.insert(regionals, { regions = regions, ipa = ipa, audio = audio })
end
end
return regionals
end
local function syllabify(text)
text = ugsub(text, "%-(" .. consonant .. ")%-(" .. consonant .. ")", "%1.%2")
text = ugsub(text, "%-", ".")
for _ = 1, 2 do
text = ugsub(
text,
syllabify_pattern,
function(a, b, c, d)
if c == "" and b ~= "" then
c, b = b, ""
end
return a .. b .. "." .. c .. d
end
)
end
text = ugsub(text, "(" .. vowel .. ") (" .. consonant .. ")%.?(" ..
consonant .. ")", "%1" .. tie .. "%2.%3")
return text
end
local function closed_syllable_shortening(text)
local shorten = {
["ā"] = "a",
["ē"] = "e",
["ī"] = "i",
["ō"] = "o",
["ū"] = "u",
}
text = ugsub(text,
closed_syllable_shortening_pattern,
function(vowel, tie, consonant)
return shorten[vowel] .. tie .. consonant
end)
return text
end
function export.link(term)
return require("Module:links").full_link { term = term, lang = lang, sc = sc }
end
function export.toIPA(list, silent_error)
local translit
if list.tr then
translit = list.tr
elseif list.term then
require("Module:script utilities").checkScript(list.term, "Arab")
translit = lang:transliterate(list.term)
if not translit then
if silent_error then
return ''
else
error('Module:ar-translit failed to generate a transliteration from "' .. list.term .. '".')
end
end
else
if silent_error then
return ''
else
error('No Arabic text or transliteration was provided to the function "toIPA".')
end
end
translit = ugsub(translit, "llāh", "ḷḷāh")
translit = ugsub(translit, "([iī] ?)ḷḷ", "%1ll")
translit = ugsub(translit, "%(t%)", "")
translit = ugsub(translit, "(" .. vowel .. ") " .. vowel, "%1 ")
translit = ugsub(translit, "%-?l%-?", "l")
translit = syllabify(translit)
translit = closed_syllable_shortening(translit)
local output = ugsub(translit, ".", correspondences)
output = ugsub(output, "%-", "")
return output
end
function export.get_pron_info(terms, pagename, paramname)
if #terms == 1 and terms[1].respelling == "-" then
return { pron_list = nil }
end
local pron_list = {}
local brackets = "/%s/"
for _, term in ipairs(terms) do
local respelling = term.respelling
local ar_term, tr
if not respelling or respelling == "" or respelling == "#" then
ar_term = pagename
elseif rfind(respelling, "[a-zA-Z]") then
tr = respelling
elseif respelling:find("[ء-ي]") then
ar_term = respelling
else
tr = respelling
end
local pron = export.toIPA({ term = ar_term, tr = tr }, false)
if pron and pron ~= "" then
local bracketed_pron = brackets:format(pron)
table.insert(pron_list, {
pron = bracketed_pron,
q = term.q,
qq = term.qq,
a = term.a,
aa = term.aa,
refs = term.refs,
})
end
end
return { pron_list = pron_list }
end
function export.show_old(frame)
local params = {
[1] = { list = true, allow_holes = true },
["tr"] = { list = true, allow_holes = true },
["qual"] = { list = true, allow_holes = true },
["nl"] = { type = "boolean" },
["ann"] = {},
}
local args = require("Module:parameters").process(frame:getParent().args, params)
local ar_terms = args[1]
local transliterations = args.tr
local qualifiers = args.qual
local nl = args.nl
if not (ar_terms.maxindex > 0 or transliterations.maxindex > 0) then
if mw.title.getCurrentTitle().nsText == "টেমপ্লেট" then
ar_terms[1] = "كَلِمَة"
ar_terms.maxindex = 1
else
error(
'Please provide vocalized Arabic in the first parameter of {{[[টেমপ্লেট:ar-IPA|ar-IPA]]}}, or transliteration in the "tr" parameter.')
end
end
local pronunciations = {}
for i = 1, math.max(ar_terms.maxindex, transliterations.maxindex) do
local ar_term = ar_terms[i]
local tr = transliterations[i]
local qual = qualifiers[i]
if not (ar_term or tr) then
error("There is a gap in the parameters. Provide either |" .. i .. "= or |tr" .. i .. "=.")
elseif ar_term and tr then
mw.logObject("Duplicate parameters |" .. i .. "= and |tr" .. i .. "= in {{ar-IPA}},")
end
local pron = export.toIPA { term = ar_term, tr = tr }
table.insert(pronunciations, { pron = "/" .. pron .. "/", qualifiers = qual and { qual } or nil })
end
local anntext = ""
if args.ann then
anntext = args.ann
if args.ann:find("%+") then
local anndefs = {}
for i = 1, ar_terms.maxindex do
local ar_term = ar_terms[i]
if ar_term then
table.insert(anndefs, "'''" .. ar_term .. "'''")
end
end
if anndefs[1] then
anndefs = table.concat(anndefs, ", ")
anntext = anntext:gsub("%+", require("Module:string utilities").replacement_escape(anndefs))
end
end
anntext = require("Module:qualifier").format_qualifier(anntext, "", "") .. ": "
end
if nl then
return anntext .. require("Module:IPA").format_IPA_multiple(lang, pronunciations)
else
return anntext .. require("Module:IPA").format_IPA_full { lang = lang, items = pronunciations }
end
end
function export.show(frame)
local parent_args = frame:getParent().args
local process = require("Module:parameters").process
local params = {
[1] = {},
["audios"] = {},
["a"] = { alias_of = "audios" },
["ph"] = {},
["pagename"] = {},
["indent"] = {},
["ann"] = {},
}
local args = process(parent_args, params)
local pagename = args.pagename or mw.loadData("Module:headword/data").pagename
local indent = args.indent or "*"
local termspec = args[1] or "#"
local terms = parse_respellings_with_modifiers(termspec, 1)
local pronobj = export.get_pron_info(terms, pagename, 1)
local regional_phonetics = parse_regional_phonetics(args.ph, pagename)
local parts = {}
local function ins(text)
table.insert(parts, text)
end
local anntext = ""
if args.ann then
anntext = args.ann
if args.ann:find("%+") then
local anndefs = {}
for _, term in ipairs(terms) do
local respelling = term.respelling
if respelling and respelling:find("[ء-ي]") then
table.insert(anndefs, "'''" .. respelling .. "'''")
end
end
if anndefs[1] then
anndefs = table.concat(anndefs, ", ")
anntext = anntext:gsub("%+", require("Module:string utilities").replacement_escape(anndefs))
end
end
anntext = require("Module:qualifier").format_qualifier(anntext, "", "") .. ": "
end
if pronobj.pron_list and #pronobj.pron_list > 0 then
local formatted = require("Module:IPA").format_IPA_full { lang = lang, items = pronobj.pron_list }
ins(indent .. anntext .. mw.ustring.toNFC(formatted))
end
if args.audios then
local format_audio = require("Module:audio").format_audio
local audio_objs = parse_audio(lang, args.audios, pagename, "audios")
for i, audio_obj in ipairs(audio_objs) do
if #audio_objs > 1 and not audio_obj.caption then
audio_obj.caption = "Audio " .. i
end
ins("\n" .. indent .. " " .. format_audio(audio_obj))
end
end
if #regional_phonetics > 0 then
local m_IPA = require("Module:IPA")
local m_accent = require("Module:accent qualifier")
for _, regional in ipairs(regional_phonetics) do
local regions = regional.regions
local ipa = regional.ipa
local pron_item = { pron = "[" .. ipa .. "]" }
local formatted_ipa = m_IPA.format_IPA_full { lang = lang, items = { pron_item } }
local formatted_region = m_accent.format_qualifiers(lang, regions)
local line = "\n" .. indent .. indent .. " " .. formatted_region .. " " .. mw.ustring.toNFC(formatted_ipa)
if regional.audio then
local audio_obj = {
lang = lang,
file = regional.audio,
}
local textobj = require(audio_module).construct_audio_textobj(audio_obj)
audio_obj.text = textobj
line = line .. " " .. require("Module:audio").format_audio(audio_obj)
end
ins(line)
end
end
return concat(parts)
end
return export
09fv2kblui6rk0u79vhieaftrylk6ek
ব্যবহারকারী:Redmin/খেলাঘর
2
66884
509641
509298
2026-06-03T14:24:41Z
Redmin
6857
509641
wikitext
text/x-wiki
[[:বিষয়শ্রেণী:IPA pronunciations with invalid IPA characters]]
[[চলা]] P9295, P5186, P5187, P11053, P11054, P9488, P5972, P9971, P6072 with P5831, P9970, P2288, P8881, P1552, P5975 (needs T185313), P8471, P5976, P10822, P282, P5978, P6719, P7219, P7220, P7221, P10339, P10927, P12027, P12028, P12410, P13045, P7481, P13504, P5548, P5401, P5426, P2440, P9021, P10822, P1249<br />
Z29940, Z17866, Z32787, Z32793, Z11795, Z31772, Z33644, Z30837 (templates), Z33814, Z32347, Z32340, Z32343, Z26333
Combine Z22853 and Z23489 to make a translation table function<br />
Etymology failing to render: [[আদর্শ]]
[[d:Lexeme:L3441]], [[throw]], [[গোলাকার]], [[:বিষয়শ্রেণী:উইকিউপাত্ত আভিধানিক উপাত্ত থেকে আমদানিকৃত]]
Pronunciation of forms
[[d:User:Rua/Wikidata for Wiktionarians]]
[[-গুলি]]
=p.all(
mw.getCurrentFrame():newChild{
title="Module: আভিধানিক উপাত্ত",
args={"L143"}
}
)
{{লে|L1422918}}
{{#invoke:আভিধানিক উপাত্ত/sandbox|all|L34813}}
{{#function:Z28602|L300026|}}
{{#function:Z27861|<div style="text-align: center;">Test</div>}}
{{#function:Z28602|L1425972|Z1011}}
{{#function:Z28602|L14259|Z1011}}
{{#function:Z30837|/ɡolakaɾ/}}
{{#function:Z33837|L511-S1}}
{{#function:Z33243|L348189|}}
ds71ftv2yh7ifo31h62uk8lzzv4j0g7
غفر
0
79549
509638
454362
2026-06-03T12:53:09Z
Redmin
6857
/* ক্রিয়া */ [[en:غفر ]] থেকে কপি করলাম
509638
wikitext
text/x-wiki
=={{ভাষা|ar}}==
===ব্যুৎপত্তি ১===
From the root {{ar-root|غ|ف|ر}}.
====উচ্চারণ====
* {{ar-IPA|غَفَرَ}}
====ক্রিয়া====
{{ar-verb|I/a~i.pass.vn:غَفْر,مَغْفِرَة,غُفْرَان}}
# ক্ষমা করা (দেখুন: to [[forgive]], [[excuse]], [[pardon]])
#* {{RQ:Qur'an|3|193|text=رَّبَّنَا إِنَّنَا سَمِعْنَا مُنَادِيًا يُنَادِي لِلْإِيمَانِ أَنْ آمِنُوا بِرَبِّكُمْ فَآمَنَّا{{nbsp}}ۚ رَبَّنَا '''فَاغْفِرْ''' لَنَا ذُنُوبَنَا وَكَفِّرْ عَنَّا سَيِّئَاتِنَا وَتَوَفَّنَا مَعَ الْأَبْرَارِ|subst=فَآمَنَّا{{nbsp}}ۚ رَ/فَآمَنَّا رَ |t=Our Lord! we have heard a [sincere] caller calling us to faith [who enjoined upon us:] “Believe in your Lord!” - and we have [thus] believed. Our Lord! [as such,] '''forgive''' us for our sins, and wipe out from our [records] our bad deeds, and [let it be that our lives be] completed [whilst we are in such a state that will cause us to be resurrected] alongside the steadfast.}}
====ক্রিয়া====
{{ar-verb|I|a|i}}
# ঢেকে দেওয়া (দেখুন:to [[cover]])
===ব্যুৎপত্তি ২===
====বিশেষ্য====
{{ar-noun|غَفْر|m}}
#[[غفر|غَفَرَ]]এর ক্রিয়াবিশেষ্য
=====অবনমন=====
{{ar-decl-noun|غَفْر}}
42c6wcpca4k9rhod9u82wgc4m9c7uc7
509639
509638
2026-06-03T12:55:07Z
Redmin
6857
/* ক্রিয়া */ [[en: غفر]] থেকে কপি করলাম
509639
wikitext
text/x-wiki
=={{ভাষা|ar}}==
===ব্যুৎপত্তি ১===
From the root {{ar-root|غ|ف|ر}}.
====উচ্চারণ====
* {{ar-IPA|غَفَرَ}}
====ক্রিয়া====
{{ar-verb|I/a~i.pass.vn:غَفْر,مَغْفِرَة,غُفْرَان}}
# ক্ষমা করা (দেখুন: to [[forgive]], [[excuse]], [[pardon]])
#* {{RQ:Qur'an|3|193|text=رَّبَّنَا إِنَّنَا سَمِعْنَا مُنَادِيًا يُنَادِي لِلْإِيمَانِ أَنْ آمِنُوا بِرَبِّكُمْ فَآمَنَّا{{nbsp}}ۚ رَبَّنَا '''فَاغْفِرْ''' لَنَا ذُنُوبَنَا وَكَفِّرْ عَنَّا سَيِّئَاتِنَا وَتَوَفَّنَا مَعَ الْأَبْرَارِ|subst=فَآمَنَّا{{nbsp}}ۚ رَ/فَآمَنَّا رَ |t=Our Lord! we have heard a [sincere] caller calling us to faith [who enjoined upon us:] “Believe in your Lord!” - and we have [thus] believed. Our Lord! [as such,] '''forgive''' us for our sins, and wipe out from our [records] our bad deeds, and [let it be that our lives be] completed [whilst we are in such a state that will cause us to be resurrected] alongside the steadfast.}}
====ক্রিয়া====
{{ar-verb|I/a~i.pass.vn:غَفْر}}
# ঢেকে দেওয়া (দেখুন:to [[cover]])
{{ar-conj|I/a~i.pass.vn:غَفْر}}
===ব্যুৎপত্তি ২===
====বিশেষ্য====
{{ar-noun|غَفْر|m}}
#[[غفر|غَفَرَ]]এর ক্রিয়াবিশেষ্য
=====অবনমন=====
{{ar-decl-noun|غَفْر}}
109z9e48gtrfvodqfpzm3nqbapwbbkp
সহজাত
0
115568
509675
497885
2026-06-04T11:35:49Z
Redmin
6857
লেক্সিম লিংকার এক্সটেনশনের সাহায্যে উইকিউপাত্ত লেক্সিম L1567347-এর সাথে সংযোগ তৈরি করছি
509675
wikitext
text/x-wiki
{{লে|L1567347}}
=== বিশেষণ ===
{{bn-বিশেষণ}}
# একসঙ্গে [[উৎপন্ন]], একই সময়ে জাত। [[স্বাভাবিক]] ([[সহজাত]] প্রবৃত্তি)।
anoi3wtrjp2uzxoslvfd20sh7zj1ahz
মডিউল:languages/data/3/p
828
123286
509649
323712
2026-06-04T08:22:09Z
Redmin
6857
509649
Scribunto
text/plain
local m_langdata = require("Module:languages/data")
-- Loaded on demand, as it may not be needed (depending on the data).
local function u(...)
u = require("Module:string utilities").char
return u(...)
end
local c = m_langdata.chars
local p = m_langdata.puaChars
local s = m_langdata.shared
local m = {}
m["pab"] = {
"Pareci",
3504312,
"awd",
"Latn",
}
m["pac"] = {
"Pacoh",
3441136,
"mkh-kat",
"Latn",
}
m["pad"] = {
"Paumarí",
389827,
"auf",
"Latn",
}
m["pae"] = {
"Pagibete",
7124357,
"bnt-bta",
"Latn",
}
m["paf"] = {
"Paranawát",
12953806,
"tup-gua",
"Latn",
}
m["pag"] = {
"Pangasinan",
33879,
"phi",
"Latn, Tglg",
entry_name = {
Latn = {remove_diacritics = c.grave .. c.acute .. c.circ .. c.diaer},
},
}
m["pah"] = {
"Tenharim",
10266010,
"tup-gua",
"Latn",
}
m["pai"] = {
"Pe",
3914871,
"nic-tar",
"Latn",
}
m["pak"] = {
"Parakanã",
12953804,
"tup-gua",
"Latn",
}
m["pal"] = {
"Middle Persian",
32063,
"ira-swi",
"Latn, Phli, pal-Avst, Mani, Phlp, Phlv", -- Latn for translit; Phlv not in Unicode
translit = {
Phli = "Phli-translit",
["pal-Avst"] = "Avst-translit",
Mani = "Mani-translit",
},
ancestors = "peo",
}
m["pam"] = {
"Kapampangan",
36121,
"phi",
"Latn, Kulit",
entry_name = {
Latn = {remove_diacritics = c.grave .. c.acute .. c.circ}
},
standardChars = {
Latn = "AaBbDdEeGgHhIiKkLlMmNnOoPpRrSsTtUuWwYy",
c.punc
},
sort_key = {
Latn = "tl-sortkey"
},
}
m["pao"] = {
"Northern Paiute",
3360656,
"azc-num",
"Latn",
}
m["pap"] = {
"Papiamentu",
33856,
"crp",
"Latn",
ancestors = "pt",
}
m["paq"] = {
"Parya",
1135134,
"inc-cen",
}
m["par"] = {
"Panamint",
33926,
"azc-num",
"Latn",
}
m["pas"] = {
"Papasena",
7132508,
"paa-lkp",
"Latn",
}
m["pau"] = {
"Palauan",
33776,
"poz",
"Latn, Kana",
sort_key = {
Kana = "Kana-sortkey"
},
}
m["pav"] = {
"Wari'",
3027909,
"sai-cpc",
"Latn",
}
m["paw"] = {
"Pawnee",
56751,
"cdd",
"Latn",
entry_name = {remove_diacritics = c.acute},
}
m["pax"] = {
"Pankararé",
25559779,
nil,
"Latn",
}
m["pay"] = {
"Pech",
4898889,
"cba",
"Latn",
}
m["paz"] = {
"Pankararú",
7131310,
nil,
"Latn",
}
m["pbb"] = {
"Páez",
33677,
nil,
"Latn",
}
m["pbc"] = {
"Patamona",
3915921,
"sai-pem",
"Latn",
}
m["pbe"] = {
"Mezontla Popoloca",
42365630,
"omq-pop",
"Latn",
}
m["pbf"] = {
"Coyotepec Popoloca",
5180100,
"omq-pop",
"Latn",
}
m["pbg"] = {
"Paraujano",
3501747,
"awd-taa",
"Latn",
}
m["pbh"] = {
"Panare",
56610,
"sai-ven",
"Latn",
}
m["pbi"] = {
"Podoko",
3515096,
"cdc-cbm",
"Latn",
}
m["pbl"] = {
"Mak (Nigeria)",
3915349,
"alv-bwj",
"Latn",
}
m["pbm"] = {
"Puebla Mazatec",
31102530,
"omq-maz",
"Latn",
}
m["pbn"] = {
"Kpasam",
3914902,
"alv-mye",
"Latn",
}
m["pbo"] = {
"Papel",
36314,
"alv-pap",
"Latn",
}
m["pbp"] = {
"Badyara",
35095,
"alv-ten",
"Latn",
}
m["pbr"] = {
"Pangwa",
3847550,
"bnt-bki",
"Latn",
}
m["pbs"] = {
"Central Pame",
3361763,
"omq",
"Latn",
}
m["pbv"] = {
"Pnar",
3501850,
"aav-pkl",
"Latn",
}
m["pby"] = {
"Pyu (New Guinea)",
2567925,
"paa-asa",
"Latn",
}
m["pca"] = {
"Santa Inés Ahuatempan Popoloca",
42365276,
"omq-pop",
"Latn",
}
m["pcb"] = {
"Pear",
6583669,
"mkh-pea",
"Khmr",
}
m["pcc"] = {
"Bouyei",
35100,
"tai-nor",
"Latn, Hani",
sort_key = {
Hani = "Hani-sortkey"
},
}
m["pcd"] = {
"Picard",
34024,
"roa-oil",
"Latn",
sort_key = s["roa-oil-sortkey"],
}
m["pce"] = {
"Ruching Palaung",
12953798,
"mkh-pal",
"Mymr",
}
m["pcf"] = {
"Paliyan",
7127643,
"dra-tam",
}
m["pcg"] = {
"Paniya",
7131211,
"dra-mal",
}
m["pch"] = {
"Pardhan",
7133207,
"dra-gon",
}
m["pci"] = {
"Duruwa",
56753,
"dra-pgd",
"Deva, Orya",
}
m["pcj"] = {
"Parenga",
3111396,
"mun",
}
m["pck"] = {
"Paite",
12952337,
"tbq-kuk",
}
m["pcl"] = {
"Pardhi",
7136554,
"inc-bhi",
}
m["pcm"] = {
"Nigerian Pidgin",
33655,
"crp",
"Latn",
ancestors = "en",
entry_name = {remove_diacritics = c.grave .. c.acute .. c.circ .. c.caron .. c.macronbelow},
sort_key = {
remove_diacritics = c.tilde,
from = {"ẹ", "gb", "kp", "ọ", "sh", "zh"},
to = {"e" .. p[1], "g" .. p[1], "k" .. p[1], "o" .. p[1], "s" .. p[1], "z" .. p[1]}
},
}
m["pcn"] = {
"Piti",
3913375,
"nic-kne",
"Latn",
}
m["pcp"] = {
"Pacahuara",
2591165,
"sai-pan",
"Latn",
}
m["pcw"] = {
"Pyapun",
3438807,
nil,
"Latn",
}
m["pda"] = {
"Anam",
3501930,
"ngf-mad",
"Latn",
}
m["pdc"] = {
"Pennsylvania German",
22711,
"gmw-hgm",
"Latn",
ancestors = "gmw-rfr",
}
m["pdi"] = {
"Pa Di",
3359940,
nil,
"Latn",
}
m["pdn"] = {
"Fedan",
7206699,
"poz-ocw",
"Latn",
}
m["pdo"] = {
"Padoe",
3360370,
"poz-btk",
"Latn",
}
m["pdt"] = {
"Plautdietsch",
1751432,
"gmw-lgm",
"Latn",
ancestors = "nds-de",
}
m["pdu"] = {
"Kayan",
7123283,
"kar",
"Latn",
}
m["pea"] = {
"Peranakan Indonesian",
653415,
"crp",
"Latn",
ancestors = "ms",
}
m["peb"] = {
"Eastern Pomo",
3396032,
"nai-pom",
"Latn",
}
m["ped"] = {
"Mala (New Guinea)",
11732569,
"ngf-mad",
"Latn",
}
m["pee"] = {
"Taje",
12953902,
nil,
"Latn",
}
m["pef"] = {
"Northeastern Pomo",
3396018,
"nai-pom",
"Latn",
}
m["peg"] = {
"Pengo",
56758,
"dra-kki",
"Orya",
translit = "kxv-translit",
}
m["peh"] = {
"Bonan",
32983,
"xgn-shr",
"Latn",
}
m["pei"] = {
"Chichimeca-Jonaz",
3915427,
"omq-otp",
"Latn",
}
m["pej"] = {
"Northern Pomo",
3396021,
"nai-pom",
"Latn",
}
m["pek"] = {
"Penchal",
3374631,
"poz-aay",
"Latn",
}
m["pel"] = {
"Pekal",
3241781,
nil,
"Latn",
}
m["pem"] = {
"Phende",
7162372,
"bnt-pen",
"Latn",
}
m["peo"] = {
"Old Persian",
35225,
"ira-swi",
"Xpeo, Latn",
translit = "peo-translit",
}
m["pep"] = {
"Kunja",
6444807,
nil,
"Latn",
}
m["peq"] = {
"Southern Pomo",
3396023,
"nai-pom",
"Latn",
}
-- "pes" IS TREATED AS "fa" (or as etymology-only), SEE WT:LT
m["pev"] = {
"Pémono",
3439012,
"sai-map",
"Latn",
}
m["pex"] = {
"Petats",
3376353,
"poz-ocw",
"Latn",
}
m["pey"] = {
"Petjo",
940486,
nil,
"Latn",
}
m["pez"] = {
"Eastern Penan",
18638342,
"poz-swa",
"Latn",
}
m["pfa"] = {
"Pááfang",
3063517,
"poz-mic",
"Latn",
}
m["pfe"] = {
"Peere",
36377,
"alv-dur",
"Latn",
}
m["pga"] = {
"Juba Arabic",
1262143,
"crp",
"Latn",
ancestors = "apd",
}
m["pgd"] = {
"Gandhari",
3124623,
"inc-mid",
"Deva, Khar",
ancestors = "inc-ash",
translit = "Khar-translit",
}
m["pgg"] = {
"Pangwali",
13600429,
"him",
"Deva, Takr",
translit = "hi-translit",
}
m["pgi"] = {
"Pagi",
7124354,
"paa-brd",
"Latn",
}
m["pgk"] = {
"Rerep",
586907,
"poz-vnc",
"Latn",
}
m["pgl"] = {
"Primitive Irish",
3320030,
"cel-gae",
"Ogam, Latn",
translit = "pgl-translit",
}
m["pgn"] = {
"Paelignian",
65455883,
"itc-sbl",
"Ital, Latn",
translit = {
Ital = "Ital-translit",
},
display_text = {
Latn = s["itc-Latn-displaytext"]
},
entry_name = {
Latn = s["itc-Latn-entryname"]
},
sort_key = {
Latn = s["itc-Latn-sortkey"]
},
}
m["pgs"] = {
"Pangseng",
3914027,
"alv-mum",
"Latn",
}
m["pgu"] = {
"Pagu",
7124462,
"paa-nha",
"Latn",
}
m["pgz"] = {
"Papua New Guinean Sign Language",
25044405,
"sgn",
}
m["pha"] = {
"Pa-Hng",
2625410,
"hmn",
}
m["phd"] = {
"Phudagi",
7188289,
}
m["phg"] = {
"Phuong",
7188376,
"mkh-kat",
}
m["phh"] = {
"Phukha",
7188298,
"tbq-phw",
}
m["phk"] = {
"Phake",
7675798,
"tai-swe",
"Mymr",
translit = "aio-phk-translit",
entry_name = {remove_diacritics = c.VS01},
}
m["phl"] = {
"Phalura",
2449549,
"inc-shn",
"Latn, ur-Arab",
entry_name = {
-- character "ۂ" code U+06C2 to "ه" and "هٔ" (U+0647 + U+0654) to "ه"; hamzatu l-waṣli to a regular alif
from = {"هٔ", "ۂ", "ٱ"},
to = {"ہ", "ہ", "ا"},
remove_diacritics = c.fathatan .. c.dammatan .. c.kasratan .. c.fatha .. c.damma .. c.kasra .. c.shadda .. c.sukun .. c.nunghunna .. c.superalef
},
}
m["phm"] = {
"Phimbi",
11007144,
"bnt-sna",
"Latn",
}
m["phn"] = {
"Phoenician",
36734,
"sem-can",
"Phnx",
translit = "Phnx-translit",
}
m["pho"] = {
"Phunoi",
7188361,
"tbq-bis",
}
m["phq"] = {
"Phana'",
7180427,
"tbq-sil",
}
m["phr"] = {
"Pahari-Potwari",
33739,
"inc-pan",
"pa-Arab, Guru",
ancestors = "lah",
translit = {
Guru = "Guru-translit",
["pa-Arab"] = "pa-Arab-translit",
},
entry_name = {
["pa-Arab"] = {
remove_diacritics = c.fathatan .. c.dammatan .. c.kasratan .. c.fatha .. c.damma .. c.kasra .. c.shadda .. c.sukun .. c.nunghunna,
from = {"ݨ", "ࣇ"},
to = {"ن", "ل"}
},
}
}
m["pht"] = {
"Phu Thai",
3626597,
"tai-swe",
"Thai",
}
m["phu"] = {
"Phuan",
3915665,
}
m["phv"] = {
"Pahlavani",
7124567,
}
m["phw"] = {
"Phangduwali",
12953036,
"sit-kie",
ancestors = "ybh",
}
m["pia"] = {
"Pima Bajo",
3388544,
"azc-pim",
"Latn",
}
m["pib"] = {
"Yine",
3135432,
"awd",
"Latn",
}
m["pic"] = {
"Pinji",
36296,
"bnt-tso",
"Latn",
}
m["pid"] = {
"Piaroa",
3382207,
nil,
"Latn",
}
m["pie"] = {
"Piro",
7198055,
"nai-kta",
"Latn",
}
m["pif"] = {
"Pingelapese",
36421,
"poz-mic",
"Latn",
}
m["pig"] = {
"Pisabo",
966883,
"sai-pan",
"Latn",
}
m["pih"] = {
"Pitcairn-Norfolk",
36554,
"crp",
"Latn",
ancestors = "en",
}
m["pii"] = {
"Pini",
10631925,
}
m["pij"] = {
"Pijao",
7193519,
}
m["pil"] = {
"Yom",
36893,
"nic-yon",
}
m["pim"] = {
"Powhatan",
2270532,
"alg-eas",
"Latn",
}
m["pin"] = {
"Piame",
7190042,
}
m["pio"] = {
"Piapoco",
3382208,
"awd-nwk",
"Latn",
}
m["pip"] = {
"Pero",
2411063,
"cdc-wst",
}
m["pir"] = {
"Piratapuyo",
3389119,
"sai-tuc",
"Latn",
}
m["pis"] = {
"Pijin",
36699,
"crp",
"Latn",
ancestors = "en",
}
m["pit"] = {
"Pitta-Pitta",
6433116,
"aus-kar",
"Latn",
}
m["piu"] = {
"Pintupi-Luritja",
2591175,
"aus-pam",
"Latn",
}
m["piv"] = {
"Pileni",
2976736,
"poz-pnp",
"Latn",
}
m["piw"] = {
"Pimbwe",
3894132,
"bnt-mwi",
}
m["pix"] = {
"Piu",
7199578,
}
m["piy"] = {
"Piya-Kwonci",
3440492,
}
m["piz"] = {
"Pije",
3388339,
"poz-cln",
"Latn",
}
m["pjt"] = {
"Pitjantjatjara",
2982063,
"aus-pam",
"pjt-Latn",
}
m["pkb"] = {
"Kipfokomo",
7208693,
"bnt-sab",
"Latn",
}
m["pkc"] = {
"Baekje",
4841264,
"qfa-kor",
"Hani, Kana",
sort_key = {
Hani = "Hani-sortkey",
Kana = "Kana-sortkey"
},
}
m["pkg"] = {
"Pak-Tong",
3360711,
}
m["pkh"] = {
"Pankhu",
7130962,
"tbq-kuk",
}
m["pkn"] = {
"Pakanha",
954916,
"aus-pmn",
}
m["pko"] = {
"Pökoot",
36323,
"sdv-kln",
"Latn",
}
m["pkp"] = {
"Pukapukan",
36447,
"poz-pnp",
"Latn",
}
m["pkr"] = {
"Attapady Kurumba",
16835180,
"dra-imd",
"Mlym",
}
m["pks"] = {
"Pakistan Sign Language",
22964057,
"sgn",
}
m["pkt"] = {
"Maleng",
6583562,
"mkh-vie",
}
m["pku"] = {
"Paku",
2932604,
"poz-bre",
"Latn",
}
m["pla"] = {
"Miani",
12952844,
nil,
"Latn",
}
m["plb"] = {
"Polonombauk",
7225957,
"poz-vnn",
"Latn",
}
m["plc"] = {
"Central Palawano",
12953795,
"phi",
"Latn",
}
m["ple"] = {
"Palu'e",
2196866,
"poz-cet",
"Latn",
}
m["plg"] = {
"Pilagá",
2748259,
"sai-guc",
"Latn",
}
m["plh"] = {
"Paulohi",
7155331,
"poz-cma",
}
m["plj"] = {
"Polci",
3914383,
}
m["plk"] = {
"Kohistani Shina",
12953882,
"inc-shn",
"ur-Arab",
}
m["pll"] = {
"Shwe Palaung",
27941664,
"mkh-pal",
"Mymr",
}
m["pln"] = {
"Palenquero",
36665,
"crp",
"Latn",
ancestors = "es",
}
m["plo"] = {
"Oluta Popoluca",
5908687,
"nai-miz",
"Latn",
}
m["plq"] = {
"Palaic",
36582,
"ine-ana",
"Xsux",
}
m["plr"] = {
"Palaka Senoufo",
36346,
"alv-snf",
"Latn",
}
m["pls"] = {
"San Marcos Tlalcoyalco Popoloca",
12641692,
"omq-pop",
"Latn",
}
m["plu"] = {
"Palikur",
3073448,
"awd",
"Latn",
}
m["plv"] = {
"Southwest Palawano",
15614922,
"phi",
"Latn",
}
m["plw"] = {
"Brooke's Point Palawano",
12953796,
"phi",
"Latn",
}
m["ply"] = {
"Bolyu",
3361723,
"mkh-pkn",
"Latn",
}
m["plz"] = {
"Paluan",
7128795,
nil,
"Latn",
}
m["pma"] = {
"Paamese",
3130286,
"poz-vnc",
"Latn",
}
m["pmb"] = {
"Pambia",
36267,
"znd",
"Latn",
}
m["pmd"] = {
"Pallanganmiddang",
7127734,
"aus-pam",
"Latn",
}
m["pme"] = {
"Pwaamèi",
3411152,
"poz-cln",
"Latn",
}
m["pmf"] = {
"Pamona",
3513320,
"poz-kal",
"Latn",
}
m["pmi"] = {
"Northern Pumi",
3403245,
"sit-qia",
}
m["pmj"] = {
"Southern Pumi",
3403246,
"sit-qia",
}
m["pmk"] = {
"Pamlico",
111366045,
"alg-eas",
"Latn",
}
m["pml"] = {
"Sabir",
636479,
"crp",
"Latn",
ancestors = "lij, pro, vec",
}
m["pmm"] = {
"Pol",
36408,
"bnt-kak",
"Latn",
}
m["pmn"] = {
"Pam",
7129017,
"alv-mbm",
}
m["pmo"] = {
"Pom",
7227178,
"poz-hce",
"Latn",
}
m["pmq"] = {
"Northern Pame",
3361762,
"omq",
"Latn",
}
m["pmr"] = {
"Paynamar",
3450824,
}
m["pms"] = {
"Piedmontese",
15085,
"roa-git",
"Latn",
}
m["pmt"] = {
"Tuamotuan",
36763,
"poz-pep",
"Latn",
}
m["pmu"] = {
"Mirpur Panjabi",
6874480,
}
m["pmw"] = {
"Plains Miwok",
3391031,
"nai-you",
"Latn",
}
m["pmx"] = {
"Poumei Naga",
12952910,
"tbq-anp",
}
m["pmy"] = {
"Papuan Malay",
12473446,
"crp",
"Latn",
ancestors = "ms",
}
m["pmz"] = {
"Southern Pame",
3361765,
"omq",
"Latn",
}
m["pna"] = {
"Punan Bah-Biau",
4842201,
"poz-bnn",
"Latn",
}
m["pnc"] = {
"Pannei",
7131391,
}
m["pnd"] = {
"Mpinda",
63308194,
"bnt-kmb",
}
m["pne"] = {
"Western Penan",
12953808,
"poz-swa",
"Latn",
}
m["png"] = {
"Pongu",
36282,
"nic-shi",
}
m["pnh"] = {
"Penrhyn",
3130301,
"poz-pep",
"Latn",
}
m["pni"] = {
"Aoheng",
4778608,
"poz",
"Latn",
}
m["pnj"] = {
"Pinjarup",
33103591,
}
m["pnk"] = {
"Paunaka",
2064378,
"awd",
"Latn",
}
m["pnl"] = {
"Paleni",
7127118,
"alv-wan",
"Latn",
}
m["pnm"] = {
"Punan Batu",
7259892,
}
m["pnn"] = {
"Pinai-Hagahai",
5638511,
}
m["pno"] = {
"Panobo",
3141869,
"sai-pan",
"Latn",
}
m["pnp"] = {
"Pancana",
7130204,
}
m["pnq"] = {
"Pana (West Africa)",
7129739,
"nic-gnn",
"Latn",
}
m["pnr"] = {
"Panim",
11732562,
"ngf-mad",
}
m["pns"] = {
"Ponosakan",
7227956,
"phi",
"Latn",
}
m["pnt"] = {
"Pontic Greek",
36748,
"grk",
"Grek, Latn, Cyrl",
ancestors = "gkm",
translit = {
Grek = "el-translit"
},
display_text = {
Grek = s["Grek-displaytext"]
},
entry_name = {
Grek = s["Grek-entryname"]
},
sort_key = {
Grek = s["Grek-sortkey"]
},
}
m["pnu"] = {
"Jiongnai Bunu",
56325,
"hmn",
}
m["pnv"] = {
"Pinigura",
10631927,
"aus-psw",
"Latn",
}
m["pnw"] = {
"Panyjima",
3913830,
"aus-nga",
"Latn",
}
m["pnx"] = {
"Phong-Kniang",
3914627,
"mkh",
}
m["pny"] = {
"Pinyin",
36250,
"nic-nge",
"Latn",
}
m["pnz"] = {
"Pana (Central Africa)",
36241,
"alv-mbm",
"Latn",
}
m["poc"] = {
"Poqomam",
36416,
"myn",
"Latn",
}
m["poe"] = {
"San Juan Atzingo Popoloca",
12953819,
"omq-pop",
"Latn",
}
m["pof"] = {
"Poke",
7208577,
"bnt-ske",
}
m["pog"] = {
"Potiguára",
56722,
"tup-gua",
"Latn",
}
m["poh"] = {
"Poqomchi'",
36414,
"myn",
"Latn",
}
m["poi"] = {
"Highland Popoluca",
7511556,
"nai-miz",
"Latn",
}
m["pok"] = {
"Pokangá",
25559704,
"sai-tuc",
"Latn",
}
m["pom"] = {
"Southeastern Pomo",
3396025,
"nai-pom",
"Latn",
}
m["pon"] = {
"Pohnpeian",
28422,
"poz-mic",
"Latn",
}
m["poo"] = {
"Central Pomo",
3396020,
"nai-pom",
"Latn",
}
m["pop"] = {
"Pwapwâ",
3411153,
"poz-cln",
"Latn",
}
m["poq"] = {
"Texistepec Popoluca",
5908707,
"nai-miz",
"Latn",
}
m["pos"] = {
"Sayula Popoluca",
5908722,
"nai-miz",
"Latn",
}
m["pot"] = {
"Potawatomi",
56749,
"alg",
"Latn",
}
m["pov"] = {
"Guinea-Bissau Creole",
33339,
"crp",
"Latn",
ancestors = "pt",
}
m["pow"] = {
"San Felipe Otlaltepec Popoloca",
25559598,
"omq-pop",
"Latn",
}
m["pox"] = {
"Polabian",
36741,
"zlw-lch",
"Latn",
}
m["poy"] = {
"Pogolo",
2429648,
"bnt-kil",
}
m["ppa"] = {
"Pao",
7132069,
}
m["ppe"] = {
"Papi",
7132809,
}
m["ppi"] = {
"Paipai",
56726,
"nai-yuc",
"Latn",
}
m["ppk"] = {
"Uma",
7881036,
"poz-kal",
"Latn",
}
m["ppl"] = {
"Pipil",
1186896,
"azc-nah",
"Latn",
entry_name = {remove_diacritics = c.acute .. c.macron},
}
m["ppm"] = {
"Papuma",
7133239,
"poz-hce",
"Latn",
}
m["ppn"] = {
"Papapana",
3362757,
"poz-ocw",
"Latn",
}
m["ppo"] = {
"Folopa",
5464843,
"paa",
"Latn",
}
m["ppq"] = {
"Pei",
7160903,
}
m["pps"] = {
"San Luís Temalacayuca Popoloca",
25559602,
"omq-pop",
"Latn",
}
m["ppt"] = {
"Pa",
3504757,
"ngf",
"Latn",
}
m["ppu"] = {
"Papora",
2094884,
"map",
"Latn",
}
m["pqa"] = {
"Pa'a",
3441315,
"cdc-wst",
}
m["pqm"] = {
"Malecite-Passamaquoddy",
3183144,
"alg-eas",
"Latn",
}
m["pra"] = {
"প্রাকৃত",
192170,
"inc-mid",
"Brah, Deva, Gujr, Knda",
ancestors = "inc-ash",
translit = {
Brah = "Brah-translit",
Deva = "pra-Deva-translit",
Gujr = "sa-Gujr-translit",
Knda = "pra-Knda-translit",
},
entry_name = {
from = {"ऎ", "ऒ", u(0x0946), u(0x094A), "य़", "ಯ಼", u(0x11071), u(0x11072), u(0x11073), u(0x11074)},
to = {"ए", "ओ", u(0x0947), u(0x094B), "य", "ಯ", "𑀏", "𑀑", u(0x11042), u(0x11044)}
} ,
}
m["prc"] = {
"Parachi",
2640637,
"ira-orp",
"Arab",
}
-- "prd" IS NOT INCLUDED, SEE WT:LT
m["pre"] = {
"Principense",
36520,
"crp",
"Latn",
ancestors = "pt",
}
m["prf"] = {
"Paranan",
7135433,
"phi",
}
m["prg"] = {
"Old Prussian",
35501,
"bat-wes",
"Latn",
}
m["prh"] = {
"Porohanon",
6583710,
"phi",
"Latn",
}
m["pri"] = {
"Paicî",
732131,
"poz-cln",
"Latn",
}
m["prk"] = {
"Parauk",
3363719,
"mkh-pal",
"Latn",
}
m["prl"] = {
"Peruvian Sign Language",
3915508,
"sgn",
}
m["prm"] = {
"Kibiri",
56745,
"paa",
}
m["prn"] = {
"Prasuni",
32689,
"nur-nor",
}
m["pro"] = {
"Old Occitan",
2779185,
"roa-ocr",
"Latn",
sort_key = {remove_diacritics = c.cedilla},
}
-- "prp" IS NOT INCLUDED, SEE WT:LT
m["prq"] = {
"Ashéninka Perené",
3450601,
"awd",
"Latn",
}
m["prr"] = {
"Puri",
7261687,
}
-- "prs" IS TREATED AS "fa" (or as etymology-only), SEE WT:LT
m["prt"] = {
"Phai",
7180184,
"mkh",
}
m["pru"] = {
"Puragi",
7260800,
"ngf-sbh",
}
m["prw"] = {
"Parawen",
7136291,
"ngf-mad",
}
m["prx"] = {
"Purik",
567905,
"sit-lab",
}
m["prz"] = {
"Providencia Sign Language",
3322084,
"sgn",
}
m["psa"] = {
"Asue Awyu",
11266334,
}
m["psc"] = {
"Persian Sign Language",
7170221,
"sgn",
}
m["psd"] = {
"Plains Indian Sign Language",
2380124,
"sgn",
}
m["pse"] = {
"Central Malay",
3367751,
"poz-mly",
"Latn, Rjng",
}
m["psg"] = {
"Penang Sign Language",
4924925,
"sgn",
}
m["psh"] = {
"Southwest Pashayi",
16112270,
"inc-pas",
"fa-Arab",
}
m["psi"] = {
"Southeast Pashayi",
23713536,
"inc-pas",
"fa-Arab",
}
m["psl"] = {
"Puerto Rican Sign Language",
7258608,
"sgn-fsl",
}
m["psm"] = {
"Pauserna",
2912846,
"tup-gua",
"Latn",
}
m["psn"] = {
"Panasuan",
7130113,
"poz",
}
m["pso"] = {
"Polish Sign Language",
3915194,
"sgn-gsl",
}
m["psp"] = {
"Philippine Sign Language",
3551357,
"sgn-fsl",
}
m["psq"] = {
"Pasi",
7142091,
}
m["psr"] = {
"Portuguese Sign Language",
3915472,
"sgn",
}
m["pss"] = {
"Kaulong",
3194294,
"poz-ocw",
}
m["psw"] = {
"Port Sandwich",
3398324,
"poz-vnc",
"Latn",
}
m["psy"] = {
"Piscataway",
3504233,
"alg-eas",
}
m["pta"] = {
"Pai Tavytera",
7124619,
"tup-gua",
"Latn",
}
m["pth"] = {
"Pataxó Hã-Ha-Hãe",
7144304,
}
m["pti"] = {
"Pintiini",
10632026,
"aus-pam",
}
m["ptn"] = {
"Patani",
7144242,
"poz-hce",
"Latn",
}
m["pto"] = {
"Zo'é",
8073148,
"tup-gua",
"Latn",
}
m["ptp"] = {
"Patep",
3368679,
"poz-ocw",
"Latn",
}
m["ptq"] = {
"Pattapu",
60785085,
"dra-tam",
}
m["ptr"] = {
"Piamatsina",
7190040,
"poz-vnn",
"Latn",
}
m["ptt"] = {
"Enrekang",
12953520,
nil,
"Latn",
}
m["ptu"] = {
"Bambam",
4853321,
"poz-ssw",
"Latn",
}
m["ptv"] = {
"Port Vato",
3398323,
"poz-vnc",
"Latn",
}
m["ptw"] = {
"Pentlatch",
2069475,
"sal",
"Latn",
}
m["pty"] = {
"Pathiya",
7144790,
"dra-mal",
}
m["pua"] = {
"Purepecha",
16114351,
"qfa-iso",
"Latn",
sort_key = {remove_diacritics = c.acute},
}
m["pub"] = {
"Purum",
6400562,
"tbq-kuk",
"Latn",
}
m["puc"] = {
"Punan Merap",
7259895,
"poz",
"Latn",
}
m["pud"] = {
"Punan Aput",
4782333,
"poz-swa",
"Latn",
}
m["pue"] = {
"Puelche",
33660,
}
m["puf"] = {
"Punan Merah",
7259894,
"poz-swa",
"Latn",
}
m["pug"] = {
"Phuie",
36375,
"nic-gnw",
}
m["pui"] = {
"Puinave",
3027918,
nil,
"Latn",
}
m["puj"] = {
"Punan Tubu",
7259896,
"poz-swa",
"Latn",
}
m["pum"] = {
"Puma",
33736,
"sit-kic",
}
m["puo"] = {
"Puoc",
6440803,
"mkh",
"Latn",
}
m["pup"] = {
"Pulabu",
7259163,
"ngf-mad",
}
m["puq"] = {
"Puquina",
1207739,
}
m["pur"] = {
"Puruborá",
7261619,
"tup",
}
m["put"] = {
"Putoh",
12953832,
"poz-swa",
"Latn",
}
m["puu"] = {
"Punu",
36401,
"bnt-sir",
"Latn",
}
m["puw"] = {
"Puluwat",
36397,
"poz-mic",
"Latn",
}
m["pux"] = {
"Puare",
3507983,
}
m["puy"] = {
"Purisimeño",
2967638,
"nai-chu",
"Latn",
}
m["pwa"] = {
"Pawaia",
7156099,
"paa",
"Latn",
}
m["pwb"] = {
"Panawa",
47385077,
"nic-jer",
"Latn",
ancestors = "jer",
}
m["pwg"] = {
"Gapapaiwa",
3095245,
"poz-ocw",
"Latn",
}
m["pwi"] = {
"Patwin",
3370188,
"nai-wtq",
"Latn",
}
m["pwm"] = {
"Molbog",
6895718,
"poz-san",
"Latn",
}
m["pwn"] = {
"Paiwan",
715755,
"map",
"Latn",
}
m["pwo"] = {
"Western Pwo",
7988202,
"kar",
"Mymr",
}
m["pwr"] = {
"Powari",
12640277,
"inc-hie",
"Deva",
}
m["pww"] = {
"Northern Pwo",
7058885,
"kar",
"Thai",
}
m["pxm"] = {
"Quetzaltepec Mixe",
6842374,
"nai-miz",
"Latn",
}
m["pye"] = {
"Pye Krumen",
11157382,
"kro-grb",
}
m["pym"] = {
"Fyam",
3914025,
"nic-ple",
"Latn",
}
m["pyn"] = {
"Poyanáwa",
3401023,
"sai-pan",
}
m["pys"] = {
"Paraguayan Sign Language",
7134698,
"sgn",
}
m["pyu"] = {
"Puyuma",
716690,
"map",
"Latn",
}
m["pyx"] = {
"Pyu (Myanmar)",
36259,
"sit",
}
m["pyy"] = {
"Pyen",
7262966,
"tbq-bis",
}
m["pzh"] = {
"Pazeh",
36435,
"map",
"Latn",
}
m["pzn"] = {
"Para Naga",
7133667,
"sit-aao",
}
return require("Module:languages").finalizeData(m, "language")
pq7ghpcdqjx5ub514l8prb7nghl6s8p
জড়
0
125846
509676
272088
2026-06-04T11:40:08Z
Redmin
6857
লেক্সিম লিংকার এক্সটেনশনের সাহায্যে উইকিউপাত্ত লেক্সিম L1567161-এর সাথে সংযোগ তৈরি করছি
509676
wikitext
text/x-wiki
{{লে|L1567161|ব্যুৎপত্তি=* [[সংস্কৃত]] জাত
* √জল্+ অ|উচ্চারণ=জড়ো|meaning=#[[ইন্দ্রিয়গ্রাহ্য]], [[ভৌত]]
#মোহগ্রস্ত, [[মূঢ়]]}}
=== বিশেষ্য ===
{{বিশেষ্য|bn}}
#[[বস্তু|বস্তুর]] [[উপাদান]], [[অচেতন]] [[পদার্থ]]
#নিষ্ক্রিয় ব্যক্তি
#[[পঞ্চভূত]]
=== ব্যুৎপত্তি ২ ===
* [[সংস্কৃত]] [[জটা]] থেকে।
===উচ্চারণ===
* জড়্
=== বিশেষ্য ===
{{বিশেষ্য|bn}}
#[[শিকড়]], [[মূল]]
#[[আদি]] [[কারণ]], [[গোড়া]]
buatbec2efxs9u9cmp88380nuxfoj7r
-ক
0
127720
509656
271550
2026-06-04T09:21:48Z
Redmin
6857
/* প্রত্যয় */
509656
wikitext
text/x-wiki
== {{ভাষা|bn}} ==
===ব্যুৎপত্তি===
* '-ক' (কপ) সংস্কৃত থেকে বাংলায় আগত তদ্ধিত প্রত্যয়। এটি সাধারণত মূল শব্দের শেষে যুক্ত হয়ে নতুন শব্দ তৈরি করে যা কিছু নির্দিষ্ট অর্থ বা বৈশিষ্ট্য নির্দেশ করে।
===উচ্চারণ===
* -ক্
===প্রত্যয়===
*'-ক' প্রত্যয় বাংলা ভাষায় বিভিন্ন শব্দে ব্যবহৃত হয় এবং মূল শব্দের শেষে যুক্ত হয়ে নতুন অর্থবহ শব্দ তৈরি করে।
# নায়ক (নায় + ক): "নায়" মানে নেতা বা পথপ্রদর্শক, "ক" যুক্ত হয়ে এটি "নায়ক" অর্থাৎ নেতা বা প্রধান ব্যক্তি হয়।
# দর্শক (দর্শ + ক): "দর্শ" মানে দেখা, "ক" যুক্ত হয়ে এটি "দর্শক" অর্থাৎ যে দেখে এমন ব্যক্তি হয়।
hhmvkkjtxrzbgeqrw6ru34qu6s9iz0u
أهل
0
128643
509640
334346
2026-06-03T12:57:43Z
Redmin
6857
/* দক্ষিণ লেভান্তীয় আরবি */
509640
wikitext
text/x-wiki
=={{ভাষা|ar}}==
===ব্যুৎপত্তি ১===
{{inh|ar|sem-pro|*ʔahl-}} থেকে।
====উচ্চারণ====
* {{audio|ar|Ar-أهل.ogg}}
====বিশেষ্য====
{{ar-noun|أَهْل|m|g2=p|pl=أَهْلُونَ|pl2=أَهَالٍ|plcons=أَهَالِي}}
# [[পরিবার]], [[গৃহস্থালি]]; [[লোকজন]], [[আত্মীয়স্বজন]], [[আত্মীয়]]
# {{lb|ar|অপ্রচলিত}} [[স্ত্রী]]
# [[লোকজন]], [[সদস্যপদ]], [[অনুগামী]]
## [[অনুসারী]]
##: أَهْل الْحَدِيث ― ''ʔahl al-ḥadīṯ'' ― [[আহলে হাদীস]]
##: أَهْل الْكِتَاب ― ''ʔahl al-kitāb'' ― [[আহলে কিতাব]]
## [[সুদক্ষ]]
# [[বসবাসকারী]]
====ক্রিয়া====
{{ar-verb|I|a|i,u}}
# [[বিয়ে]] করা
====ক্রিয়া====
{{ar-verb|I|i,a|a}}
# পরিচিত হওয়া
# {{lb|ar|সকর্মক}} অভ্যস্ত হওয়া
====ক্রিয়া====
{{ar-verb|I|a|u|passive=only}} {{lb|ar|passive}} <br />
{{ar-verb|I|a|u}}
# {{lb|ar|stative}} কোথাও আবাস করা
====বিশেষণ====
{{ar-adj|أَهْل|f=أَهْل|cpl=أَهْل|d=أَهْل}}
# [[যোগ্য]]
====বিশেষ্য====
{{ar-noun|أَهَل|m}}
#[[أهل]] (ʾআহিলা) এর ক্রিয়া বিশেষণ
#[[أهل]] (ʾআহালা) এর ক্রিয়া বিশেষণ
# [[পরিচিতকরণ]]
# [[অভ্যস্তকরণ]]
====ক্রিয়া====
{{ar-verb|II}}
# [[যোগ্য]] হওয়া
# [[যোগ্য]] বা [[উপযুক্ত]] হিসেবে বিবেচিত হওয়া।
=== ব্যুৎপত্তি ২ ===
{| class="wikitable"
!মূল
|-
|ه ل ل (h l l)
|}
====ক্রিয়া====
{{ar-verb|IV}}
# আরম্ভ করা, খ্যাতনামা হওয়া
# আনয়ন করা, [[offer|অফার]] করা
==দক্ষিণ লেভান্তীয় আরবি==
{{ar-rootbox|ء ه ل}}
===ব্যুৎপত্তি ১===
{{inh|ajp|ar|أَهَّلَ}} থেকে।
====উচ্চারণ====
* {{ajp-IPA|ʔahhal<p:ˈʔah.hal>}}
* {{audio|ajp|LL-Q55633582 (ajp)-Khalil.rantissi-أهَّل.wav|a=al-Lidd}}
====ক্রিয়া====
{{ajp-verb|II|head=أهّل|tr=ʔahhal|pres=بأهّل|prestr=biʔahhel}}
# (কাউকে) [[প্রশিক্ষণ]] [[দেওয়া]], [[যোগ্য]] করে [[গড়া|গড়ে]] [[তোলা]]
# [[স্বাগত]] জানানো
# {{lb|ajp|সকর্মক}} বিবাহ করা
===ব্যুৎপত্তি ২===
{{inh|ajp|ar|أَهْل}} থেকে
====উচ্চারণ====
* {{ajp-IPA|ʔahl<p:ˈʔa.h(ɪ)l>}}
* {{audio|ajp|LL-Q55633582 (ajp)-MahmoudM (AdrianAbdulBaha)-اهل.wav|a=Jerusalem}}
====বিশেষ্য====
{{ajp-noun|g=m|tr=ʔahl|pl=أهالي|pltr=ʔahāli}}
# [[parents|বাবা-মা]]
# [[পরিবার]] {{q|[[বর্ধিত পরিবার]]}}
# [[লোকজন]]
6xitlkd56ejlifquk5pij1vm1fkc0cj
विश्वयुद्ध
0
148010
509655
440770
2026-06-04T09:20:46Z
Redmin
6857
/* বিশেষ্য */
509655
wikitext
text/x-wiki
=={{ভাষা|hi}}==
===ব্যুৎপত্তি===
{{calque|hi|en|world war}}, {{com|hi|विश्व|युद्ध|tr1=বিশ্ব|tr2=য়ুদ্ধ|t1=world|t2=war}}। তুল্য শব্দ {{cog|bn|বিশ্বযুদ্ধ}}।
===উচ্চারণ===
* {{IPA|hi| /ʋɪʃ.ʋə.jʊd̪d̪ʱ/, [ʋɪʃ.ʋɐ.jʊd̪(ː)ʱ]}}
===বিশেষ্য===
{{hi-noun}}
# [[বিশ্বযুদ্ধ]]
bo12vx1yxx585fywjlpaoqukuwcalxl
½
0
148090
509659
308324
2026-06-04T09:25:22Z
Redmin
6857
509659
wikitext
text/x-wiki
{{character info}}
==বহুভাষিক==
===সংখ্যা===
* ([[গণিত]]), [[অর্ধাংশ]], [[দুই]] ভাগের এক ভাগ।
jb09ozbjzm546x55iogocg8rwq3lwl7
¼
0
148091
509658
308325
2026-06-04T09:24:54Z
Redmin
6857
/* সংখ্যা */
509658
wikitext
text/x-wiki
{{character info}}
==বহুভাষিক==
===সংখ্যা===
* ([[গণিত]]), এক-চতুর্থাংশ, [[চার]] ভাগের এক ভাগ।
h0b5xe1k0ly0wokjoa3coa8yxt9puah
¾
0
148092
509661
308326
2026-06-04T09:35:30Z
Redmin
6857
/* সংখ্যা */
509661
wikitext
text/x-wiki
{{character info}}
==বহুভাষিক==
===সংখ্যা===
* ([[গণিত]]), তিন-চতুর্থাংশ, [[চার]] ভাগের [[তিন]] ভাগ।
rb5ku29hxaajp72rnnjt2sntr8luvm5
يلنجوج
0
148335
509651
335110
2026-06-04T09:16:32Z
Redmin
6857
/* আরবি */
509651
wikitext
text/x-wiki
=={{ভাষা|ar}}==
===বিকল্প রূপ===
* {{alter|ar|يَلَنْجَج|يَلَنْجِيج|أَلَنْجُوج|أَلَنْجَج|أَلَنْجِيج|يَنْجُوج|أَنْجُوج}}
===ব্যুৎপত্তি===
{{cog|grc|ἀγάλοχον}} এর অনুরূপ। {{etystub|ar}}
===উচ্চারণ===
* {{ar-IPA|يَلَنْجُوج}}
===বিশেষ্য===
{{ar-বিশেষ্য}}
# {{lb|ar|উদ্ভিদবিদ্যা}} একটি সুগন্ধি, গাঢ় এবং রজনযুক্ত কাঠ যা ধূপ, সুগন্ধি এবং ছোট হস্তনির্মিত খোদাইয়ে ব্যবহৃত হয়, যা এক প্রকারের সুগন্ধি প্রদায়ক উদ্ভিদ থেকে উৎপাদিত হয় ([[Aquilaria malaccensis]])।
8pwegrrwwe89qwj72xi1uimwfb50py2
509652
509651
2026-06-04T09:16:44Z
Redmin
6857
509652
wikitext
text/x-wiki
=={{ভাষা|ar}}==
===বিকল্প রূপ===
* {{alter|ar|يَلَنْجَج|يَلَنْجِيج|أَلَنْجُوج|أَلَنْجَج|أَلَنْجِيج|يَنْجُوج|أَنْجُوج}}
===ব্যুৎপত্তি===
{{cog|grc|ἀγάλοχον}} এর অনুরূপ। {{etystub|ar}}
===উচ্চারণ===
* {{ar-IPA|يَلَنْجُوج}}
===বিশেষ্য===
{{ar-noun}}
# {{lb|ar|উদ্ভিদবিদ্যা}} একটি সুগন্ধি, গাঢ় এবং রজনযুক্ত কাঠ যা ধূপ, সুগন্ধি এবং ছোট হস্তনির্মিত খোদাইয়ে ব্যবহৃত হয়, যা এক প্রকারের সুগন্ধি প্রদায়ক উদ্ভিদ থেকে উৎপাদিত হয় ([[Aquilaria malaccensis]])।
lp3l98yqh12jhc85551jbidu9nikbov
মডিউল:আভিধানিক উপাত্ত/i18n
828
148710
509671
509631
2026-06-04T11:24:06Z
Redmin
6857
509671
Scribunto
text/plain
local p = {}
p['fallback_wikipedia'] = 'enwiki'
p['content_lang_name'] = 'বাংলা'
p['content_lang_code'] = 'bn' -- The proper way to do this would be to use mw.ustring.gsub(mw.site.wikiId, 'wiktionary', '') but that does mean wasting compute so instead, just use the 'cached' config which isn't really labour-intensive.
p['wikipedia'] = 'bnwiki' -- p['content_lang_code'] .. 'wiki'
p['heading_etymology'] = 'ব্যুৎপত্তি'
p['heading_pronunciation'] = 'উচ্চারণ'
p['heading_translation'] = 'অনুবাদ'
p['heading_references'] = 'তথ্যসূত্র'
p['heading_external_links'] = 'বহিঃসংযোগ'
p['heading_alternative_spellings'] = 'বিকল্প বানান'
p['heading_inflection_table'] = 'বিভক্তির সারণী'
p['heading_form'] = 'রূপ'
p['heading_grammatical_features'] = 'ব্যাকরণিক বৈশিষ্ট্য'
p['heading_image'] = 'চিত্র'
p['heading_alternative_script'] = 'বিকল্প রূপ'
p['text_instance_of'] = 'একটি'
p['text_audio'] = 'অডিও'
p['text_iso15919'] = '[[w:আইএসও ১৫৯১৯|আইএসও ১৫৯১৯]]:'
p['text_iast'] = '[[w:সংস্কৃত লিপ্যন্তরের আন্তর্জাতিক বর্ণমালা|সলিআব]]:'
p['text_itrans'] = '[[w:en:ITRANS|ITRANS]]:'
p['text_xsampa'] = '[[w:এক্স-সাম্পা|এক্স-সাম্পা]]:'
p['text_syllable_count'] = '[[w:অক্ষর (সিলেবল)|অক্ষর]] সংখ্যা:'
p['template_lexeme'] = 'উইকিউপাত্ত লেক্সিম' -- Q81739987
p['template_wikipedia'] = 'উইকিপিডিয়া' -- Q6275256
p['template_audio'] = 'অডিও ভাষার নাম' -- Q138620346
p['template_anchor'] = 'anchor' -- Q5412976
p['template_rfdef'] = 'rfdef' -- Q30733154
p['template_ipa'] = 'আধ্বব ভাষার নাম' -- Q138608718
p['template_t'] = 't' -- Q30769953
p['template_trans-top'] = 'অনুবাদ-শীর্ষ' -- Q30528422
p['template_trans-bottom'] = 'অনুবাদ-নিচ' -- Q30528419
p['template_antonym'] = 'বিপরীতার্থক' -- Q35305357
p['template_synonym'] = 'synonyms' -- Q32751230
p['template_hypernym'] = 'hypernyms' -- Q35305454
p['template_demonym-noun'] = 'demonym-noun' -- Q130360250
p['template_demonym-adj'] = 'demonym-adj' -- Q135184225
p['template_homophones'] = 'সমোচ্চারিত' -- Q30557565
p['template_borrowed'] = 'ধারকৃত শব্দ' -- Q30872304
p['template_inherited'] = 'inherited' -- Q30864051
p['template_ar-rootbox'] = 'ar-rootbox' -- Q115627074
p['template_root'] = 'root' -- Q104521645
p['template_ellipsis'] = 'ellipsis' -- Q107037827
p['template_prefixsee'] = 'prefixsee' -- Q47517865
p['template_rootsee'] = 'rootsee' -- Q105987491
p['template_suffixsee'] = 'suffixsee' -- Q47517855
p['noun_template_suffix'] = '-noun'
p['noun_template_suffix_fallback'] = '-বিশেষ্য'
p['proper noun_template_suffix'] = '-proper noun'
p['proper noun_template_suffix_fallback'] = '-নামবাচক বিশেষ্য'
p['verb_template_suffix'] = '-verb'
p['verb_template_suffix_fallback'] = '-ক্রিয়া'
p['manual_category'] = {'বিষয়শ্রেণী', 'category'}
p['manual_etymology'] = {'ব্যুৎপত্তি', 'etymology'}
p['manual_pronunciation'] = {'উচ্চারণ', 'pronunciation'}
p['manual_meaning'] = {'অর্থ', 'meaning'}
p['manual_reference'] = {'তথ্যসূত্র', 'reference'}
p['manual_external_link'] = {'বহিঃসংযোগ', 'external_link'}
p['etymology_borrowing'] = 'থেকে [[w:ঋণশব্দ|ঋণকৃত]]'
p['etymology_learned_borrowing'] = '$1 থেকে [[d:Q845079|শিক্ষিতভাবে ঋণকৃত]]'
p['etymology_inheritance'] = 'থেকে [[উত্তরলব্ধ]]'
p['etymology_ellipsis'] = '[[w:en:Ellipsis (linguistics)|সংক্ষিপ্ত হয়ে]] উপলব্ধ'
-- অংশচ্ছেদ (ভাষাবিজ্ঞান)
p['edit_wikidata'] = 'উইকিউপাত্তে সম্পাদনা করুন'
-- বিষয়শ্রেণী যেগুলো অন্য উইকিঅভিধানে আছে
p['category_rfdef'] = 'ভাষা অনুযায়ী সংজ্ঞার জন্য অনুরোধ' -- Q33129136
p['category_rfdef_equivalent'] = 'শব্দার্থের বাংলা মানের অনুরোধ' -- [[:wikt:en:Category:Requests for English equivalent term by language]]-এর কাছাকাছি
p['category_no_matching_lemma'] = 'যেসব ভুক্তিতে লেমার হেডিং দেখানো অসম্ভব' -- Entries will be put in this category whenever a lemma heading cannot be rendered because no lemma of the linked lexeme matched the page title.
p['category_given_names'] = 'প্রদত্ত নাম' -- Q8492384
-- বিষয়শ্রেণী যেগুলো অন্য উইকিঅভিধানে নেই
p['text_category_rfdef'] = 'এই শব্দের লেক্সিমে অর্থ প্রয়োজন। দয়া করে লেক্সিম পাতায় গিয়ে একটি অর্থ যোগ করুন, যাতে অন্য পাঠকরা জানতে পারবে এটা মানে কি।'
function p.tocatlink(str)
return '[[বিষয়শ্রেণী:' .. str .. ']]'
end
function p.lang_category(lex_cat, language)
local cat_text = p.tocatlink(language .. ' লেমা')
cat_text = cat_text .. p.tocatlink(language .. ' ' .. lex_cat)
return cat_text
end
function p.trans_category(language)
return p.tocatlink(language .. ' অনুবাদসহ পাতা')
end
function p.wplink(qid, display_text, wb)
local link = wb.getSitelink(qid, p['wikipedia'])
if link == nil then
link = wb.getSitelink(qid, p['fallback_wikipedia'])
if link ~= nil then
return '[[w:en:' .. link .. '|' .. display_text .. ']]'
end
else
return '[[w:' .. link .. '|' .. display_text .. ']]'
end
return ''
end
function p.rfref_category(language)
local cat_text = p.tocatlink('তথ্যসূত্রহীন ' .. language .. ' শব্দ',language)
return cat_text
end
p.nolinks = { -- These are words that are so commonly used that links to entries about them are unnecessary.
['করা'] = true,
['হওয়া'] = true,
['যাওয়া'] = true,
['যে'] = true,
['ও'] = true,
['তার'] = true,
['এবং'] = true,
['যার'] = true,
['যায়'] = true,
}
return p
36gdragd4s7iybusbtn4f0xsrnlu5ct
-তম
0
149158
509657
314313
2026-06-04T09:22:43Z
Redmin
6857
/* প্রত্যয় */
509657
wikitext
text/x-wiki
=={{ভাষা|bn}}==
===উচ্চারণ===
*[তমো]
===ব্যুৎপত্তি ১===
*{(তৎসম বা সংস্কৃত) [[তদ্ধিত প্রত্যয়]] তমট্}
===প্রত্যয়===
#প্রত্যয়বিশেষ; সংখ্যার পূরক বা ভাগবাচক প্রত্যয় ([[অশীতিতম]])।
===ব্যুৎপত্তি ২===
*{(তৎসম বা সংস্কৃত) তদ্ধিত প্রত্যয়তমপ্}
===অব্যয়===
#বহু বস্তু বা ব্যক্তির মধ্যে একজনের সর্বাধিক [[উৎকর্ষ]] বা [[অপকর্ষ]]বোধক প্রত্যয় (বৃহত্তম, মহত্তম, নীচতম)।
fvbdq9wmcdat89o6bl00kyahmbq3itv
ꠄꠛꠟꠣ
0
152977
509648
460682
2026-06-04T06:59:27Z
ꠢꠣꠍꠘ ꠞꠣꠎꠣ
9520
/* তথ্যসূত্র */
509648
wikitext
text/x-wiki
=={{ভাষা|syl}}==
===উচ্চারণ===
* এব্লা
===ব্যুৎপত্তি===
এর উৎস {{der|syl|pra|𑀯𑁂𑀮𑀸}}.
===ক্রিয়া বিশেষণ===
{{head|syl|adক্রিয়া}}
# [[এখন]]
===তথ্যসূত্র===
* {{R:syl:Gwynn|ebla}}
gd6vnj2u118bh8ck245cu99ozb4w4o0
समुद्र
0
156501
509660
324895
2026-06-04T09:34:55Z
Redmin
6857
509660
wikitext
text/x-wiki
== {{ভাষা|sa}} ==
=== উচ্চারণ ===
* {{ipa|sa|/ʃo.mud.ro/}}
=== ব্যুৎপত্তি ===
{{af|sa|सम्|उद्र}}
* সংস্কৃত सम (সম্) = একত্রে + उद्र (উদ্র) = জল → সমুদ্র = 'সম্মিলিত জলরাশি'
=== বিশেষ্য ===
{{sa-noun}}
# বৃহৎ জলরাশি; [[সাগর]]
# [[মহাসাগর]] বা [[সমুদ্র]]
owk179lry5spxvftpk6c5znn3v4hgvj
wobbly
0
157222
509643
333541
2026-06-03T14:53:13Z
Redmin
6857
/* উৎসারিত শব্দ */
509643
wikitext
text/x-wiki
=={{ভাষা|en}}==
{{also|Wobbly}}
===উত্পত্তি===
{{suffix|en|wobble|y}} – ‘wobble’ (দুলে ওঠা বা টলমল করা) থেকে ‘-y’ যোগে গঠিত।
===উচ্চারণ===
* {{IPA|en|/ˈwɒb(ə)li/|a=ব্রিটিশ}}
* {{IPA|en|/ˈwɑb(ə)li/|a=আমেরিকান}}
* {{audio|en|En-au-wobbly.ogg|a=অস্ট্রেলিয়ান}}
* {{rhymes|en|ɒbəli|s=3}}
===বিশেষণ===
{{en-adj|er}}
# [[অস্থিতিশীল]], এবং [[দুলতে]] বা [[টলমল]] করতে প্রবণ।
#: {{syn|en|precarious|rickety|shaky|tottering|unsafe|unstable|unsteady}}
#: {{co|en|'''wobbly''' bridge|দুলতে থাকা সেতু}}
#: {{co|en|'''wobbly''' chair|দুলে ওঠা চেয়ার}}
#: {{co|en|'''wobbly''' movement|অস্থির চলন}}
#: {{co|en|'''wobbly''' table|টলমল করা টেবিল}}
====উৎসারিত শব্দ====
* {{l|en|wobbly boots}} – মাতাল হওয়ার অবস্থা বোঝাতে ব্যবহৃত
* {{l|en|wobbly cat syndrome}} – স্নায়বিক ব্যাধি
* {{l|en|wobbly egg}} – আংশিক গঠিত ডিম
* {{l|en|wobbly hedgehog syndrome}} – একটি নিউরোডিজেনারেটিভ রোগ
* {{l|en|wobbly pop}} – নরম কিন্তু নাচার উপযোগী পপ সঙ্গীত
====অনুবাদ====
{{trans-top|অস্থিতিশীল ও দুলে ওঠে এমন}}
* ডাচ: {{t+|nl|wankel}}, {{t+|nl|mank}}, {{t+|nl|kreupel}}
* ফিনিশ: {{t+|fi|horjuva}}
* ফরাসি: {{t+|fr|vacillant|m}}, {{t+|fr|chancelant|m}}
* গালিসিয়ান: {{t+|gl|penzo}}
* আইসল্যান্ডীয়: {{t|is|valtur}}
* ইতালীয়: {{t+|it|traballante}}
* জাপানি: {{t|ja|ブラブラした|tr=burabura-shita|sc=Jpan}}
* লাতিন: {{t|la|cādūcus}}
* মাওরি: {{t|mi|tīmangamanga}}, {{t|mi|ānewanewa}}, {{t|mi|tatutatu}}
* পোলিশ: {{t+|pl|chwiejny}}
* রুশ: {{t+|ru|шаткий|m}}, {{t+|ru|зыбкий|m}}
* ইউক্রেনীয়: {{t|uk|хистки́й|m}}, {{t|uk|хитки́й|m}}
{{trans-bottom}}
===বিশেষ্য===
{{en-noun}}
# {{alternative spelling of|en|Wobbly}} – "Wobbly" শব্দের বিকল্প বানান, সাধারণত {{w|Industrial Workers of the World}} সদস্যদের বোঝাতে ব্যবহৃত।
# {{lb|en|সাধারণ কথ্য ভাষা|বিশেষ করে|যুক্তরাজ্য|আয়ারল্যান্ড|কমনওয়েলথ}} [[রেগে যাওয়া]], [[ক্ষোভের বহিঃপ্রকাশ]], [[tantrum]]।
#: {{syn|en|Thesaurus:tantrum}}
====উৎসারিত শব্দ====
{{col|en|chuck a wobbly|throw a wobbly}}
===আনাগ্রাম===
* {{anagrams|en|a=bblowy|Bowlby|blow-by|blowby|by-blow|byblow}}
{{C|en|Anger}}
ay3e965xna3r16d3i9g1oyszn26o0z8
hydrator
0
157859
509666
332919
2026-06-04T09:41:51Z
Redmin
6857
/* ইংরেজি */
509666
wikitext
text/x-wiki
=={{ভাষা|en}}==
===উৎপত্তি===
ইংরেজি {{m|en|hydrate}} শব্দের সঙ্গে {{m|en|-or}} প্রত্যয় যোগে গঠিত।
===বিশেষ্য===
{{en-noun}}
# {{l|en|hydrate}} ক্রিয়াপদের ভিত্তিতে, এমন কিছু যা [[জলযোগ|জল যোগায়]] বা [[আর্দ্রতা]] প্রদান করে।
# [[রেফ্রিজারেটর]]-এর একটি [[কক্ষ|কক্ষ]] বা [[ড্রয়ার]], যেখানে [[দ্রব্য|দ্রব্যগুলি]] [[আর্দ্রতা|আর্দ্রতা]] হ্রাস না পেয়ে [[তাজা]] থাকে।
====ব্যুৎপন্ন শব্দ====
* [[রি-হাইড্রেটর]]
====অনুবাদসমূহ====
{{trans-top|যা আর্দ্রতা প্রদান করে}}
* ফরাসি: {{t+|fr|hydrateur|m}}
{{trans-bottom}}
===তথ্যসূত্র===
* [http://dictionary.reference.com/browse/hydrator?s=t Dictionary.com]
===আনাগ্রাম===
* {{anagrams|bn|hortyard}}
bjwxt89shdt21mbx0uhagf8j4dkb7dd
509667
509666
2026-06-04T09:42:03Z
Redmin
6857
509667
wikitext
text/x-wiki
=={{ভাষা|en}}==
===উৎপত্তি===
ইংরেজি {{m|en|hydrate}} শব্দের সঙ্গে {{m|en|-or}} প্রত্যয় যোগে গঠিত।
===বিশেষ্য===
{{en-noun}}
# {{l|en|hydrate}} ক্রিয়াপদের ভিত্তিতে, এমন কিছু যা [[জলযোগ|জল যোগায়]] বা [[আর্দ্রতা]] প্রদান করে।
# [[রেফ্রিজারেটর]]-এর একটি [[কক্ষ|কক্ষ]] বা [[ড্রয়ার]], যেখানে [[দ্রব্য|দ্রব্যগুলি]] [[আর্দ্রতা|আর্দ্রতা]] হ্রাস না পেয়ে [[তাজা]] থাকে।
====ব্যুৎপন্ন শব্দ====
* [[রি-হাইড্রেটর]]
====অনুবাদসমূহ====
{{trans-top|যা আর্দ্রতা প্রদান করে}}
* ফরাসি: {{t+|fr|hydrateur|m}}
{{trans-bottom}}
===তথ্যসূত্র===
* [http://dictionary.reference.com/browse/hydrator?s=t Dictionary.com]
===আনাগ্রাম===
* {{anagrams|en|hortyard}}
53jqlxlqtoa2oh3so3gipduxu5x2uuc
usucapion
0
157970
509650
333499
2026-06-04T08:57:04Z
Redmin
6857
509650
wikitext
text/x-wiki
=={{ভাষা|en}}==
===উৎপত্তি===
লাতিন {{m|la|usucapio}} থেকে আগত, {{m|la|usucapionis}} যার অর্থ ‘‘অধিকার বা সম্পত্তি নিয়মতান্ত্রিকভাবে সময়ের দ্বারা অর্জন করা’’।
===বিশেষ্য===
{{en-noun}}
# [[সময়]] অতিক্রান্ত হওয়ার মাধ্যমে কোনো [[বস্তু]] বা [[সম্পত্তি]]র উপর [[অধিকার]] বা [[মালিকানা]] অর্জনের প্রক্রিয়া। সাধারণ আইনের সমতুল্য ধারণা হল [[প্রতিকূল অধিকারে ভোগ]]।
====বিকল্প রূপ====
* [[usucaption]]
====সমার্থক শব্দ====
* [[acquisitive prescription]]
* [[adverse possession]]
====অনুবাদ====
{{trans-see|প্রতিকূল অধিকারে ভোগ}}
===আনাগ্রাম===
* {{anagrams|en|punacious}}
{{C|en|আইন}}
6edbm2jevjrx0yc03ai6zuv9ob0dp0y
RNGesus
0
158440
509662
500696
2026-06-04T09:36:23Z
Redmin
6857
509662
wikitext
text/x-wiki
=={{ভাষা|en}}==
===বিকল্প রূপ===
* {{alter|bn|RNJesus}}
===উৎপত্তি===
{{blend|bn|RNG|Jesus}} — ‘‘RNG’’ (Random Number Generator, অর্থাৎ এলোমেলো সংখ্যা নির্ধারক) এবং ‘‘Jesus’’ (যিশু) শব্দ দুটির সংমিশ্রণ।
===উচ্চারণ===
* {{IPA|bn|/আর এন জিসাস/}}
===বিশেষ্য===
{{head|en|বিশেষ্য|g=}}
# {{lb|en|গেমিং|হাস্যকর|আনুষ্ঠানিক নয়}} একটি [[কাল্পনিক]] [[দেবতা]], যিনি [[ভিডিও গেম]] বা [[টেবিলটপ গেম]]-এ {{l|bn|RNG|t=এলোমেলোতা; সম্ভাব্যতা, ভাগ্য}} নিয়ন্ত্রণ করেন।
#* {{quote-journal|1=bn|year=2015|work=Play Magazine|issn=1358-9474|issue=263|editor=Luke Albigés|title=Get More Exotics|page=88|text=Exotic Quests থাকলে, Destiny-তে তোমার সব প্রার্থনা '''RNGesus'''-এর প্রতি নিবেদিত করতে হবে না।}}
#* {{quote-book|bn|date=2021-09-27|author=Schuld|title=Min-Maxing My TRPG Build in Another World: Volume 1|publisher=J-Novel Club|isbn=9781718384484
|text=অন্যদিকে, আমার আগের জীবনে আমি কখনোই এমন সিস্টেমের সাথে মানিয়ে নিতে পারিনি, যেখানে প্রচুর ডাইস রোলের ফলে '''RNGesus'''-এর চোখে আমি অপছন্দনীয় হয়ে উঠতাম, কিন্তু এখন আমি সেসব গেম খেলার সময় আমার ভূমিকা মেনে নিয়েছি। আমি তীব্রভাবে চাইতাম আরেকটি...}}
tuemkrno0j6rd06dwll6uw13bokip69
509663
509662
2026-06-04T09:36:48Z
Redmin
6857
509663
wikitext
text/x-wiki
=={{ভাষা|en}}==
===বিকল্প রূপ===
* {{alter|bn|RNJesus}}
===উৎপত্তি===
{{blend|bn|RNG|Jesus}} — ‘‘RNG’’ (Random Number Generator, অর্থাৎ এলোমেলো সংখ্যা নির্ধারক) এবং ‘‘Jesus’’ (যিশু) শব্দ দুটির সংমিশ্রণ।
===বিশেষ্য===
{{head|en|বিশেষ্য|g=}}
# {{lb|en|গেমিং|হাস্যকর|আনুষ্ঠানিক নয়}} একটি [[কাল্পনিক]] [[দেবতা]], যিনি [[ভিডিও গেম]] বা [[টেবিলটপ গেম]]-এ {{l|bn|RNG|t=এলোমেলোতা; সম্ভাব্যতা, ভাগ্য}} নিয়ন্ত্রণ করেন।
#* {{quote-journal|1=bn|year=2015|work=Play Magazine|issn=1358-9474|issue=263|editor=Luke Albigés|title=Get More Exotics|page=88|text=Exotic Quests থাকলে, Destiny-তে তোমার সব প্রার্থনা '''RNGesus'''-এর প্রতি নিবেদিত করতে হবে না।}}
#* {{quote-book|bn|date=2021-09-27|author=Schuld|title=Min-Maxing My TRPG Build in Another World: Volume 1|publisher=J-Novel Club|isbn=9781718384484
|text=অন্যদিকে, আমার আগের জীবনে আমি কখনোই এমন সিস্টেমের সাথে মানিয়ে নিতে পারিনি, যেখানে প্রচুর ডাইস রোলের ফলে '''RNGesus'''-এর চোখে আমি অপছন্দনীয় হয়ে উঠতাম, কিন্তু এখন আমি সেসব গেম খেলার সময় আমার ভূমিকা মেনে নিয়েছি। আমি তীব্রভাবে চাইতাম আরেকটি...}}
ellrhysj3g95vue26f38ve13j1vkp8r
509664
509663
2026-06-04T09:38:14Z
Redmin
6857
/* ইংরেজি */
509664
wikitext
text/x-wiki
=={{ভাষা|en}}==
===বিকল্প রূপ===
* {{alter|en|RNJesus}}
===উৎপত্তি===
{{blend|en|RNG|Jesus}} — ‘‘RNG’’ (Random Number Generator, অর্থাৎ এলোমেলো সংখ্যা নির্ধারক) এবং ‘‘Jesus’’ (যিশু) শব্দ দুটির সংমিশ্রণ।
===বিশেষ্য===
{{head|en|বিশেষ্য|g=}}
# {{lb|en|গেমিং|হাস্যকর|আনুষ্ঠানিক নয়}} একটি [[কাল্পনিক]] [[দেবতা]], যিনি [[ভিডিও গেম]] বা [[টেবিলটপ গেম]]-এ {{l|bn|RNG|t=এলোমেলোতা; সম্ভাব্যতা, ভাগ্য}} নিয়ন্ত্রণ করেন।
citckpt7w6lv9xda0lac8817cir4om3
509665
509664
2026-06-04T09:39:04Z
Redmin
6857
/* বিশেষ্য */
509665
wikitext
text/x-wiki
=={{ভাষা|en}}==
===বিকল্প রূপ===
* {{alter|en|RNJesus}}
===উৎপত্তি===
{{blend|en|RNG|Jesus}} — ‘‘RNG’’ (Random Number Generator, অর্থাৎ এলোমেলো সংখ্যা নির্ধারক) এবং ‘‘Jesus’’ (যিশু) শব্দ দুটির সংমিশ্রণ।
===বিশেষ্য===
{{head|en|বিশেষ্য|g=}}
# {{lb|en|গেমিং|হাস্যকর|আনুষ্ঠানিক নয়}} একটি [[কাল্পনিক]] [[দেবতা]], যিনি [[ভিডিও গেম]] বা [[টেবিলটপ গেম]]-এ {{l|en|RNG|t=এলোমেলোতা; সম্ভাব্যতা, ভাগ্য}} নিয়ন্ত্রণ করেন।
js2hvupu2e044hymle953uy9u5baeii
escalate
0
161375
509634
461064
2026-06-03T12:29:02Z
Redmin
6857
509634
wikitext
text/x-wiki
==ইংরেজি==
===উৎপত্তি===
{{back-form|en|escalator}}।
===উচ্চারণ===
* {{enPR|esʹ kə lāt|a=UK,US}}, {{IPA|en|/ˈɛs.kə.leɪt/}}
* {{audio|en|LL-Q1860 (eng)-Vealhurl-escalate.wav|a=Southern England}}
===ক্রিয়া===
{{en-verb}}
# {{lb|en|ambitransitive}} কিছু ([[extent]] বা [[intensity]]) বৃদ্ধি করা; [[তীব্র করা]] বা [[ধাপে ধাপে বাড়ানো]]।
#: {{ux|en|Violence '''escalated''' during the election.}}
#: {{ux|en|The shooting '''escalated''' the existing hostility.}}
#: {{ux|en|A small fight '''escalated''' into a big fight.}}
#* {{quote-journal|en|date=2020 May 6|author=Philip Haigh|title=Just one more stop on the long journey to HS2 fulfillment [sic]|journal=Rail|page=65|text="Operating the [[WCML]] at this intensity makes it challenging to maintain acceptable performance levels, resulting in a frustratingly unreliable service for passengers. Minor disruption can '''escalate''' into significant delays because a train running only a few minutes late can miss its slot across a junction, resulting in a snowballing effect across the network."}}
# {{lb|en|transitive}} টেকনিক্যাল সাপোর্টে, গ্রাহক, সমস্যা ইত্যাদি পরবর্তী উচ্চতর স্তরে স্থানান্তরিত করা
#: {{ux|en|The tech 1 '''escalated''' the caller to a tech 2.}}
# {{lb|en|বিরল}} [[চড়াই]] করা।
#* {{quote-journal|en|journal=w:Federal Reporter|year=1975|page=537|passage=Thus, actually a prior uncounselled misdemeanor conviction may often prove to be a boon to one '''escalating''' the ladder of crime to the point where he has been convicted of a major aggravated offense.}}
#* {{quote-book|en|author=Graham Jackson|title=The Coals of Juniper|publisher=Champion Publications|year=1977|page=48|isbn=0 9597008 0 3|passage=They '''escalated''' upstairs to the Mall coffee tables.}}
#* {{quote-book|en|author=[[w:Richard D'Aveni|Richard A. D’Aveni]]|title=Hypercompetition: Managing the Dynamics of Strategic Maneuvering|publisher=[[w:Free Press (publisher)|The Free Press]]|year=1994|isbn=0-02-906938-6|passage=Firms move to higher and higher levels of conflict in each arena, as if they are '''escalating''' up a ladder with each rung representing the new level of competition introduced by the last competitive maneuver.}}
#* {{quote-book|en|author=Chris R. Jamison|title=The Chesler Legacy|publisher=Writer’s Showcase|year=2000|page=30|isbn=0-595-16167-7|passage=James worked through the basics, '''escalating''' up the ladder of complex moves.}}
#* {{quote-book|en|author=V. William Barrett|title=Sticks and Shovels: A Modern Western Mystery|publisher=Writers Club Press|year=2002|page=68|isbn=0-595-23632-4|passage=We were '''escalating''' up a few hundred feet into the canyon.}}
#* {{quote-journal|en|journal=w:Ducks Unlimited|year=2003|page=51|passage=“Pretty soon, I ended up on the committee, and from there it was pretty much '''escalating''' up the ladder as district chair; sponsor chair; and two, two-year terms as ladies state chair,” Sandi says.}}
#* {{quote-book|en|author=Solly Border|title=I Won 1000 Battles but Lost the War|publisher=Publish For Less|year=2004|page=9|isbn=0-9759457-3-4|passage=Unlike everybody else, I moved in the opposite direction, '''escalating''' the ladder leading to the roof-top, so I’d watch the fireworks.}}
#* {{quote-book|en|author=George Harpen|title=The Forbidden Palace of the Wiseman|publisher=w:AuthorHouse|year=2010|page=115|isbn=978-1-4490-4212-7|passage=The soul’s hand was within a few feet from his hand as he had not noticed that the souls were '''escalating''' up to where he was and as his soul screamed at him to look away.}}
#* {{quote-book|en|author=Abdelhak Azzouzi|title=Moroccan Yearbook of Strategy and International Relations 2012|publisher=[[w:L'Harmattan|L’Harmattan]]|year=2012|page=14|isbn=978-2-296-99386-0|passage=For needs of substantial natural resources such as oil, which can guarantee social peace in a majority of oil producing Arab States, Morocco would not be enjoying the position it has now on the international scene, had it not been for the fact that it has '''escalated''' the ladder in matters pertaining to democracy and economic liberalization.}}
#* {{quote-book|en|author=Steve Kuehn|title=The Viridian Path|publisher=w:AuthorHouse|year=2016|isbn=978-1-5246-0345-8|passage='''Escalating''' up the ladder, she looked straight into Alex’s eyes.}}
#* {{quote-book|en|author=w:Raphael Israeli|title=Black Upon White: White Racism and Black Dignity in America: A Comparison with Arabs in Israel|publisher=Strategic Book Publishing and Rights Co.|year=2020|page=2|isbn=978-1-68235-252-6|passage=In the eyes of many Americans, the Blacks have been associated with poverty, backwardness and violence, even though many of them have '''escalated''' the ladder of success into the Middle Class, having realized the American dream in terms of prosperity, education, jobs, social and political positions and even attained the presidency with Barack Obama (2008-16).}}
#* {{quote-book|en|author=Jean René Bazin PierrePierre|title=When Hugo Meets Shakespeare|volume=1|publisher=w:Xlibris|year=2022|isbn=978-1-6698-1099-5|passage=I don’t waste any time '''escalating''' ladders; / I break their portal chains when invading their forts / And tumble down their walls just like a ram batters / And wrestle their towers, ignoring the blatter / Of the great many folks; they shield from my efforts.}}
# {{lb|en|rare}} [[এস্কেলেটর]] দ্বারা [[চলানো]]।
#: {{syn|en|escalator#Verb}}
#* {{quote-book|en|author=David Damrosch|title=Meetings of the Mind|location=Princeton, N.J.; Oxford|publisher=w:Princeton University Press|year=2000|page=31|isbn=1-4008-0150-8|passage=Escalator after escalator flowed up to the heights above, {{...}} Dov '''escalated''' up beside me, scowling.}}
#* {{quote-book|en|author=w:James Patterson|title=Burn|series=''[[w:Michael Bennett (book series)|Michael Bennett]]''|publisher=Arrow Books|year=2014|year_published=2015|page=32|isbn=9780099574040|passage=There were people just about everywhere, packing the garish fluorescent-lit corridors, riding in humming golf carts, '''escalating''' up and down escalators, floating along on those George Jetson moving sidewalk thingies.}}
#* {{quote-book|en|author=Kes Gray|title=Daisy and the Trouble with London|publisher=Red Fox|year=2022|isbn=978-1-787-62088-9|passage='''Escalating''' up the up escalator at Green Park Tube station was a hundred times better than walking up two loads of steps at Oxford Circus.}}
====ব্যুৎপন্ন শব্দ====
{{col|en|unescalated|escalation|escalatory|reescalate|escalatingly|de-escalate|escalation}}
====সম্পর্কিত শব্দসমূহ====
{{col|en|escalator|scale<pos:verb>}}
====অনুবাদ====
{{trans-top|তীব্রতা বাড়ানো}}
* আলবেনিয়ান: {{t-needed|sq}}
* আর্মেনিয়ান: {{t+|hy|սրել}}
* বুলগেরিয়ান: {{t|bg|escaliram|sc=Cyrl}}, {{t|bg|повишавам се|sc=Cyrl}}
* চীনা:
*: মান্ডারিন: {{t+|cmn|升級|tr=shēngjí}}, {{t+|cmn|升高|tr=shēnggāo}}
* চেক: {{t|cs|eskalovat}}, {{t|cs|zintenzivnit}}, {{t+|cs|zvýšit}}
* ডাচ: {{t+|nl|escaleren}}
* এসপেরান্তো: {{t-needed|eo}}
* এস্টোনীয়: {{t-needed|et}}
* ফিনিশ: {{t+|fi|nousta}}, {{t+|fi|kohota}}, {{t+|fi|eskaloitua}}, {{t+|fi|kasvaa}}, {{t+|fi|yltyä}}, {{t+|fi|voimistua}}
* ফরাসি: {{t+|fr|intensifier}}
* জর্জিয়ান: {{t-needed|ka}}
* জার্মান: {{t+|de|eskalieren}}, {{t+|de|steigern}}, {{t+|de|verschärfen}}, {{t|de|sich ausweiten}}
* হাঙ্গেরীয়: {{q|transitive senses}} {{t+|hu|fokoz}}, {{t+|hu|eszkalál}}, {{t+|hu|elmérgesít}}; {{q|intransitive senses}} {{t+|hu|fokozódik}}, {{t+|hu|eszkalálódik}}, {{t+|hu|elmérgesedik}}
* জাপানি: {{t+|ja|エスカレート|tr=esukarēto suru|alt=エスカレートする|sc=Jpan}}
* লাটভীয়: {{t-needed|lv}}
* লিথুয়ানীয়: {{t-needed|lt}}
* মাওরি: {{t|mi|whakapiki}}
* ফার্সি: {{t|fa|بالا گرفتن|tr=bâlâ gereftan}}, {{t|fa|تشدید شدن|tr=tašdid šodan}}
* পোলিশ: {{t|pl|[[nasilać]] [[się]]|impf}}, {{t|pl|[[nasilić]] [[się]]|pf}}
* পর্তুগীজ: {{t+|pt|intensificar}}
* রোমানীয়: {{t|ro|([[se]]) [[intensifica]]}}, {{t+|ro|escalada}} {{qualifier|of conflicts}}
* রুশ: {{t+|ru|обостря́ть|impf}}, {{t+|ru|углубля́ть|impf}}
* স্লোভাক: {{t-needed|sk}}
* সুইডিশ: {{t+|sv|eskalera}}, {{t+|sv|trappa upp}}
* তুর্কি: {{t+|tr|yoğunlaştırmak}}
* ইউক্রেনীয়: {{t|uk|заго́стрювати|impf}}, {{t|uk|загостри́ти|pf}} {{q|transitive senses}}; {{t|uk|заго́стрюватися|impf}}, {{t|uk|загостри́тися|pf}} {{q|intransitive senses}}
* ওয়েলশ: {{t+|cy|dwysáu}}
{{trans-bottom}}
{{trans-top|একটি ফোন কল বা সমস্যাকে পরবর্তী উচ্চতর স্তরে স্থানান্তরিত করা}}
* ফরাসি: {{t|fr|faire remonter}}
* জার্মান: {{t+|de|weiterleiten}}, {{t+check|de|eskalieren}}
* হাঙ্গেরীয়: {{t+|hu|továbbít}}, {{t+|hu|eszkalál}}
* পর্তুগীজ: {{t+|pt|transferir}}
* স্প্যানিশ: {{t|es|[[elevar]] [[a]] [[un]] [[nivel]] [[superior]]}}, {{t+|es|transferir}}
{{trans-bottom}}
{{cln|en|ergative verbs}}
==স্প্যানিশ==
===ক্রিয়া===
{{head|es|verb form}}
# {{es-verb form of|escalar}}
5e72t6jzbb5m1rpz3saeoga5ry2asa2
sura
0
163336
509646
463283
2026-06-04T02:49:49Z
Redmin
6857
/* বিভক্তি */ [[en:sura]] থেকে কপি করলাম
509646
wikitext
text/x-wiki
==ইংরেজি==
{{wp}}
===উচ্চারণ===
* {{IPA|en|/ˈsʊə.ɹə/|/ˈsɔː.ɹə/|a=RP}}
* {{IPA|en|/ˈsʊ.ɹə/|a=RP}}
* {{audio|en|LL-Q1860 (eng)-Neøn-sura.wav|a=US}}
* {{rhymes|en|ʊəɹə|s=2}}
* {{hyph|en|su|ra}}
===ব্যুৎপত্তি ১===
{{root|en|ar|س و ر}}
{{bor|en|ar|سُورَة|t=কুরআনের অধ্যায়}} থেকে।
===বিশেষ্য===
{{en-noun|s|suwar}}
# কুরআনের ১১৪টি [[অধ্যায়]]-এর যেকোনো একটি।
#* '''১৯৮৫''', Kristina Nelson, ''The Art of Reciting the Qurʾan'', {{w|কায়রো}}, {{w|মিশর}}: {{w|American University in Cairo Press}} (২০০১), {{ISBN|9774245946}}, অধ্যায় ২: “[[w:Tajwid|''তাজউইদ'']]”, [http://books.google.co.uk/books?id=faS0GZ6wPCMC&pg=PA25&dq=%22suwar%22&hl=en&sa=X&ei=9u3wUu61EKHG7Abmw4CoAQ&ved=0CDYQ6AEwAjgK#v=onepage&q=%22suwar%22&f=false পৃষ্ঠা ২৫]:
#*: এই অপরিবর্তিত সিলেবলগুলির (CV̄C) বেশিরভাগই বর্ণমালার অক্ষরের নাম এবং কুরআনিক পাঠ্যের কিছু '''''সুরা''''' পরিচয় করিয়ে দেয়।
====বিকল্প রূপ====
* {{alt|en|surah}}
====অনুবাদ====
{{trans-top|কুরআনের অধ্যায়}}
* বাংলা: {{t+|bn|সূরা}}
* আলবেনীয়: {{t+|sq|sure|f}}
* আমহারিক: {{t|am|ሱራ}}
* আরবি: {{t+|ar|سُورَة|f}}
* আর্মেনীয়: {{t+|hy|սուրահ}}
* আজারবাইজানি: {{t+|az|surə}}
* বাশকির: {{t|ba|сүрә}}
* বেলারুশীয়: {{t|be|су́ра|f}}
* বুলগেরীয়: {{t+|bg|су́ра|f}}
* কাতালান: {{t+|ca|sura|f}}
* চীনা:
*: মান্দারিন: {{t+|cmn|蘇拉|tr=sūlā}}
* ক্রিমীয় তাতার: {{t|crh|sure}}
* চেক: {{t+|cs|súra|f}}
* ড্যানিশ: {{t+|da|sura|c}}
* ওলন্দাজ: {{t+|nl|soera|f|m}}
* এস্পেরান্তো: {{t|eo|surao}}
* ইস্তোনীয়: {{t|et|suura}}
* ফিনিশ: {{t+|fi|suura}}
* ফরাসি: {{t+|fr|sourate|f}}
* জর্জীয়: {{t|ka|ყურანის თავი}}, {{t+|ka|სურა}}
* জার্মান: {{t+|de|Sure|f}}
* গ্রিক: {{t|el|σουράτ}}
* হাউসা: {{t+|ha|sura|f|alt=sūr̃ā̀}}
* হিব্রু: {{t|he|סוּרָה|f|tr=sura}}
* হিন্দি: {{t+|hi|सूरा|f}}, {{t+|hi|सूरत|f}}
* হাঙ্গেরীয়: {{t+|hu|szúra}}
* ইন্দোনেশীয়: {{t+|id|surah}}
* জাপানী: {{t|ja|スーラ|tr=sūra}}
* কারাচে-বালকার: {{t|krc|суура|tr=suura}}
* কারাকালপাক: {{t|kaa|suʻre}}
* কাজাখ: {{t|kk|сүре}}
* কোরীয়: {{t+|ko|수라}}
* কিরগিজ: {{t+|ky|сүрөө}}
* লাতিন: {{t|la|azoara|f}}
* লাতভীয়: {{t|lv|sūra|f}}
* লিথুয়ানীয়: {{t|lt|sura|f}}
* ম্যাসিডোনীয়: {{t|mk|су́ра|f}}
* মালয়: {{t+|ms|surah}}
* নোগাই: {{t|nog|суьре}}
* নরওয়েজীয়:
*: বোকমål: {{t|no|sura}}
*: নিনর্স্ক: {{t|nn|sura}}
* পশতু: {{t+|ps|সুরত|m|tr=surat}}
* ফার্সি: {{t+|fa|سورِه|tr=sure}}
* পোলিশ: {{t+|pl|sura|f}}
* পর্তুগিজ: {{t+|pt|surata|f}}, {{t+|pt|sura|f}}
* রুশ: {{t+|ru|су́ра|f}}
* সার্বো-ক্রোয়েশীয়:
*: সিরিলিক: {{t|sh|су̀ра|f}}
*: রোমান: {{t+|sh|sùra|f}}
* স্লোভাক: {{t|sk|súra|f}}
* স্লোভেনীয়: {{t|sl|sura|f}}
* স্পেনীয়: {{t+|es|azora}}
* সোয়াহিলি: {{t+|sw|sura}}
* সুইডিশ: {{t+|sv|sura|c}}
* তাজিক: {{t|tg|сура}}
* তাতার: {{t+|tt|сүрә}}
* তুর্কি: {{t+|tr|sure}}
* তুর্কমেন: {{t|tk|sure}}
* ইউক্রেনীয়: {{t|uk|су́ра|f}}
* উর্দু: {{t|ur|سُورَت|f}}, {{t|ur|سُورَہ|f}}, {{t|ur|سُورَۃ|f|tr=sūra}}
* উইঘুর: {{t+|ug|سۈرە}}
* উজবেক: {{t+|uz|sura}}
* ওয়েলশ: {{t|cy|sŵra}}
{{trans-bottom}}
===ব্যুৎপত্তি ২===
{{bor|en|hi|सुरा|t=মদ}} এর মাধ্যমে, চূড়ান্তভাবে {{der|en|sa|सुरा}} থেকে।
===বিশেষ্য===
{{en-noun|-}}
# [[তাল]] বা [[নারকেল]] গাছের [[রস]], [[পাম ওয়াইন]]।
===অ্যানাগ্রাম===
* {{anagrams|en|a=arsu|asur|-saur|USRA|'saur|USAR|Ruas|URAs|saur}}
==আলাঙ্গান==
===বিশেষ্য===
{{head|alj|noun|head=surâ}}
# [[মাছ]]
==বালিনীয়==
===উচ্চারণ===
* {{IPA|ban|/surə/}}
* {{hyphenation|ban|su|ra}}
===ব্যুৎপত্তি ১===
{{bor+|ban|kaw|sura|t=দেবতা, দৈব; দিব্য, স্বর্গীয়}}, {{der|ban|sa|सुर|t=দেবতা, দৈব, দেবদেবী; মূর্তি; তেত্রিশ; ঋষি, পণ্ডিত; দেবতার প্রতিমূর্তি; সূর্য}} থেকে।
===বিশেষ্য===
{{head|ban|noun|Balinese script|ᬲᬸᬭ}}
# [[দেবতা]]
===ব্যুৎপত্তি ২===
{{bor+|ban|kaw|surā|t=মদ্য}}, {{der|ban|sa|सुरा|t=মদ, সুরা}} থেকে।
===বিশেষ্য===
{{head|ban|noun|Balinese script|ᬲᬸᬭᬵ}}
# [[মদ্য]] {{q|মাদক}}
===ব্যুৎপত্তি ৩===
{{bor+|ban|kaw|śūra|t=বীর, সাহসী, বীরত্বপূর্ণ; বীর ব্যক্তি, যোদ্ধা, চ্যাম্পিয়ন, নায়ক}}, {{der|ban|sa|शूर|t=শক্তিশালী, ক্ষমতাশালী, বীর, বীরত্বপূর্ণ, সাহসী}} থেকে।
===বিশেষ্য===
{{head|ban|noun|Balinese script|ᬰᬹᬭ}}
# [[সাহসী]], [[নায়ক]]
==বানতায়ানন==
===ব্যুৎপত্তি===
{{unk|bfx}}।
===উচ্চারণ===
* {{hyphenation|bfx|su|ra}}
===ক্রিয়াবিশেষণ===
{{head|bfx|adverb}}
# [[তারপর]]; [[এবং]] [[তারপর]]
==কাতালান==
===ব্যুৎপত্তি ১===
{{bor+|ca|ar|سُورَة|t=কুরআনের অধ্যায়}} থেকে।
===উচ্চারণ===
* {{ca-IPA}}
* {{rhymes|ca|uɾa|s=2}}
===বিশেষ্য===
{{ca-noun|f}}
# {{lb|ca|Islam}} {{l|en|sura}}
===আরও পড়ুন===
* {{R:ca:IEC2}}
===ব্যুৎপত্তি ২===
===ক্রিয়া===
{{head|ca|verb form}}
# surar ক্রিয়ার রূপ
==সেবুয়ানো==
===উচ্চারণ===
* {{hyphenation|ceb|su|ra}}
* {{ceb-IPA}}
===ব্যুৎপত্তি ১===
{{unk|ceb}}।
===ক্রিয়া===
{{head|ceb|verb}}
# [[পরিহাস করা]]
===ব্যুৎপত্তি ২===
{{der|ceb|bfx|sura}} থেকে।
===বিশেষ্য===
{{head|ceb|noun}}
# {{lb|ceb|chiefly Bantayan island}} [[তারপর]]; [[এবং]] [[তারপর]]
==ড্যানিশ==
===ব্যুৎপত্তি===
{{bor|da|ar|سُورَة|t=কুরআনের অধ্যায়}} থেকে।
===বিশেষ্য===
{{da-noun|en|er}}
# {{lb|da|Islam}} {{l|en|sura}}
==হাউসা==
===ব্যুৎপত্তি ১===
{{bor|ha|ar|سُورَة|t=কুরআনের অধ্যায়}} থেকে।
===উচ্চারণ===
* {{ha-IPA|sūr̃ā̀}}
===বিশেষ্য===
{{ha-noun|f|sūr̃ā̀|sūr̃ōr̃ī}}
# {{lb|ha|Islam}} [[#ইংরেজি|sura]]
===ব্যুৎপত্তি ২===
{{bor|ha|ar|صُورَة}} থেকে।
===উচ্চারণ===
* {{ha-IPA|sūr̃ā̀}}
===বিশেষ্য===
{{ha-noun|f|sūr̃ā̀|sūr̃ōr̃ī}}
# [[ছবি]], [[চিত্র]], [[আকৃতি]], [[আবির্ভাব]]
===ব্যুৎপত্তি ৩===
===উচ্চারণ===
* {{ha-IPA|sū̀r̃ā}}
===ক্রিয়া===
{{ha-verb|3|sū̀r̃ā}}
# কিছু ধরার জন্য ঝাঁপিয়ে পড়া
==আইরিশ==
===ব্যুৎপত্তি===
{{rfe|ga}}
===বিশেষ্য===
{{ga-noun|m|~|-}}
# {{lb|ga|textiles}} [[সুরাহ]] ([[নরম]] টুইল সিল্ক)
==ইতালীয়==
===উচ্চারণ===
{{it-pr|sùra}}
===ব্যুৎপত্তি ১===
{{der|it|la|sūra}} থেকে।
===বিশেষ্য===
{{it-noun|f}}
# {{lb|it|anatomy}} [[পায়ের পিছনের মাংসপেশি]]
===ব্যুৎপত্তি ২===
{{bor|it|ar|سُورَة|t=কুরআনের অধ্যায়}} থেকে।
===বিশেষ্য===
{{it-noun|f}}
# {{lb|it|Islam}} {{l|en|sura}}
==জাপানী==
===রোমানীকরণ===
{{ja-rom|sura}}
# [[すら]] এর রোমানীকরণ
==জাভানীয়==
===রোমানীকরণ===
{{jv-rom}}
# {{romanization of|jv|ꦱꦸꦫ}}
==লাতিন==
===ব্যুৎপত্তি===
সম্ভবত {{m|la|sū̆rus||টি-শাখা, খুঁটি}} সম্পর্কিত বা {{der|la|ine-pro|-}} একটি মূল থেকে যা {{cog|grc|ὥρα||বলির অংশ}} এর সাথে সাধারণ।
===উচ্চারণ===
* {{la-IPA|sūra}}
===বিশেষ্য===
{{la-noun|sūra<1>}}
# [[পায়ের পিছনের মাংসপেশি]]
====বিভক্তি====
{{la-ndecl|sūra<1>}}
====উদ্ভূত শব্দ====
* {{l|la|Sulla}}
* {{l|la|Sūra}}
* {{l|la|surula}}
===সূত্র===
* {{R:la:Lewis Short}}
==মাল্টীয়==
===ব্যুৎপত্তি===
{{der|mt|ar|صُورَة}} থেকে।
===উচ্চারণ===
* {{IPA|mt|/ˈsuː.ra/}}
* {{rhymes|mt|uːra|s=2}}
===বিশেষ্য===
{{mt-noun|f|suriet}}
# [[ছবি]]
# [[চিত্র]]
# [[ফটোগ্রাফ]]
==নরওয়েজীয় বোকমাল==
===বিশেষ্য===
{{head|nb|noun}}
# {{alt form|nb|sure}}
==নরওয়েজীয় নিনর্স্ক==
===বিশেষ্য===
{{head|nn|noun}}
# {{alt form|nn|sure}}
==ওল্ড ইংরেজি==
===উচ্চারণ===
* {{ang-IPA|sūra}}
===বিশেষণ===
{{head|ang|adjective form}}
# {{inflection of|ang|sūr||str|nom|f|s}}
# {{inflection of|ang|sūr||str|acc|f|s}}
# {{inflection of|ang|sūr||str|nom|n|p}}
# {{inflection of|ang|sūr||str|acc|n|p}}
# {{inflection of|ang|sūr||wk|nom|m|s}}
==ওল্ড জাভানীয়==
===ব্যুৎপত্তি===
{{bor|kaw|sa|सुर||দেবতা, দৈব, দেবদেবী; মূর্তি; তেত্রিশ; ঋষি, পণ্ডিত; দেবতার প্রতিমূর্তি; সূর্য}} থেকে।
===উচ্চারণ===
* {{IPA|kaw|/su.ra/}}
* {{rhymes|kaw|ra|s=1}}
* {{homophones|kaw|sura|surā|śūra}}
* {{hyphenation|kaw|su|ra}}
===বিশেষ্য===
{{head|kaw|noun}}
# [[দেবতা]], [[দেবদেবী]]
===বিশেষণ===
{{head|kaw|adjective}}
# [[দিব্য]], [[স্বর্গীয়]]
====বংশধর====
* {{desc|jv|sura}}
* {{desc|ban|sura|bor=1}}
===আরও পড়ুন===
* {{R:kaw:Zoetmulder}}
==পোলিশ==
===ব্যুৎপত্তি===
{{bor|pl|ar|سُورَة||কুরআনের অধ্যায়}} থেকে।
===উচ্চারণ===
* {{pl-IPA|sura}}
* {{audio|pl|Pl-sura.ogg}}
* {{rhymes|pl|ura|s=2}}
* {{syllables|pl|su|ra}}
===বিশেষ্য===
{{pl-noun|f}}
# {{lb|pl|Islam}} সুরা
====বিভক্তি====
{{pl-decl-noun-f|su|r}}
===আরও পড়ুন===
* {{R:pl:WSJP}}
* {{R:pl:PWN}}
==রোমানি==
{{rfv|rom}}
===ব্যুৎপত্তি===
সম্ভবত {{der|rom|hy|սուր||তরবারি}} থেকে।
===বিশেষ্য===
{{rom-noun|sur|sur}}
# [[তরবারি]]
===সূত্র===
* {{R:hy:Adamyan}}
==সার্ডিনীয়==
===উচ্চারণ===
* {{IPA|sc|/ˈsura/}}
===পূর্বসর্গ===
{{head|sc|preposition}}
# {{alt form|sc|subra}}
===সূত্র===
* {{R:sc:Rubattu}}
* {{R:sc:Wagner}}
==সোরা==
===ব্যুৎপত্তি===
{{cog|sat|sɛ̃ɽa||বড়, প্রধান}} এর সাথে তুলনা করুন।
===উচ্চারণ===
* {{IPA|srb|/suɽa/}}
* {{IPA|srb|/suɽaː/}}
===বিশেষণ===
{{head|srb|adjective}}
# [[বড়]]
===বিশেষ্য===
{{head|srb|noun}}
# [[বড়]]
==স্পেনীয়==
===উচ্চারণ===
* {{es-IPA|sura}}
* {{rhymes|es|uɾa|s=2}}
* {{syllables|es|su|ra}}
===বিশেষ্য===
{{es-noun|m|suras}}
# সূরা
===আরও পড়ুন===
* {{R:es:DLE}}
==সোয়াহিলি==
===উচ্চারণ===
* {{audio|sw|Sw-ke-sura.flac|a=কেনিয়া}}
===ব্যুৎপত্তি ১===
{{bor|sw|ar|سُورَة||কুরআনের অধ্যায়}} থেকে।
===বিশেষ্য===
{{sw-noun|n|sura|sura}}
# [[অধ্যায়]]
# কুরআনের [[সূরা]]
===ব্যুৎপত্তি ২===
{{bor|sw|ar|صُورَة}} থেকে।
===বিশেষ্য===
{{sw-noun|n|sura|sura}}
# [[আবির্ভাব]], [[চেহারা]]
# [[মুখ]] {{syn|sw|uso}}
==সুইডিশ==
===উচ্চারণ===
* {{sv-IPA|sura}}
* {{rhymes|sv|²ʉːra|s=2}}
===ব্যুৎপত্তি ১===
{{m|sv|sur}} বিশেষণের derivation।
===বিশেষণ===
{{head|sv|adjective form}}
# {{inflection of|sv|sur||def|s}}
# {{inflection of|sv|sur||pl}}
===ক্রিয়া===
{{sv-verb}}
# [[ভাব]] করা
====দেখুন====
* {{l|sv|försura}}
* {{l|sv|syra}}
===ব্যুৎপত্তি ২===
{{bor|sv|ar|سُورَة||কুরআনের অধ্যায়}} থেকে।
===বিশেষ্য===
{{sv-noun|c}}
# কুরআনের [[সুরা]]
====দেখুন====
* {{l|sv|Koranen}}
===সূত্র===
* {{R:sv:SO}}
* {{R:sv:SAOL}}
* {{R:sv:SAOB}}
===অ্যানাগ্রাম===
* {{anagrams|sv|a=arsu|rusa}}
==তেতুম==
===ব্যুৎপত্তি===
{{rfe|tet}}
===ক্রিয়া===
{{head|tet|verb}}
# [[গণনা]] করা
===আরও পড়ুন===
* {{R:tet:Monteiro}}
==উজবেক==
===ব্যুৎপত্তি===
{{bor|uz|ar|سُورَة||কুরআনের অধ্যায়}} থেকে।
===বিশেষ্য===
{{uz-noun|suralar}}
# {{lb|uz|Islam}} সুরা
====বিভক্তি====
{{uz-decl-noun|sura|suralar}}
==জাঘাওয়া==
===বিশেষ্য===
{{head|zag|noun}}
# [[সিংহ]]
===সূত্র===
* {{R:zag:ADESK}}
53lxch9mn6dh23pkwiwgthtv4oiyvtm
प्रेमभाजा
0
163454
509654
463602
2026-06-04T09:18:26Z
Redmin
6857
/* বিশেষ্য */
509654
wikitext
text/x-wiki
=={{ভাষা|sa}}==
===উচ্চারণ===
* প্রেম্-ভাজা
===ব্যুৎপত্তি===
[[প্রেমভাজা]] (“प्रेमभाजा”) শব্দটি গঠিত হয়েছে “প্রেম” ([[:sa:प्रेम|प्रेम]]) এবং “ভাজা” ([[:sa:भाजा|भाजा]]) থেকে।
===উদ্ভূত===
* সংস্কৃত (प्रेमभाजा)
===বিশেষ্য===
{{বিশেষ্য|sa|লিঙ্গ=c}}
# [[প্রেমিক]], [[প্রেমিকা]], প্রেমের পাত্র বা প্রাপক।
#: {{ux|sa|श्रीकृष्णे सत्यभामा कुचकलशभुवि स्फीतमातङ्गकुम्भो तुङ्गायां पारिजातस्रजमुपनयति प्रेमभाजा करेण ।|translation=শ্রীকৃষ্ণে, [[:wikipedia:সত্যভামা|সত্যভামা]], বক্ষঃকলমের ([https://www.wisdomlib.org/definition/kucakalasha নারীদের কামুক স্তন]) মাটিতে, বিস্তৃত হাতির জলপাত্র, তিনি তাঁর শিখরে পারিজাতের মালা ধারণ করেন এবং তাঁর হাতে প্রেমের পাত্র ধারণ করেন।|source=[https://sa.wikisource.org/wiki/%E0%A4%95%E0%A4%BE%E0%A4%B5%E0%A5%8D%E0%A4%AF%E0%A4%AD%E0%A5%82%E0%A4%B7%E0%A4%A3%E0%A4%B6%E0%A4%A4%E0%A4%95%E0%A4%AE%E0%A5%8D কাব্যভূষণশতকম (কাব্যমালা) - কবি ভদ্রশ্রীকৃষ্ণবল্লভ (১৯৩০)] [[:en:wikipedia:Jinvijay|জিনবিজয়]], [https://jainqq.org/pagetext/samaysar/018015/797 Catalogue of Sanskrit and Prakrit Manuscripts], [[:en:wikipedia:Rajasthan Oriental Research Institute|রাজস্থান ওরিয়েন্টাল রিসার্চ ইনস্টিটিউট]], পৃ: ৭৯৭}}
[[বিষয়শ্রেণী:সংস্কৃত বিশেষ্য]]
#: {{ux|sa|किं ब्रवीषि- किं ते लब्यः कुवलयहशा संगमः प्रेममाजा, संरम्मो वा किमु विरचितः संगमे संनताङ्गचा । किं बाकार्षीः कुचमरपरीरम्भमस्याः कदाचि रसंलापं या सकृद्धि तथा सानुरागं व्यथाः किम्।|translation=তুমি কি বলছো- কুভালা আর হাশার মিলনস্থল, ভালোবাসার পাত্র, নাকি রাগের উৎস? স্নেহপূর্ণ কথোপকথনের বেদনা কী, যা মাঝে মাঝে তাকে তার স্তন জড়িয়ে ধরতে বাধ্য করে?|source=[https://books.google.com.bd/books?id=lyaTewHnXyIC&pg=RA3-PA31&dq=%E0%A4%AA%E0%A5%8D%E0%A4%B0%E0%A5%87%E0%A4%AE%E0%A4%AD%E0%A4%BE%E0%A4%9C%E0%A4%BE&hl=en&newbks=1&newbks_redir=0&source=gb_mobile_search&ovdme=1&sa=X&ved=2ahUKEwj1ju7N3pyRAxX29zgGHfqIJloQ6AF6BAgKEAM#v=onepage&q=%E0%A4%AA%E0%A5%8D%E0%A4%B0%E0%A5%87%E0%A4%AE%E0%A4%AD%E0%A4%BE%E0%A4%9C%E0%A4%BE&f=false গঙ্গাবতরণ - নীলকন্ঠ দীক্ষিত (১৯০২)]}}
#: {{ux|sa|अक्रूर क्रूरकारानिलयनिगडिताऽवस्थितिर्येन माता तातोऽपि प्रेमभाजा सपदि रिपुहतिं संविधायाऽन्वमोचि ।|translation=নিষ্ঠুর বন্দীদের আশ্রমে বন্দী [[:bn:wikipedia:অক্রূর|আক্রূর]] তৎক্ষণাৎ তার মা এবং বাবাকে, যারা তার ভালোবাসার প্রাপক ছিলেন, তাদের শত্রুদের হত্যা করার হাত থেকে মুক্তি দেন।|source=[https://books.google.com.bd/books?id=EzYtAQAAIAAJ&q=%E0%A4%AA%E0%A5%8D%E0%A4%B0%E0%A5%87%E0%A4%AE%E0%A4%AD%E0%A4%BE%E0%A4%9C%E0%A4%BE&dq=%E0%A4%AA%E0%A5%8D%E0%A4%B0%E0%A5%87%E0%A4%AE%E0%A4%AD%E0%A4%BE%E0%A4%9C%E0%A4%BE&hl=en&newbks=1&newbks_redir=0&source=gb_mobile_search&ovdme=1&sa=X&ved=2ahUKEwj1ju7N3pyRAxX29zgGHfqIJloQ6AF6BAgLEAM শ্রীভক্তিরসায়নম - হরিসুরি (১৯৬৯)]}}
16flkshamgdiiuh3xxgw95rayysh25p
profane
0
167172
509645
507486
2026-06-03T17:24:24Z
Redmin
6857
/* বিশেষণ */
509645
wikitext
text/x-wiki
{{also|profané}}
==লাতিন==
===ক্রিয়াবিশেষণ===
{{la-adv|profānē}}
# [[অপবিত্র]] বা [[অধার্মিক]] উপায়ে বা পদ্ধতিতে।
===বিশেষণ===
{{head|la|adjective form|head=profāne}}
# {{inflection of|la|profānus||voc|m|s}} (সম্বোধন পদ, একবচন, পুংলিঙ্গ)।
==ফরাসি==
===ব্যুৎপত্তি===
লাতিন {{bor+|fr|la|profānus}} থেকে ধার করা।
===বিশেষণ===
{{fr-adj}}
# [[লৌকিক]]; [[ধর্মনিরপেক্ষ]]।
#: {{syn|fr|laïque|séculier}}
# [[অপবিত্র]] বা [[লৌকিক]] (ইংরেজি profane এর সমতুল্য)।
==ইংরেজি==
===ব্যুৎপত্তি===
ইন্দো-ইউরোপীয় আদিভাষা {{root|en|ine-pro|*per-|id1=পূর্বে|*dʰeh₁-}} থেকে উদ্ভূত।
মধ্য ফরাসি {{der|en|frm|prophane}} থেকে এবং তার আগে লাতিন {{der|en|la|profānus|t=ধর্মীয় নয়, অপবিত্র}} থেকে আগত; যা মূলত {{m|la|pro-|t=সামনে/বাইরে}} + {{m|la|fānum|t=মন্দির}} এর সমন্বয়।
===উচ্চারণ===
* আধ্বব: /pɹəˈfeɪn/ (যুক্তরাজ্য)
===বিশেষণ===
{{en-adj|comp=er,more}}
# [[অপবিত্র]]; [[ধর্মীয়ভাবে]] [[অশুদ্ধ]]; [[অশুচি]]; [[পবিত্র]] স্থান বা বস্তুর [[অবমাননাকারী]]।
#: {{syn|en|defiled|mishallowed|unhallowed}}
# পবিত্র বা ধর্মীয় নয় এমন; [[লৌকিক]]; পার্থিব বা জাগতিক বিষয় সংক্রান্ত।
#: {{syn|en|secular|temporal|worldly|irreligious}}
#: {{ant|en|faithful|holy|religious|sacred|spiritual}}
#: {{ux|en|'''profane''' authors|লৌকিক বা ধর্মনিরপেক্ষ লেখকগণ}}
# পবিত্র জিনিসের প্রতি অবজ্ঞা বা অশ্রদ্ধা প্রদর্শনকারী; [[ঈশ্বরনিন্দামূলক]]।
# [[ভাষা|ভাষার]] ক্ষেত্রে [[অভদ্র]] বা [[কুরুচিপূর্ণ]]; [[গালিগালাজপূর্ণ]]।
#: {{syn|en|irreverent|vulgar|obscene}}
===বিশেষ্য===
{{en-noun}}
# এমন ব্যক্তি বা বস্তু যা অপবিত্র বা লৌকিক।
# {{lb|en|ফ্রিমেসনরি}} এমন ব্যক্তি যে ফ্রিম্যাসন নয়।
===ক্রিয়া===
{{en-verb}}
# {{lb|en|সকর্মক}} পবিত্র কোনো কিছুর অবমাননা করা; [[অপবিত্র করা]]; অশ্রদ্ধা বা অপব্যবহারের মাধ্যমে কোনো কিছুর পবিত্রতা নষ্ট করা।
#: {{ux|en|One should not '''profane''' the name of God.|কারও ঈশ্বরের নামের অবমাননা করা উচিত নয়।}}
# {{lb|en|সকর্মক}} কোনো কিছুর ভুল বা অযোগ্য ব্যবহার করা; [[কলুষিত করা]]।
npegs09st0cj8gmcfj14qj76f052l4y
বিষয়শ্রেণী:আরবি শব্দের প্রতিবর্ণীকরণের জন্য অনুরোধ
14
168210
509653
2026-06-04T09:17:22Z
Redmin
6857
+
509653
wikitext
text/x-wiki
__HIDDENCAT__
2twjmejn56ditxo46hqinfh52nh6flb
বিষয়শ্রেণী:ফরাসি অনুবাদসহ পাতা
14
168211
509668
2026-06-04T09:44:17Z
Redmin
6857
/* */ +
509668
wikitext
text/x-wiki
[[বিষয়শ্রেণী:অনুবাদসহ পাতা]]
ocbk2qm0kj7e8l1bhqz9s4szoehf6k6
জড়বৎ
0
168212
509677
2026-06-04T11:40:50Z
Redmin
6857
লেক্সিম লিংকার এক্সটেনশনের সাহায্যে উইকিউপাত্ত লেক্সিম L1567162-এর জন্য একটি নতুন ভুক্তি তৈরি করছি
509677
wikitext
text/x-wiki
{{লে|L1567162}}
30c1wnysqiixs518zteu3zeowiq2skj
বিষয়শ্রেণী:আরবি থেকে আগত বাংলা শব্দ
14
168213
509678
2026-06-04T11:43:28Z
Redmin
6857
+
509678
wikitext
text/x-wiki
[[বিষয়শ্রেণী:আরবি থেকে আগত শব্দ]]
[[বিষয়শ্রেণী:বাংলা শব্দ]]
psf03tzupx7rnv4rjclqrcqmgqfkpto
বিষয়শ্রেণী:ফারসি লেমা
14
168214
509679
2026-06-04T11:44:22Z
Redmin
6857
+
509679
wikitext
text/x-wiki
phoiac9h4m842xq45sp7s6u21eteeq1