มอดูล:ISO 639 name

require('strict');
local getArgs = require ('Module:Arguments').getArgs;
local override_data = mw.loadData ('Module:ISO 639 name/ISO 639 override');
local deprecated_data = mw.loadData ('Module:ISO 639 name/ISO 639 deprecated');
local parts = {
	{'Module:ISO 639 name/ISO 639-1', '1'},
	{'Module:ISO 639 name/ISO 639-2', '2'},
	{'Module:ISO 639 name/ISO 639-2B', '2B'},
	{'Module:ISO 639 name/ISO 639-3', '3'},
	{'Module:ISO 639 name/ISO 639-5', '5'},
	}
------------------------------------------------------------------------------
local error_messages = {
	'err_msg' = 'error: $1 (help)',
	'err_text' = {															-- error messages used only in the code to name functions
		'ietf' = '$1 is an IETF tag',											-- $1 is the ietf tag
		'required' = 'ISO 639$1 code is required',							-- $1 is the 639 '-1', '-2', '-3', '-5' part suffix; may be empty string
																				-- code to name functions and iso_639_name_to_code()
		'not_found' = '$1 not found in ISO 639-$2 list',						-- $1 is code or language name; $2 is 639 part suffix(es)
																				-- iso_639_name_to_code() only
		'name' = 'language name required',
		'not_part' = '$1 not an ISO 639 part',								-- $1 is invalid 639 suffix (without hyphen)
		'code_name' = 'language code or name required',						-- iso_639() only
		}
	}
local error_cat = ;
--------------------------------------------------------------------------------------
Populates numbered arguments in a message string using an argument table.
local function substitute (msg, args)
	return args and mw.message.newRawMessage (msg, args):plain() or msg;
end
----------------------------------------------------------------------------------------
create an error message
local function error_msg (msg, arg, hide, nocat)
	local retval = ;
	if not hide then
		retval = substitute (error_messages.err_msg, substitute (error_messages.err_textmsg, arg));
		retval = nocat and retval or (retval .. error_cat);
	end
	return retval
end
----------------------------------------------------------------------------------------------
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
--=-----------------------------------------------------------------------------
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
--------------------------------------------------------------------------------
returns first listed language name for code from data{} table; strips parenthetical disambiguation; wikilinks to
the language article if link is true; returns nil else
local function lang_name_get (code, data, link, label, raw)
	local name;
	if datacode then
--		name = raw and datacode1 or datacode1:gsub ('%s*%b()', );		-- get the name; strip parenthetical disambiguators if any when  is false
		if raw then
			name = datacode1;
		else
			name = datacode1:gsub ('%s*%b()', );							-- strip parenthetical disambiguators if any
			name = name:gsub ('(^,-), +(.+)', '%2 %1');						-- if inverted, uninvert
		end
		if link then															-- make a link to the language article?
			if name:find ('ภาษา') or name:find ('languages') or name:find ('Llanguage$') then
				name = make_wikilink (name, label);								-- simple wikilink for collective languages or langauges ending in 'Language' unless there is a label
			elseif override_data.article_namecode then
				name = make_wikilink (override_data.article_namecode1, label or name);	-- language name or label with wikilink from override data
			else
				name = make_wikilink ('ภาษา' .. name, label or name);		-- name or label
			end
		end
		return name;
	end
end
--cat=no supresses category
local function add_ietf_error_msg (text, ietf_err, hide, nocat)
	if hide then
		ietf_err = ;
	end
	if not nocat then															-- |cat= empty or omitted -> nocat=false
		nocat =  == ietf_err;													-- spoof; don't add cat when no error
	end
	return table.concat ({														-- tack on ietf error message if one exists
		text,																	-- code name, language name, or error message
		 ~= ietf_err and ' ' or ,											-- needs a space when ietf_err is not empty
		ietf_err,
		nocat and  or error_cat,												-- add error category when |cat=
		});
