Difference between revisions of "Module:Lang"

From Eat Every Plant
Jump to navigation Jump to search
m (1 revision)
 
m (1 revision: Vanilla)
(One intermediate revision by the same user not shown)
Line 10: Line 10:
 
local getArgs = require ('Module:Arguments').getArgs;
 
local getArgs = require ('Module:Arguments').getArgs;
 
local lang_name_table = mw.loadData ('Module:Language/name/data');
 
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 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 namespace = mw.title.getCurrentTitle().namespace; -- used for categorization
 +
 +
local maint_cats = {}; -- maintenance categories go here
 +
local maint_msgs = {}; -- and their messages go here
  
  
Line 109: Line 114:
 
each of lang, script, region, and variant, when used, must be valid
 
each of lang, script, region, and variant, when used, must be valid
  
returns four values.  Valid parts are returned as themselves; omitted parts are returned as empty strings, invalid
+
For {{lang-xx}} templates, the parameters |script=, |region=, and |variant= are supported (not supported in {{lang}}
parts are returned as nil.
+
because those parameters are superfluous to the IETF subtags in |code=)
 +
 
 +
returns five values.  Valid parts are returned as themselves; omitted parts are returned as empty strings, invalid
 +
parts are returned as nil; the fifth 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
 
see http://www.rfc-editor.org/rfc/bcp/bcp47.txt section 2.1
Line 116: Line 124:
 
]]
 
]]
  
local function get_ietf_parts (source)
+
local function get_ietf_parts (source, args_script, args_region, args_variant)
 
local code;
 
local code;
 
local script = '';
 
local script = '';
Line 124: Line 132:
 
 
 
if not is_set (source) then
 
if not is_set (source) then
return nil, nil, nil, nil;
+
return nil, nil, nil, nil, 'missing language tag';
 
end
 
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)
+
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)$');
 
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)
+
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)$');
 
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)
 
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)
Line 136: Line 144:
 
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]?)$');
 
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)
+
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)$');
 
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)
 
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]?)$');
 
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)
+
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)$');
 
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)
+
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)$');
 
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)
 
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)
Line 150: Line 158:
 
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]?)$');
 
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)
+
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)$');
 
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)
 
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)
Line 175: Line 183:
  
 
else
 
else
return nil, nil, nil, nil; -- don't know what we got but it is malformed
+
return nil, nil, nil, nil, table.concat {'unrecognized language tag: ', source}; -- don't know what we got but it is malformed
 
end
 
end
+
 
 
code = code:lower(); -- ensure that we use and return lower case version of this
 
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
 
if not (lang_data.override[code] or lang_name_table.lang[code]) then
return nil, nil, nil, nil; -- invalid language code, don't know about the others (don't care?)
+
return nil, nil, nil, nil, table.concat {'unrecognized language code: ', code}; -- invalid language code, don't know about the others (don't care?)
 
end
 
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, '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
 
if is_set (script) then
 
script = script:lower(); -- ensure that we use and return lower case version of this
 
script = script:lower(); -- ensure that we use and return lower case version of this
 
if not lang_name_table.script[script] then
 
if not lang_name_table.script[script] then
return code, nil, nil, nil; -- language code ok, invalid script, don't know about the others (don't care?)
+
return code, 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
 
end
 
end
 
 
 +
if is_set (region) then
 +
if is_set (args_region) then
 +
return code, 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
 
if is_set (region) then
 
region = region:lower(); -- ensure that we use and return lower case version of this
 
region = region:lower(); -- ensure that we use and return lower case version of this
 
if not lang_name_table.region[region] then
 
if not lang_name_table.region[region] then
return code, script, nil, nil;
+
return code, script, nil, nil, table.concat {'unrecognized region: ', region, ' for code: ', code};
 
end
 
end
 
end
 
end
 
 
 +
if is_set (variant) then
 +
if is_set (args_variant) then
 +
return code, 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
 
if is_set (variant) then
 
variant = variant:lower(); -- ensure that we use and return lower case version of this
 
