模組:Families
此模块用於檢索和管理維基詞典的各種語言系屬及其相關資訊。請參閱Wiktionary:語系以了解更多資訊。
此模块提供對其他模块的存取。 要從模板中存取資訊,請參閱Module:families/templates。
資訊本身儲存在 Module:families/data 中,該模块不應被任何其他模块直接使用,只能透過 Module:families 提供的函數存取資料。
找尋語言系屬
此模組導出許多用於查找語言系屬的函數。
getByCode
getByCode(code)
尋找代碼與所提供代碼相符的語言系屬。如果存在,則傳回代表該族的 Family
物件,否則返回 nil
。
尋找代碼與所提供的代碼相符的系列。 如果存在,則傳回代表家庭的 Family 物件。 否則,它會返回零。
getByCanonicalName
getByCanonicalName(name)
尋找其規範名稱(維基詞典上用於表示該語言的名稱)與所提供的名稱相符的語言系屬。如果存在,則回傳代表家庭的 Family
物件,否則回傳 nil
。 家族的規範名稱應該始終是唯一的(維基詞典上,兩個語言系屬共享相同的規範名稱是錯誤的),因此這保證最多給出一個結果。
Family 物件
Family
物件是從上述函數之一傳回的。它是一個語言系屬及其關聯資料的 Lua 表示形式。它有許多可以呼叫的方法(使用 :
語法),例如:
local m_families = require("Module:families")
local fam = m_families.getByCode("ine")
local name = fam:getCanonicalName()
-- "name" will now be "Indo-European"
Family:getCode
:getCode()
返回語言系屬的代碼。例如:印歐語系的 "ine"
。
Family:getCanonicalName
:getCanonicalName()
返回語言系屬的規範名稱。這是維基詞典上用來代表該系屬的唯一名稱。例如:印歐語系的"印歐語系"
。
Family:getDisplayForm
:getDisplayForm()
返回語言系屬的顯示形式。對於語言系屬,這與 :getCategoryName("nocap")
傳回的值相同,即它讀取「NAME」(如"印歐語系"
)(和英語維基詞典不同,中文維基詞典的 NAME 亦包含語言系屬後綴)。對於語言和僅用於詞源的語言,這與規範名稱相同;對於文字,它讀取「NAME」(例如"阿拉伯文"
)。 :makeCategoryLink
中使用的顯示文字始終與顯示形式相同。
Family:getAllNames
:getAllNames()
傳回該系屬已知的所有名稱的表,包括規範名稱。這些名稱不能保證是唯一的,有時多個系屬共享同一名稱。例如:斯拉夫語支的 {"Slavic", "Slavonic"}
。
Family:getFamily
:getFamily()
傳回該系屬的上層系屬的 Family
物件。
Family:getProtoLanguage
:getProtoLanguage()
傳回該系屬的祖語的Language
物件(如果存在;請參閱 Module:languages)。
Family:getCategoryName
:getCategoryName(nocap)
傳回該系屬的主要分類的名稱。例如:"日耳曼語族"
的分類 Category:日耳曼語族
除非給予可選參數 nocap,否則傳回值開頭的系屬名稱將大寫;這對於分類名稱是正確的,但如果系屬名稱是小寫且該函數的傳回值用在句子中間,則不正確。(以下文字為針對英語維基詞典的情況。)例如,代碼為 qfa-mix
的偽系屬名稱為 "混合語言"
,當用作分類名稱 Category:派生自混合語言的詞,該名稱應保持小寫,但在 Category:混合語言 中應大寫。如果您正在考慮使用getCategoryName("nocap")
,請改用 getDisplayForm()
。
Family:getWikidataItem
:getWikidataItem()
回傳該系屬的維基數據項。
Family:getWikipediaArticle
:getWikipediaArticle()
傳回該系屬的維基百科條目,通常源自 :getWikidataItem()
。
local export = {}
function export.makeObject(code, data, useRequire)
local Family = {}
function Family:getCode()
return self._code
end
function Family:getCanonicalName()
return self._rawData[1]
end
function Family:getDisplayForm()
return self:getCategoryName("nocap")
end
function Family:getOtherNames(onlyOtherNames)
return require("Module:language-like").getOtherNames(self, onlyOtherNames)
end
function Family:getAliases()
return self._rawData.aliases or {}
end
function Family:getVarieties(flatten)
return require("Module:language-like").getVarieties(self, flatten)
end
--function Family:getAllNames()
-- return self._rawData.names
--end
--[==[Given a list of types as strings, returns true if the family has all of them.
The following types are recognized:
* {family}: This object is a family.
* {full}: This object is a "full" family. This includes all families but a couple of etymology-only
families for Old and Middle Iranian languages.
* {etymology-only}: This object is an etymology-only family, similar to etymology-only languages. There
are currently only two such families, for Old Iranian languages and Middle Iranian
languages (which do not represent proper clades and have no proto-languages, hence
cannot be full families).
]==]
function Family:hasType(...)
if not self._type then
self._type = {family = true}
if self:getNonEtymologicalCode() == self:getCode() then
self._type.full = true
else
self._type["etymology-only"] = true
end
if self._rawData.type then
for _, type in ipairs(mw.text.split(self._rawData.type, "%s*,%s*")) do
self._type[type] = true
end
end
end
for _, type in ipairs{...} do
if not self._type[type] then
return false
end
end
return true
end
--[==[Returns a {Family} object for the superfamily that the family belongs to.]==]
function Family:getFamily()
if self._familyObject == nil then
local familyCode = self:getFamilyCode()
if familyCode then
self._familyObject = export.getByCode(familyCode, useRequire)
else
self._familyObject = false
end
end
return self._familyObject or nil
end
--[==[Returns the code of the family's superfamily.]==]
function Family:getFamilyCode()
if not self._familyCode then
self._familyCode = self._rawData[3]
end
return self._familyCode
end
--[==[Returns the canonical name of the family's superfamily.]==]
function Family:getFamilyName()
if self._familyName == nil then
local family = self:getFamily()
if family then
self._familyName = family:getCanonicalName()
else
self._familyName = false
end
end
return self._familyName or nil
end
--[==[Check whether the family belongs to {superfamily} (which can be a family code or object), and returns a boolean. If more than one is given, returns {true} if the family belongs to any of them. A family is '''not''' considered to belong to itself.]==]
function Family:inFamily(...)
for _, superfamily in ipairs{...} do
if type(superfamily) == "table" then
superfamily = superfamily:getCode()
end
local family, code = self:getFamily()
while true do
if not family then
return false
end
code = family:getCode()
family = family:getFamily()
-- If family is parent to itself, return false.
if family and family:getCode() == code then
return false
elseif code == superfamily then
return true
end
end
end
end
function Family:getParent()
if self._parentObject == nil then
local parentCode = self:getParentCode()
if parentCode then
self._parentObject = require("Module:languages").getByCode(parentCode, nil, true, true, useRequire)
else
self._parentObject = false
end
end
return self._parentObject or nil
end
function Family:getParentCode()
if not self._parentCode then
self._parentCode = self._rawData[5]
end
return self._parentCode
end
function Family:getParentName()
if self._parentName == nil then
local parent = self:getParent()
if parent then
self._parentName = parent:getCanonicalName()
else
self._parentName = false
end
end
return self._parentName or nil
end
function Family:getParentChain()
if not self._parentChain then
self._parentChain = {}
local parent = self:getParent()
while parent do
table.insert(self._parentChain, parent)
parent = parent:getParent()
end
end
return self._parentChain
end
function Family:hasParent(...)
--checkObject("family", nil, ...)
for _, other_family in ipairs{...} do
for _, parent in ipairs(self:getParentChain()) do
if type(other_family) == "string" then
if other_family == parent:getCode() then return true end
else
if other_family:getCode() == parent:getCode() then return true end
end
end
end
return false
end
--[==[If the family is etymology-only, iterates through its parents until a regular family is found and returns it. If the family is a regular family, then it simply returns itself.]==]
function Family:getNonEtymological()
if not self._nonEtymologicalObject then
local nonEtymologicalCode = self:getNonEtymologicalCode()
if nonEtymologicalCode ~= self:getCode() then
self._nonEtymologicalObject = require("Module:languages").getByCode(nonEtymologicalCode, nil, nil, true, useRequire)
else
self._nonEtymologicalObject = self
end
end
return self._nonEtymologicalObject
end
function Family:getNonEtymologicalCode()
return self._nonEtymologicalCode or self:getCode()
end
function Family:getNonEtymologicalName()
if self._nonEtymologicalName == nil then
local nonEtymological = self:getNonEtymological()
if nonEtymological then
self._nonEtymologicalName = nonEtymological:getCanonicalName()
else
self._nonEtymologicalName = false
end
end
return self._nonEtymologicalName or nil
end
function Family:getProtoLanguage()
if self._protoLanguageObject == nil then
self._protoLanguageObject = require("Module:languages").getByCode(self._rawData.protoLanguage or self:getCode() .. "-pro", nil, true, nil, useRequire) or false
end
return self._protoLanguageObject or nil
end
function Family:getProtoLanguageCode()
if self._protoLanguageCode == nil then
local protoLanguage = self:getProtoLanguage()
self._protoLanguageCode = protoLanguage and protoLanguage:getCode() or false
end
return self._protoLanguageCode or nil
end
function Family:getProtoLanguageName()
if not self._protoLanguageName then
self._protoLanguageName = self:getProtoLanguage():getCanonicalName()
end
return self._protoLanguageName
end
function Family:hasAncestor(...)
-- Go up the family tree until a protolanguage is found.
local family = self
local protolang = family:getProtoLanguage()
while not protolang do
family = family:getFamily()
protolang = family:getProtoLanguage()
-- Return false if the family is its own family, to avoid an infinite loop.
if family:getFamilyCode() == family:getCode() then
return false
end
end
-- If the protolanguage is not in the family, it must therefore be ancestral to it. Check if it is a match.
for _, otherlang in ipairs{...} do
if (
type(otherlang) == "string" and protolang:getCode() == otherlang or
type(otherlang) == "table" and protolang:getCode() == otherlang:getCode()
) and not protolang:inFamily(self) then
return true
end
end
-- If not, check the protolanguage's ancestry.
return protolang:hasAncestor(...)
end
local function fetch_descendants(self, format)
local languages = require("Module:languages/code to canonical name")
local etymology_languages = require("Module:etymology languages/code to canonical name")
local families = require("Module:families/code to canonical name")
local descendants = {}
local family = self:getFamily()
-- Iterate over all three datasets.
for _, data in ipairs{languages, etymology_languages, families} do
for code in pairs(data) do
local lang = require("Module:languages").getByCode(code, nil, true, true, useRequire)
if lang:inFamily(self) then
if format == "object" then
table.insert(descendants, lang)
elseif format == "code" then
table.insert(descendants, code)
elseif format == "name" then
table.insert(descendants, lang:getCanonicalName())
end
end
end
end
return descendants
end
function Family:getDescendants()
if not self._descendantObjects then
self._descendantObjects = fetch_descendants(self, "object")
end
return self._descendantObjects
end
function Family:getDescendantCodes()
if not self._descendantCodes then
self._descendantCodes = fetch_descendants(self, "code")
end
return self._descendantCodes
end
function Family:getDescendantNames()
if not self._descendantNames then
self._descendantNames = fetch_descendants(self, "name")
end
return self._descendantNames
end
function Family:hasDescendant(...)
for _, lang in ipairs{...} do
if type(lang) == "string" then
lang = require("Module:languages").getByCode(lang, nil, true, nil, useRequire)
end
if lang:inFamily(self) then
return true
end
end
return false
end
function Family:getCategoryName(nocap)
local name = self._rawData[1]
-- If the name already has "languages" in it, don't add it.
if not name:find("[Ll]anguages$") then
name = name .. ""
end
if not nocap then
name = mw.getContentLanguage():ucfirst(name)
end
return name
end
function Family:makeCategoryLink()
return "[[:Category:" .. self:getCategoryName() .. "|" .. self:getDisplayForm() .. "]]"
end
function Family:getWikidataItem()
local item = self._rawData[2] or self._rawData.wikidata_item
if not item then
return nil
end
if type(item) ~= "number" then
error("The method getWikidataItem expects the item to be stored as a number, but it is currently a " .. type(code) .. ".")
end
return "Q" .. item
end
function Family:getWikipediaArticle()
return (self:getWikidataItem() and mw.wikibase and mw.wikibase.sitelink(self:getWikidataItem(), 'enwiki')) or
self:getCategoryName()
end
function Family:makeWikipediaLink()
return "[[w:" .. self:getWikipediaArticle() .. "|" .. self:getCanonicalName() .. "]]"
end
function Family:toJSON()
if not self._type then
self:hasType()
end
local types = {}
for type in pairs(self._type) do
table.insert(types, type)
end
local ret = {
canonicalName = self:getCanonicalName(),
categoryName = self:getCategoryName("nocap"),
code = self:getCode(),
family = self._rawData[3],
protoLanguage = self._rawData.protoLanguage,
otherNames = self:getOtherNames(true),
aliases = self:getAliases(),
varieties = self:getVarieties(),
type = types,
wikidataItem = self:getWikidataItem(),
}
return require("Module:JSON").toJSON(ret)
end
function Family:getRawData()
return self._rawData
end
Family.__index = Family
return data and setmetatable({ _rawData = data, _code = code }, Family) or nil
end
function export.getByCode(code, useRequire)
local function conditionalRequire(modulename)
if useRequire then
return require(modulename)
else
return mw.loadData(modulename)
end
end
local data = conditionalRequire("Module:families/data")[code]
if data then
return export.makeObject(code, data, useRequire)
end
data = require("Module:families/track-bad-etym-code")(code) and conditionalRequire("Module:families/data/etymology")[code]
if data then
return require("Module:languages").makeObject(code, data, useRequire)
end
return nil
end
function export.getByCanonicalName(name, useRequire)
local function conditionalRequire(modulename)
if useRequire then
return require(modulename)
else
return mw.loadData(modulename)
end
end
local byName = conditionalRequire("Module:families/canonical names")
local code = byName and byName[name] or
byName[name:match("^(.*) languages$")]
return export.getByCode(code, useRequire)
end
return export