end
------------------------------------------------------------------------------
gets index suitable for parts{} table from ISO 639- (usually args2)
return valid index 1 - 5; nil else
	1   ------------------------
this is code that is common to all of the iso_639_code_n_to_name() functions which serve only as template entry
points to provide the frame, the name of the appropriate data source, and to identify which 639 part applies.
this function returns a language name or an error message.  data is searched in this order:
	part-specific override data -> standard part data -> part-specific deprecated data
a second retval used by _iso_639_code_to_name() is true when a code is found; nil else
local function iso_639_code_to_name_common (args, source, part)
	local hide = 'yes' == args'hide-err';										-- suppress error messages and error categorization
	local nocat = 'no' == args.cat;												-- suppress error categorization (primarily for demo use)
	local raw = 'yes' == args.raw;												-- disable override and dab removal
	local data;																	-- one of the override or part tables
	local name;																	-- holds language name from data
	if not args1 then															-- if code not provided in the template call
		return error_msg ('required', '-' .. part, hide, nocat);						-- abandon
	end
	local code;																	-- used for error messaging
	local ietf_err;																-- holds an error message when args1 (language code) is in IETF tag form (may or may not be a valid IETF tag)
	code, ietf_err = args1:gsub('(.-)%-.*', '%1');							-- strip ietf subtags; ietf_err is non-zero when subtags are stripped
	ietf_err = (0 ~= ietf_err) and error_msg ('ietf', args1, hide, nocat) or ;		-- when tags are stripped create an error message; empty string for concatenation else
	if not raw then																-- when raw is true, fetch name as is from part data; ignore override
		data = override_data'override_' .. part;								-- get override data for this part
		name = lang_name_get (code:lower(), data, args.link, args.label, raw);	-- get override language name if there is one
	end
	
	if not name then
		data = mw.loadData (source);											-- get the data for this ISO 639 part
		name = lang_name_get (code:lower(), data, args.link, args.label, raw);	-- get language name if there is one
	end
	if not name then															-- TODO: do something special to indicate when a name is fetched from deprecated data?
		data = deprecated_data'deprecated_' .. part;							-- get deprecated data for this part
		name = lang_name_get (code:lower(), data, args.link, args.label, raw);	-- get deprecated language name if there is one
		if not name then
			return error_msg ('not_found', {code, part}, hide, nocat);			-- code not found, return error message
		end
	end		
	return add_ietf_error_msg (name, ietf_err, hide, nocat), true;				-- return language name with ietf error message if any; true because we found a code
end
----------------------------------------------------------------
searches through the ISO 639 language tables for a name that matches the supplied code.  on success returns first
language name that matches code from template frame perhaps with an error message and a second return value of true;
on failure returns an error message and a second return value of nil.  The second return value is a return value
used by iso_639_code_exists()
looks first in the override data and then sequentially in the 639-1, -2, -3, and -5 data
local function _iso_639_code_to_name (frame)
	local args = getArgs(frame);
	local hide = 'yes' == args'hide-err';										-- suppress error messages and error categorization
	local nocat = 'no' == args.cat;												-- suppress error categorization (primarily for demo use)
	
	if not args1 then															-- if code not provided in the template call
		return error_msg ('required', , hide, nocat);							-- abandon
	end
	local name;																	-- the retrieved language name and / or error message
	local found;																-- set to true when language name is found
	for _, part in ipairs (parts) do
		name, found = iso_639_code_to_name_common (args, part1,  part2);
		if found then
			return name, true;													-- second retval for iso_639_name_exists()
		end
	end
	return error_msg ('not_found', {args1, '1, -2, -2B, -3, -5'}, hide, nocat);	-- here when code (args1) is not found in the data tables
end
------------------------------------------------------------------
template entry point; returns first language name that matches code from template frame or an error message
looks first in the override data and then sequentially in the 639-1, -2, -3, and -5 data
local function iso_639_code_to_name (frame)
	local ret_val = _iso_639_code_to_name (frame);								-- ignore second return value
	return ret_val;																-- return language name and / or error message