variant = variant:lower(); -- ensure that we use and return lower case version of this
if not lang_name_table.variant[variant] then
+
if not lang_name_table.variant[variant] then -- make sure variant is valid
return code, script, region, nil;
+
return code, script, region, nil, table.concat {'unrecognized variant: ', variant};
 
end -- does this duplicate/replace tests in lang() and lang_xx()?
 
end -- does this duplicate/replace tests in lang() and lang_xx()?
if not in_array (code, lang_name_table.variant[variant]['prefixes']) and not in_array (table.concat ({code, '-', script}), lang_name_table.variant[variant]['prefixes']) then
+
if is_set (script) then -- if script set it must be part of the 'prefix'
return code, script, region, nil;
+
if not in_array (table.concat ({code, '-', script}), lang_name_table.variant[variant]['prefixes']) then
 +
return code, script, region, 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, table.concat {'unrecognized variant: ', variant, ' for code: ', code};
 +
end
 
end
 
end
 
end
 
end
Line 254: Line 298:
  
 
--[[--------------------------< M A K E _ T E X T _ S P A N >--------------------------------------------------
 
--[[--------------------------< 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
  
 
]]
 
]]
Line 285: Line 333:
  
 
--[[--------------------------< M A K E _ C A T E G O R Y >----------------------------------------------------
 
--[[--------------------------< 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.
  
 
]]
 
]]
Line 313: Line 364:
 
--[[--------------------------< M A K E _ T R A N S L I T >----------------------------------------------------
 
--[[--------------------------< M A K E _ T R A N S L I T >----------------------------------------------------
  
return translit <span>...</span> else return empty string
+
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
 
The value |script= is not used in {{transl}} for this purpose; instead it uses |code.  Because language scripts
Line 328: Line 379:
 
local title_table = lang_data.translit_title_table; -- table of transliteration standards and the language codes and scripts that apply to those standards
 
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, "''<span lang=\"");
+
table.insert (tout, "<i lang=\"");
 
table.insert (tout, code);
 
table.insert (tout, code);
table.insert (tout, "-Latn\" title=\"");
+
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
 
if not is_set (std) and not is_set (tscript) then -- when neither standard nor script specified
Line 362: Line 413:
 
table.insert (tout, '">');
 
table.insert (tout, '">');
 
table.insert (tout, translit);
 
table.insert (tout, translit);
table.insert (tout, "</span>''");
+
table.insert (tout, "</i>");
 
return table.concat (tout);
 
return table.concat (tout);
 
end
 
end
Line 397: Line 448:
 
return make_error_msg (table.concat ({'{{', template, '}}: text has malformed markup'}), args.nocat);
 
return make_error_msg (table.concat ({'{{', template, '}}: text has malformed markup'}), args.nocat);
 
end
 
end
 +
end
 
 
if args.italic then -- protect single quote marks from being converted to bold markup
+
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 ("^\'[^\']+", "<span></span>%1"); -- leading single quote mark
args.text = args.text:gsub ("[^\']+\'$", "%1<span></span>"); -- trailing single quote mark
+
args.text = args.text:gsub ("[^\']+\'$", "%1<span></span>"); -- trailing single quote mark
end
 
 
end
 
end
 
end
 
end
Line 414: Line 465:
 
returns empty string and nil error message when both language code subtag and matching subtag parameter are not set
 
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
 
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().
  
 
]]
 
]]
Line 470: Line 524:
 
 
 
args.rtl = args.rtl == 'yes'; -- convert to boolean: 'yes' -> true, other values -> false
 
args.rtl = args.rtl == 'yes'; -- convert to boolean: 'yes' -> true, other values -> false
args.italic = to_boolean(args.italic); -- convert to boolean or nil: 'yes' -> true, 'no' -> false; else nil
+
 
 
 
local out = {};
 
local out = {};
 
local language_name;
 
local language_name;
 
local subtags = {};
 
local subtags = {};
 
local code;
 
local code;
 +
local msg;
  
