require('strict');  
----------------------------< E R R O R _ M E S S A G E S >--------------------------------------------------  
error messaging; keys to this table are the template base names:
'nihongo', 'nihongo3', 'nihongo krt', 'nihongo foot' → 'nihongo' etc  
local err_msg = {
['nihongo'] = 'Japanese or romaji text required',
['hanyu'] = 'Chinese or pinyin text required',
}  
local err_cat = {
['nihongo'] = '',
['hanyu'] = '',
}  
----------------------------< C O N F I G U R A T I O N >----------------------------------------------------  
configuration setting for the various templates. keys to this table are the template names without spacing  
local cfg = {
['nihongo'] = {
tag = 'ja',
system = 'hepburn',
system_link = 'Hepburn',
err_msg = err_msg.nihongo,
err_cat = err_cat.nihongo,
},
['nihongo3'] = {
tag = 'ja',
system = 'hepburn',
err_msg = err_msg.nihongo,
err_cat = err_cat.nihongo,
},
['nihongo krt'] = {
tag = 'ja',
system = 'hepburn',
err_msg = err_msg.nihongo,
err_cat = err_cat.nihongo,
},
['nihongo foot'] = {
tag = 'ja',
system = 'hepburn',
system_link = 'Hepburn',
err_msg = err_msg.nihongo,
err_cat = err_cat.nihongo,
},
['hanyu'] = {
tag = 'zh',
system = 'pinyin',
system_link = 'Pinyin',
err_msg = err_msg.hanyu,
err_cat = err_cat.hanyu,
},
['hanyu3'] = {
tag = 'zh',
system = 'pinyin',
err_msg = err_msg.hanyu,
err_cat = err_cat.hanyu,
},
['hanyu hrt'] = {
tag = 'zh',
system = 'pinyin',
err_msg = err_msg.hanyu,
err_cat = err_cat.hanyu,
},
['hanyu foot'] = {
tag = 'zh',
system = 'pinyin',
system_link = 'Pinyin',
err_msg = err_msg.hanyu,
err_cat = err_cat.hanyu,
},
}  
----------------------------< E R R O R _ M E S S A G E >----------------------------------------------------  
Creates an error message for , , , and when these template are missing <japanese>
or <romaji> inputs; names the offending template, links to template page, and adds article to Category:Nihongo template errors  
local function error_message (template)
local msg = {'<span>error: : ');
table.insert (msg, cfg[template].err_msg);
table.insert (msg, ' (help)</span>');
if 0 == mw.title.getCurrentTitle.namespace then
table.insert (msg, cfg[template].err_cat);
end  
return table.concat (msg);
end  
--[[--------------------------< R E N D E R E R >--------------------------------------------------------------  
Shared support function for nihingo, nihongo3, and nihongo_foot. Calculates an index into formatting{}
from set/unset parameters:
args[1] (english text) has a value of 8 (set) or 0 (unset)
args[2] (native text) has a value of 4
args[3] (romanized text) has a value of 2
args[4] (extra) has a value of 1
index, the sum of these values, gets the appropriate format string from formatting{} table with associated values
from the formatting[index][2] table  
local function renderer (args, formatting, extra2)
local output;
local index = 0; -- index into formatting{}
local param_weight = {8, 4, 2, 1}; -- binary parameter weights: [1] = english (8), [2] = japanese (4), [3] = romaji (2), [4] = extra (1)  
for i=1, 5 do -- spin through args[1] – args[4]
index = index + (args[i] and param_weight[i] or 0); -- calculate an index into formatting{}
end  
output = (0 ~= index) and string.format (formatting[index][1] and formatting[index][1], formatting[index][2][1], formatting[index][2][2], formatting[index][2][3], formatting[index][2][4]) or nil;  
if extra2 then -- always just attached to the end (if there is an end) so not part of formatting{}
output = output and (output .. ' ' .. extra2) or '<5p4n>' .. extra2; -- <5p4n> and </5p4n>: place holders for font-weight style spans; akin to stripmarkers, to be replaced
end -- (nihongo and nihongo3) or removed (nihongo foot)  
return output and (output .. '</5p4n>') or ''; -- where there is output, add secret tag close
end  
----------------------------< R O M A N I Z E D _ K E R N >--------------------------------------------------  
Add kerning when first or last character of romanized text contacts adjacent opening or closing paranthesis  
In this example, without kerning, the romanized characters 'j' and 'V' are italicized so will contact the parentheses
(<i lang=\"ja-Latn\" title=\"Hepburn transliteration\">jV</i>)  
<ret_string> is the formatted template output (except that the magic string '<5p4n>' has not yet been replaced)
<romanized> is the return from lang_module._transl so is not wrapped in parentheses  
local function romanized_kern (ret_string, romanized)
if not romanized or ('' == romanized) then -- if romanized not set
return ret_string; -- then we're done
end  
local romanized_text = romanized:gsub ('%b<>', *):gsub ('\'\'+', *):gsub ('%[%[', *):gsub ('%]%]', *); -- strip html tags  
romanized = romanized:gsub ('([%(%)%.%%%+%-%*%?%[%^%$%]])', '%%%1'); -- escape lua pattern characters  
local romanized_has_leading_paren = ret_string:match ('%(' .. romanized); -- has a value if (<romanized>; nil else
local romanized_has_trailing_paren = ret_string:match (romanized .. '%)'); -- has a value if <romanized>); nil else  
local kern_lead_pattern = '^[jpy]'; -- list of characters that when italicized contact unitalicized leading parenthesis
local kern_tail_pattern = '[dfijkltCEFHIJKMNPR-Z\'"%?!%]]$'; -- list of characters that when italicized contact unitalicized trailing parenthesis  
local kern_right = '<span>(</span>%1'; -- %1 is <romanized> capture
local kern_left = '%1<span>)</span>'; -- %1 is <romanized> capture  
if romanized_has_leading_paren and romanized_text:match (kern_lead_pattern) then
ret_string = ret_string:gsub ('%((' .. romanized .. ')', kern_right); -- replace plain '(' with kerned '('; <romanized> included here to ensure that the correct '(' is kerned
end
if romanized_has_trailing_paren and romanized_text:match (kern_tail_pattern) then
ret_string = ret_string:gsub ('(' .. romanized .. ')%)', kern_left); -- replace plain ')' with kerned ')'; <romanized> included here to ensure that the correct ')' is kerned
end  
return ret_string; -- done
end  
----------------------------< C O M M O N >------------------------------------------------------------------  
Common support for and  
render order: is translated (English), native, romanized  
<template> is used to select the appropriate cfg table  
local function common (, template)
local lang_module = require ('Module:Lang' .. (:getTitle:match ('/sandbox') or '')); -- if this module is the sandbox, use Module:lang/sandbox; Module:Lang else  
local args = require ('Module:Arguments').getArgs ;  
local english, native, romanized, extra, extra2 = args[1], args[2], args[3], args.extra or args[4], args.extra2 or args[5]; -- meaningful names
args[4] = extra or args[4]; -- ensure that extra is 'positional' for use by renderer  
local lead = 'yes' == args.lead; -- make boolean  
if not (native or romanized) then -- not present, return an error message
return error_message (template);
end
if native then
native = lead and lang_module._lang_xx_inherit ({['code']=cfg[template].tag, native, ['template']=template}) or lang_module._lang ({cfg[template].tag, native, ['template']=template}); -- add ja script with/without language prefix
end
if romanized then
romanized = (lead and english and (cfg[template].system_link .. ': ') or '') .. lang_module._transl ({'ja', cfg[template].system, romanized}) or nil;
end  
local formatting = { -- <5p4n> and </5p4n>: place holders for font-weight style spans; akin to stripmarkers, replaced before function returns
{'<5p4n>(%s)', {extra}}, -- 1 - (extra)
{'%s<5p4n>', {romanized}}, -- 2 - romanized
{'%s<5p4n> (%s)', {romanized, extra}}, -- 3 - romanized (extra)
{'<5p4n>(%s)', {native}}, -- 4 - native
{'<5p4n>(%s, %s)', {native, extra}}, -- 5 - (native, extra)
{'%s<5p4n> (%s)', {romanized, native}}, -- 6 - romanized (native)
{'%s<5p4n> (%s, %s)', {romanized, native, extra}}, -- 7 - romanized (native, extra)
{'%s<5p4n>', {english}}, -- 8 - english
{'%s<5p4n> (%s)', {english, extra}}, -- 9 - english (extra)