Module:Lang
From Vigyanwiki
Documentation for this module may be created at Module:Lang/doc
--[=[
Lua support for the {{lang}} and {{lang-xx}} templates and replacement of various supporting templates.
]=]
require('Module:No globals');
local p = {};
local getArgs = require ('Module:Arguments').getArgs;
local lang_name_table = mw.loadData ('Module:Language/name/data');
local synonym_table = mw.loadData ('Module:Lang/ISO 639 synonyms'); -- ISO 639-2/639-2T code translation to 639-1 code
local lang_data = mw.loadData ('Module:Lang/data'); -- language name override and transliteration tool-tip tables
local namespace = mw.title.getCurrentTitle().namespace; -- used for categorization
local maint_cats = {}; -- maintenance categories go here
local maint_msgs = {}; -- and their messages go here
--[[--------------------------< I S _ S E T >------------------------------------------------------------------
Returns true if argument is set; false otherwise. Argument is 'set' when it exists (not nil) or when it is not an empty string.
]]
local function is_set( var )
return not (var == nil or var == '');
end
--[[--------------------------< T O _ B O O L E A N >----------------------------------------------------------
Returns true for 'yes', false for 'no', and nil for any other value.
]]
local function to_boolean (param)
if param == 'yes' then
return true
elseif param == 'no' then
return false
else
return nil
end
end
--[[--------------------------< I N _ A R R A Y >--------------------------------------------------------------
Whether needle is in haystack
]]
local function in_array( needle, haystack )
if needle == nil then
return false;
end
for n,v in ipairs( haystack ) do
if v == needle then
return n;
end
end
return false;
end
--[[--------------------------< F O R M A T _ I E T F _ T A G >------------------------------------------------
prettify ietf tags to use recommended subtag formats:
code: lower case
script: sentence case
region: upper case
variant: lower case
]]
local function format_ietf_tag (code, script, region, variant)
local out = {};
local c;
table.insert (out, code:lower());
if is_set (script) then
c = script:match ('^%a'):upper(); -- make script sentence case
script = script:lower():gsub ('^%a', c, 1);
table.insert (out, script);
end
if is_set (region) then
table.insert (out, region:upper());
end
if is_set (variant) then
table.insert (out, variant:lower());
end
return table.concat (out, '-');
end
--[[--------------------------< G E T _ I E T F _ P A R T S >--------------------------------------------------
extracts and returns IETF language tag parts:
primary language subtag (required) - 2 or 3 character IANA language code
script subtag - four character IANA script code
region subtag - two-letter or three digit IANA region code
variant subtag - four digit or 5-8 alnum variant code
private subtag - x- followed by 1-8 alnum private code; only supported with the primary language tag
in any one of these forms
lang lang-variant
lang-script lang-script-variant
lang-region lang-region-variant
lang-script-region lang-script-region-variant
lang-x-private
each of lang, script, region, variant, and private, when used, must be valid
Languages with both two- and three-character code synonyms are promoted to the two-character synonym because
the IANA registry file omits the synonymous three-character code; we cannot depend on browsers understanding
the synonymous three-character codes in the lang= attribute.
For {{lang-xx}} templates, the parameters |script=, |region=, and |variant= are supported (not supported in {{lang}}
because those parameters are superfluous to the IETF subtags in |code=)
returns six values. Valid parts are returned as themselves; omitted parts are returned as empty strings, invalid
parts are returned as nil; the sixth returned item is an error message (if an error detected) or nil.
see http://www.rfc-editor.org/rfc/bcp/bcp47.txt section 2.1
]]
local function get_ietf_parts (source, args_script, args_region, args_variant)
local code;
local script = '';
local region = '';
local variant = '';
local private = '';
local c;
if not is_set (source) then
return nil, nil, nil, nil, nil, 'missing language tag';
end
if source:match ('^%a%a%a?%-%a%a%a%a%-%a%a%-%d%d%d%d$') then -- ll-Ssss-RR-variant (where variant is 4 digits)
code, script, region, variant = source:match ('^(%a%a%a?)%-(%a%a%a%a)%-(%a%a)%-(%d%d%d%d)$');
elseif source:match ('^%a%a%a?%-%a%a%a%a%-%d%d%d%-%d%d%d%d$') then -- ll-Ssss-DDD-variant (where region is 3 digits; variant is 4 digits)
code, script, region, variant = source:match ('^(%a%a%a?)%-(%a%a%a%a)%-(%d%d%d)%-(%d%d%d%d)$');
elseif source:match ('^%a%a%a?%-%a%a%a%a%-%a%a%-[%a%d][%a%d][%a%d][%a%d][%a%d]+$') then -- ll-Ssss-RR-variant (where variant is 5-8 alnum characters)
code, script, region, variant = source:match ('^(%a%a%a?)%-(%a%a%a%a)%-(%a%a)%-([%a%d][%a%d][%a%d][%a%d][%a%d][%a%d]?[%a%d]?[%a%d]?)$');
elseif source:match ('^%a%a%a?%-%a%a%a%a%-%d%d%d%-[%a%d][%a%d][%a%d][%a%d][%a%d]+$') then -- ll-Ssss-DDD-variant (where region is 3 digits; variant is 5-8 alnum characters)
code, script, region, variant = source:match ('^(%a%a%a?)%-(%a%a%a%a)%-(%d%d%d)%-([%a%d][%a%d][%a%d][%a%d][%a%d][%a%d]?[%a%d]?[%a%d]?)$');
elseif source:match ('^%a%a%a?%-%a%a%a%a%-%d%d%d%d$') then -- ll-Ssss-variant (where variant is 4 digits)
code, script, variant = source:match ('^(%a%a%a?)%-(%a%a%a%a)%-(%d%d%d%d)$');
elseif source:match ('^%a%a%a?%-%a%a%a%a%-[%a%d][%a%d][%a%d][%a%d][%a%d]+$') then -- ll-Ssss-variant (where variant is 5-8 alnum characters)
code, script, variant = source:match ('^(%a%a%a?)%-(%a%a%a%a)%-([%a%d][%a%d][%a%d][%a%d][%a%d][%a%d]?[%a%d]?[%a%d]?)$');
elseif source:match ('^%a%a%a?%-%a%a%-%d%d%d%d$') then -- ll-RR-variant (where variant is 4 digits)
code, region, variant = source:match ('^(%a%a%a?)%-(%a%a)%-(%d%d%d%d)$');
elseif source:match ('^%a%a%a?%-%d%d%d%-%d%d%d%d$') then -- ll-DDD-variant (where region is 3 digits; variant is 4 digits)
code, region, variant = source:match ('^(%a%a%a?)%-(%d%d%d)%-(%d%d%d%d)$');
elseif source:match ('^%a%a%a?%-%a%a%-[%a%d][%a%d][%a%d][%a%d][%a%d]+$') then -- ll-RR-variant (where variant is 5-8 alnum characters)
code, region, variant = source:match ('^(%a%a%a?)%-(%a%a)%-([%a%d][%a%d][%a%d][%a%d][%a%d][%a%d]?[%a%d]?[%a%d]?)$');
elseif source:match ('^%a%a%a?%-%d%d%d%-[%a%d][%a%d][%a%d][%a%d][%a%d]+$') then -- ll-DDD-variant (where region is 3 digits; variant is 4 digits)
code, region, variant = source:match ('^(%a%a%a?)%-(%d%d%d)%-([%a%d][%a%d][%a%d][%a%d][%a%d][%a%d]?[%a%d]?[%a%d]?)$');
elseif source:match ('^%a%a%a?%-%d%d%d%d$') then -- ll-variant (where variant is 4 digits)
code, variant = source:match ('^(%a%a%a?)%-(%d%d%d%d)$');
elseif source:match ('^%a%a%a?%-[%a%d][%a%d][%a%d][%a%d][%a%d]+$') then -- ll-variant (where variant is 5-8 alnum characters)
code, variant = source:match ('^(%a%a%a?)%-([%a%d][%a%d][%a%d][%a%d][%a%d][%a%d]?[%a%d]?[%a%d]?)$');
elseif source:match ('^%a%a%a?%-%a%a%a%a%-%a%a$') then -- ll-Ssss-RR
code, script, region = source:match ('^(%a%a%a?)%-(%a%a%a%a)%-(%a%a)$');
elseif source:match ('^%a%a%a?%-%a%a%a%a%-%d%d%d$') then -- ll-Ssss-DDD (region is 3 digits)
code, script, region = source:match ('^(%a%a%a?)%-(%a%a%a%a)%-(%d%d%d)$');
elseif source:match ('^%a%a%a?%-%a%a%a%a$') then -- ll-Ssss
code, script = source:match ('^(%a%a%a?)%-(%a%a%a%a)$');
elseif source:match ('^%a%a%a?%-%a%a$') then -- ll-RR
code, region = source:match ('^(%a%a%a?)%-(%a%a)$');
elseif source:match ('^%a%a%a?%-%d%d%d$') then -- ll-DDD (region is 3 digits)
code, region = source:match ('^(%a%a%a?)%-(%d%d%d)$');
elseif source:match ('^%a%a%a?$') then -- ll
code = source:match ('^(%a%a%a?)$');
elseif source:match ('^%a%a%a?%-x%-[%a%d][%a%d]?[%a%d]?[%a%d]?[%a%d]?[%a%d]?[%a%d]?[%a%d]?$') then -- ll-x-pppppppp)
code, private = source:match ('^(%a%a%a?)%-x%-([%a%d]+)$');
else
return nil, nil, nil, nil, nil, table.concat ({'unrecognized language tag: ', source}); -- don't know what we got but it is malformed
end
code = code:lower(); -- ensure that we use and return lower case version of this
if not (lang_data.override[code] or lang_name_table.lang[code]) then
return nil, nil, nil, nil, nil, table.concat ({'unrecognized language code: ', code}); -- invalid language code, don't know about the others (don't care?)
end
if synonym_table[code] then -- if 639-2/639-2T code has a 639-1 synonym
table.insert (maint_cats, table.concat ({'Lang and lang-xx code promoted to ISO 639-1|', code}));
table.insert (maint_msgs, table.concat ({'code: ', code, ' promoted to code: ', synonym_table[code]}));
code = synonym_table[code]; -- use the synonym
end
if is_set (script) then
if is_set (args_script) then
return code, nil, nil, nil, nil, 'redundant script tag'; -- both code with script and |script= not allowed
end
else
script = args_script or ''; -- use args.script if provided
end
if is_set (script) then
script = script:lower(); -- ensure that we use and return lower case version of this
if not lang_name_table.script[script] then
return code, nil, nil, nil, nil, table.concat ({'unrecognized script: ', script, ' for code: ', code}); -- language code ok, invalid script, don't know about the others (don't care?)
end
end
-- if lang_name_table.suppressed[script] then -- ensure that code-script does not use a suppressed script
-- if in_array (code, lang_name_table.suppressed[script]) then
-- return code, nil, nil, nil, table.concat ({'script: ', script, ' not supported for code: ', code}); -- language code ok, script is suppressed for this code
-- end
-- end
if is_set (region) then
if is_set (args_region) then
return code, nil, nil, nil, nil, 'redundant region tag'; -- both code with region and |region= not allowed
end
else
region = args_region or ''; -- use args.region if provided
end
if is_set (region) then
region = region:lower(); -- ensure that we use and return lower case version of this
if not lang_name_table.region[region] then
return code, script, nil, nil, nil, table.concat ({'unrecognized region: ', region, ' for code: ', code});
end
end
if is_set (variant) then
if is_set (args_variant) then
return code, nil, nil, nil, nil, 'redundant variant tag'; -- both code with variant and |variant= not allowed
end
else
variant = args_variant or ''; -- use args.variant if provided
end
if is_set (variant) then
variant = variant:lower(); -- ensure that we use and return lower case version of this
if not lang_name_table.variant[variant] then -- make sure variant is valid
return code, script, region, nil, nil, table.concat ({'unrecognized variant: ', variant});
end -- does this duplicate/replace tests in lang() and lang_xx()?
if is_set (script) then -- if script set it must be part of the 'prefix'
if not in_array (table.concat ({code, '-', script}), lang_name_table.variant[variant]['prefixes']) then
return code, script, region, nil, nil, table.concat ({'unrecognized variant: ', variant, ' for code-script pair: ', code, '-', script});
end
else
if not in_array (code, lang_name_table.variant[variant]['prefixes']) then
return code, script, region, nil, nil, table.concat ({'unrecognized variant: ', variant, ' for code: ', code});
end
end
end
if is_set (private) then
private = private:lower(); -- ensure that we use and return lower case version of this
if not lang_data.override[table.concat ({code, '-x-', private})] then -- make sure private tag is valid; note that index
return code, script, region, nil, nil, table.concat ({'unrecognized private tag: ', private});
end
end
return code, script, region, variant, private, nil; -- return the good bits; make sure that msg is nil
end
--[=[-------------------------< M A K E _ E R R O R _ M S G >--------------------------------------------------
]=]
local function make_error_msg (msg, nocat)
local out = {};
table.insert (out, '<span style="font-size:100%" class="error">error: ');
table.insert (out, msg);
table.insert (out, ' ([[:Category:lang and lang-xx template errors|help]])')
table.insert (out, '</span>');
if (0 == namespace) and not is_set (nocat) then -- only categorize in article space
table.insert (out, '[[Category:lang and lang-xx template errors]]');
end
return table.concat (out);
end
--[=[-------------------------< M A K E _ W I K I L I N K >----------------------------------------------------
Makes a wikilink; when both link and display text is provided, returns a wikilink in the form [[L|D]]; if only
link is provided, returns a wikilink in the form [[L]]; if neither are provided or link is omitted, returns an
empty string.
]=]
local function make_wikilink (link, display)
if is_set (link) then
if is_set (display) then
return table.concat ({'[[', link, '|', display, ']]'});
else
return table.concat ({'[[', link, ']]'});
end
else
return '';
end
end
--[[--------------------------< M A K E _ T E X T _ S P A N >--------------------------------------------------
TODO: replace wiki italic markup with html tags?
TODO: if wikimarkup replaced with html tags, don't need span tags when text is to be italicized, put lang= and other attributes in the html: <i lang="language name"> ...
TODO: add support for block: div tags instead of span tags; would need some sort of proper parameter to control the switch
]]
local function make_text_span (code, text, rtl, italic, size)
local span = {};
table.insert (span, '<span lang="'); -- open <span> tag
table.insert (span, code); -- language attribute
table.insert (span, '"');
if rtl then
table.insert (span, ' dir="rtl"'); -- for right to left languages
end
if is_set (size) then -- {{lang}} only
table.insert (span, table.concat ({' style="font-size:', size, ';"'}))
end
table.insert (span, '>'); -- close the opening span tag
if italic then
table.insert (span, table.concat ({"''", text, "''"})); -- text with italic markup
else
table.insert (span, text); -- DEFAULT: text is not italicized
end
table.insert (span, '</span>'); -- close the span
if rtl then
table.insert (span, '‎'); -- make sure the browser knows that we're at the end of the rtl
end
return table.concat (span); -- put it all together and done
end
--[[--------------------------< M A K E _ C A T E G O R Y >----------------------------------------------------
TODO: figure out how to correctly support collective language codes: sem, Semitic languages (collective names
appear to always include the word 'languages').. May need new categories so that the category names are sensible.
]]
local function make_category (code, language_name, nocat)
local cat = {};
if (0 ~= namespace) or nocat then -- only categorize in article space
return ''; -- return empty string for concatenation
end
table.insert (cat, '[[Category:Articles containing ');
if ('en' == code) or ('eng' == code) then
table.insert (cat, 'explicitly cited English');
elseif 'art' == code then
table.insert (cat, 'constructed')
else
table.insert (cat, language_name);
end
table.insert (cat, '-language text]]');
return table.concat (cat);
end
--[[--------------------------< M A K E _ T R A N S L I T >----------------------------------------------------
return translit <i lang=xx-Latn>...</i> where xx is the language code; else return empty string
The value |script= is not used in {{transl}} for this purpose; instead it uses |code. Because language scripts
are listed in the {{transl}} switches they are included in the data tables. The script parameter is introduced
at {{Language with name and transliteration}}. If |script= is set, this function uses it in preference to code.
To avoid confusion, in this module and the templates that use it, the transliteration script parameter is renamed
to be |translit-script= (in this function, tscript)
]]
local function make_translit (code, language_name, translit, std, tscript)
local title;
local tout = {};
local title_table = lang_data.translit_title_table; -- table of transliteration standards and the language codes and scripts that apply to those standards
table.insert (tout, "<i lang=\"");
table.insert (tout, code);
table.insert (tout, "-Latn\" title=\""); -- transliterations are always Latin script
if not is_set (std) and not is_set (tscript) then -- when neither standard nor script specified
table.insert (tout, language_name); -- write a generic tool tip
table.insert (tout, ' transliteration');
elseif is_set (std) and is_set (tscript) then -- when both are specified
if title_table[std][tscript] then -- and legitimate
table.insert (tout, title_table[std][tscript]); -- add the appropriate text to the tool tip
else
return ''; -- one or both invalid, set up for an error message
end
elseif is_set (std) then -- translit-script not set, use language code
if not title_table[std] then return ''; end -- invalid standard, setupt for error message
if title_table[std][code] then
table.insert (tout, title_table[std][code]);
else -- code doesn't match
table.insert (tout, title_table[std]['default']); -- so use the standard's default
end
else -- here if translit-script set but translit-std not set
if title_table['NO_STD'][tscript] then
table.insert (tout, title_table['NO_STD'][tscript]); -- use translit-script if set
elseif title_table['NO_STD'][code] then
table.insert (tout, title_table['NO_STD'][code]); -- use language code
else
table.insert (tout, language_name); -- write a generic tool tip
table.insert (tout, ' transliteration');
end
end
table.insert (tout, '">');
table.insert (tout, translit);
table.insert (tout, "</i>");
return table.concat (tout);
end
--[[--------------------------< V A L I D A T E _ T E X T >---------------------------------------------------
This function checks the content of args.text and returns empty string if nothing is amiss else it returns an
error message. The tests are for empty or missing text and for improper or disallowed use of apostrophe markup.
Italic rendering is controlled by the |italic= template parameter so italic markup should never appear in args.text
either as ''itself''' or as '''''bold italic'''''.
Also protects single leading and trailing single quote marks from being converted to bold by the addition of
adjacent italic markup.
]]
local function validate_text (template, args)
if not is_set (args.text) then
return make_error_msg (table.concat ({'{{', template, '}}: no text'}), args.nocat);
end
if 'lang-xx' == template then -- for the time being, this error checking does not apply to {{lang}}
if args.text:find ("\'\'\'\'\'[\']+") then -- because we're looking, look for 6+ appostrophes
return make_error_msg (table.concat ({'{{', template, '}}: text has malformed markup'}), args.nocat);
end
if args.text:match ("%f[\']\'\'[^\']+\'\'%f[^\']") or args.text:match ("\'\'\'\'\'[^\']+\'\'\'\'\'") then -- italic but not bold, or bold italic
return make_error_msg (table.concat ({'{{', template, '}}: text has italic markup'}), args.nocat);
end
if args.text:find ("\'\'\'\'") then -- because we're looking, look for 4 apostrophes
return make_error_msg (table.concat ({'{{', template, '}}: text has malformed markup'}), args.nocat);
end
end
if args.italic then -- protect single quote marks from being converted to bold markup
args.text = args.text:gsub ("^\'[^\']+", "<span></span>%1"); -- leading single quote mark
args.text = args.text:gsub ("[^\']+\'$", "%1<span></span>"); -- trailing single quote mark
end
end
--[[--------------------------< S U B T A G _ C H E C K >------------------------------------------------------
checks the subtags: script, region, and variant to be valid and in agreement with any tags that are included in
the main language code: if |script=Cyrl and language code is 'abq-Cyrl' the two script must agree (case insensitive)
returns the selected subtag and nil error message;
returns empty string and nil error message when both language code subtag and matching subtag parameter are not set
returns nil and error message else
TODO: this not required any longer? Parameter subtags |script=, |region=, and |variant= are all consolidated
with code subtags, and validated, in get_ietf_parts().
]]
local function subtag_check (name, args_code, code_subtag, args_subtag, nocat)
if not is_set (code_subtag) and not is_set (args_subtag) then -- no subtags, then bale
return ''; -- empty string for concatenation
end
args_subtag = args_subtag and args_subtag:lower(); -- so we only need do this once; prettify later
if not is_set (code_subtag) then -- if no ietf subtag in args.code
if is_set (args_subtag) then -- and if |<name>= has a value
if lang_name_table[name][args_subtag] then -- and that value is legitimate subtag
code_subtag = args_subtag; -- then use |<name>=
else
return nil, make_error_msg (table.concat ({'{{lang-xx}}: invalid ' .. name .. ': ', args_subtag}), nocat);
end
end
else -- here when language code has a subtag
if is_set (args_subtag) and (code_subtag ~= args_subtag) then -- if there is a subtag parameter then it must match
return nil, make_error_msg (table.concat ({'{{lang-xx}}: code / ' .. name .. ' mismatch: ', args_code:lower(), ' / ', args_subtag}), nocat);
end
end
return code_subtag;
end
--[[--------------------------< R E N D E R _ M A I N T >------------------------------------------------------
render mainenance messages and categories
]]
local function render_maint(nocat)
local maint = {};
if 0 < #maint_msgs then
table.insert (maint, table.concat ({'<span class="lang-comment" style="font-style:normal; display:none; color:#33aa33; margin-left:0.3em">'}));
for _, msg in ipairs (maint_msgs) do
table.insert (maint, table.concat ({msg, ' '}));
end
table.insert (maint, '</span>');
end
if (0 < #maint_cats) and (0 == namespace) and not is_set (nocat) then
for _, cat in ipairs (maint_cats) do
table.insert (maint, table.concat ({'[[Category:', cat, ']]'}));
end
end
return table.concat (maint);
end
--[[--------------------------< L A N G >----------------------------------------------------------------------
<includeonly>{{#invoke:lang|lang|code={{{1|}}}|text={{{2|}}}|rtl={{{rtl|}}}|italic={{{italic|}}}|size={{{size|}}}|nocat={{{nocat|}}}}}</includeonly>
|code = the BCP47 language code
|text = the displayed text in language specified by code
|rtl = boolean true identifies the language specified by code as a right-to-left language
|size = css keyword appropriate for use with css font-size:<size>
|nocat = boolean true inhibits normal categorization; error categories are not affected
]]
function p.lang (frame)
local args = getArgs(frame);
if args[1] and args.code then
return make_error_msg ('{{lang}}: conflicting: {{{1}}} and |code=', args.nocat);
else
args.code = args[1] or args.code;
end
if args[2] and args.text then
return make_error_msg ('{{lang}}: conflicting: {{{2}}} and |text=', args.nocat);
else
args.text = args[2] or args.text;
end
args.rtl = args.rtl == 'yes'; -- convert to boolean: 'yes' -> true, other values -> false
local out = {};
local language_name;
local subtags = {};
local code;
local msg;
code, subtags.script, subtags.region, subtags.variant, subtags.private, msg = get_ietf_parts (args.code); -- |script=, |region=, |variant= not supported because they should be part of args.code ({{{1}}} in {{lang}})
if not (code and subtags.script and subtags.region and subtags.variant) then
return make_error_msg (table.concat ({'{{lang}}: ', msg}), args.nocat);
end
args.italic = to_boolean(args.italic); -- convert to boolean or nil: 'yes' -> true, 'no' -> false; else nil
if nil == args.italic then -- args.italic controls
if 'latn' == subtags.script then -- script set to latn
args.italic = true; -- DEFAULT for {{lang}} templates is upright; but if latn script
else
args.italic = false; -- italic not set; script not latn
end
end
msg = validate_text ('lang', args); -- ensure that |text= is set (italic test disabled for the time being)
if is_set (msg) then
return msg;
end
if is_set (subtags.script) then -- if script set override rtl setting
if in_array (subtags.script, lang_data.rtl_scripts) then
args.rtl = true; -- script is an rtl script
else
args.rtl = false; -- script is not an rtl script
end
end
args.code = format_ietf_tag (code, subtags.script, subtags.region, subtags.variant); -- format to recommended subtag styles; private omitted because private
if is_set (subtags.private) and lang_data.override[table.concat ({code, 'x-', subtags.private})] then -- look for private use tags
language_name = lang_data.override[table.concat ({code, 'x-', subtags.private})][1];
elseif lang_data.override[code] then
language_name = lang_data.override[code][1]
elseif lang_name_table.lang[code] then
language_name = lang_name_table.lang[code][1]; -- table entries sometimes have multiple names, always take the first one
end
table.insert (out, make_text_span (args.code, args.text, args.rtl, args.italic, args.size));
table.insert (out, make_category (code, language_name, args.nocat));
table.insert (out, render_maint(args.nocat)); -- maintenance messages and categories
return table.concat (out); -- put it all together and done
end
--[[--------------------------< L A N G _ X X >----------------------------------------------------------------
<includeonly>{{#invoke:lang|lang_xx
|code=<code>
|script={{{script|}}}
|region={{{region|}}}
|variant={{{variant|}}}
|text={{{1|}}}
|link={{{links|{{{link}}}}}}
|rtl={{{rtl|}}}
|nocat={{{nocat|}}}
|italic={{{italic|}}}
|translation={{{lit|}}}
|translit={{{translit|}}}
|translit-script={{{translit-script|}}}
|translit-std={{{translit-std|}}}}}</includeonly>
|code = (required) the BCP47 language code
|script = BCP47 script name; especially for use with languages that use multiple writing systems; yields to the script subtag in |code= if present [not yet implemented]
|text = (required) the displayed text in language specified by code
|link = boolean true (default) links language specified by code to associated language article
|rtl = boolean true identifies the language specified by code as a right-to-left language
|nocat = boolean true inhibits normal categorization; error categories are not affected
|italic = boolean true (default) renders displayed text in italic font; when |italic= not set and |script= set to something other than Latn then args.italic='no' [not yet implemented]
|lit = text that is a literal translation of text
for those {{lang-xx}} templates that support transliteration:
|translit = text that is a transliteration of text
|translit-std = the standard that applies to the transliteration
|translit-script = ISO 15924 script name; falls back to code
For {{lang-xx}}, the positional parameters are:
{{{1}}} text
{{{2}}} transliterated text
{{{3}}} literal translation text
]]
local function _lang_xx (frame)
local args = getArgs(frame, {parentFirst= true}); -- parameters in the template override parameters set in the {{#invoke:}}; is that the right thing to do?
if args[1] and args.text then
return make_error_msg ('{{lang-xx}}: conflicting: {{{1}}} and |text=', args.nocat);
else
args.text = args[1] or args.text;
end
if args[2] and args.translit then
return make_error_msg ('{{lang-xx}}: conflicting: {{{2}}} and |translit=', args.nocat);
else
args.translit = args[2] or args.translit
end
if args[3] and (args.translation or args.lit) then
return make_error_msg ('{{lang-xx}}: conflicting: {{{3}}} and |lit= or |translation=', args.nocat);
elseif args.translation and args.lit then
return make_error_msg ('{{lang-xx}}: conflicting: |lit= and |translation=', args.nocat);
else
args.translation = args[3] or args.translation or args.lit;
end
if args.links and args.link then
return make_error_msg ('{{lang-xx}}: conflicting: |links= and |link=', args.nocat);
else
args.link = args.link or args.links;
end
args.rtl = args.rtl == 'yes'; -- convert to boolean: 'yes' -> true, other values -> false
local out = {};
local language_name;
local subtags = {};
local code;
local translit_script;
local translit;
local translit_title;
local msg;
code, subtags.script, subtags.region, subtags.variant, subtags.private, msg = get_ietf_parts (args.code, args.script, args.region, args.variant); -- private omitted because private
if not (code and subtags.script and subtags.region and subtags.variant) then
return make_error_msg (table.concat ({'{{lang-xx}}: ', msg}), args.nocat);
end
args.italic = to_boolean (args.italic); -- convert to boolean or nil: 'yes' -> true, 'no' -> false; else nil
if args.italic == nil then -- args.italic controls
if not is_set (subtags.script) or ('latn' == subtags.script) then -- script not set then default; script set to latn same
args.italic = true; -- DEFAULT for {{lang-xx}} templates is to italicize
else
args.italic = false; -- italic not set; script not latn
end
end
msg = validate_text ('lang-xx', args); -- ensure that |text= is set, does not contain italic markup and is protected from improper bolding
if is_set (msg) then
return msg;
end
if is_set (subtags.script) then -- if script set override rtl setting
if in_array (subtags.script, lang_data.rtl_scripts) then
args.rtl = true; -- script is an rtl script
else
args.rtl = false; -- script is not an rtl script
end
end
args.code = format_ietf_tag (code, subtags.script, subtags.region, subtags.variant); -- format to recommended subtag styles
if is_set (subtags.private) and lang_data.override[table.concat ({code, 'x-', subtags.private})] then -- look for private use tags
language_name = lang_data.override[table.concat ({code, 'x-', subtags.private})][1];
elseif lang_data.override[args.code:lower()] then -- first look for whole IETF tag in override table
language_name = lang_data.override[args.code:lower()][1];
elseif lang_data.override[code] then -- not there so try basic language code
language_name = lang_data.override[code][1];
elseif not is_set (subtags.variant) then
if lang_name_table.lang[code] then
language_name = lang_name_table.lang[code][1]; -- table entries sometimes have multiple names, always take the first one
end
else -- TODO: is this the right thing to do: take language display name from variants table?
if lang_name_table.variant[subtags.variant] then -- TODO: there is some discussion at Template talk:Lang about having a label parameter for use when variant name is not desired among other things
language_name = lang_name_table.variant[subtags.variant]['descriptions'][1]; -- table entries sometimes have multiple names, always take the first one
end
end
translit_script = args['translit-script'] or language_name; -- for translit prefer |trans-script= over language
if 'no' == args.link then
table.insert (out, language_name); -- language name without wikilink
else
table.insert (out, make_wikilink (language_name .. ' language', language_name)); -- language name with wikilink
end
table.insert (out, ': '); -- separator
table.insert (out, make_text_span (args.code, args.text, args.rtl, args.italic, args.size))
if is_set (args.translit) then -- transliteration (not supported in {{lang}}); not supported in all {{lang-xx}}
table.insert (out, ', <small>');
translit_title = mw.title.makeTitle (0, 'Romanization of ' .. language_name)
if translit_title.exists and ('no' ~= args.link) then
table.insert (out, make_wikilink ('Romanization of ' .. translit_script or language_name, 'translit.'));
else
table.insert (out, '<abbr title="transliteration">translit.</abbr>');
end
table.insert (out, ' </small>');
translit = make_translit (args.code, language_name, args.translit, args['translit-std'], args['translit-script'])
if is_set (translit) then
table.insert (out, translit);
else
return make_error_msg (table.concat ({'{{lang-xx}}: invalid translit-std: \'', args['translit-std'] or 'missing', '\' or translit-script: \'', args['translit-script'] or 'missing', '\''}), args.nocat);
end
end
if is_set (args.translation) then -- translation (not supported in {{lang}})
table.insert (out, ', <small>');
if 'no' == args.link then
table.insert (out, '<abbr title="literal translation">lit.</abbr>');
else
table.insert (out, make_wikilink ('Literal translation', 'lit.'));
end
table.insert (out, " </small>'");
table.insert (out, args.translation);
table.insert (out, "'");
end
table.insert (out, make_category (code, language_name, args.nocat));
table.insert (out, render_maint(args.nocat)); -- maintenance messages and categories
return table.concat (out); -- put it all together and done
end
--[[--------------------------< L A N G _ X X >----------------------------------------------------------------
Entry point for those {{lang-xx}} templates that have not been converted to call either of lang_xx_normal() or
lang_xx_italic(). Sets the initial style state to italic. When all {{lang-xx}} template that use this module
have been converted, this finction can go away.
]]
function p.lang_xx (frame)
initial_style_state = 'italic';
return _lang_xx (frame);
end
--[[--------------------------< L A N G _ X X _ I T A L I C >--------------------------------------------------
Entry point for those {{lang-xx}} templates that have been converted to call lang_xx_italic(). Sets the initial
style state to italic.
]]
function p.lang_xx_italic (frame)
initial_style_state = 'italic';
return _lang_xx (frame);
end
--[[--------------------------< L A N G _ X X _ N O R M A L >--------------------------------------------------
Entry point for those {{lang-xx}} templates that have been converted to call lang_xx_normal(). Sets the initial
style state to normal.
]]
function p.lang_xx_normal (frame)
initial_style_state = 'normal';
return _lang_xx (frame);
end
return p;