code, subtags.script, subtags.region, subtags.variant = get_ietf_parts (args.code);
+
code, subtags.script, subtags.region, subtags.variant, msg = get_ietf_parts (args.code); -- |script=, |region=, |variant= not supported because they should be part of args.code ({{{1}}})
  
 
if not (code and subtags.script and subtags.region and subtags.variant) then
 
if not (code and subtags.script and subtags.region and subtags.variant) then
return make_error_msg (table.concat ({'{{lang}}: unknown language code: ', args.code or 'missing'}), args.nocat);
+
return make_error_msg (table.concat ({'{{lang}}: ', msg}), args.nocat);
 
end
 
end
  
local msg = validate_text ('lang', args); -- ensure that |text= is set  (italic test disabled for the time being)
+
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
 
if is_set (msg) then
 
return msg;
 
return msg;
Line 493: Line 557:
 
else
 
else
 
args.rtl = false; -- script is not an rtl script
 
args.rtl = false; -- script is not an rtl script
end
 
end
 
 
if is_set (subtags.variant) then -- special case test for |variant=
 
if is_set (subtags.script) then
 
if not in_array (table.concat ({code, '-', subtags.script}), lang_name_table.variant[subtags.variant]['prefixes']) then
 
return make_error_msg (table.concat ({'{{lang}}: invalid code-variant combination: ', code, '-', subtags.script, '-', subtags.variant}), args.nocat);
 
end
 
else
 
if not in_array (code, lang_name_table.variant[subtags.variant]['prefixes']) then
 
return make_error_msg (table.concat ({'{{lang}}: invalid code-variant combination: ', code, '-', subtags.variant}), args.nocat);
 
end
 
 
end
 
end
 
end
 
end
  
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
 
 
 
args.code = format_ietf_tag (code, subtags.script, subtags.region, subtags.variant); -- format to recommended subtag styles
 
args.code = format_ietf_tag (code, subtags.script, subtags.region, subtags.variant); -- format to recommended subtag styles
  
Line 525: Line 569:
  
 
table.insert (out, make_text_span (args.code, args.text, args.rtl, args.italic, args.size));
 
table.insert (out, make_text_span (args.code, args.text, args.rtl, args.italic, args.size));
table.insert (out, make_category (args.code, language_name, args.nocat));
+
table.insert (out, make_category (code, language_name, args.nocat));
 +
if 0 < #maint_msgs then
 +
