跳至內容

模組:Lang

文出維基大典

夫斯模組之設,首為大典之殊方異語,施以定式,正其標識;其次則欲其敷彩具象,了然入目也。茲局統括{{lang}}及諸模板:Tld模板,總核源流,而所本之簿錄,皆采自萬國通規,確然有憑。

簿錄所出,具列於Module:Language/name/data及附庸諸局:

名與示號
Module:Lang/data/iana languages – 出 IANA殊方語言副卷標註冊簿
Module:Language/data/ISO 639-3 – 出 sil.org 所載UTF-8字符全表
Module:Language/data/wp languages – 凡莫知所出者,皆彙於斯。
文字
Module:Language/data/iana scripts – 亦出IANA註冊簿。
方域
Module:Language/data/iana regions – 亦出IANA註冊簿。
流變
Module:Language/data/iana variants – 亦出IANA註冊簿。
芟夷文字
Module:Language/data/iana suppressed scripts – 亦出IANA註冊簿。

此外復有補遺之局:

變通及殊例
Module:Lang/data – 蓋IANA與ISO 639-3參差不齊,或名號冗繁,或其說未宜公之於正文,則以override(變通)之表權存其辭。
Module:Lang/ISO 639 synonyms – 納三符之ISO 639-2、-2T、-3 於兩符之ISO 639-1。其法本乎表示語言名稱之規範

斯局既立,則繁冗之模板:Tld{{Language with name}}{{Language with name and transliteration}}{{transl}}諸模皆可廢也。且能自糾謬誤,明察失漏,俾標識無疵,以適披閱誦讀之用。

凡有訛誤,則入:

Category:lang與lang-xx模板紕漏曰「lang和lang-xx模板錯誤」(惟正文暨主空間適用)

脩治之錄:

Category:改lang與lang-xx示號為ISO 639-1

殊方文字格式:

zh:Wikipedia:格式手冊/文字格式

--[=[

茲篇以 Lua 輔助 {{lang}}、{{lang-xx}} 及 {{transl}} 諸模板,並代曩時林林總總之從屬模板。

]=]

require('strict');
local p = {};

local initial_style_state;														-- 設於 lang_xx_inherit() 及 lang_xx_italic() 之中

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 碼轉為 639-1 碼之對照

local lang_data =  mw.loadData ('Module:Lang/data');							-- 語名覆寫及轉寫提示框之數據表

local namespace = mw.title.getCurrentTitle().namespace;							-- 用以辨別名態而歸類

local maint_cats = {};															-- 護理類屬咸集於此
local maint_msgs = {};															-- 護理之辭亦系乎此

local function page_exists (title)
    -- 斯凾式行 #ifexist 之功
    local noError, titleObject = pcall (mw.title.new, title)
    if not noError then
        return false
    else
        if titleObject then
            return titleObject.exists
        else
            return false
        end
    end
end

--[[--------------------------< I S _ S E T >------------------------------------------------------------------

察變數之有無。實則報真,虛則報假。所謂實者,存而不空之謂也(非 nil 且非空字串)。

]]

local function is_set( var )
	return not (var == nil or var == '');
end


