모듈:affix
보이기
하위 모듈
[편집]local export = {}
local debug_force_cat = false -- if set to true, always display categories even on userspace pages
local m_links = require("Module:also/link")
local m_str_utils = require("Module:string utilities")
local m_table = require("Module:table")
local etymology_module = "Module:etymology"
local pron_qualifier_module = "Module:pron qualifier"
local scripts_module = "Module:scripts"
local utilities_module = "Module:utilities"
local m_ko_utils = require("Module:ko-utilities")
-- [[Module:category tree/etymology]]에서 접근할 수 있도록 export
export.affix_lang_data_module_prefix = "Module:affix/lang-data/"
local rsub = m_str_utils.gsub
local usub = m_str_utils.sub
local ulen = m_str_utils.len
local rfind = m_str_utils.find
local rmatch = m_str_utils.match
local u = m_str_utils.char
local ucfirst = m_str_utils.ucfirst
local unpack = unpack or table.unpack -- Lua 5.2 호환성
-- [[Module:category tree/etymology]]에서 접근할 수 있도록 export
export.langs_with_lang_specific_data = {
["az"] = true,
["fi"] = true,
["izh"] = true,
["la"] = true,
["sah"] = true,
["tr"] = true,
}
local default_pos = "낱말"
--[==[ 소개:
=== 다른 종류의 하이픈("틀", "표시", "조회")에 대하여: ===
* "틀 하이픈"은 틀 호출에서 어떤 항이 접사임을 나타내기 위해 사용되는 스크립트별 하이픈 문자입니다.
항상 단일 유니코드 문자이지만, 특정 스크립트에 대해서는 여러 하이픈이 가능할 수 있습니다.
보통은 일반 하이픈 문자 "-"이지만, 일부 라틴 문자가 아닌 언어(현재는 오른쪽에서 왼쪽으로 쓰는 언어만 해당)에서는 다릅니다.
* "표시 하이픈"은 어떤 항이 접사임을 나타내기 위해 표시되고 링크될 때 해당 항에 추가되는 문자열입니다 (빈 문자열일 수도 있음).
현재는 항상 틀 하이픈과 같거나 빈 문자열이지만, 아래 코드는 임의의 표시 하이픈을 처리할 수 있을 만큼 일반적입니다. 구체적으로:
*# 동아시아 언어의 경우, 표시 하이픈은 항상 비어 있습니다.
*# 아랍 문자 언어의 경우, 타트윌(ـ) 또는 ZWNJ(폭 없는 비연결자)가 틀 하이픈으로 허용됩니다. ZWNJ는 일부 접미사가
비연결 동작을 보이기 때문에 주로 페르시아어에서 지원됩니다. 타트윌에 해당하는 표시 하이픈은 역시 타트윌이지만,
ZWNJ에 해당하는 표시 하이픈은 비어 있습니다 (명시적 하이픈이 없는 {{접두사}}/{{접미사}} 등의 호출에서는 타트윌이 기본 표시 하이픈임).
* "조회 하이픈"은 언어별 접사 매핑을 조회할 때 사용되는 하이픈입니다. (이 매핑은 아래 링크 접사를 논할 때 더 자세히 설명).
이는 해당 접사의 스크립트에만 의존합니다. 대부분의 스크립트(동아시아 스크립트 포함)는 일반 하이픈 "-"을 조회 하이픈으로 사용하지만,
히브리어와 아랍어는 각각 고유의 조회 하이픈(마켑과 타트윌)을 가집니다. 특히 아랍어의 경우, 세 가지 가능한 틀 하이픈
(타트윌, ZWNJ, 일반 하이픈)이 인식되지만, 매핑에서는 반드시 타트윌을 사용해야 합니다.
=== 다른 종류의 접사("틀", "표시", "링크", "조회", "분류")에 대하여: ===
* "틀 접사"는 틀 호출에 나타나는 소스 형태의 접사입니다. 일반적으로 틀 접사에는 틀 하이픈이 붙어 접사임을 나타내고
어떤 종류의 접사인지(접두사, 접미사, 접요사, 양접사)를 나타내지만, {{접미사}}, {{접두사}} 등 구식 틀 중 일부는
"위치적" 접사를 가지며, 특정 위치(예: 두 번째 또는 세 번째 매개변수)에 접사가 존재한다는 사실 자체가 틀 하이픈의
존재 여부와 관계없이 특정 종류의 접사임을 나타냅니다.
* "표시 접사"는 사용자에게 실제로 표시되는 해당 접사입니다. 표시 접사는 여러 이유로 틀 접사와 다를 수 있습니다:
*# 표시 접사는 `|altN=` 매개변수, `<alt:...>` 인라인 수정자 또는 `[[-kas|-käs]]` 형태의 파이프 링크를 사용하여
명시적으로 지정될 수 있습니다 (여기서는 접사가 -käs로 표시되지만 -kas로 링크되어야 함을 나타냄).
이때 틀 접사는 전체 파이프 링크이고, 표시 접사는 -käs입니다.
*# 위와 같은 명시적 지정이 없더라도, 특정 언어는 틀에 지정된 "틀 하이픈"과 "표시 하이픈"이 다를 수 있으며,
이에 따라 틀 접사와 표시 접사도 달라집니다.
* (일반) "링크 접사"는 접사가 사용자에게 보일 때 링크되는 접사입니다. 링크 접사는 보통 표시 접사와 같지만,
세 가지 경우에 달라집니다:
*# 위 "표시 접사"에서 설명한 대로 명시적 지정으로 표시 접사와 링크 접사가 달라지는 경우.
*# 특정 언어의 경우, 특정 접사가 언어별 매핑을 통해 표준형으로 변환됩니다. 예를 들어, 핀란드어에서 형용사 형성
접미사 [[-kas]]는 전설모음 뒤에서 [[-käs]]로 나타나지만, 논리적으로 두 형태는 동일한 접미사이므로 동일하게
링크되고 분류되어야 합니다. 매핑은 `모듈:affix/lang-data/언어코드`에 제공되어 핀란드어 [[-käs]]를
링크 및 분류 목적으로 [[-kas]]로 변환합니다. 매핑에 있는 접사들은 "조회 하이픈"을 사용하는데, 이는 보통
틀 하이픈과 같지만 아랍 문자에서는 다릅니다.
* "정리된 링크 접사"는 언어의 `makeEntryName()` 함수를 거친 링크 접사로, 특정 발음 구별 기호를 제거할 수 있습니다.
정리된 링크 접사는 현재 분류명에 사용되는 것입니다.
* "조회 접사"는 위에서 설명한 언어별 조회 매핑에서 조회되는 형태의 접사입니다. 실제로는 두 단계의 조회가 있습니다:
*# 먼저, 수정된 표시 형태(표시 접사와 같지만 조회 하이픈 사용)로 접사를 조회합니다.
*# 항목을 찾지 못하면, 발음 구별 기호를 제거한 수정된 링크 형태로 다시 조회합니다.
이 이중 조회 절차는 발음 구별 기호에 민감한 매핑과 그렇지 않은 매핑을 모두 허용하기 위함입니다.
* "분류 접사"는 [[:분류:-kas가 붙은 핀란드어 낱말]]과 같은 분류에 나타나는 접사입니다.
분류 접사는 현재 항상 정리된 링크 접사와 동일합니다.
]==]
-----------------------------------------------------------------------------------------
-- Template and display hyphens --
-----------------------------------------------------------------------------------------
--[=[
스크립트별 틀 하이픈. 틀 하이픈은 {{접사}}/{{접두사}}/{{접미사}} 등 틀의 위키코드에 나타나는 것입니다. 위 설명 참조.
아래 키는 스크립트 코드에서 하이픈과 그 앞부분을 제거한 것입니다. 따라서 'fa-Arab', 'ur-Arab' 같은 스크립트 코드는 'Arab'과 일치합니다.
값은 하나 이상의 하이픈 문자로 이루어진 문자열입니다.
]=]
local ZWNJ = u(0x200C) -- 폭 없는 비연결자(Zero-width non-joiner)
local template_hyphens = {
-- This covers all Arabic scripts. See above.
["Arab"] = "ـ" .. ZWNJ .. "-", -- 타트윌 + ZWNJ + 일반 하이픈
["Hebr"] = "־", -- 히브리어 고유 하이픈 "마켑(maqqef)"
["Mong"] = "᠊",
-- FIXME! 아래 오른쪽-왼쪽 스크립트들에 대한 처리 필요
-- Adlm (Adlam)
-- Armi (Imperial Aramaic)
-- Avst (Avestan)
-- Cprt (Cypriot)
-- Khar (Kharoshthi)
-- Mand (Mandaic/Mandaean)
-- Mani (Manichaean)
-- Mend (Mende/Mende Kikakui)
-- Narb (Old North Arabian)
-- Nbat (Nabataean/Nabatean)
-- Nkoo (N'Ko)
-- Orkh (Orkhon runes)
-- Phli (Inscriptional Pahlavi)
-- Phlp (Psalter Pahlavi)
-- Phlv (Book Pahlavi)
-- Phnx (Phoenician)
-- Prti (Inscriptional Parthian)
-- Rohg (Hanifi Rohingya)
-- Samr (Samaritan)
-- Sarb (Old South Arabian)
-- Sogd (Sogdian)
-- Sogo (Old Sogdian)
-- Syrc (Syriac)
-- Thaa (Thaana)
}
-- 언어별 접사 매핑에서 접사를 조회할 때 사용되는 하이픈. 기본값은 일반 하이픈(-).
-- 키는 스크립트 코드. 값은 단일 문자여야 함.
local lookup_hyphens = {
["Hebr"] = "־",
-- 모든 아랍계 스크립트에 해당.
["Arab"] = "ـ",
}
-- 기본 표시-하이픈 함수
local function default_display_hyphen(script, hyph)
if not hyph then
return template_hyphens[script] or "-"
end
return hyph
end
-- 아랍계 스크립트의 표시-하이픈 함수
local function arab_get_display_hyphen(script, hyph)
if not hyph then
return "ـ" -- 타트윌
elseif hyph == ZWNJ then
return ""
else
return hyph
end
end
local function no_display_hyphen(script, hyph)
return ""
end
-- 스크립트별로 올바른 표시 하이픈을 반환하는 함수
-- 키는 스크립트 코드
local display_hyphens = {
-- 모든 아랍계 스크립트에 해당
["Arab"] = arab_get_display_hyphen,
["Bopo"] = no_display_hyphen, -- 주음부호
["Hani"] = no_display_hyphen, -- 한자(통합)
["Hans"] = no_display_hyphen, -- 한자(간체)
["Hant"] = no_display_hyphen, -- 한자(정체)
["Jpan"] = no_display_hyphen, -- 일본어
["Jurc"] = no_display_hyphen, -- 여진 문자
["Kitl"] = no_display_hyphen, -- 거란 소자
["Kits"] = no_display_hyphen, -- 거란 대자
["Laoo"] = no_display_hyphen, -- 라오 문자
["Nshu"] = no_display_hyphen, -- 여서 문자
["Shui"] = no_display_hyphen, -- 수 문자
["Tang"] = no_display_hyphen, -- 서하 문자
["Thaa"] = no_display_hyphen, -- 타나 문자
["Thai"] = no_display_hyphen, -- 타이 문자
}
-----------------------------------------------------------------------------------------
-- Basic Utility functions --
-----------------------------------------------------------------------------------------
local function glossary_link(entry, text)
text = text or entry
return "[[부록:용어사전#" .. entry .. "|" .. text .. "]]"
end
local function track(page)
if type(page) == "table" then
for i, pg in ipairs(page) do
page[i] = "affix/" .. pg
end
else
page = "affix/" .. page
end
require("Module:debug/track")(page)
end
local function ine(val)
return val ~= "" and val or nil
end
-----------------------------------------------------------------------------------------
-- 복합어 유형 --
-----------------------------------------------------------------------------------------
-- 복합어 유형 항목 생성 (용어사전 링크 사용)
local function make_compound_type(typ, alttext)
return {
text = glossary_link(typ, alttext) .. " 복합어",
cat = typ .. " 복합어",
}
end
-- 복합어 유형 항목 생성 (단순 링크 사용)
-- 추후 용어집에 항목이 생성되면 glossary_link로 대체해야 함
local function make_non_glossary_compound_type(typ, alttext)
local link = alttext and "[[" .. typ .. "|" .. alttext .. "]]" or "[[" .. typ .. "]]"
return {
text = link .. " 복합어",
cat = typ .. " 복합어",
}
end
-- 원시 복합어 유형 항목 생성
local function make_raw_compound_type(typ, alttext)
return {
text = glossary_link(typ, alttext),
cat = typ,
}
end
-- 차용 유형 항목 생성
local function make_borrowing_type(typ, alttext)
return {
text = glossary_link(typ, alttext),
borrowing_type = typ,
}
end
-- 각종 어원 유형 및 약어 정의
export.etymology_types = {
["adapted borrowing"] = make_borrowing_type("adapted borrowing", "순화 차용"),
["adap"] = "adapted borrowing",
["abor"] = "adapted borrowing",
["alliterative"] = make_non_glossary_compound_type("alliterative", "두운"),
["allit"] = "alliterative",
["antonymous"] = make_non_glossary_compound_type("antonymous", "반의"),
["ant"] = "antonymous",
["bahuvrihi"] = make_compound_type("bahuvrihi", "bahuvrīhi", "바후브리히"),
["bahu"] = "bahuvrihi",
["bv"] = "bahuvrihi",
["coordinative"] = make_compound_type("coordinative", "대등"),
["coord"] = "coordinative",
["descriptive"] = make_compound_type("descriptive", "기술"),
["desc"] = "descriptive",
["determinative"] = make_compound_type("determinative", "한정"),
["det"] = "determinative",
["dvandva"] = make_compound_type("dvandva", "드반드바"),
["dva"] = "dvandva",
["dvigu"] = make_compound_type("dvigu", "드비구"),
["dvi"] = "dvigu",
["endocentric"] = make_compound_type("endocentric", "내심"),
["endo"] = "endocentric",
["exocentric"] = make_compound_type("exocentric", "외심"),
["exo"] = "exocentric",
["izafet I"] = make_compound_type("izafet I", "이자페트 I"),
["iz1"] = "izafet I",
["izafet II"] = make_compound_type("izafet II", "이자페트 II"),
["iz2"] = "izafet II",
["izafet III"] = make_compound_type("izafet III", "이자페트 III"),
["iz3"] = "izafet III",
["karmadharaya"] = make_compound_type("karmadharaya", "karmadhāraya", "카르마다라야"),
["karma"] = "karmadharaya",
["kd"] = "karmadharaya",
["kenning"] = make_raw_compound_type("kenning", "케닝"),
["ken"] = "kenning",
["rhyming"] = make_non_glossary_compound_type("rhyming", "압운"),
["rhy"] = "rhyming",
["synonymous"] = make_non_glossary_compound_type("synonymous", "동의"),
["syn"] = "synonymous",
["tatpurusa"] = make_compound_type("tatpurusa", "tatpuruṣa", "타트푸루샤"),
["tat"] = "tatpurusa",
["tp"] = "tatpurusa",
}
-- 어원 유형 처리
local function process_etymology_type(typ, nocap, notext, has_parts)
local text_sections = {}
local categories = {}
local borrowing_type
if typ then
local typdata = export.etymology_types[typ]
if type(typdata) == "string" then
typdata = export.etymology_types[typdata]
end
if not typdata then
error("내부 오류: 인식할 수 없는 유형 '" .. typ .. "'")
end
local text = typdata.text
if not nocap then
text = ucfirst(text)
end
local cat = typdata.cat
borrowing_type = typdata.borrowing_type
local oftext = typdata.oftext or "의 "
if not notext then
if has_parts then
table.insert(text_sections, text)
table.insert(text_sections, oftext)
else
table.insert(text_sections, text)
end
end
if cat then
table.insert(categories, cat)
end
end
return text_sections, categories, borrowing_type
end
-----------------------------------------------------------------------------------------
-- 유틸리티 함수 --
-----------------------------------------------------------------------------------------
-- 배열에서 가장 큰 정수 인덱스까지 반복
local function ipairs_with_gaps(t)
local indices = m_table.numKeys(t)
local max_index = #indices > 0 and math.max(unpack(indices)) or 0
local i = 0
return function()
while i < max_index do
i = i + 1
return i, t[i]
end
end
end
export.ipairs_with_gaps = ipairs_with_gaps
--[==[
형식화된 부분들(`parts_formatted`)을 합치고, 직역(`lit`), 분류(`categories`) 등을 추가하는 함수.
`nocat`이 주어지면 분류를 추가하지 않음. `force_cat`은 사용자 문서 등에서도 분류를 강제로 추가함.
]==]
function export.join_formatted_parts(data)
local cattext
local lang = data.data.lang
local force_cat = data.data.force_cat or debug_force_cat
if data.data.nocat then
cattext = ""
else
for i, cat in ipairs(data.categories) do
local cat_base_string
local sort_key
local sort_base
if type(cat) == "table" then
cat_base_string = cat.cat
sort_key = cat.sort_key
sort_base = cat.sort_base
else
cat_base_string = cat
sort_key = data.data.sort_key
sort_base = nil
end
local full_cat_string
if cat_base_string:find(" 붙은 ", 1, true) then
full_cat_string = cat_base_string:gsub(" 붙은 ", " 붙은 " .. lang:getFullName() .. " ", 1)
else
full_cat_string = lang:getFullName() .. " " .. cat_base_string
end
data.categories[i] = require(utilities_module).format_categories(full_cat_string, lang, sort_key, sort_base, force_cat)
end
cattext = table.concat(data.categories)
end
local result = table.concat(data.parts_formatted, " +‎ ") .. (data.data.lit and ", 직역하면 " ..
m_links.mark(data.data.lit, "gloss") or "")
local q = data.data.q
local qq = data.data.qq
local l = data.data.l
local ll = data.data.ll
if q and q[1] or qq and qq[1] or l and l[1] or ll and ll[1] then
result = require(pron_qualifier_module).format_qualifiers {
lang = lang,
text = result,
q = q,
qq = qq,
l = l,
ll = ll,
}
end
return result .. cattext
end
--[==[
join_formatted_parts()를 호출하는 구식 함수. 호출부를 수정해야 함.
]==]
function export.concat_parts(lang, parts_formatted, categories, nocat, sort_key, lit, force_cat)
return export.join_formatted_parts {
data = {
lang = lang,
nocat = nocat,
sort_key = sort_key,
lit = lit,
force_cat = force_cat,
},
parts_formatted = parts_formatted,
categories = categories,
}
end
-- 링크를 제거하고 lang:makeEntryName(term) 호출
local function make_entry_name_no_links(lang, term)
return (lang:makeEntryName(m_links.remove_links(term)))
end
--[=[
매개변수로 넘어온 원시(raw) 부분을 링크 가능한 부분으로 변환.
전체 언어/스크립트를 기본값으로 사용하고, 용어에서 조각(#)을 파싱함.
]=]
local function canonicalize_part(part, lang, sc)
if not part then
return
end
-- Save the original (user-specified, part-specific) value of `lang`. If such a value is specified, we don't insert
-- a '*fixed with' category, and we format the part using format_derived() in [[Module:etymology]] rather than
-- full_link() in [[Module:links]].
part.part_lang = part.lang
part.lang = part.lang or lang
part.sc = part.sc or sc
local term = part.term
if not term then
return
elseif not part.fragment then
part.term, part.fragment = m_links.get_fragment(term)
else
part.term = m_links.get_fragment(term)
end
end
--[==[
주어진 부분(`part`)의 정보를 바탕으로 링크된 부분을 생성.
`part.part_lang`이 지정되면 모듈:etymology의 `format_derived`를 호출하고,
아니면 모듈:links의 `full_link`를 호출.
]==]
function export.link_term(part, data)
local result
if part.part_lang then
-- format_derived() processes per-part qualifiers, labels and references, but they end up on the wrong side
-- of the source (at least on the left), so we need to move them up.
local q = part.q
local qq = part.qq
local l = part.l
local ll = part.ll
local refs = part.refs
part.q = nil
part.qq = nil
part.l = nil
part.ll = nil
part.refs = nil
result = require(etymology_module).format_derived {
lang = data.lang,
terms = {part},
sources = {part.lang},
sort_key = data.sort_key,
nocat = data.nocat,
borrowing_type = data.borrowing_type,
force_cat = data.force_cat or debug_force_cat,
q = q,
qq = qq,
l = l,
ll = ll,
refs = refs,
}
else
result = m_links.full_link(part, "term", nil, "show qualifiers")
end
return result
end
-- 스크립트 코드를 표준화 (예: fa-Arab -> Arab)
local function canonicalize_script_code(scode)
return (scode:gsub("^.*%-", ""))
end
-----------------------------------------------------------------------------------------
-- 접사 처리 함수 --
-----------------------------------------------------------------------------------------
-- 주어진 접사와 언어에 맞는 스크립트를 파악하고, 그에 맞는 하이픈 값들을 반환.
local function detect_script_and_hyphens(text, lang, sc)
local scode
if sc then
scode = sc:getCode()
else
local possible_script_codes = lang:getScriptCodes()
local num_possible_script_codes = m_table.length(possible_script_codes)
if num_possible_script_codes == 0 then
error("심각한 오류 발생! " .. lang:getCanonicalName() .. " 언어에 스크립트 코드가 없습니다.")
end
if num_possible_script_codes == 1 then
scode = possible_script_codes[1]
else
local may_have_nondefault_hyphen = false
for _, script_code in ipairs(possible_script_codes) do
script_code = canonicalize_script_code(script_code)
if template_hyphens[script_code] or display_hyphens[script_code] then
may_have_nondefault_hyphen = true
break
end
end
if not may_have_nondefault_hyphen then
scode = "Latn"
else
scode = lang:findBestScript(text):getCode()
end
end
end
scode = canonicalize_script_code(scode)
local template_hyphen = template_hyphens[scode] or "-"
local lookup_hyphen = lookup_hyphens[scode] or "-"
local display_hyphen = display_hyphens[scode] or default_display_hyphen
return scode, template_hyphen, display_hyphen, lookup_hyphen
end
--[=[
주어진 틀 접사(`term`)와 접사 유형(`affix_type`)에 따라, 관련된 틀 하이픈을
새로운 하이픈(`new_hyphen`)으로 변경하거나, 없는 경우 추가함.
]=]
local function reconstruct_term_per_hyphens(term, affix_type, scode, thyph_re, new_hyphen)
local function get_hyphen(hyph)
if type(new_hyphen) == "string" then
return new_hyphen
end
return new_hyphen(scode, hyph)
end
if affix_type == "non-affix" then
return term
elseif affix_type == "circumfix" then
local before, before_hyphen, after_hyphen, after = rmatch(term, "^(.*)" .. thyph_re .. " " .. thyph_re
.. "(.*)$")
if not before or ulen(term) <= 3 then
-- Unlike with other types of affixes, don't try to add hyphens in the middle of the term to convert it to
-- a circumfix. Also, if the term is just hyphen + space + hyphen, return it.
return term
end
return before .. get_hyphen(before_hyphen) .. " " .. get_hyphen(after_hyphen) .. after
elseif affix_type == "infix" or affix_type == "interfix" then
local before_hyphen, middle, after_hyphen = rmatch(term, "^" .. thyph_re .. "(.*)" .. thyph_re .. "$")
if before_hyphen and ulen(term) <= 1 then
-- If the term is just a hyphen, return it.
return term
end
return get_hyphen(before_hyphen) .. (middle or term) .. get_hyphen(after_hyphen)
elseif affix_type == "prefix" then
local middle, after_hyphen = rmatch(term, "^(.*)" .. thyph_re .. "$")
if middle and ulen(term) <= 1 then
-- If the term is just a hyphen, return it.
return term
end
return (middle or term) .. get_hyphen(after_hyphen)
elseif affix_type == "suffix" then
local before_hyphen, middle = rmatch(term, "^" .. thyph_re .. "(.*)$")
if before_hyphen and ulen(term) <= 1 then
-- If the term is just a hyphen, return it.
return term
end
return get_hyphen(before_hyphen) .. (middle or term)
else
error(("내부 오류: 인식할 수 없는 접사 유형 '%s'"):format(affix_type))
end
end
--[=[
주어진 접사 변이형을 표준형으로 매핑하는 함수.
매핑 테이블은 언어별, ID별로 지정될 수 있음.
]=]
local function lookup_affix_mapping(affix, affix_type, lang, scode, thyph_re, lookup_hyph, affix_id)
local function do_lookup(affix)
-- Ensure that the affix uses lookup hyphens regardless of whether it used a different type of hyphens before
-- or no hyphens.
local lookup_affix = reconstruct_term_per_hyphens(affix, affix_type, scode, thyph_re, lookup_hyph)
local function do_lookup_for_langcode(langcode)
if export.langs_with_lang_specific_data[langcode] then
local langdata = mw.loadData(export.affix_lang_data_module_prefix .. langcode)
if langdata.affix_mappings then
local mapping = langdata.affix_mappings[lookup_affix]
if mapping then
if type(mapping) == "table" then
mapping = mapping[affix_id or false]
if mapping then
return mapping
end
else
return mapping
end
end
end
end
end
-- If `lang` is an etymology-only language, look for a mapping both for it and its full parent.
local langcode = lang:getCode()
local mapping = do_lookup_for_langcode(langcode)
if mapping then
return mapping
end
local full_langcode = lang:getFullCode()
if full_langcode ~= langcode then
mapping = do_lookup_for_langcode(full_langcode)
if mapping then
return mapping
end
end
return nil
end
if affix:find("%[%[") then
return nil
end
-- Double parens because makeEntryName() returns multiple values. Yuck.
return do_lookup(affix) or do_lookup((lang:makeEntryName(affix))) or nil
end
--[==[
주어진 틀 용어(`term`)에 대해 접사 유형, 링크용어, 표시용어, 조회용어를 반환하는 핵심 분석 함수.
]==]
local function parse_term_for_affixes(term, lang, sc, affix_type, do_affix_mapping, return_lookup_affix, affix_id)
if not term then
return "non-affix", nil, nil, nil
end
if term == "^" then
-- Indicates a null term to emulate the behavior of {{suffix|foo||bar}}.
term = ""
return "non-affix", term, term, term
end
if term:find("^%^") then
-- HACK! ^ at the beginning of Korean languages has a special meaning, triggering capitalization of the
-- transliteration. Don't interpret it as "force non-affix" for those languages.
local langcode = lang:getCode()
if langcode ~= "ko" and langcode ~= "okm" and langcode ~= "jje" then
-- Formerly we allowed ^ to force non-affix type; this is now handled using an inline modifier
-- <naf>, <root>, etc. Throw an error for the moment when the old way is encountered.
error("^ 기호를 사용하여 비접사 상태를 강제하는 것은 더 이상 지원되지 않습니다. 요소 뒤에 <naf> 또는 <root>와 같은 인라인 수정자를 사용하세요.")
end
end
-- Remove an asterisk if the morpheme is reconstructed and add it back at the end.
local reconstructed = ""
if term:find("^%*") then
reconstructed = "*"
term = term:gsub("^%*", "")
end
local scode, thyph, dhyph, lhyph = detect_script_and_hyphens(term, lang, sc)
thyph = "([" .. thyph .. "])"
if not affix_type then
if rfind(term, thyph .. " " .. thyph) then
affix_type = "circumfix"
else
local has_beginning_hyphen = rfind(term, "^" .. thyph)
local has_ending_hyphen = rfind(term, thyph .. "$")
if has_beginning_hyphen and has_ending_hyphen then
affix_type = "interfix"
elseif has_ending_hyphen then
affix_type = "prefix"
elseif has_beginning_hyphen then
affix_type = "suffix"
else
affix_type = "non-affix"
end
end
end
local link_term, display_term, lookup_term
if affix_type == "non-affix" then
link_term = term
display_term = term
lookup_term = term
else
display_term = reconstruct_term_per_hyphens(term, affix_type, scode, thyph, dhyph)
if do_affix_mapping then
link_term = lookup_affix_mapping(term, affix_type, lang, scode, thyph, lhyph, affix_id)
-- The return value of lookup_affix_mapping() may be an affix mapping with lookup hyphens if a mapping
-- was found, otherwise nil if a mapping was not found. We need to convert to display hyphens in
-- either case, but in the latter case we can reuse the display term, which has already been converted.
if link_term then
link_term = reconstruct_term_per_hyphens(link_term, affix_type, scode, thyph, dhyph)
else
link_term = display_term
end
else
link_term = display_term
end
if return_lookup_affix then
lookup_term = reconstruct_term_per_hyphens(term, affix_type, scode, thyph, lhyph)
else
lookup_term = display_term
end
end
link_term = reconstructed .. link_term
display_term = reconstructed .. display_term
lookup_term = reconstructed .. lookup_term
return affix_type, link_term, display_term, lookup_term
end
--[==[
Add a hyphen to a term in the appropriate place, based on the specified affix type, stripping off any existing hyphens
in that place. For example, if `affix_type` == {"prefix"}, we'll add a hyphen onto the end if it's not already there (or
is of the wrong type). Three values are returned: the link term, display term and lookup term. This function is a thin
wrapper around `parse_term_for_affixes`; see the comments above that function for more information. Note that this
function is exposed externally because it is called by [[Module:category tree/affixes and compounds]]; see the comment
in `parse_term_for_affixes` for more information.
]==]
function export.make_affix(term, lang, sc, affix_type, do_affix_mapping, return_lookup_affix, affix_id)
if not (affix_type == "prefix" or affix_type == "suffix" or affix_type == "circumfix" or affix_type == "infix" or
affix_type == "interfix" or affix_type == "non-affix") then
error("내부 오류: 유효하지 않은 접사 유형 " .. (affix_type or "(없음)"))
end
local _, link_term, display_term, lookup_term = parse_term_for_affixes(term, lang, sc, affix_type,
do_affix_mapping, return_lookup_affix, affix_id)
return link_term, display_term, lookup_term
end
-----------------------------------------------------------------------------------------
-- 메인 진입점 --
-----------------------------------------------------------------------------------------
--[==[
==[{{affix}} 및 {{surface analysis}} 구현.
`data` 테이블은 표시에 필요한 모든 정보를 담고 있음.
* `.lang` (필수): 전체 언어 객체.
* `.parts` (필수): 표시할 접사 부분들의 목록.
* `.pos`: 전체 품사 (기본값: "낱말"). 분류에 사용됨.
경고: 이 함수는 `data`와 `.parts` 안의 개별 구조를 직접 수정함.
]==]
function export.show_affix(data)
data.pos = data.pos or default_pos
local text_sections, categories, borrowing_type =
process_etymology_type(data.type, data.surface_analysis or data.nocap, data.notext, #data.parts > 0)
data.borrowing_type = borrowing_type
-- Process each part
local parts_formatted = {}
local whole_words = 0
local is_affix_or_compound = false
-- 모든 부분을 먼저 표준화하고 링크를 생성
for i, part in ipairs_with_gaps(data.parts) do
part = part or {}
data.parts[i] = part
canonicalize_part(part, data.lang, data.sc)
-- Determine affix type and get link and display terms (see text at top of file). Store them in the part
-- (in fields that won't clash with fields used by full_link() in [[Module:links]] or link_term()), so they
-- can be used in the loop below when categorizing.
part.affix_type, part.affix_link_term, part.affix_display_term = parse_term_for_affixes(part.term,
part.lang, part.sc, part.type, not part.alt, nil, part.id)
-- If link_term is an empty string, either a bare ^ was specified or an empty term was used along with inline
-- modifiers. The intention in either case is not to link the term.
part.term = ine(part.affix_link_term)
-- If part.alt would be the same as part.term, make it nil, so that it isn't erroneously tracked as being
-- redundant alt text.
part.alt = part.alt or (part.affix_display_term ~= part.affix_link_term and part.affix_display_term) or nil
-- Make a link for the part.
table.insert(parts_formatted, export.link_term(part, data))
end
-- 그 다음 분류 작업을 수행
for i, part in ipairs_with_gaps(data.parts) do
local affix_type = part.affix_type
if affix_type ~= "non-affix" then
is_affix_or_compound = true
-- Make a sort key. For the first part, use the second part as the sort key; the intention is that if the
-- term has a prefix, sorting by the prefix won't be very useful so we sort by what follows, which is
-- presumably the root.
local part_sort_base = nil
local part_sort = part.sort or data.sort_key
if i == 1 and data.parts[2] and data.parts[2].term then
local part2 = data.parts[2]
-- If the second-part link term is empty, the user requested an unlinked term; avoid a wikitext error
-- by using the alt value if available.
part_sort_base = ine(part2.affix_link_term) or ine(part2.alt)
if part_sort_base then
part_sort_base = make_entry_name_no_links(part2.lang, part_sort_base)
end
end
if part.pos and rfind(part.pos, "patronym") then
table.insert(categories, {cat = "인명", sort_key = part_sort, sort_base = part_sort_base})
end
if data.pos ~= "낱말" and part.pos and rfind(part.pos, "diminutive") then
table.insert(categories, {cat = "지소 " .. data.pos, sort_key = part_sort,
sort_base = part_sort_base})
end
-- Don't add a '*fixed with' category if the link term is empty or is in a different language.
if ine(part.affix_link_term) and not part.part_lang then
local affix_type_ko
if affix_type == "prefix" then affix_type_ko = "접두사"
elseif affix_type == "suffix" then affix_type_ko = "접미사"
elseif affix_type == "infix" then affix_type_ko = "접요사"
elseif affix_type == "circumfix" then affix_type_ko = "양접사"
elseif affix_type == "interfix" then affix_type_ko = "연결사"
end
if affix_type_ko then
local affix_name = make_entry_name_no_links(part.lang, part.affix_link_term) .. (part.id and " (" .. part.id .. ")" or "")
table.insert(categories, {cat = affix_type_ko .. " " .. m_ko_utils.allomorphy(affix_name, "sbj") .. " 붙은 " .. data.pos,
sort_key = part_sort, sort_base = part_sort_base})
end
end
else
whole_words = whole_words + 1
if whole_words == 2 then
is_affix_or_compound = true
if data.pos == default_pos then
table.insert(categories, "복합어")
else
table.insert(categories, "복합 " .. data.pos)
end
end
end
end
-- Make sure there was either an affix or a compound (two or more non-affix terms).
if not is_affix_or_compound then
error("매개변수에 접사가 포함되지 않았거나, 복합어가 아닙니다. 적어도 하나의 접사를 제공해주세요.")
end
if data.surface_analysis then
local text = glossary_link("표면 분석") .. "에 따르면, "
if not data.nocap then
text = ucfirst(text)
end
table.insert(text_sections, 1, text)
end
table.insert(text_sections, export.join_formatted_parts { data = data, parts_formatted = parts_formatted,
categories = categories })
return table.concat(text_sections)
end
function export.show_surface_analysis(data)
data.surface_analysis = true
return export.show_affix(data)
end
--[==[
{{compound}} 구현.
경고: 이 함수는 `data`와 `.parts` 안의 개별 구조를 직접 수정함.
]==]
function export.show_compound(data)
data.pos = data.pos or default_pos
local text_sections, categories, borrowing_type =
process_etymology_type(data.type, data.nocap, data.notext, #data.parts > 0)
data.borrowing_type = borrowing_type
local parts_formatted = {}
if data.pos == default_pos then
table.insert(categories, "복합어")
else
table.insert(categories, "복합 " .. data.pos)
end
-- Make links out of all the parts
local whole_words = 0
for i, part in ipairs(data.parts) do
canonicalize_part(part, data.lang, data.sc)
local affix_type, link_term, display_term = parse_term_for_affixes(part.term, part.lang, part.sc,
part.type, not part.alt, nil, part.id)
if affix_type == "interfix" or (part.type and part.type ~= "non-affix") then
if link_term and link_term ~= "" and not part.part_lang then
table.insert(categories, {cat = data.pos .. " " .. affix_type .. "ed with " ..
make_entry_name_no_links(part.lang, link_term), sort_key = part.sort or data.sort_key})
end
part.term = link_term ~= "" and link_term or nil
part.alt = part.alt or (display_term ~= link_term and display_term) or nil
else
if affix_type ~= "non-affix" then
local langcode = data.lang:getCode()
track { affix_type, affix_type .. "/lang/" .. langcode }
local full_langcode = data.lang:getFullCode()
if langcode ~= full_langcode then
track(affix_type .. "/lang/" .. full_langcode)
end
else
whole_words = whole_words + 1
end
end
table.insert(parts_formatted, export.link_term(part, data))
end
if whole_words == 1 then
track("one whole word")
elseif whole_words == 0 then
track("looks like confix")
end
table.insert(text_sections, export.join_formatted_parts { data = data, parts_formatted = parts_formatted,
categories = categories })
return table.concat(text_sections)
end
--[==[
{{blend}}, {{univerbation}} 등 구현.
]==]
function export.show_compound_like(data)
local parts_formatted = {}
local categories = {}
if data.cat then
table.insert(categories, data.cat)
end
-- Make links out of all the parts
for i, part in ipairs(data.parts) do
canonicalize_part(part, data.lang, data.sc)
table.insert(parts_formatted, export.link_term(part, data))
end
local text_sections = {}
table.insert(text_sections, export.join_formatted_parts { data = data, parts_formatted = parts_formatted, categories = categories })
if #data.parts > 0 and data.oftext then
table.insert(text_sections, data.oftext)
end
if data.text then
table.insert(text_sections, " ")
table.insert(text_sections, data.text)
end
return table.concat(text_sections)
end
--[==[
주어진 부분을 특정 유형의 접사로 만들고, 관련 매핑을 적용.
구식 틀들({{prefix}}, {{suffix}} 등)에서 사용됨.
경고: 이 함수는 `part`를 직접 수정함.
]==]
local function make_part_into_affix(part, lang, sc, affix_type)
canonicalize_part(part, lang, sc)
local link_term, display_term = export.make_affix(part.term, part.lang, part.sc, affix_type, not part.alt, nil, part.id)
part.term = link_term
-- When we don't specify `do_affix_mapping` to make_affix(), link and display terms (first and second retvals of
-- make_affix()) are the same.
-- If part.alt would be the same as part.term, make it nil, so that it isn't erroneously tracked as being
-- redundant alt text.
part.alt = part.alt and export.make_affix(part.alt, part.lang, part.sc, affix_type) or (display_term ~= link_term and display_term) or nil
local Latn = require(scripts_module).getByCode("Latn")
part.tr = export.make_affix(part.tr, part.lang, Latn, affix_type)
part.ts = export.make_affix(part.ts, part.lang, Latn, affix_type)
end
-- 잘못된 접사 유형 사용을 추적하는 도우미 함수
local function track_wrong_affix_type(template, part, expected_affix_type)
if part and not part.type then
local affix_type = parse_term_for_affixes(part.term, part.lang, part.sc)
if affix_type ~= expected_affix_type then
local part_name = expected_affix_type or "base"
local langcode = part.lang:getCode()
local full_langcode = part.lang:getFullCode()
require("Module:debug/track") {
template,
template .. "/" .. part_name,
template .. "/" .. part_name .. "/" .. (affix_type or "none"),
template .. "/" .. part_name .. "/" .. (affix_type or "none") .. "/lang/" .. langcode
}
-- If `part.lang` is an etymology-only language, track both using its code and its full parent's code.
if full_langcode ~= langcode then
require("Module:debug/track")(
template .. "/" .. part_name .. "/" .. (affix_type or "none") .. "/lang/" .. full_langcode
)
end
end
end
end
-- 접사 분류를 추가하는 도우미 함수
local function insert_affix_category(categories, pos, affix_type, part, sort_key, sort_base)
if part.term and not part.part_lang then
local affix_name = make_entry_name_no_links(part.lang, part.term) .. (part.id and " (" .. part.id .. ")" or "")
local affix_type_ko
if affix_type == "prefix" then affix_type_ko = "접두사"
elseif affix_type == "suffix" then affix_type_ko = "접미사"
elseif affix_type == "infix" then affix_type_ko = "접요사"
elseif affix_type == "circumfix" then affix_type_ko = "양접사"
elseif affix_type == "interfix" then affix_type_ko = "연결사"
end
if affix_type_ko then
local cat = affix_type_ko .. " " .. m_ko_utils.allomorphy(affix_name, "sbj") .. " 붙은 " .. pos
if sort_key or sort_base then
table.insert(categories, {cat = cat, sort_key = sort_key, sort_base = sort_base})
else
table.insert(categories, cat)
end
end
end
end
--[==[
{{circumfix}} 구현.
경고: 이 함수는 `data`, `.prefix`, `.base`, `.suffix`를 직접 수정함.
]==]
function export.show_circumfix(data)
data.pos = data.pos or default_pos
canonicalize_part(data.base, data.lang, data.sc)
-- Hyphenate the affixes and apply any affix mappings.
make_part_into_affix(data.prefix, data.lang, data.sc, "prefix")
make_part_into_affix(data.suffix, data.lang, data.sc, "suffix")
track_wrong_affix_type("circumfix", data.prefix, "prefix")
track_wrong_affix_type("circumfix", data.base, nil)
track_wrong_affix_type("circumfix", data.suffix, "suffix")
-- Create circumfix term.
local circumfix = nil
if data.prefix.term and data.suffix.term then
circumfix = data.prefix.term .. " " .. data.suffix.term
data.prefix.alt = data.prefix.alt or data.prefix.term
data.suffix.alt = data.suffix.alt or data.suffix.term
data.prefix.term = circumfix
data.suffix.term = circumfix
end
-- Make links out of all the parts.
local parts_formatted = {}
local categories = {}
local sort_base
if data.base.term then
sort_base = make_entry_name_no_links(data.base.lang, data.base.term)
end
table.insert(parts_formatted, export.link_term(data.prefix, data))
table.insert(parts_formatted, export.link_term(data.base, data))
table.insert(parts_formatted, export.link_term(data.suffix, data))
-- Insert the categories, but don't add a '*fixed with' category if the link term is in a different language.
if not data.prefix.part_lang then
local affix_name = make_entry_name_no_links(data.prefix.lang, circumfix)
table.insert(categories, {cat=affix_name .. " 양접사가 붙은 " .. data.pos, sort_key=data.sort_key, sort_base=sort_base})
end
return export.join_formatted_parts { data = data, parts_formatted = parts_formatted, categories = categories }
end
--[==[
{{confix}} 구현.
]==]
function export.show_confix(data)
data.pos = data.pos or default_pos
canonicalize_part(data.base, data.lang, data.sc)
-- Hyphenate the affixes and apply any affix mappings.
make_part_into_affix(data.prefix, data.lang, data.sc, "prefix")
make_part_into_affix(data.suffix, data.lang, data.sc, "suffix")
track_wrong_affix_type("confix", data.prefix, "prefix")
track_wrong_affix_type("confix", data.base, nil)
track_wrong_affix_type("confix", data.suffix, "suffix")
-- Make links out of all the parts.
local parts_formatted = {}
local prefix_sort_base
if data.base and data.base.term then
prefix_sort_base = make_entry_name_no_links(data.base.lang, data.base.term)
elseif data.suffix.term then
prefix_sort_base = make_entry_name_no_links(data.suffix.lang, data.suffix.term)
end
-- Insert the categories and parts.
local categories = {}
table.insert(parts_formatted, export.link_term(data.prefix, data))
insert_affix_category(categories, data.pos, "prefix", data.prefix, data.sort_key, prefix_sort_base)
if data.base then
table.insert(parts_formatted, export.link_term(data.base, data))
end
table.insert(parts_formatted, export.link_term(data.suffix, data))
-- FIXME, should we be specifying a sort base here?
insert_affix_category(categories, data.pos, "suffix", data.suffix)
return export.join_formatted_parts { data = data, parts_formatted = parts_formatted, categories = categories }
end
--[==[
{{infix}} 구현.
]==]
function export.show_infix(data)
data.pos = data.pos or default_pos
canonicalize_part(data.base, data.lang, data.sc)
-- Hyphenate the affixes and apply any affix mappings.
make_part_into_affix(data.infix, data.lang, data.sc, "infix")
track_wrong_affix_type("infix", data.base, nil)
track_wrong_affix_type("infix", data.infix, "infix")
-- Make links out of all the parts.
local parts_formatted = {}
local categories = {}
table.insert(parts_formatted, export.link_term(data.base, data))
table.insert(parts_formatted, export.link_term(data.infix, data))
-- Insert the categories.
-- FIXME, should we be specifying a sort base here?
insert_affix_category(categories, data.pos, "infix", data.infix)
return export.join_formatted_parts { data = data, parts_formatted = parts_formatted, categories = categories }
end
--[==[
{{prefix}} 구현.
'''WARNING''': This destructively modifies both `data` and the structures within `.prefixes`, as well as `.base`.
]==]
function export.show_prefix(data)
data.pos = data.pos or default_pos
canonicalize_part(data.base, data.lang, data.sc)
-- Hyphenate the affixes and apply any affix mappings.
for i, prefix in ipairs(data.prefixes) do
make_part_into_affix(prefix, data.lang, data.sc, "prefix")
end
for i, prefix in ipairs(data.prefixes) do
track_wrong_affix_type("prefix", prefix, "prefix")
end
track_wrong_affix_type("prefix", data.base, nil)
-- Make links out of all the parts.
local parts_formatted = {}
local first_sort_base = nil
local categories = {}
if data.prefixes[2] then
first_sort_base = ine(data.prefixes[2].term) or ine(data.prefixes[2].alt)
if first_sort_base then
first_sort_base = make_entry_name_no_links(data.prefixes[2].lang, first_sort_base)
end
elseif data.base then
first_sort_base = ine(data.base.term) or ine(data.base.alt)
if first_sort_base then
first_sort_base = make_entry_name_no_links(data.base.lang, first_sort_base)
end
end
for i, prefix in ipairs(data.prefixes) do
table.insert(parts_formatted, export.link_term(prefix, data))
insert_affix_category(categories, data.pos, "prefix", prefix, data.sort_key, i == 1 and first_sort_base or nil)
end
if data.base then
table.insert(parts_formatted, export.link_term(data.base, data))
else
table.insert(parts_formatted, "")
end
return export.join_formatted_parts { data = data, parts_formatted = parts_formatted, categories = categories }
end
--[==[
{{suffix}} 구현.
'''WARNING''': This destructively modifies both `data` and the structures within `.suffixes`, as well as `.base`.
]==]
function export.show_suffix(data)
local categories = {}
data.pos = data.pos or default_pos
canonicalize_part(data.base, data.lang, data.sc)
-- Hyphenate the affixes and apply any affix mappings.
for i, suffix in ipairs(data.suffixes) do
make_part_into_affix(suffix, data.lang, data.sc, "suffix")
end
track_wrong_affix_type("suffix", data.base, nil)
for i, suffix in ipairs(data.suffixes) do
track_wrong_affix_type("suffix", suffix, "suffix")
end
-- Make links out of all the parts.
local parts_formatted = {}
if data.base then
table.insert(parts_formatted, export.link_term(data.base, data))
else
table.insert(parts_formatted, "")
end
for i, suffix in ipairs(data.suffixes) do
table.insert(parts_formatted, export.link_term(suffix, data))
end
-- Insert the categories.
for i, suffix in ipairs(data.suffixes) do
-- FIXME, should we be specifying a sort base here?
insert_affix_category(categories, data.pos, "suffix", suffix)
if suffix.pos and rfind(suffix.pos, "patronym") then
table.insert(categories, "인명")
end
end
return export.join_formatted_parts { data = data, parts_formatted = parts_formatted, categories = categories }
end
return export