table.insert (out, 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 (out, table.concat ({msg, ' '}));
 +
end
 +
table.insert (out, '</span>');
 +
end
 +
 +
if (0 < #maint_cats) and (0 == namespace) and not is_set (args.nocat) then
 +
for _, cat in ipairs (maint_cats) do
 +
table.insert (out, table.concat ({'[[Category:', cat, ']]'}));
 +
end
 +
end
 +
 
return table.concat (out); -- put it all together and done
 
return table.concat (out); -- put it all together and done
 
end
 
end
Line 597: Line 655:
 
end
 
end
  
args.size = nil; -- size not supported in {{lang-xx}}
 
 
 
args.rtl = args.rtl == 'yes'; -- convert to boolean: 'yes' -> true, other values -> false
 
args.rtl = args.rtl == 'yes'; -- convert to boolean: 'yes' -> true, other values -> false
args.italic = to_boolean(args.italic); -- convert to boolean or nil: 'yes' -> true, 'no' -> false; else nil
 
  
 
local out = {};
 
local out = {};
Line 610: Line 665:
 
local translit;
 
local translit;
 
local translit_title;
 
local translit_title;
 +
local msg;
 +
 +
code, subtags.script, subtags.region, subtags.variant, msg = get_ietf_parts (args.code, args.script, args.region, args.variant);
  
code, subtags.script, subtags.region, subtags.variant = get_ietf_parts (args.code);
+
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
 
 
if not (code and subtags.script and subtags.region and subtags.variant) then
+
args.italic = to_boolean (args.italic); -- convert to boolean or nil: 'yes' -> true, 'no' -> false; else nil
return make_error_msg (table.concat ({'{{lang-xx}}: unknown language code: ', args.code or 'missing'}), args.nocat);
+
 
 +
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
 
end
 
 
local msg = validate_text ('lang-xx', args); -- ensure that |text= is set, does not contain italic markup and is protected from improper bolding
+
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
 
if is_set (msg) then
 
return msg;
 
return msg;
end
 
 
for name, value in pairs (subtags) do -- spin through the subtags table and consolidate code and parameter-provided language subtags
 
subtags[name], msg = subtag_check (name, args.code, value, args[name], args.nocat);
 
if not subtags[name] then
 
return msg; -- some sort of error detected, return the error message
 
end
 
 
end
 
end
  
Line 634: Line 693:
 
else
 
else
 
args.rtl = false; -- script is not an rtl script
 
args.rtl = false; -- script is not an rtl script
end
 
end
 
 
if is_set (subtags.variant) then -- special case test for |variant=
 
if is_set (subtags.script) then
 
if not in_array (table.concat ({code, '-', subtags.script}), lang_name_table.variant[subtags.variant]['prefixes']) then
 
return make_error_msg (table.concat ({'{{lang}}: invalid code-variant combination: ', code, '-', subtags.script, '-', subtags.variant}), args.nocat);
 
end
 
else
 
if not in_array (code, lang_name_table.variant[subtags.variant]['prefixes']) then
 
return make_error_msg (table.concat ({'{{lang}}: invalid code-variant combination: ', code, '-', subtags.variant}), args.nocat);
 
end
 
 
end
 
end
 
end
 
end
  
if args.italic == nil then -- args.italic controls
+
args.code = format_ietf_tag (code, subtags.script, subtags.region, subtags.variant); -- format to recommended subtag styles
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
 
 
args.code = format_ietf_tag (code, subtags.script, subtags.region, subtags.variant); -- format to recommended subtag styles
 
  
if lang_data.override[code] then
+
if lang_data.override[args.code] then -- first look for whole IETF tag in override table
 +
language_name = lang_data.override[args.code][1]
 +
elseif lang_data.override[code] then -- not there so try basic language code
 
language_name = lang_data.override[code][1]
 
language_name = lang_data.override[code][1]
elseif lang_name_table.lang[code] then
+
elseif not is_set (subtags.variant) then
language_name = lang_name_table.lang[code][1]; -- table entries sometimes have multiple names, always take the first one
+
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
 
end
  
Line 705: Line 752:
 
end
 
end
 
 
table.insert (out, make_category (args.code, language_name, args.nocat));
+
table.insert (out, make_category (code, language_name, args.nocat));
 
return table.concat (out); -- put it all together and done
 
return table.concat (out); -- put it all together and done
 
end
 
end
  
 
return p;
 
return p;

Revision as of 12:55, 14 December 2017

--[=[

Lua support for the error: {{lang}}: missing language tag (help) and Template: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

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

each of lang, script, region, and variant, when used, must be valid

For Template:Lang-xx templates, the parameters |script=, |region=, and |variant= are supported (not supported in error: {{lang}}: missing language tag (help) because those parameters are superfluous to the IETF subtags in |code=)

returns five values. Valid parts are returned as themselves; omitted parts are returned as empty strings, invalid parts are returned as nil; the fifth 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 c;

if not is_set (source) then return 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 ('^1%a+$') then -- special case for two Linguist list identifiers in Module:Lang/data override table code = source:match ('^(1%a%a)$'); -- these may need tweaks in future if other alphanumeric Linguist list identifiers are added

else return 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, 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, '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, 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 is_set (region) then if is_set (args_region) then return code, 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, 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, '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, 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, 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, table.concat {'unrecognized variant: ', variant, ' for code: ', code}; end end end

return code, script, region, variant; -- return the good bits 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, 'error: '); table.insert (out, msg); table.insert (out, ' (help)') table.insert (out, '');

if (0 == namespace) and not is_set (nocat) then -- only categorize in article space table.insert (out, ); 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 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 ({'', 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: ... 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 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 -- error: {{lang}}: missing language tag (help) 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, ''); -- 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 ... where xx is the language code; else return empty string

The value |script= is not used in Script error: The function "transl" does not exist. for this purpose; instead it uses |code. Because language scripts are listed in the Script error: The function "transl" does not exist. switches they are included in the data tables. The script parameter is introduced at [[{{{2}}} language|{{{2}}}]]: error: {{lang}}: unrecognized language tag: {{{1}}} (help). 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, "'); table.insert (tout, translit); table.insert (tout, ""); 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:', template, ': no text'}), args.nocat); end

if 'lang-xx' == template then -- for the time being, this error checking does not apply to error: {{lang}}: missing language tag (help) if args.text:find ("\'\'\'\'\'[\']+") then -- because we're looking, look for 6+ appostrophes return make_error_msg (table.concat ({'Template:', 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:', 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:', 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 ("^\'[^\']+", "%1"); -- leading single quote mark args.text = args.text:gsub ("[^\']+\'$", "%1"); -- 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 ({'Template: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 ({'Template:Lang-xx: code / ' .. name .. ' mismatch: ', args_code:lower(), ' / ', args_subtag}), nocat); end end

return code_subtag; end


--[[--------------------------< L A N G >----------------------------------------------------------------------


|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 ('error: {{lang}}: missing language tag (help): 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 ('error: {{lang}}: missing language tag (help): 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, msg = get_ietf_parts (args.code); -- |script=, |region=, |variant= not supported because they should be part of args.code ({{{1}}})

if not (code and subtags.script and subtags.region and subtags.variant) then return make_error_msg (table.concat ({'error: {{lang}}: missing language tag (help): ', 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 error: {{lang}}: missing language tag (help) 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

if 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)); if 0 < #maint_msgs then table.insert (out, table.concat ({''); end

if (0 < #maint_cats) and (0 == namespace) and not is_set (args.nocat) then for _, cat in ipairs (maint_cats) do table.insert (out, table.concat ({})); end end

return table.concat (out); -- put it all together and done end


--[[--------------------------< L A N G _ X X >----------------------------------------------------------------


|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 Template: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 Template:Lang-xx, the positional parameters are: {{{1}}} text {{{2}}} transliterated text {{{3}}} literal translation text

]]

function p.lang_xx (frame) local args = getArgs(frame, {parentFirst= true}); -- parameters in the template override parameters set in the Script error: You must specify a function to call.; is that the right thing to do?

if args[1] and args.text then return make_error_msg ('Template: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 ('Template: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 ('Template:Lang-xx: conflicting: {{{3}}} and |lit= or |translation=', args.nocat); elseif args.translation and args.lit then return make_error_msg ('Template: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 ('Template: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, msg = get_ietf_parts (args.code, args.script, args.region, args.variant);

if not (code and subtags.script and subtags.region and subtags.variant) then return make_error_msg (table.concat ({'Template: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 Template: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 lang_data.override[args.code] then -- first look for whole IETF tag in override table language_name = lang_data.override[args.code][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 error: {{lang}}: missing language tag (help)); not supported in all Template:Lang-xx table.insert (out, ', '); 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, 'translit.'); end table.insert (out, ' '); 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 ({'Template:Lang-xx: invalid translit-std: \, args['translit-std'] or 'missing', '\' or transli-script: \, args['translit-script'] or 'missing', '\}), args.nocat); end end

if is_set (args.translation) then -- translation (not supported in error: {{lang}}: missing language tag (help)) table.insert (out, ', '); if 'no' == args.link then table.insert (out, 'lit.'); else table.insert (out, make_wikilink ('Literal translation', 'lit.')); end table.insert (out, " '"); table.insert (out, args.translation); table.insert (out, "'"); end

table.insert (out, make_category (code, language_name, args.nocat)); return table.concat (out); -- put it all together and done end

return p;