end
--------------------------------------------------------------------
template entry point; returns true if language code maps to a language name; intended as a replacement for:
	
Instead of that expensive parser function call use this function:
	
on success, returns true; nil else
local function iso_639_code_exists (frame)
	local _, exists;
	 _, exists = _iso_639_code_to_name (frame);									-- ignore name/error message return;  is true when name found for code; nil else
	 return exists;
end
--------------------------------------------------------------
template entry point; returns first language name that matches ISO 639-1 code from template frame or an error message
local function iso_639_code_1_to_name (frame)
	local args = getArgs (frame);
	local retval = iso_639_code_to_name_common (args, parts11,  parts12);	-- suppress second return value
	return retval;
end
--------------------------------------------------------------
template entry point; returns first language name that matches ISO 639-2 code from template frame or an error message
local function iso_639_code_2_to_name (frame)
	local args = getArgs (frame);
	local retval = iso_639_code_to_name_common (args, parts21,  parts22);	-- suppress second return value
	return retval;
end
------------------------------------------------------------
template entry point; returns first language name that matches ISO 639-2 code from template frame or an error message
local function iso_639_code_2B_to_name (frame)
	local args = getArgs (frame);
	local retval = iso_639_code_to_name_common (args, parts31,  parts32);	-- suppress second return value
	return retval;
end
--------------------------------------------------------------
template entry point; returns first language name that matches ISO 639-3 code from template frame or an error message
local function iso_639_code_3_to_name (frame)
	local args = getArgs (frame);
	local retval = iso_639_code_to_name_common (args, parts41,  parts42);	-- suppress second return value
	return retval;
end
--------------------------------------------------------------
template entry point; returns first language name that matches ISO 639-5 code from template frame or an error message
local function iso_639_code_5_to_name (frame)
	local args = getArgs (frame);
	local retval = iso_639_code_to_name_common (args, parts51,  parts52);	-- index 4 -> part 5 because there is no part 4; suppress second return value
	return retval;
end
----------------------------------------------------------------
indexes into the  using  and extracts the language code assigned to  (1, 2, 2B, 3, 5).
attempts to index override data first; returns code on success, nil else
local function name_in_part_code_get (name, part, part_idx, name_data)
	return name_dataname and (
		name_datanamepart_idx+5	or											-- see if the name exists in the part's override table
		name_datanamepart_idx or											-- see if the name exists in the part's main table
		name_datanamepart_idx+10											-- see if the name exists in the part's deprecated table
		);
end
----------------------------------------------------------------
module entry point; returns ISO 639-1, -2, -2B, -3, or -5 code associated with language name according to part
(1, 2, 2B, 3, 5) argument; when part is not provided scans 1, 2, 2B, 3, 5 and returns first code
override data are examined first
is frame arguments from getArgs(frame)
local function _iso_639_name_to_code (args)
	local hide = 'yes' == args'hide-err';										-- suppress error messages and error categorization
	local nocat = 'no' == args.cat;												-- suppress error categorization (primarily for demo use)
	if not args1 then
		return error_msg ('name', , hide, nocat);								-- abandon when language name missing
	end
	
	local name = args1;														-- used in error messaging
	local lc_name = name:gsub(' +', ' '):lower();								-- lowercase version of name for indexing into the data table; strip extraneous space characters
	local part_idx;
	local part = args2;
	if part then
		part_idx = get_part_index (part);
		if not part_idx then
			return error_msg ('not_part', part, hide, nocat);					-- abandon; args2 is not a valid ISO 639 part
		end
	end
	local name_data = mw.loadData ('Module:ISO 639 name/ISO 639 name to code');	-- ISO 639 language names to code table
	local code;
	
	if part then
		code = name_in_part_code_get (lc_name, part, part_idx, name_data);		-- search the specified override table + part table
	else
		for part_idx, part_tag in ipairs ({'1', '2', '2B', '3', '5'}) do		-- no part provided, spin through all parts override first and get the first available code
			code = name_in_part_code_get (lc_name, part_tag, part_idx, name_data);
			if code then														-- nil when specified  does not have code for specified language 
				break;															-- when code is not nil, done
			end
		end
	end
	
	if code then
		return code, true;
	end
	return error_msg ('not_found', {name, part or '1, -2, -2B, -3, -5'}, hide, nocat), false;
