local export = {}
--]
and ]
to store translated text. Further usage can be found at the
documentation page of {{tl|Babel}}. ]==]
local m_data = "Module:User:Saph/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_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 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
local function is_userpage()
-- Categorization and tracking should be suppressed if the page is not a root user page.
local title = mw.title.getCurrentTitle()
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, "-$", ""),
match(code, "-()$") or "N"
end
local message_memo = {}
local function grab_message(lang, proficiency, type)
if message_memo then
return message_memo
elseif type == ("language" or "sign language") then
local subpage = safe_load_data("Module:Babel/data/" .. sub(lang, 1, 1))
mw.log(lang .. "-" .. proficiency)
if subpage and subpage then
message_memo = subpage
return message_memo, true -- The boolean indicates it's translated for tagging purposes.
end
elseif type == "script" then
local message = load_data(m_data).script_messages
if message then
message_memo = 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 = load_data(m_data)
or soft_error("invalid proficiency", "Invalid proficiency: " .. proficiency)
end
return message_memo
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 ] 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)
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,
}
else
if type == "programming" then
return {
"Category:User " .. lang .. " coder-" .. proficiency,
"Category:User " .. lang .. " coder",
}
end
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 ]. This is '''required''' if the box being generated is for a programming language.
* `translated`: Whether `text` is translated. If this is set to {true}, the text will be appropriately tagged with ].
]==]
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, "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 = "]" or ""
data.text = 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
do
-- 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 = "]" .. (proficiency or "")
end
if data.obj and data.translated then
local tagged = {}
local n = 1
for i in gmatch(gsub(data.text, "<r>", "\n"), "+") do
tagged = tag_text(i, obj)
n = n + 1
end
data.text = concat(tagged, match(data.text, "<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()
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,
language = load_data(m_data).custom_codes,
script = {
--[[ Invalid in Module:scripts but still used, so exceptions should
be made for these two. Probably should be phased out. ]]
= "IPA",
= "UPA",
},
}
locations.script = locations.script
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")
data.obj = lang_get_by_code(load_data(m_data).custom_codes.fallback)
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 or "A"
end
if data.script then
data.cats = generate_cats(data.lang, data.proficiency, data.type, data.script)
elseif data.type ~= "programming" then
data.cats = generate_cats(data.lang, data.proficiency, data.type)
else
data.prog_obj = load_data(m_data).proglangs
data.cats = generate_cats((data.prog_obj.cat or data.prog_obj.display or data.lang), proficiency, data.type)
end
if not data.translated then
data.text = gsub(data.text, "$1", ":" .. data.cats)
data.text = gsub(data.text, "$2", ":" .. data.cats)
data.text = gsub(data.text, "$3", data.langname)
else
data.text = gsub(data.text, "$1", ":" .. data.cats)
data.text = gsub(data.text, "$2", ":" .. data.cats)
if find(data.text, "{") then
data.text = gsub(data.text, "{-}",
-- Gender parsing.
function(arg)
if not find(arg, "%++%+*}") then
return soft_error("missing second delimiter", "Message " .. arg .. " missing second + delimiter: ", lang)
end
local forms = {
match(arg, "{(+)"), -- masculine
match(arg, "%+(+)%+"), -- feminine
match(arg, "(+)}") -- neutral
}
return mw.getContentLanguage():gender(mw.title.getCurrentTitle().rootText, forms)
end)
end
end
local ret = export.generate_box(data)
if data.cats and is_userpage() and proficiency ~= "0" then
if not inactive then
ret = ret .. "]]"
else
for i = 1, #data.cats do
ret = ret .. " .. " (inactive)]]"
end
end
end
return ret .. '</tr></table></div>'
end
--==]
function export.t_userbox(frame)
local args = require(m_para).process(frame:getParent().args, {
= true,
= true,
})
return make_table(args, args)
end
--==]
function export.show(frame)
local args = require(m_para).process(frame:getParent().args, {
= { list = true },
= { type = "boolean" },
= { set = { "left", "right", "none", "center" }, default = "right" },
= { alias_of = "float" },
= { default = "248px" },
= { default = "#99B3FF" },
= { default = "]" },
= { default = "inherit" },
= {
default = "Search [[:Category:User languages|"
.. "user languages]] or ]"
},
= { alias_of = "border_color" },
= { alias_of = "border_color" },
= true,
= { alias_of = "gender" },
= { boolean = true },
})
local result = {}
if not args then
result = make_table()
else
for i = 1, #args do
result = make_table(args, args)
end
end
local colspan = #result
result = concat(result, "")
if args then
return result
end
if args then
args = args .. " (inactive)"
end
if args and require(m_pages).is_preview() then
args = args .. "\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 == "center" then
debug_track("float center")
args = "right"
end
return "{| class=\"babel-box-wrapper\" name=\"userboxes\" id=\"userboxes\""
.. "style=\"float: " .. args .. "; margin-left: 1em;"
.. "margin-bottom: 0.5em; width: " .. args .. "; border: "
.. args .. " solid 1px;"
.. "clear: " .. args .. ";\"\n"
.. "! style=\"background-color: " .. args .. "; text-align:"
.. "center\" colspan=\"10\" | " .. args .. "\n"
.. "|- style=vertical-align:top\n"
.. "|" .. result .. "\n"
.. "|-\n| style=\"background-color: " .. args .. ";"
.. "text-align: center;\" colspan=\"" .. colspan .. "\" | " .. args
.. "\n|}"
end
return export