မေႃႇၵျူး:Babel
Appearance
Documentation for this module may be created at မေႃႇၵျူး:Babel/doc
local export = {}
--[==[ intro: This module implements {{tl|Babel}}, using [[Module:Babel/data]]
and [[Special:PrefixIndex/Module:Babel/data/|its subpages]]
to store translated text. Further usage can be found at the
documentation page of {{tl|Babel}}. ]==]
local m_data = "Module:Babel/data"
local m_fam = "Module:families"
local m_lang = "Module:languages"
local m_load = "Module:load"
local m_pages = "Module:pages"
local m_para = "Module:parameters"
local m_sc = "Module:scripts"
local m_sc_utils = "Module:script utilities"
local m_str_utils = "Module:string utilities"
local m_track = "Module:debug/track"
local concat = table.concat
local find = string.find
local gmatch = string.gmatch
local gsub = string.gsub
local lower = string.lower
local match = string.match
local next = next
local sub = string.sub
local upper = string.upper
-- Loaders for functions from other modules.
local function debug_track(...)
debug_track = require(m_track)
return debug_track("Babel/" .. ...)
end
local function fam_get_by_code(...)
fam_get_by_code = require(m_fam).getByCode
return fam_get_by_code(...)
end
local function lang_get_by_code(...)
lang_get_by_code = require(m_lang).getByCode
return lang_get_by_code(...)
end
local function load_data(...)
load_data = require(m_load).load_data
return load_data(...)
end
local function plain_gsub(...)
plain_gsub = require(m_str_utils).plain_gsub
return plain_gsub(...)
end
local function safe_load_data(...)
safe_load_data = require(m_load).safe_load_data
return safe_load_data(...)
end
local function sc_get_by_code(...)
sc_get_by_code = require(m_sc).getByCode
return sc_get_by_code(...)
end
local function tag_text(...)
tag_text = require(m_sc_utils).tag_text
return tag_text(...)
end
-- Loaders for objects.
local current_title
local function get_current_title()
current_title, get_current_title = mw.title.getCurrentTitle(), nil
return current_title
end
local current_frame
local function get_current_frame()
current_frame, get_current_frame = mw.getCurrentFrame(), nil
return current_frame
end
local content_language
local function get_content_language()
content_language, get_content_language = mw.language.getContentLanguage(), nil
return content_language
end
local function is_userpage()
-- Categorization and tracking should be suppressed if the page is not a root user page.
local title = current_title or get_current_title()
return title:inNamespace("User") and title.text == title.rootText
end
local function soft_error(key, message)
if is_userpage() then
debug_track(key)
end
return '<span class="error">' .. message .. '</span>'
end
local function process(code)
return
gsub(code, "-[012345N]$", ""),
match(code, "-([012345N])$") or "N"
end
local message_memo = {}
local function grab_message(lang, proficiency, type)
if message_memo[lang .. proficiency] then
return message_memo[lang .. proficiency]
elseif type == ("language" or "sign language") then
local subpage = safe_load_data("Module:Babel/data/" .. sub(lang, 1, 1))
if subpage and subpage[lang .. "-" .. proficiency] then
message_memo[lang .. proficiency] = subpage[lang .. "-" .. proficiency]
return message_memo[lang .. proficiency], true -- The boolean indicates it's translated for tagging purposes.
end
elseif type == "script" then
local message = load_data(m_data).script_messages[lang .. "-" .. proficiency]
if message then
message_memo[lang .. proficiency] = message
return message
end
end
if not proficiency then
error("Message for " .. lang .. " was not grabbed because proficiency was not provided.")
else
-- Fetch from /data based on type.
message_memo[type .. proficiency] = load_data(m_data)[type .. "_proficiencies"][proficiency]
or soft_error("invalid proficiency", "Invalid proficiency: " .. proficiency)
end
return message_memo[type .. proficiency]
end
local function special_table(code)
if not code then
return "align=center style=\"font-style:italic;\" | "
.. "You haven't set up any languages. "
.. "Please see [[Wiktionary:Babel]] for help."
elseif code == "!" then
return "\n|\n"
elseif code == "-" then
return "<div style=\"float: left; margin: 2px;\">\n"
.. "{| cellspacing=\"0\" style=\"width: 238px;\"\n"
.. "| style=\"width: 45px; height: 45px;\" | \n"
.. "|style=\"font-size: 8pt; padding: 4pt; line-height: 1.25em;\" | \n|}</div>"
elseif code == "----" then
return "<div style=\"clear: both; padding: 0.25em 0;\"><hr/></div>"
else
--[[ This doesn't have the same checks as make_table, so it needs to
be able to deal with the fact you can technically pass something like
----- and bypass all the langcode checks. ]]
error(code .. " is not a recognised feature; please see [[Template:"
.. "Babel/documentation]] for a list of these.")
end
end
local function generate_cats(lang, proficiency, type, script, fallback)
if not lang then
error("Categories could not be generated because language code was not provided.")
elseif script then
-- for example, ko-Kore should categorise as ko, ko-Kore, Kore
return {
"Category:User " .. lang .. "-" .. script .. "-" .. proficiency,
"Category:User " .. lang .. "-" .. script,
"Category:User " .. lang .. "-" .. proficiency,
"Category:User " .. lang,
"Category:User " .. script .. "-" .. proficiency,
"Category:User " .. script,
}
elseif fallback and fallback ~= lang then
return {
"Category:User " .. lang .. "-" .. proficiency,
"Category:User " .. lang,
"Category:User " .. fallback .. "-" .. proficiency,
"Category:User " .. fallback,
}
elseif type == "programming" then
return {
"Category:User " .. lang .. " coder-" .. proficiency,
"Category:User " .. lang .. " coder",
}
else
return {
"Category:User " .. lang .. "-" .. proficiency,
"Category:User " .. lang,
}
end
end
--[==[ This is where modules can interface with Babel. `data` is a table
containing the following arguments:
* `obj` '''usually required''': Language, script, or family object.
* `lang`: Language code. This is '''required''' if obj is not passed.
* `code` '''required''': Language code affixed with proficiency (or, if unaffixed, proficiency should be native).
* `proficiency` '''required''': This can be 0, 1, 2, 3, 4, 5, or N.
* `text` '''required''': Text to display in the box.
* `type`: Used to determine what formatting to use. This is '''required''' if the box being generated is for a script, (non-sign) language, or programming language.
* `glyph`: The glyph to show above the script code. This is '''required''' for scripts.
* `prog_obj`: Programming language data from [[Module:Babel/data]]. This is '''required''' if the box being generated is for a programming language.
* `translated`: Whether `text` is in English. If this is set to {true}, the text will be appropriately tagged with [[Module:script utilities]].
]==]
function export.generate_box(data)
if not (data.obj or data.lang) then
error("generate_box received neither `obj` nor `lang`.")
elseif not (data.code and data.proficiency) then
error("`code` and `proficiency` must be passed to generate_box.")
end
local lang = data.obj and data.obj:getCode() or data.lang
local ret = '<div class="babel-box babel-' .. data.proficiency .. '">\n' ..
'<table class="babel-content"><tr>'
if data.type == "script" then
if not data.glyph then
error("Glyph must be passed to generate_box, because " .. lang .. " is a script code.")
end
if lang ~= "Latn" then
if data.glyph == "A" then
debug_track("Babel/missing script glyph")
end
if not find(lang, "[IU]PA") then
data.glyph = tag_text(data.glyph, sc_get_by_code())
else
data.glyph = '<span class="IPA">' .. data.glyph .. '</span>'
end
end
ret = ret .. '<td class="babel-code" style="font-size:9pt;line-height:100%>' ..
'<span style="font-size:14pt;line-height:130%;">' .. data.glyph ..
'</span><br><b>' .. data.code .. '</b></td><td class="babel-text">'
.. data.text .. '</span>'
else
if data.type == "programming" then
data.link = "[[" .. (data.prog_obj.link) .. "|" .. (data.prog_obj.display_code or lang) .. "]]" or ""
data.text = plain_gsub(data.text, "|" .. lang, "|" .. (data.prog_obj.display or data.prog_obj.display_code or lang))
if lang ~= data.code then
data.link = data.link .. "-" .. data.proficiency
end
else
if data.type == "language" then
data.dir = "left"
if obj and find(obj:findBestScript(data.text):getDirection(), "rtl") then
data.dir = "right"
end
if data.script then
lang = lang .. "-" .. data.script
end
end
-- Display what was inputted - en for en, en-N for en-N.
local proficiency = ""
data.link = data.link or lang
if data.link ~= data.code then
proficiency = "-" .. data.proficiency
end
data.link = "[[w:ISO 639:" .. data.link .. "|" .. data.link .. "]]" .. proficiency
if data.obj and data.translated then
local tagged = {}
local n = 1
for i in gmatch(gsub(data.text, "<[hb]r>", "\n"), "[^\n]+") do
tagged[n] = tag_text(i, data.obj)
n = n + 1
end
data.text = concat(tagged, match(data.text, "<[hb]r>"))
end
end
ret = ret .. '<td class="babel-code" style="font-size:14pt;">' ..
'<b>' .. data.link .. '</b></td><td class="babel-text" style="text-align:' ..
(data.dir or "") .. ';">' .. data.text .. '</td>'
end
return ret
end
local function make_table(code, inactive)
if not code or find(code, "^[!%-]+$") then
return special_table(code)
elseif find(code, "UNIQ") then
--[[ If it has a MW strip marker, it's probably a template or some
other rubbish that shouldn't be passed. ]]
error("Do not pass a template as a parameter.")
end
local lang, proficiency = process(code)
local data = { code = code, proficiency = proficiency, lang = lang }
--[[ Data needs to have 4 things to be able to build the table:
- `type` of table to make: language, script, proglang, or family
- `message`, which is either fetched from /data or /data/...
- `langname`, for $3 in the generic messages
- `link`, to be displayed on the left
If the message is translated, it will also have an item `translated`
indicating that.
Script data will also have a glyph.
Programming languages will sometimes have custom codes to display, and,
for internal use only, a titlecase version of the name (e.g. Php for
PHP, Asm for asm, etc.). ]]
if find(lang, "-%u%l+$") then
-- Allow language codes affixed with script codes.
data.script = match(data.lang, "-(%u%l+)$")
data.display_code = data.lang
data.lang = gsub(data.lang, "-%u%l+$", "")
if not sc_get_by_code(data.script) then
return soft_error("invalid script code", "Invalid script code: " .. data.script)
end
end
local type
type, data.obj = next{
language = lang_get_by_code(data.lang, nil, true),
script = sc_get_by_code(data.lang),
family = fam_get_by_code(data.lang),
}
if data.obj then
data.langname = data.obj:getCanonicalName()
if type == "language" then
local full_code = data.obj:getFullCode()
if full_code ~= data.lang then
data.fallback = full_code
end
end
data.type = type
local is_sign_language = (type == "language" and data.obj:inFamily("sgn") and "sign_") or ""
data.text, data.translated = grab_message(data.lang .. (data.script and "-" .. data.script or ""), proficiency, is_sign_language .. data.type)
else
data.titlecase = upper(sub(lang, 1, 1)) .. lower(sub(lang, 2, -1))
local locations = {
programming = load_data(m_data).proglangs[data.titlecase],
language = load_data(m_data).custom_codes[data.lang],
script = {
--[[ Invalid in Module:scripts but still used, so exceptions should
be made for these two. Probably should be phased out. ]]
["IPA"] = "IPA",
["UPA"] = "UPA",
},
}
locations.script = locations.script[data.lang]
locations.language = locations.language and locations.language[1]
local type, langname = next(locations)
if type == "programming" then
data.type = type
data.text = grab_message(data.lang, data.proficiency, type)
data.langname = data.lang
elseif langname then
data.type = type
data.text, data.translated = grab_message(data.lang .. (data.script and "-" .. data.script or ""), data.proficiency, data.type)
data.langname = langname
if type == "language" then
debug_track("custom code")
local custom = load_data(m_data).custom_codes[data.lang]
data.obj = lang_get_by_code(custom.fallback) -- auto cat won't work for custom codes, so use the fallback to categorise
data.link = data.lang
end
else
return soft_error("invalid language code", "Invalid language code: " .. lang)
end
end
if data.type == "script" then
data.glyph = load_data(m_data).script_glyphs[lang] or "A"
end
if data.script then
data.cats = generate_cats(data.obj:getCode(), data.proficiency, data.type, data.script)
elseif data.type ~= "programming" then
data.cats = generate_cats(data.obj and data.obj:getCode() or data.lang, data.proficiency, data.type, nil, data.fallback)
else
data.prog_obj = load_data(m_data).proglangs[data.titlecase]
data.cats = generate_cats((data.prog_obj.cat or data.prog_obj.display or data.titlecase), proficiency, data.type)
end
if not data.translated then
data.text = gsub(data.text, "$1", ":" .. data.cats[1])
data.text = gsub(data.text, "$2", ":" .. data.cats[2])
data.text = gsub(data.text, "$3", data.langname)
else
data.text = gsub(data.text, "$1", ":" .. data.cats[1])
data.text = gsub(data.text, "$2", ":" .. data.cats[2])
if find(data.text, "{") then
data.text = gsub(data.text, "{[^}]-}",
-- Gender parsing.
function(arg)
local parts = {}
local n = 1
for part in gmatch(arg, "[^/{}]+") do
parts[n] = part
n = n + 1
end
if not parts then
soft_error("malformed gender switch", "Malformed gender switch in message for " .. code .. ": " .. arg)
end
return (content_language or get_content_language()):gender((current_title or get_current_title()).rootText, parts)
end)
end
if find(data.text, "hiero") then
data.text = (current_frame or get_current_frame()):preprocess(data.text)
end
end
local ret = export.generate_box(data)
if data.cats and is_userpage() and proficiency ~= "0" then
if not inactive then
ret = ret .. "[[" .. concat(data.cats, "]][[") .. "]]"
else
for i = 1, #data.cats do
ret = ret .. "[[" .. data.cats[i] .. " (inactive)]]"
end
end
end
return ret .. '</tr></table></div>'
end
--[==[ Entry point for {{tl|Babel userbox}}. ]==]
function export.t_userbox(frame)
local args = require(m_para).process(frame:getParent().args, {
[1] = true,
["inactive"] = true,
})
return make_table(args[1], args["inactive"])
end
--[==[ Main entry point, for {{tl|Babel}}. ]==]
function export.show(frame)
local args = require(m_para).process(frame:getParent().args, {
[1] = { list = true },
["inactive"] = { type = "boolean" },
["float"] = { set = { "left", "right", "none", "center" }, default = "right" },
["align"] = { alias_of = "float" },
["margin_left"] = { default = "1em" },
["margin_right"] = { default = "0em" },
["margin_bottom"] = { default = "0.5em" },
["width"] = { default = "248px" },
["border_color"] = { default = "#99B3FF" },
["header"] = { default = "[[Wiktionary:Babel]]" },
["color"] = { default = "inherit" },
["footer"] = {
default = "Search [[:Category:User languages|"
.. "user languages]] or [[:Category:User scripts|scripts]]"
},
["border"] = { alias_of = "border_color" },
["bordercolor"] = { alias_of = "border_color" },
["gender"] = true,
["g"] = { alias_of = "gender" },
["no-table"] = { boolean = true },
})
local result = {}
if not args[1][1] then
result[1] = make_table()
else
for i = 1, #args[1] do
result[i] = make_table(args[1][i], args["inactive"])
end
end
local colspan = #result
result = concat(result, "")
if args["no-table"] then
return result
end
if args["inactive"] then
args["header"] = args["header"] .. " <span title=\"This user has not "
.. "contributed in 2 or more years.\">(inactive)</span>"
end
if args["gender"] and require(m_pages).is_preview() then
args["header"] = args["header"] .. "\n<span class=\"error\""
.. "style =\"font-size:75%;\">"
.. "Gender is automatically grabbed from your preferences.\n"
.. "You can remove the gender parameter.</span>"
end
if args["float"] == "center" then
debug_track("float center")
args["float"] = "right"
end
return "{| class=\"babel-box-wrapper userboxes\" name=\"userboxes\""
.. "style=\"float: " .. args["float"] .. "; margin-left: " .. args["margin_left"] .. "; margin-right: " .. args["margin_right"]
.. "; margin-bottom: " .. args["margin_bottom"] .. "; width: " .. args["width"] .. "; border: "
.. args["border_color"] .. " solid 1px;"
.. "clear: " .. args["float"] .. ";\"\n"
.. "! style=\"background-color: " .. args["color"] .. "; text-align:"
.. "center\" colspan=\"10\" | " .. args["header"] .. "\n"
.. "|- style=vertical-align:top\n"
.. "|" .. result .. "\n"
.. "|-\n| style=\"background-color: " .. args["color"] .. ";"
.. "text-align: center;\" colspan=\"" .. colspan .. "\" | " .. args["footer"]
.. "\n|}"
end
return export