end
------------------------------------------------------------------
template entry point; returns ISO 639-1, -2, -2B, -3, or -5 code associated with language name according to part
(1, 2, 2B, 3, 5) argument; when part is not provided scans 1, 2, 2B, 3, 5 and returns first code
override data are examined first
args1 is language name
args2 is ISO 639 part
local function iso_639_name_to_code (frame)
	local args = getArgs(frame);
	local result, _ = _iso_639_name_to_code (args);								-- suppress true/false return used by iso_639_name_exists()
	return result;
end
--------------------------------------------------------------------
template entry point; returns ISO 639-1, -2, -3, or -5 code associated with language name according to part (1, 2, 3, 5) argument;
when part is not provided scans 1, 2, 3 , 5 and returns first code
override data are examined first
args1 is language name
args2 is ISO 639 part
local function iso_639_name_exists (frame)
	local args = getArgs(frame);
	local _, result = _iso_639_name_to_code (args);								-- suppress code return used by iso_639_name_to_code()
	return result and true or nil;
end
--------------------------------------------------------------------------------------------
template entry point.
returns:
	language name if args1 is valid language code
	language code if args1 is valid language name
this function is constrained to the ISO 639 part specified in args2 which must be 1, 2, 2B, 3, or 5.  When not provided
all parts are tested. The first match is found
local function iso_639 (frame)
	local args = getArgs (frame);
	local hide = 'yes' == args'hide-err';										-- suppress error messages and error categorization
	local nocat = 'no' == args.cat;												-- suppress error categorization (primarily for demo use)
	local result;
	local found;																-- set to true when language name is found
	if not args1 then
		return error_msg ('code_name', , hide, nocat);
	end
	local part = args2;
	if part then																-- if ISO 639 part supplied
		local part_idx = get_part_index (part);									-- map index from ; anything else nil
		if not part_idx then
			return error_msg ('not_part', part, hide, nocat);					-- abandon; args2 is not a valid ISO 639 part
		end
		result, found = iso_639_code_to_name_common (args, partspart_idx1, partspart_idx2);		-- attempt to find a code match
		if found then
			return result;														-- found the code so return the language name
		end
		result = _iso_639_name_to_code (args);									-- might be a language name; return code if it is; error message or empty string else
		return result;															-- this way to suppress second return
	else		
		for _, part in ipairs (parts) do										-- for each of the iso 639 parts
			result, found = iso_639_code_to_name_common (args, part1, part2);	-- attempt to find a code match
			if found then
				return result;													-- found the code so return the language name
			end
		end
	end	
	result = _iso_639_name_to_code (args);										-- might be a language name; return code if it is; error message or empty string else
	return result;																-- this way to suppress second return
end
----------------------------------------------------------------------
return {
	iso_639 = iso_639,															-- returns code when given name; returns name when given code
	iso_639_code_exists = iso_639_code_exists,
	iso_639_name_exists = iso_639_name_exists,
	iso_639_code_to_name = iso_639_code_to_name,
	iso_639_code_1_to_name = iso_639_code_1_to_name,
	iso_639_code_2_to_name = iso_639_code_2_to_name,
	iso_639_code_2B_to_name = iso_639_code_2B_to_name,
	iso_639_code_3_to_name = iso_639_code_3_to_name,
	iso_639_code_5_to_name = iso_639_code_5_to_name,
	iso_639_name_to_code = iso_639_name_to_code,
	};