--[[--------------------------< I S _ L A T N >----------------------------------------------------------------

察文辭悉爲拉丁字、數碼與句讀與否。若然則報真,否則報假。

案:此處所謂拉丁字者,乃扣除控制字元之萬國碼(Unicode 10.0)字元,其表具列如下:
	[http://www.unicode.org/charts/PDF/U0000.pdf C0 Controls and Basic Latin] U+0020–U+007E (20 - 7E) + see note about <poem>...</poem> support
	[http://www.unicode.org/charts/PDF/U0080.pdf C1 Controls and Latin-1 Supplement] U+00A0-U+00AC, U+00C0–U+00FF (C2 A0 - C2 AC, C3 80 - C3 BF: \194\160-\194\172)
	[http://www.unicode.org/charts/PDF/U0100.pdf Latin Extended-A] U+0100–U+017F (C4 80 - C5 BF)
	[http://www.unicode.org/charts/PDF/U0180.pdf Latin Extended-B] U+0180–U+024F (C6 80 - C9 8F)
	[http://www.unicode.org/charts/PDF/U1E00.pdf Latin Extended Additional] U+1E00-U+1EFF (E1 B8 80 - E1 BB BF)
	[http://www.unicode.org/charts/PDF/U2C60.pdf Latin Extended-C] U+2C60–U+2C7F (E2 B1 A0 - E2 B1 BF)
	[http://www.unicode.org/charts/PDF/UA720.pdf Latin Extended-D] U+A720-U+A7FF (EA 9C A0 - EA 9F BF)
	[http://www.unicode.org/charts/PDF/UAB30.pdf Latin Extended-E] U+AB30-U+AB6F (EA AC B0 - EA AD AF)
	[http://www.unicode.org/charts/PDF/UFB00.pdf Alphabetic Presentaion Forms] U+FB00-U+FB06 (EF AC 80 - EF AC 86)
	[http://www.unicode.org/charts/PDF/UFF00.pdf Halfwidth and Fullwidth Forms] U+FF01-U+FF3C (EF BC 81 EF BC BC)

凡詩歌辭賦,{{lang}} 常用於 <poem>...</poem> 之中。此籤將換行符化爲詩歌剝離標記,其式若:
	?'"`UNIQ--poem-67--QINU`"'?
其 '?' 實爲刪除符(U+007F, \127)。故納 '\n'(U+0010)與刪除符於拉丁字表,俾 {{lang}} 於詩歌中亦能自化斜體。

]]

function p.is_latn (text)
	local latn = table.concat (
		{
		'[',																	-- 始括號
		'\n\32-\127',															-- 基本拉丁字與控制符
		'\194\160-\194\172',													-- 拉丁字一之補充
		'\195\128-\195\191',													-- 
		'\196\128-\197\191',													-- 拉丁字擴展甲
		'\198\128-\201\143',													-- 拉丁字擴展乙
		'\225\184\128-\225\187\191',											-- 拉丁字擴展附加
		'\226\177\160-\226\177\191',											-- 拉丁字擴展丙
		'\234\156\160-\234\159\191',											-- 拉丁字擴展丁
		'\234\172\176-\234\173\175',											-- 拉丁字擴展戊
		'\239\172\128-\239\172\134',											-- 字母表現形式
		'\239\188\129-\239\188\188',											-- 半形與全形
		'–',																	-- 連結號
		'—',																	-- 破折號
		'«', '»',																-- 拉丁語文常用之引號
		']',																	-- 終括號
		});

	text = mw.text.decode (text, true);											-- 化 HTML 實體為萬國碼字元
	text = mw.ustring.gsub (text, '%[%[[^|]+|([^%]]+)%]%]', '%1');				-- 剔除維基連結,以防跨語言連結干擾
	return not is_set (mw.ustring.gsub (text, latn, ''));						-- 盡除拉丁字;若悉為空,則本為拉丁文也
end


--[[--------------------------< I N V E R T  _ I T A L I C S >-------------------------------------------------

此式旨在反覆 args.text 內之斜體標識。其法增損首尾之斜體,以成反效。其有單撇號者,皆化作 HTML 數碼,以免誤作粗體。

首尾之維基標識,皆自 args.text 析出,而分置於表。增刪替換之功,惟賴 string.gsub()。

案:此式之用,必賴 args.text 內之標識完好無缺;若有舛錯,必生異端。

]]

local function invert_italics (source)
	local invert_pattern_table = {												-- 首尾標識增刪替換之則
		[""]="\'\'",															-- 空字串化爲斜體標識
		["\'\'"]="",															-- 斜體標識化爲空字串
		["\'\'\'"]="\'\'\'\'\'",												-- 粗體化爲粗斜體
		["\'\'\'\'\'"]="\'\'\'",												-- 粗斜體化爲粗體
		};
	local seg = {};

	source = source:gsub ("%f[\']\'%f[^\']", '&#38;');							-- 護單撇號,防作粗體解

	seg[1] = source:match ('^(\'\'+%f[^\']).+') or '';							-- 取首標識(若有)
	seg[3] = source:match ('.+(%f[\']\'\'+)$') or '';							-- 取尾標識(若有)

	if '' ~= seg[1] and '' ~= seg[3] then										-- 析其文段 
		seg[2] = source:match ('^\'\'+%f[^\'](.+)%f[\']\'\'+$')					-- 居首尾標識之中者
	elseif '' ~= seg[1] then
		seg[2] = source:match ('^\'\'+%f[^\'](.+)')								-- 隨首標識之後者
	elseif '' ~= seg[3] then
		seg[2] = source:match ('(.+)%f[\']\'\'+$')								-- 居尾標識之前者
	else
		seg[2] = source															-- 杳無標識者
	end

	seg[1] = seg[1]:gsub (".*", invert_pattern_table, 1);						-- 依則替其首
	seg[3] = seg[3]:gsub (".*", invert_pattern_table, 1);						-- 依則替其尾

	return table.concat (seg);													-- 合而還之
end


--[[--------------------------< V A L I D A T E _ I T A L I C >------------------------------------------------

覈 |italic= 或 |italics= 之賦值。

若 |italic= 有值且允當,則報相應之 css font-style 屬性值;特例 'default' 則報 nil。

若 |italic= 未設,或其值不允,則報 nil 與 nil 紕漏辭。

若 |italic= 與 |italics= 俱設,報 nil 與 '抵觸' 紕漏辭。

諸般允當之值列下:
	nil		-	未設,報 nil
	default	-	備而不用,報 nil
	yes		-	勒令文辭作斜體,報 'italic'
	no		-	勒令文辭作正體,報 'normal'
	unset	-	弛其管束,字體惟外圍標記是從,報 'inherit'
	invert	-	弛其管束,反覆其內之斜體,報 'invert'

]]

local function validate_italic (italic, italics)
	local properties = {['yes'] = 'italic', ['no'] = 'normal', ['unset'] = 'inherit', ['invert'] = 'invert', ['default'] = nil};

	if italic and italics then													-- 俱設則報紕漏 
		return nil, '抵觸:&#124;italic= 與 &#124;italics= 俱設';
	end
	
	return properties[italic or italics], nil;									-- 報其值並附空紕漏辭
end


--[[--------------------------< I N _ A R R A Y >--------------------------------------------------------------

尋針於海,察物之在列與否。

]]

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 >------------------------------------------------

潤色 IETF 標籤,使符善式:
	代碼(code):小寫
	文字(script):首字大寫
	地區(region):大寫
	變體(variant):小寫

]]

local function format_ietf_tag (code, script, region, variant)
	local out = {};

	table.insert (out, code:lower());
	if is_set (script) then
		script = script:lower():gsub ('^%a', string.upper);
		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 >--------------------------------------------------

析出 IETF 語言標籤之諸部,並報之:
	主語標籤(必具) - 二三字之 IANA 語碼
	文字標籤 - 四字之 IANA 文字碼
	地區標籤 - 二字或三數碼之 IANA 地區碼
	變體標籤 - 四數碼或五至八字之變體碼
	私有標籤 - x- 冠首,繼以一至八字之私有碼;惟主語標籤可用

所析之形若下:
	lang					lang-variant
	lang-script				lang-script-variant
	lang-region				lang-region-variant
	lang-script-region		lang-script-region-variant
	lang-x-private	
	
凡所用者,皆須允當。

語碼兼具二字、三字者,升等作二字;蓋 IANA 註冊檔每遺同義之三字碼,且瀏覽器未必識三字碼也。

於 {{lang-xx}} 模板,援用 |script=, |region=, 及 |variant= 變數。

報六值。允當者報其本質;闕如者報空串;不允者報 nil;第六值為紕漏之辭(若有舛錯)或 nil。

詳參 http://www.rfc-editor.org/rfc/bcp/bcp47.txt 節 2.1

]]

local function get_ietf_parts (source, args_script, args_region, args_variant)
	local code;
	local script = '';
	local region = '';
	local variant = '';
	local private = '';

	if not is_set (source) then
		return nil, nil, nil, nil, nil, '語文之籤闕如';
	end

	if source:match ('^%a%a%a?%-%a%a%a%a%-%a%a%-%d%d%d%d$') then												-- ll-Ssss-RR-variant (variant 為四數碼)
		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
		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
		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
		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
		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
		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
		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
		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
		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
		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
		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][%a%d]?[%a%d]?[%a%d]?$') then			-- ll-variant
		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
		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
		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 ({'語籤乖式:', source});		-- 形制乖謬,莫辨何物
	end

	code = code:lower();														-- 悉化小寫
	
	if not (lang_data.override[code] or lang_name_table.lang[code]) then
		return nil, nil, nil, nil, nil, table.concat ({'語文之示號莫辨:', code});		-- 語碼不允當
	end
	
	if synonym_table[code] then													-- 若有 639-1 之同義碼
		table.insert (maint_cats, table.concat ({'改lang與lang-xx示號為ISO 639-1|', code}));
		table.insert (maint_msgs, table.concat ({'示號:', code, '改以示號:', synonym_table[code]}));
		code = synonym_table[code];												-- 採同義碼
	end

	if is_set (script) then
		if is_set (args_script) then
			return code, nil, nil, nil, nil, '文籤冗贅';			-- 不得兼具標籤內之文字與 |script= 變數
		end
	else
		script = args_script or '';												-- 採變數所賦
	end 

	if is_set (script) then
		script = script:lower();												-- 悉化小寫
		if not lang_name_table.script[script] then
			return code, nil, nil, nil, nil, table.concat ({'文字莫辨:', script, ',其示號為:', code});
		end
	end
	if lang_name_table.suppressed[script] then									-- 查有無遭抑之文字
		if in_array (code, lang_name_table.suppressed[script]) then
			return code, nil, nil, nil, nil, table.concat ({'文段:', script, ',於此示號見絀:', code});
		end
	end

	if is_set (region) then
		if is_set (args_region) then
			return code, nil, nil, nil, nil, '域籤冗贅';			-- 標籤與變數不可同具
		end
	else
		region = args_region or '';												-- 採變數所賦
	end 

	if is_set (region) then
		region = region:lower();												-- 悉化小寫
		if not lang_name_table.region[region] then
			return code, script, nil, nil, nil, table.concat ({'地區莫辨:', region, ',其示號為:', code});
		end
	end
	
	if is_set (variant) then
		if is_set (args_variant) then
			return code, nil, nil, nil, nil, '變體標籤冗贅';			-- 標籤與變數不可同具
		end
	else
		variant = args_variant or '';											-- 採變數所賦
	end 

	if is_set (variant) then
		variant = variant:lower();												-- 悉化小寫
		if not lang_name_table.variant[variant] then							-- 覈變體之允當與否
			return code, script, region, nil, nil, table.concat ({'變體莫辨:', variant});
		end																		
		if is_set (script) then													-- 若設有文字,則必爲前綴之一部
			if not in_array (table.concat ({code, '-', script}), lang_name_table.variant[variant]['prefixes']) then
				return code, script, region, nil, nil, table.concat ({'變體莫辨:', variant, ',屬乎示號、文字之對:', code, '-', script});
			end
		else
			if not in_array (code, lang_name_table.variant[variant]['prefixes']) then
				return code, script, region, nil, nil, table.concat ({'變體莫辨:', variant, ',其示號為:', code});
			end				
		end
	end
	
	if is_set (private) then
		private = private:lower();												-- 悉化小寫
		if not lang_data.override[table.concat ({code, '-x-', private})] then	-- 覈私有籤之允當與否
			return code, script, region, nil, nil, table.concat ({'私有標籤莫辨:', private});
		end
	end
	return code, script, region, variant, private, nil;							-- 報其菁華;紕漏辭須爲 nil
end


--[[--------------------------< M A K E _ E R R O R _ M S G >--------------------------------------------------

聚模板名、辭意、助連、紕漏類屬,以成紕漏之辭。

]]

local function make_error_msg (msg, args, template)
	local out = {};
	local category;
	
	if 'transl' == template then
		category = 'transl';
	else
		category = 'lang與lang-xx'
	end
	
	table.insert (out, table.concat ({'&#x5B;', args.text or '闕如', '&#x5D; '}));	-- 宣示 args.text(若有)
	table.insert (out, table.concat ({'<span style=\"font-size:100%; font-style:normal;\" class=\"error\">紕漏:{{', template, '}}:'}));
	table.insert (out, msg);
	table.insert (out, table.concat ({'([[:Category:', category, '模板紕漏|助]])'}));
	table.insert (out, '</span>');
	
	if (0 == namespace) and not is_set (args.nocat) then						-- 惟條目空間始入類
		table.insert (out, table.concat ({'[[Category:', category, '模板紕漏]]'}));
	end

	return table.concat (out);
end
	

--[=[-------------------------< M A K E _ W I K I L I N K >----------------------------------------------------

造維基連結;若具連結與示文,則報 [[L|D]] ;若僅具連結,則報 [[L]] ;俱闕則報空串。

]=]

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 >--------------------------------------------------

案:宜增區塊(block)之援:以 div 替 span;需以適宜之變數控之。

於斜體而言,若文爲斜體,則弗能徑作 ''{{lang|xx|text}}'' 而捨 <span/> 不用。蓋外之標記將化爲:
	<i><i lang="xx">text</i></i>
而後化簡爲:
	<i>text</i>
此化簡之功,超乎此模板模組之外。

是故,修繕未畢前,斜體文段必作:
	<i><span lang="xx">text</span></i>

]]

local function make_text_span (code, text, rtl, style, size, language)
	local span = {};
	local style_added = '';

	if text:match ('^%*') then
		table.insert (span, '&#42;');											-- 移原始語前綴星號於斜體之外
		text = text:gsub ('^%*', '');											-- 摘去星號
	end

	if 'italic' == style then
		table.insert (span, '<i>');												-- 啓斜體
	end
		
	table.insert (span, table.concat ({'<span lang="'}));						-- 啓 <span>
	table.insert (span, table.concat ({code, '\"'}));							-- 添語言屬性

	if rtl then
		table.insert (span, ' dir="rtl"');										-- 右至左語言添方向屬性
	end

	if 'normal' == style then													-- 遇 |italic=no
		table.insert (span, ' style=\"font-style:normal;');						-- 蓋覆外圍標識
		style_added = '\"';														-- 記屬性已添而未合
	end

	if is_set (size) then														-- 遇 |size=<某物>
		if is_set (style_added) then
			table.insert (span, table.concat ({' font-size:', size, ';'}));		-- 屬性既立則續添
		else
			table.insert (span, table.concat ({' style=\"font-size:', size, ';'}));	-- 創設字號屬性
			style_added = '\"';													-- 記屬性已添而未合
		end
	end

	if is_set (language) then
		table.insert (span, table.concat ({style_added, ' title=\"', language}));	-- 啓提示框
		if language:find ('languages') then
			table.insert (span, '集體語文\"');									-- 集體語文
		else
			table.insert (span, '語文\"');										-- 獨語
		end
		table.insert (span, '>');												-- 閉 <span> 首標籤
	else
		table.insert (span, table.concat ({style_added, '>'}));					-- 閉屬性與 <span> 首標籤
	end
	table.insert (span, text);													-- 入文段

	table.insert (span, '</span>');												-- 閉 <span>
	if 'italic' == style then
		table.insert (span, '</i>');											-- 閉斜體
	end
		
	if rtl then																	-- 遺緒;今既全包於 rtl,實屬無庸
		table.insert (span, '&lrm;');											-- 告覽器以右至左文段之終
	end
	
	return table.concat (span);													-- 合而報之
end


--[=[-------------------------< M A K E _ C A T E G O R Y >----------------------------------------------------

案:此處有易。凡中文,則歸之[[Category:明確引用中文之條目]]。
非中文者:
	若屬常用,直入[[Category:具<某語>之條目]]。
	非常用者:
		有類屬則歸其類。
		無則入[[Category:具非中文內容之條目]]。

]=]

local function make_category (code, language_name, nocat)
	local cat = {};
	
	if (0 ~= namespace) or nocat then											-- 惟條目空間始入類
		return '';																-- 報空串以合之
	end
		
	table.insert (cat, '[[Category:具');
	
	if 'zh' == code then
		table.insert (cat, '明確引用中文');
	elseif 'ar' == code then
		table.insert (cat, '阿拉伯語')
	elseif 'en' == code then
		table.insert (cat, '英語')
	elseif 'es' == code then
		table.insert (cat, '西班牙語')
	elseif 'de' == code then
		table.insert (cat, '德語')
	elseif 'fr' == code then
		table.insert (cat, '法語')
	elseif 'ja' == code then
		table.insert (cat, '日語')
	elseif 'bg' == code then
		table.insert (cat, '保加利亞語')
	elseif 'cs' == code then
		table.insert (cat, '捷克語')
	elseif 'da' == code then
		table.insert (cat, '丹麥語')
	elseif 'nl' == code then
		table.insert (cat, '荷蘭語')
	elseif 'et' == code then
		table.insert (cat, '愛沙尼亞語')
	elseif 'fi' == code then
		table.insert (cat, '芬蘭語')
	elseif 'el' == code then
		table.insert (cat, '希臘語')
	elseif 'hu' == code then
		table.insert (cat, '匈牙利語')
	elseif 'ga' == code then
		table.insert (cat, '愛爾蘭語')
	elseif 'grc' == code then
		table.insert (cat, '古希臘語')
	elseif 'kr' == code then
		table.insert (cat, '卡努里語')
	elseif 'la' == code then
		table.insert (cat, '拉丁語')
	elseif 'cy' == code then
		table.insert (cat, '威爾斯語')
	elseif 'sl' == code then
		table.insert (cat, '斯洛維尼亞語')
	elseif 'yue' == code then
		table.insert (cat, '粵語')
	elseif (page_exists ('Category:具' .. language_name .. '之條目') ) then
		table.insert (cat, language_name);
	else
		table.insert (cat, '非中文內容');
	end
	
	table.insert (cat, '之條目]]');

	return table.concat (cat);	
end


--[[--------------------------< M A K E _ T R A N S L I T >----------------------------------------------------

報轉寫 <i lang=xx-Latn>...</i>,xx 爲語言代碼;否則報空串。

{{transl}} 之 |script= 變數弗為此用,乃用 |code。蓋語言文字咸列於 {{transl}} 樞紐之中,俱在數據表矣。
文字變數肇於 {{Language with name and transliteration}}。若 |script= 有設,斯函式先取之。

為防混淆,此模組及模板中,轉寫文字變數更名為 |translit-script= (斯函式中為 tscript)。

斯函式爲 lang_xx() 及 transl() 俱用:
	lang_xx() 恆賦 code、language_name、translit;或賦 tscript;不賦 style。
	transl() 恆賦 language_name、translit;並賦 code、tscript 其一(不兼賦);恆賦 style。

於 {{transl}} 而言,惟賦 code 始生 style。
]]

local function make_translit (code, language_name, translit, std, tscript, style)
	local title;
	local tout = {};
	local title_table = lang_data.translit_title_table;							-- 轉譯基準之表,暨其語碼、文字
	
	if is_set (code) then														-- 若賦語碼({{lang-xx}} 恆有,{{transl}} 則未必然)
		if not style then														-- nil 則默作斜體
			table.insert (tout, "<span lang=\"");									-- 用 <span>
		else
			table.insert (tout, table.concat ({'<span style=\"font-style:', style, '\" lang=\"'}));	-- 非常規字體則造 span 標籤
		end
		table.insert (tout, code);
		table.insert (tout, "-Latn\" title=\"");								-- 轉寫恆爲拉丁文字
	else
		table.insert (tout, "<span title=\"");									-- 若無語碼:弗設 lang= 屬性,亦非斜體(惟 {{transl}} 有之)
	end
	
	std = std and std:lower();													-- 化小寫以索表
	
	if not is_set (std) and not is_set (tscript) then							-- 若基準與文字俱闕
		table.insert (tout, language_name);										-- 書尋常提示語
		if not language_name:find ('languages') then							-- 集體語名
			table.insert (tout, '語')											-- 單一或宏語言
		end
		table.insert (tout, '轉寫');											-- 終其提示
	elseif is_set (std) and is_set (tscript) then								-- 若兩者俱全
		if title_table[std] then												-- 且基準允當
			if title_table[std][tscript] then									-- 且所用文字允當
				table.insert (tout, table.concat ({title_table[std][tscript:lower()], '(', lang_name_table.script[tscript][1], '文)轉寫'}));	-- 添提示文
			else
				table.insert (tout, title_table[std]['default']);				-- 弗在表內則用默認;案:宜添維護類屬?
			end
		else
			return '';															-- 基準見紕,俟發紕漏辭
		end

	elseif is_set (std) then													-- 設基準而闕文字,則用語碼
		if not title_table[std] then return ''; end								-- 基準見紕,俟發紕漏辭
		
		if title_table[std][code] then											-- 若語碼在表(transl 未必有語碼)
			table.insert (tout, table.concat ({title_table[std][code:lower()], '(', lang_name_table.lang[code][1], '語)轉寫'}));	-- 添提示文
		else																	-- 語碼弗符
			table.insert (tout, title_table[std]['default']);					-- 採默認值
		end
	else																		-- 設文字而闕基準
		if title_table['no_std'][tscript] then
			table.insert (tout, title_table['no_std'][tscript]);				-- 取所設文字
		elseif title_table['no_std'][code] then
			table.insert (tout, title_table['no_std'][code]);					-- 取語碼
		else
			if is_set (tscript) then
				table.insert (tout, table.concat ({language_name, '文轉寫'}));	-- 書文字提示
			elseif is_set (code) then
				if not language_name:find ('languages') then					
					table.insert (tout, '語')							
				end
				table.insert (tout, '轉寫');						
			else
				table.insert (tout, '轉寫');						
			end
		end
	end

	table.insert (tout, '">');
	table.insert (tout, translit);
	if is_set (code) and not style then														
		table.insert (tout, "</span>");											-- 閉 span 標籤
	else
		table.insert (tout, "</span>");											
	end
	return table.concat (tout);
end


--[=[-------------------------< V A L I D A T E _ T E X T >---------------------------------------------------

此式查 args.text 之旨。無爽則報空串,有誤則報紕漏之辭。所覈者:文之闕如、撇號標識之乖謬。

斜體由 |italic= 變數所司,故 args.text 內斷不應有 ''斜體'' 或 '''''粗斜體''''' 之標識。

]=]

local function validate_text (template, args)
	if not is_set (args.text) then
		return make_error_msg ('文字闕如', args, template);
	end

	if args.text:find ("%f[\']\'\'\'\'%f[^\']") or args.text:find ("\'\'\'\'\'[\']+") then	-- 索四撇號或六撇號以上者
		return make_error_msg ('標記乖式', args, template);
	end

	local style = args.italic or args.italics;

	if ('unset' ~= style) and ('invert' ~=style) then
		if args.text:find ("%f[\']\'\'%f[^\']") or args.text:find ("%f[\']\'\'\'\'\'%f[^\']") then	-- 斜而不粗,或粗斜體
			return make_error_msg ('文含斜體標記', args, template);
		end
	end
end


--[[--------------------------< R E N D E R _ M A I N T >------------------------------------------------------

宣示護理之辭與類屬

]]

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">'}));	-- 啓 <span>
		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


--[[--------------------------< P R O T O _ P R E F I X >------------------------------------------------------

若乃原始語言,文前綴以星號。吾人於此為之,俾 make_text_span() 知之,使星號居斜體標識之外(若有斜體)。若文本已有星號,則不加。

]]

local function proto_prefix (text, language_name)
	if language_name:find ('^Proto%-') and not text:find ('^*') then			-- 屬原始語且未冠星號
		return table.concat ({'*', text});										-- 冠以星號
	end
	
	return text;
end


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

{{lang}} 之戶樞

本無庸於 {{lang}} {{#invoke:}} 設變數
	<includeonly>{{#invoke:lang|lang}}</includeonly>

諸變數皆受諸模板外框。

]]

function p.lang (frame)
	local args = getArgs(frame);
	local out = {};
	local language_name;														-- 司歸類
	local subtags = {};															-- IETF 之諸子籤
	local code;																	-- 語碼
	local msg;																	-- 紕漏之辭

	if args[1] and args.code then
		return make_error_msg ('鉏鋙:{{{1}}} 與 &#124;code=', args, 'lang');
	else
		args.code = args[1] or args.code;										-- 尚 args.code
	end

	if args[2] and args.text then
		return make_error_msg ('鉏鋙:{{{2}}} 與 &#124;text=', args, 'lang');
	else
		args.text = args[2] or args.text;										-- 尚 args.text
	end

	if nil == args.italic then													-- nil 時以 args.italic 司之
		args.italic = 'unset';
	end
	
	msg = validate_text ('lang', args);											-- 覈 |text= 俱在
	if is_set (msg) then														-- 遇紕漏則發
		return msg;
	end

	args.rtl = args.rtl == 'yes';												-- 化為真假:'yes' 報真,餘皆報假

	code, subtags.script, subtags.region, subtags.variant, subtags.private, msg = get_ietf_parts (args.code);	-- |script=, |region=, |variant= 皆不與焉,蓋宜併於 args.code

	if msg then
		return make_error_msg ( msg, args, 'lang');
	end

	args.italic, msg = validate_italic (args.italic, args.italics);
	if msg then
		return make_error_msg (msg, args, 'lang');
	end
	
	if is_set (subtags.script) then												-- 若設文字,蓋覆 rtl
		if in_array (subtags.script, lang_data.rtl_scripts) then
			args.rtl = true;													-- 乃右至左文
		else
			args.rtl = false;													-- 否
		end
	end

	args.code = format_ietf_tag (code, subtags.script, subtags.region, subtags.variant);	-- 依善式排布;私有者略去

	if is_set (subtags.private) and lang_data.override[table.concat ({code, '-x-', subtags.private})] then	
		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];							-- 語名繁多者,取其首
	end

	if 'invert' == args.italic then
		args.text = invert_italics (args.text)
	end

	-- 中文版特化部分:為非中文文字禁用繁簡轉換,中文文字與其他以漢字作為書寫系統之漢語方言文字啟用繁簡轉換。
	if 'zh' == code then
		args.text = args.text
	elseif 'gan' == code then
		args.text = args.text
	elseif 'wuu' == code then
		args.text = args.text
	elseif 'yue' == code then
		args.text = args.text
	elseif 'lzh' == code then
		args.text = args.text
	elseif 'hsn' == code then
		args.text = args.text
	else
		args.text = args.text
	end
	
	args.text = proto_prefix (args.text, language_name);						-- 原始語文綴星號

	table.insert (out, make_text_span (args.code, args.text, args.rtl, args.italic, args.size, language_name));
	table.insert (out, make_category (code, language_name, args.nocat));
	table.insert (out, render_maint(args.nocat));											-- 宣護理之屬

	return table.concat (out);													-- 畢其功
end


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

{{lang-xx}} 諸模板,惟語言代碼不可或缺。餘者皆宜書於模板呼求之中。凡有多端書寫之語,亦宜設 |script=。

為各模板擇其戶樞,俾知默認之字體。

正體:
	<includeonly>{{#invoke:lang|lang_xx_inherit|code=xx}}</includeonly>
斜體:
	<includeonly>{{#invoke:lang|lang_xx_italic|code=xx}}</includeonly>

餘變數受諸外框。

容納變數列下:
	|code = (必) IANA 語碼
	|script = IANA 文字碼;多書寫系統之語宜用;遇 |code= 之文字標籤則退讓 [尚未行]
	|region = IANA 地區碼
	|variant = IANA 變體碼
	|text = (必)示文
	|link = 布林假('no')則絕其語言之連結
	|rtl = 布林真('yes')則視為右至左語
	|nocat = 布林真('yes')則止其歸類;紕漏類不與焉
	|italic = 布林真('yes')作斜體;假('no')作正體;未設則從 initial_style_state
	|lit = 文本之直譯
	|label =	'none' 則悉抑標識(語名、'轉寫'、'直譯'等)
				其餘字串則代語名標識 - 自行連結之功亦隳
	
	於容納轉寫之模板:
	|translit = 轉寫文
	|translit-std = 轉譯基準
	|translit-script = ISO 15924 碼;退讓於 code

無名參量:
	{{{1}}}	文段
	{{{2}}}	轉寫文
	{{{3}}}	直譯文
餘者弗許。

]]

local function _lang_xx (frame)
	local args = getArgs(frame, {parentFirst= true});							-- 模板變數蓋覆 {{#invoke:}} 者
	local out = {};
	local language_name;														-- 司宣示、連結
	local category_name;														-- 同上,惟留其括號消歧義
	local subtags = {};															
	local code;																	

	local translit_script_name;													
	local translit;
	local translit_title;
	local msg;																	

	if args[1] and args.text then
		return make_error_msg ('鉏鋙:{{{1}}} 與 &#124;text=', args, 'lang-xx');
	else
		args.text = args[1] or args.text;										
	end
	
	msg = validate_text ('lang-xx', args);										-- 覈 |text= 且無斜體之乖
	if is_set (msg) then
		return msg;
	end

	if args[2] and args.translit then
		return make_error_msg ('鉏鋙:{{{2}}} 與 &#124;translit=', args, 'lang-xx');
	else
		args.translit = args[2] or args.translit								
	end
	
	if args[3] and (args.translation or args.lit) then
		return make_error_msg ('鉏鋙:{{{3}}} 與 &#124;lit= 或 &#124;translation=', args, 'lang-xx');
	elseif args.translation and args.lit then
		return make_error_msg ('鉏鋙:&#124;lit= 與 &#124;translation=', args, 'lang-xx');
	else
		args.translation = args[3] or args.translation or args.lit;				
	end

	if args.links and args.link then
		return make_error_msg ('鉏鋙:&#124;links= 與 &#124;link=', args, 'lang-xx');
	else
		args.link = args.link or args.links;									
	end

	args.rtl = args.rtl == 'yes';												

	code, subtags.script, subtags.region, subtags.variant, subtags.private, msg = get_ietf_parts (args.code, args.script, args.region, args.variant);	

	if msg then																	
		return make_error_msg (msg, args, 'lang-xx');
	end
	
	args.italic, msg = validate_italic (args.italic, args.italics);
	if msg then
		return make_error_msg (msg, args, 'lang-xx');
	end

	if nil == args.italic then													
		if is_set (subtags.script) then
			if 'latn' == subtags.script then
				args.italic = 'italic';											-- |script=Latn; 設斜體
			else
				args.italic = initial_style_state;								-- 非拉丁文;從 initial_style_state
			end
		else
			args.italic = initial_style_state;									-- 皆未設;從 initial_style_state
		end
	end
	
	if is_set (subtags.script) then												
		if in_array (subtags.script, lang_data.rtl_scripts) then
			args.rtl = true;													
		else
			args.rtl = false;													
		end
	end

	args.code = format_ietf_tag (code, subtags.script, subtags.region, subtags.variant);	
	
	if is_set (subtags.private) and lang_data.override[table.concat ({code, '-x-', subtags.private})] then	
		language_name = lang_data.override[table.concat ({code, '-x-', subtags.private})][1];				
	elseif lang_data.override[args.code:lower()] then							
		language_name = lang_data.override[args.code:lower()][1];				
	elseif lang_data.override[code] then										
		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];						
		end
	else																		-- 案:取變體名果善乎?
		if lang_name_table.variant[subtags.variant] then						
			language_name = lang_name_table.variant[subtags.variant]['descriptions'][1];	
		end
	end

	category_name = language_name;												-- 類名留括號
	language_name = language_name:gsub ('%s+%b()', '');							-- 語名去括號與消歧義

	if args.label then
		if 'none' ~= args.label then
			table.insert (out, table.concat ({args.label, ':'}));				-- 自定標識
		end
	else
		if 'no' == args.link then
			table.insert (out, language_name);									-- 語名無連結
		else
			table.insert (out, make_wikilink (language_name));				-- 語名設連
		end
		table.insert (out, ':');												-- 割斷
	end

	if 'invert' == args.italic then
		args.text = invert_italics (args.text)
	end

	-- 中文版特化部分:為非中文文字禁用繁簡轉換,中文文字與其他以漢字作為書寫系統之漢語方言文字啟用繁簡轉換。
	if 'zh' == code then
		args.text = args.text
	elseif 'gan' == code then
		args.text = args.text
	elseif 'wuu' == code then
		args.text = args.text
	elseif 'yue' == code then
		args.text = args.text
	elseif 'lzh' == code then
		args.text = args.text
	elseif 'hsn' == code then
		args.text = args.text
	else
		args.text =  args.text
	end
	
	args.text = proto_prefix (args.text, language_name);						

	table.insert (out, make_text_span (args.code, args.text, args.rtl, args.italic, args.size))

	if is_set (args.translit) and not p.is_latn (args.text) then					-- 有轉寫且文非全拉丁
		args.translit = args.translit
		table.insert (out, ',');												-- 以逗號間之
		if 'none' ~= args.label then
			table.insert (out, '<small>');
			if lang_name_table.script[args['translit-script']] then				
				translit_script_name = lang_name_table.script[args['translit-script'][1]];
			else
				translit_script_name = language_name;							-- 否則退求語名
			end
			translit_title = mw.title.makeTitle (0, table.concat ({translit_script_name, '羅馬化'}));		
			if translit_title.exists and ('no' ~= args.link) then
				table.insert (out, make_wikilink ((translit_script_name or language_name) .. '羅馬化', '轉寫'));	
			else
				table.insert (out, '轉寫');	
			end
			table.insert (out, ':</small>');								
		end
		
		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 ({'轉譯基準不允:\'', args['translit-std'] or '[闕如]'}), args, 'lang-xx');
		end
	end
	
	if is_set (args.translation) then											-- 直譯
		table.insert (out, ',');
		if 'none' ~= args.label then
			table.insert (out, '<small>');
			if 'no' == args.link then
				table.insert (out, '直譯');
			else
				table.insert (out, make_wikilink ('直譯'));
			end
			table.insert (out, ":</small>");
		end
		table.insert (out, table.concat ({args.translation}));	
	end
	
	table.insert (out, make_category (code, category_name, args.nocat));
	table.insert (out, render_maint(args.nocat));								

	return table.concat (out);													
end


--[[--------------------------< L A N G _ X X _ I T A L I C >--------------------------------------------------

呼 lang_xx_italic() 諸 {{lang-xx}} 模板之戶樞。設初始字體爲斜體。

]]

function p.lang_xx_italic (frame)
	initial_style_state = 'italic';
	return _lang_xx (frame);
end


--[[--------------------------< L A N G _ X X _ I N H E R I T >------------------------------------------------

呼 lang_xx_inherit() 諸模板之戶樞。設初始字體爲承繼(inherit)。

]]

function p.lang_xx_inherit (frame)
	initial_style_state = 'inherit';
	return _lang_xx (frame);
end


--[[--------------------------< N A M E _ F R O M _ C O D E >--------------------------------------------------

若 IETF 語言標籤允當,則報其語言之名;否則報空串。

{{lang}} 與 {{lang-xx}} 所容之合碼,咸爲此函式所援。

]]

function p.name_from_code (frame)
	local subtags = {};															
	local raw_code = (frame.args and frame.args[1]) or frame;					
	local code;																	
	local msg;																	
	local language_name = '';

	code, subtags.script, subtags.region, subtags.variant, subtags.private, msg = get_ietf_parts (raw_code);
	if msg then
		return '';
	end

	if lang_data.override[raw_code:lower()] then								
		language_name = lang_data.override[raw_code:lower()][1];
	elseif lang_data.override[code] then										
		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];						
		end
	else																		
		if lang_name_table.variant[subtags.variant] then						
			language_name = lang_name_table.variant[subtags.variant]['descriptions'][1];	
		end
	end

	language_name = language_name:gsub ('%s+%b()', '');							

	return language_name;

end


--[[--------------------------< T R A N S L >------------------------------------------------------------------

擬代 {{transl}} 模板者

]]

function p.transl (frame)
	local args = getArgs(frame);												-- 無 {{#invoke:}} 參量
	local title_table = lang_data.translit_title_table;							
	local language_name;														
	local translit;																
	local script;																
	local msg;																	

	if is_set (args[3]) then													-- 形如 {{transl|code|standard|text}}
		args.text = args[3];													
		args.translit_std = args[2] and args[2]:lower();						

		if not title_table[args.translit_std] then
			return make_error_msg (table.concat ({'轉譯基準莫辨:', args.translit_std}), args, 'transl');
		end
	else
		if is_set (args[2]) then												-- 形如 {{transl|code|text}}
			args.text = args[2];												
		else
			if args[1] and args[1]:match ('^%a%a%a?%a?$') then					-- args[2] 既無,args[1] 果爲語碼乎?
				return make_error_msg ('文字闕如', args, 'transl');				-- args[1] 爲碼,是缺文字也
			else
				args.text = args[1];											-- args[1] 非碼,俟報紕漏
				return make_error_msg ('語文示號闕如', args, 'transl');
			end
		end
	end

	if is_set (args[1]) then													-- IANA 語碼或 ISO 15924 碼
		if args[1]:match ('^%a%a%a?%a?$') then									
			args.code = args[1]:lower();										
		else
			return make_error_msg (table.concat ({'語文示號莫辨:', args[1]}), args, 'transl');	
		end
	else
		return make_error_msg ('語文示號闕如', args, 'transl');			
	end

	args.italic, msg = validate_italic (args.italic, args.italics);
	if msg then
		return make_error_msg (msg, args, 'transl');
	end
	
	if 'italic' == args.italic then												-- 遇 |italic=yes
		args.italic = nil;														-- 置為 nil 
	end

	if lang_data.override[args.code] then										
		language_name = lang_data.override[args.code][1];
	elseif lang_name_table.lang[args.code] then									
		language_name = lang_name_table.lang[args.code][1];
	elseif lang_name_table.script[args.code] then								
		language_name = lang_name_table.script[args.code][1];
		script = args.code;														
		args.code = '';															
	else
		return make_error_msg (table.concat ({'語文示號莫辨:', args.code}), args, 'transl');	
	end
																				
	return make_translit (args.code, language_name, args.text, args.translit_std, script, args.italic);
end


return p;