Modulo:ja

El Vikivortaro
 MODULO
Memtesto ne disponeblas.

local export = {}

local titleObj = mw.title.getCurrentTitle()
local pagename = titleObj.text
local namespace = titleObj.nsText

local find = mw.ustring.find
local length = mw.ustring.len
local trim = mw.text.trim
local split = mw.text.split
local sub, gsub = mw.ustring.sub, mw.ustring.gsub
local match, gmatch = mw.ustring.match, mw.ustring.gmatch
local to_cp, to_char = mw.ustring.codepoint, mw.ustring.char

local Jpan = require("Module:scripts").getByCode("Jpan")
local lang = require("Module:languages").getByCode("ja")

-- note that arrays loaded by mw.loadData cannot be directly used by gsub
local data = mw.loadData("Module:ja/data")

-- Unicode normalization often converts these to the corresponding CJK Unified Ideographs characters
local compat_ideo = mw.ustring.char(0xF900) .. "-" .. mw.ustring.char(0xFAD9)

export.data = {
 joyo_kanji = data.joyo_kanji,
 jinmeiyo_kanji = data.jinmeiyo_kanji,
 grade1 = data.grade1,
 grade2 = data.grade2,
 grade3 = data.grade3,
 grade4 = data.grade4,
 grade5 = data.grade5,
 grade6 = data.grade6
}

local function track(code)
 if type(code) ~= "string" then
  error("The track function requires a string as argument.")
 end
 require("Module:debug").track("ja/" .. code)
end

local function enlarge(text)
 return '<span style="font-size: 1.2em">' .. text .. '</span>'
end

local function change_codepoint(added_value)
 return function(char)
  return to_char(to_cp(char) + added_value)
 end
end

function export.hira_to_kata(text)
 if type(text) == "table" then text = text.args[1] end

 return (gsub(text, '[ぁ-ゖ]', change_codepoint(96)))
end

function export.kata_to_hira(text)
 if type(text) == "table" then text = text.args[1] end

 return (gsub(text, '[ァ-ヶ]', change_codepoint(-96)))
end

function export.fullwidth_to_halfwidth(text)
 if type(text) == "table" then text = text.args[1] end

 text = gsub(text, ' ', ' ')
 return (gsub(text, '[!-~]', change_codepoint(-65248)))
end

function export.kana_to_romaji(text, options)
 -- options: no_diacritics, keep_period, hist, phonetic
 local str_find = string.find
 local str_gsub = string.gsub

 if type(text) == "table" then
  text = text.args[1]
 end

 if not options then options = {} end

 local tracking_has_percent = str_find(text, '%%')
 local text_old = ''
 if not options.hist then
  text_old = trim(require('Module:ja/k2r-old').kana_to_romaji(text, options.no_diacritics, options.keep_period))
 end

 -- conversions
 if not options.phonetic then
  text = gsub(text, '(%-)([でデとト]?)([はハ])$', '%1㊟㈛㊟%2%3') -- は, では or とは as suffix and appearing at the end of string
  text = gsub(text, '(%-)([でデとト]?)([はハ]) ', '%1㊟㈛㊟%2%3 ') -- は, では or とは as suffix and appearing mid-sentence
 end

 text = str_gsub(text, '%', '㊟㌫㊟') -- at [[見込む]], for example; avoid collision with % used in our ruby syntax
 text = str_gsub(text, '\'\'\'', '㊟⒝㊟')
 text = str_gsub(text, '<u>', '㊟㋑⒰㊟')
 text = str_gsub(text, '</u>', '㊟㋺⒰㊟')

 local text_styling = "㊟[㋑㋺⒝⒰]+㊟"

 -- avoid tampering with existing latin text: store it away
 local escape = {}
 local id = 0
 for latin in string.gmatch(text, "[a-z]+") do
  escape[id] = latin
  text = str_gsub(text, latin, "㊟㊕㊕㊟" .. id .. "㊟㊕㊕㊟")
  id = id + 1
 end

 -- special preformatting
 text = str_gsub(text, 'ヶげつ', 'かげつ')
 text = gsub(text, 'ヶ(' .. text_styling .. ')げつ', 'か%1げつ') -- 「'''ヶ'''げつ」
 text = str_gsub(text, 'ヶ', 'が')
 text = str_gsub(text, 'ヵ', 'か')
 text = gsub(text, '(.)ゝ', '%1%1')
 text = gsub(text, '(.)ゞ', function(char) return mw.ustring.toNFC(char .. char .. '゙') end) -- unicode hax

 -- [[Wiktionary:Grease_pit/2017/May#Formatting_for_individual_Japanese_readings]]
 if options.hist then
  text = gsub(text, '[やゆよわゐゑを]', '㊟⒳㊟%0')
  text = gsub(text, '.',
   {
    ['つ'] = 'tu',
    ['ち'] = 'ti',
    ['づ'] = 'du',
    ['ぢ'] = 'di',
    ['し'] = 'si',
    ['じ'] = 'zi',
    ['ゐ'] = 'wi',
    ['を'] = 'wo',
   }
  )
 end

 text = export.hira_to_kata(text)
 text = gsub(text, '.', data.kr)
 text = export.fullwidth_to_halfwidth(text)

 if options.hist then
  text = str_gsub(text, 'oo', 'o.o')
  text = str_gsub(text, 'ou', 'o.u')
  text = str_gsub(text, 'h', 'f')

  local old = text
  text = str_gsub(text, 'i㊟⒳㊟y', 'y') -- くゐやう kwyau
  text = str_gsub(text, '([kg])u㊟⒳㊟w', '%1w')
  if old ~= text then
   --[=[
    There may be cases in which i or u is deleted incorrectly, and a
    period should be inserted.
    "Syncope" isn't quite accurate, as there wasn't a sound change.
    It's just an orthographic convention.
    [[Special:WhatLinksHere/Template:tracking/ja/mora syncope]]
   ]=]
   mw.log(str_gsub(old, '㊟⒳㊟', '')  .. ' → ' .. str_gsub(text, '㊟⒳㊟', '') )
   track('mora syncope')
  end

  text = str_gsub(text, '㊟⒳㊟', '') -- ゑつ wetsu
 end

 -- markup
 text = str_gsub(text, '%%', '.') -- ruby "percent sign" syntax
 text = gsub(text, '([ッ¤])%.', '%1') -- 「し を ぼっ.す」; 「るい%じん%えん」→「rui.jin¤.en¤」

 -- 「テェェェ」→「テェーー」 (avoid funky romaji effected by the "(テュ→)teユ→tyu" line below)
 local kogaki_vowels = {'ァ','ィ','ゥ','ェ','ォ'}
 for _, char in ipairs(kogaki_vowels) do
  text = gsub(text, '('..char..')('..char..'+)', function(a,b) return a .. mw.ustring.rep('ー', length(b)) end)
 end

 -- (ゲェ→)geェ→gee (note that this causes things like ウゥ→ū and ギィ→gī)
 text = gsub(text, '[aiueo][ァィゥェォ]', {['aァ']='aa',['iィ']='ii',['uゥ']='uu',['eェ']='ee',['oォ']='oo',})

 -- (クヮ→)kuヮ→kwa
 text = gsub(text, '[u]([ヮ])', {['ヮ']='wa',})

 -- (クァ→)kuァ→kwa, (トァ→)toァ→twa, (ウィ→)uィ→wi
 text = gsub(text, '[uo]([ァィェォ])', {['ァ']='wa',['ィ']='wi',['ェ']='we',['ォ']='wo',})
 if not options.hist then
  -- (ツァ→)cwa→ca
  text = str_gsub(text, '([fvcsz])w', '%1')
 end

 -- (テュ→)teユ→tyu, (ギェ→)giェ→gye
 text = gsub(text, '[aiueo]([ャュェョ])', {['ャ']='ya',['ュ']='yu',['ェ']='ye',['ョ']='yo',})
 -- (ジュ→)jyu→ju
 text = gsub(text, '([xjq])y', '%1')

 -- (ティ→)teィ→ti (essentially forget about the vowel in between)
 text = gsub(text, '[aiueo]([ァィゥェォ])', {['ァ']='a',['ィ']='i',['ゥ']='u',['ェ']='e',['ォ']='o',})

 -- chouonpu and sokuon
 while str_find(text, '[aiueo]ー') or str_find(text, 'ッ *[bcdfghjklmnpqrstvwxyz]') or find(text, 'ッ' .. text_styling .. '[bcdfghjklmnpqrstvwxyz]') do
  text = str_gsub(text, '([aiueo])ー', '%1%1')
  text = str_gsub(text, 'ッ( *)([bcdfghjklmnpqrstvwxyz])', '%2%1%2')
  text = gsub(text, 'ッ(' .. text_styling .. ')([bcdfghjklmnpqrstvwxyz])', '%2%1%2')
 end
 -- deal with leftover sokuon not used as geminate
 text = str_gsub(text, 'ッ', '&apos;')

 -- (ん→)n¤
 text = str_gsub(text, '¤([aiueoy])', "'%1")
 text = str_gsub(text, '¤', '')

 -- は, では, とは, へ
 if not options.phonetic and str_find(text, "h[ae]") then
  for i, v in ipairs{
   { "ha", "wa" },
   { "toha", "towa" },
   { "deha", "dewa" },
   { "he", "e" }
  } do
   local thingy = '[^a-z.㊟-]' -- not sure what this should be named
   text = gsub(text, "(" .. thingy .. ")" .. v[1] .. "(" .. thingy .. ")", "%1" .. v[2] .. "%2")
   text = gsub(text, "(" .. thingy .. ")" .. v[1] .. "$", "%1" .. v[2])
   text = gsub(text, "^" .. v[1] .. "(" .. thingy .. ")", v[2] .. "%1")
   if find(text, text_styling) then
    text = gsub(text, "(" .. thingy .. ")" .. v[1] .. "(" .. text_styling .. thingy ..")", "%1" .. v[2] .. "%2")
    text = gsub(text, "(" .. thingy .. ")" .. v[1] .. "(" .. text_styling .. ")$", "%1" .. v[2] .. "%2")
    text = gsub(text, "(" .. thingy .. text_styling .. ")" .. v[1] .. "(" .. text_styling .. thingy ..")", "%1" .. v[2] .. "%2")
    text = gsub(text, "(" .. thingy .. text_styling .. ")" .. v[1] .. "(" .. text_styling .. ")$", "%1" .. v[2] .. "%2")
   end
  end
 end
 -- change only when
 --   ① not flanked by a-z or a period ("^sore wa nani$", "^hyappou no .he hitotsu$")
 --   ② at the end of the string and not preceded by a-z or a period ("^are wa$")
 --   ③ at the beginning of the string and not followed by a-z or a period ("^he ikou$") [not sure this is actually necessary, but I suppose it is consistent with ②]
 -- this also means that "^ha$" becomes "ha"
 -- period can be used next to the kana (either side) to force the "dumb" romanization (i.e. "ha", "he")

 -- fix sh, ch, ts
 local function handle_digraphs(geminate, intervening, main, following)
  --「めちゃ」→「mecha」
  --「めっちゃ」→「metcha」
  --「めっっちゃ」→「mettcha」
  local corresp_geminate_form = {['x']='s',['q']='t',['c']='t'}
  local corresp_main = {['x']='sh',['q']='ch',['c']='ts'}

  local geminate_repl, main_repl

  -- So as not to convert ch to tsh.
  if not following or main .. following ~= "ch" then
   main_repl = corresp_main[main]
  end

  if geminate ~= "" then
   geminate_repl = mw.ustring.rep(corresp_geminate_form[main], length(geminate))
  end

  return (geminate_repl or geminate) .. (intervening or "") .. (main_repl or main) .. (following or "")
 end

 local function handle_digraphs2(geminate, main, following)
  return handle_digraphs(geminate, nil, main, following)
 end

 text = gsub(text, '([xqc]+)(' .. text_styling .. ')([xqc])', handle_digraphs)
 text = gsub(text, '([xqc]*)([xqc])(.?)', handle_digraphs2)

 -- macrons
 -- Will cause problems if combined vowel-macron characters are used below.
 if not options.no_diacritics then
  if not options.phonetic then
   text = str_gsub(text, 'ou', 'ō')
  end
  local macron = to_char(0x304)
  text = str_gsub(
   text,
   '([oaeui])%1',
   '%1' .. macron
  )
 end

 -- remove markup and convert real periods
 if not options.keep_period then
  text = str_gsub(text, '%.', '')
  text = str_gsub(text, '。', '◆.◇')
 end

 --
 text = str_gsub(text, '◇◆', '')
 text = str_gsub(text, '◆◇', '')
 text = str_gsub(text, ' *◆ *', '')
 text = str_gsub(text, ' *◇ *', ' ')

 -- restore latin text
 text = str_gsub(text, "㊟㊕㊕㊟(%d+)㊟㊕㊕㊟", function(id) return escape[tonumber(id)] end)

 -- clean up spaces
 text = trim(text)
 text = str_gsub(text, ' +', ' ')

 -- remove double ampersands used in ruby
 text = str_gsub(text, '&&(.-)&&', '%1')

 -- uppercase markup
 text = str_gsub(text, "(%^)(㊟⒝㊟)", "%2%1") -- move ^ to an effective position if placed before bold markup
 text = str_gsub(text, "(%^)( )", "%2%1") -- same but with spaces
 text = gsub(text, '%^(.)', mw.ustring.upper) -- uppercase conversion

 -- clean up spaces again
 text = str_gsub(text, ' +', ' ')

 -- conversions
 text = str_gsub(text, '㊟⒝㊟', '\'\'\'')
 text = str_gsub(text, '㊟㋑⒰㊟', '<u>')
 text = str_gsub(text, '㊟㋺⒰㊟', '</u>')
 text = str_gsub(text, '㊟㈛㊟', '')
 text = str_gsub(text, '㊟㌫㊟', '%%')

 -- unicode NFC
 text = mw.ustring.toNFC(text)

 -- comparison with old "to romaji" code
 if not options.hist then
  -- comparison with old kana_to_romaji() code
  text_old = str_gsub(text_old, '%(ba%)', ' (ba)') -- avoid flooding the tracking template with na-adjectives. ← this *is* a problem though...
  if text ~= text_old then
   if mw.ustring.lower(text) == mw.ustring.lower(text_old) then
    track('k2r diff caps')
   elseif str_find(text_old, 'ッ') then
    track('k2r diff w xtu')
   elseif tracking_has_percent then
    track('k2r diff pc')
   elseif gsub(text, '%s*([“”!?])%s*', '%1') == gsub(text_old, '%s*([”!?])%s*', '%1') then
    track('k2r diff punc spacing')
   else
    track('k2r diff')
   end
   mw.log('new]' .. text .. '[')
   mw.log('old]' .. text_old .. '[')
  end
 end

 if find(text, '[ぁ-ー]') then
  track('k2r failure')
 end

 return text
end

-- removes spaces and hyphens from input
-- intended to be used when checking manual romaji to allow the
-- insertion of spaces or hyphens in manual romaji without appearing "wrong"
function export.rm_spaces_hyphens(f)
 local text = type(f) == 'table' and f.args[1] or f
 text = gsub(text, ' ', '')
 text = gsub(text, '-', '')
 text = gsub(text, '%.', '')
 text = gsub(text, '&nbsp;', '')
 text = gsub(text, '\'', '')
 return text
end

function export.romaji_to_kata(f)
 local text = type(f) == 'table' and f.args[1] or f
 text = gsub(text, '.', data.rd)
 text = gsub(text, 'kk', 'ッk')
 text = gsub(text, 'ss', 'ッs')
 text = gsub(text, 'tt', 'ッt')
 text = gsub(text, 'pp', 'ッp')
 text = gsub(text, 'bb', 'ッb')
 text = gsub(text, 'dd', 'ッd')
 text = gsub(text, 'gg', 'ッg')
 text = gsub(text, 'jj', 'ッj')
 text = gsub(text, 'tc', 'ッc')
 text = gsub(text, 'tsyu', 'ツュ')
 text = gsub(text, 'ts[uoiea]', {['tsu']='ツ',['tso']='ツォ',['tsi']='ツィ',['tse']='ツェ',['tsa']='ツァ'})
 text = gsub(text, 'sh[uoiea]', {['shu']='シュ',['sho']='ショ',['shi']='シ',['she']='シェ',['sha']='シャ'})
 text = gsub(text, 'ch[uoiea]', {['chu']='チュ',['cho']='チョ',['chi']='チ',['che']='チェ',['cha']='チャ'})
 text = gsub(text, "n[uoiea']?", {['nu']='ヌ',['no']='ノ',['ni']='ニ',['ne']='ネ',['na']='ナ'})
 text = gsub(text, '[wvtrpsnmlkjhgfdbzy][yw]?[uoiea]', data.rk)
 text = gsub(text, "n'?", 'ン')
 text = gsub(text, 'u', 'ウ')
 text = gsub(text, 'o', 'オ')
 text = gsub(text, 'i', 'イ')
 text = gsub(text, 'e', 'エ')
 text = gsub(text, 'a', 'ア')
 return text
end

-- expects: any mix of kanji and kana
-- determines the script types used
-- e.g. given イギリス人, it returns Kana+Hani
function export.script(f)
 text, script = type(f) == 'table' and f.args[1] or f, {}

 if find(text, '[ぁ-ゖ]') then
  table.insert(script, 'Hira')
 end
 -- TODO: there are two kanas.  This should insert Kata.
 if find(text, '[ァ-ヺー]') then
  table.insert(script, 'Kana')
 end
 -- 一 is unicode 4e00, previously used 丁 is 4e01
 if find(text, '[㐀-䶵一-鿌' .. compat_ideo .. '𠀀-𯨟]') then
  table.insert(script, 'Hani')
 end
 -- matching %a should have worked but matched the end of every string
 if find(text, '[a-zA-ZāēīōūĀĒĪŌŪa-zA-Z]') then
  table.insert(script, 'Romaji')
 end
 if find(text, '[0-90-9]') then
  table.insert(script, 'Number')
 end
 if find(text, '[〆々]') then
  table.insert(script, 'Abbreviation')
 end

 return table.concat(script, '+')
end

-- when counting morae, most small hiragana belong to the previous mora,
-- so for purposes of counting them, they can be removed and the characters
-- can be counted to get the number of morae.  The exception is small tsu,
-- so data.nonmora_to_empty maps all small hiragana except small tsu.
function export.count_morae(text)
 if type(text) == "table" then
  text = text.args[1]
 end
 -- convert kata to hira (hira is untouched)
 text = export.kata_to_hira(text)
 -- remove all of the small hiragana such as ょ except small tsu
 text = gsub(text,'.',data.nonmora_to_empty)
 -- remove zero-width spaces
 text = gsub(text, '‎', '')
 -- return number of characters, which should be the number of morae
 return length(text)
end

-- accepts: any mix of kana
-- returns: a hiragana sort key designed for WMF software
-- this is like sort() but doesn't return |sort=sortkey,
-- just the sort key itself, but unlike sort(), this
-- replaces the long vowel mark with its vowel
function export.jsort(text)
 if type(text) == "table" then
  text = text.args[1]
 end
 local textsub = ''
 local convertedten = ''
 local result = ''
 local len = 1

 -- remove western spaces, hyphens, and periods
 -- diff=41967612: also remove caret
 text = gsub(text, '[ %-%.%^]', '')

 text = export.kata_to_hira(text)

 -- if the first character has dakuten, replace it with the corresponding
 -- character without dakuten and add an apostrophe to the end, e.g.
 -- がす > かす'
 if gsub(sub(text,1,1),'.',data.dakuten) == '' then
  len = length(text)
  textsub = sub(text,2,len)
  convertedten = gsub(sub(text,1,1),'.',data.tenconv)
  text = (convertedten .. textsub .. "'")
 else
  -- similar thing but with handuken and two apostrophes, e.g. ぱす -> はす''
  if gsub(sub(text,1,1),'.',data.handakuten) == '' then
   len = length(text)
   textsub = sub(text,2,len)
   convertedten = gsub(sub(text,1,1),'.',data.tenconv)
   text = (convertedten .. textsub .. "''")
  end
 end
 -- replace the long vowel mark with the vowel that it stands for
 for key,value in pairs(data.longvowels) do
  text = gsub(text,key,value)
 end
 return text
end

-- returns a sort key with |sort= in front, e.g.
-- |sort=はつぐん' if given ばつぐん
function export.sort(f)
 local text = type(f) == 'table' and f.args[1] or f
 local textsub = ''
 local convertedten = ''
 local result = ''
 local len = 1
 local kyreplace = ''
 kyreplace = gsub(text,'[ァ-ヺ]', '')
 if kyreplace == '' then
  result = ('|' .. 'sort' .. '=')
 end
 text = export.kata_to_hira(text)
 if gsub(sub(text,1,1),'.',data.dakuten) == '' then
  if kyreplace == '' then else result = ('|' .. 'sort' .. '=') end
  len = length(text)
  textsub = sub(text,2,len)
  convertedten = gsub(sub(text,1,1),'.',data.tenconv)
  result = (result .. convertedten .. textsub .. "'")
 else
  if gsub(sub(text,1,1),'.',data.handakuten) == '' then
   if kyreplace == '' then else result = ('|' .. 'sort' .. '=') end
   len = length(text)
   textsub = sub(text,2,len)
   convertedten = gsub(sub(text,1,1),'.',data.tenconv)
   result = (result .. convertedten .. textsub .. "''")
  else
   if kyreplace == '' then
    result = (result .. text)
   end
  end
 end
 return result
end

-- returns the "stem" of a verb or -i adjective, that is the term minus the final character
function export.definal(f)
 return sub(f.args[1],1,(length(f.args[1])-1))
end

function export.remove_ruby_markup(text)
 return string.gsub(text, "[%^%-%. %%]", "")
end

-- see also Template:JAruby
-- meant to be called from another module
function export.add_ruby_backend(term, kana, from_ja_link)
 if term == kana then
  return enlarge(term)
 end
 local pattern = ""
 -- holds the whole segments of markup enclosed in <ruby>...</ruby>
 local ruby_markup = {}
 -- range of kana: '[ぁ-ゖァ-ヺ]'
 -- nonkana: [^ぁ-ゖァ-ヺ]
 local kanji_pattern = "[A-Za-z0-9々㐀-䶵一-鿌" .. compat_ideo .. "𠀀-𯨟0-9A-Z]"

 local str_find = string.find
 local str_gsub = string.gsub

 -- If term and kana aren't identical, then term should contain stuff that can
 -- have kana put above it.
 if not find(term, kanji_pattern) then
  require("Module:debug").track("ja/ruby/no kanji")
  -- error("No kanji or other ruby-annotatable characters in first argument of add_ruby_backend.")
  return enlarge(term)
 end

 -- Escape decimal numeric entities (such as &#32; representing a space) and HTML tags, which do not need ruby annotations.
 local function escape(t, i)
  return function(text, patt)
   return str_gsub(text, patt,
    function(capture)
     i = i + 1
     t[i] = capture
     return "㊟" .. string.rep("&", i) .. "㊟"
    end)
  end
 end

 local escaped1 = {}
 local escape1 = escape(escaped1, 0)

 term = escape1(term, "(&&.-&&)")
 term = escape1(term, "(&#x?%d+;)")
 term = escape1(term, "(&[A-Za-z]+;)")
 term = escape1(term, "(<[^>]+>)")

 local escaped2 = {}
 local escape2 = escape(escaped2, 0)

 kana = escape2(kana, "(&&.-&&)")
 kana = escape2(kana, "(&#%d+;)")
 kana = escape2(kana, "(&[A-Za-z]+;)")
 kana = escape2(kana, "(<[^>]+>)")

 if escaped1[1] or escaped2[1] then
  -- [[Special:WhatLinksHere/Template:tracking/ja/ruby/escaped]]
  require("Module:debug").track("ja/ruby/escaped")
 end

 if #escaped2 ~= #escaped1 then
  local relationship
  if #escaped1 > #escaped2 then
   relationship = "more"
  else
   relationship = "fewer"
  end

  mw.logObject(escaped1)
  mw.logObject(escaped2)
  error("There are " .. relationship .. " escaped things in the text to be annotated than in the ruby text.")
 end

 -- links without pipes will fail
 term = str_gsub(term, '%[%[([^|%]]+)%]%]', '[[%1|%1]]')

 -- remove links from kana
 kana = str_gsub(kana, '%[%[([^|%]]+)%]%]', '%1')
 kana = str_gsub(kana, '%[%[[^%]]+|([^%]]+)%]%]', '%1')

 -- build up pattern
 -- escape the magic characters in the term
 pattern = str_gsub(term, '%[%[[^%]]+|([^%]]+)%]%]', '%1')
 pattern = require("Module:string").pattern_escape(pattern)

 pattern = str_gsub(pattern, "[%[%]]+", " *")
 kana = str_gsub(kana, "[%[%]]+", '')
 pattern = str_gsub(pattern, " *('+) *", "%1")
 kana = str_gsub(kana, " *('+) *", "%1")
 pattern = str_gsub(pattern, " +", " ")
 kana = str_gsub(kana, " +", " ")

 -- remove periods and caret signs and hyphens
 pattern = str_gsub(pattern, '%%[.^-]', '')
 kana = str_gsub(kana, '[.^-]', '')

 -- in order to make a pattern that will find the ruby,
 -- replace every unbroken string of kanji with a sub-pattern

 -- mw.log("before adding (.+):", pattern)
 pattern = gsub(pattern, kanji_pattern .. '+', '(.+)')
 -- get a pattern like
 -- (.+)ばか(.+)ばか(.+)ばかばかばああか(.+) when given 超ばか猿超ばか猿超ばかばかばああか猿
 -- it turns out we need to keep the spaces sometimes
 -- so that kana don't "leak" in ambiguous cases like 捨すてて撤退 where it's not clear if it's
 -- す, てったい or すて, ったい.  only solution now is to put spaces in the "term" param
 -- if they fall between kana

 -- build up term (e.g. [[歌う|歌った]])
 local replaced = {}
 local count = 0
 term = str_gsub(term, '%]', '%]') -- escape the "]" character so that it cannot appear, example becomes [[歌う|歌った%]%]
 term = gsub(
  str_gsub(term, "%-", ""),
  kanji_pattern .. '+',
  function(text)
   count = count + 1
   -- remove spaces
   text = str_gsub(text, ' ', '')
   table.insert(replaced, text)
   return '[' .. count .. ']'
  end
 ) -- example becomes [[[1]う|[2]った%]%]

 while str_find(term, '%[%[[^|]*%[%d+%][^|]*|') do
  term = str_gsub(
   term,
   '(%[%[[^|]*)%[(%d+)%]([^|]*|)',
   function(a,b,c)
    return a .. replaced[tonumber(b)] .. c
   end
  )
 end
  -- example becomes [[歌う|[2]った%]%]

 -- apply that pattern to the kana to collect the rubies
 -- if this fails, try it without spaces
 if find(kana, pattern) == nil then
  kana = str_gsub(kana, ' ', '')
  if not find(kana, pattern) then
   mw.log("failed match:", kana, pattern)
   error("The pattern did not match the kana.")
  end
 end

 local ruby = { match(kana, pattern) }
 -- local ruby = {}
 -- for c in gmatch(kana, pattern) do table.insert(ruby, c) end

 -- find the kanji strings again and combine them with their ruby to make the <ruby> markup
 local kanji_segments = {}
 for c in string.gmatch(term, '%[(%d+)%]') do
  table.insert(kanji_segments, replaced[tonumber(c)])
 end

 if #kanji_segments ~= #ruby then
  mw.logObject(kanji_segments)
  mw.logObject(ruby)
  error("There are " .. #kanji_segments .. " kanji segments but only " .. #ruby .. " ruby segments. There should be an equal number of each.")
 end

 for i, kanji_segment in pairs(kanji_segments) do
  if not ruby[i] then
   error('No ruby for kanji segment "' .. kanji_segment .. '".')
  end

  local kanji, kana
  if mw.ustring.len(kanji_segment) > 1 and mw.ustring.len(kanji_segment) == mw.ustring.len(ruby[i]) then
   -- Split kanji and kana into individual characters.
   kanji = mw.text.split(kanji_segment, "")
   kana = mw.text.split(ruby[i], "")
  else
   kanji, kana = { kanji_segment }, { ruby[i] }
  end

  --[[
   Apportions kana to kanji based on phonological rules.
   Moraic nasal and sokuon are grouped with the previous kana,
   then sequences containing long vowels are grouped together if
   necessary.
   This captures many of the easier cases.
   漢字(かんじ) 京都(きょうと)  一気(いっき)
    ↓    ↓     ↓
   漢(かん)字(じ) 京(きょう)都(と) 一(いっ)気(き)
  ]]
  if length(kanji_segment) > 1 and not find(ruby[i], "[ァ-ヶ]") then
   local value1, value2 = require("Module:User:Erutuon/ja").divideKana(kanji_segment, ruby[i])
   if value1 and value2 and #value1 == #value2 then
    kanji, kana = value1, value2
   end
  end

  local ruby_string = {}
  for i = 1, #kanji do
   -- To prevent, for instance, "stop" being annotated with "stop" in [[言う]].
   if kanji[i] == kana[i] then
    table.insert(ruby_string, kanji[i])
   else
    table.insert(ruby_string, "<ruby>" .. kanji[i] .. "<rp>&nbsp;(</rp><rt>" .. kana[i] .. "</rt><rp>) </rp></ruby>")
   end
  end
  table.insert(ruby_markup, table.concat(ruby_string))
 end

 count = 0
 term = str_gsub(term, '%[%d+%]', function()
  count = count + 1
  return ruby_markup[count]
 end)

 term = str_gsub(term, '%%%]', ']')
 term = str_gsub(term, '%%', '')
 term = str_gsub(term, ' ', '')

 term = str_gsub(term,
  "㊟(&+)㊟",
  function(ampersands)
   return escaped1[#ampersands]
  end)

 term = str_gsub(term, "&&(.-)&&", "%1")

 --done
 return enlarge(term)
end

-- do the work of Template:ja-kanji
function export.kanji(frame)
 local pagename = mw.title.getCurrentTitle().text
 -- only do this if this entry is a kanji page and not some user's page
 if find(pagename, "[㐀-䶵一-鿌" .. compat_ideo .. "𠀀-𯨟]") then
  local params = {
   grade = {},
   rs = {},
   shin = {},
   kyu = {},
   head = {},
  }
  local args = require("Module:parameters").process(frame:getParent().args, params)

  local rs = args["rs"] or require("Module:zh-sortkey").makeSortKey(pagename, "ja")
  local shin = args["shin"]
  local kyu = args["kyu"]
  local head = args["head"]

  local grade_replacements = {
   c = "7",
   n = "8",
   uc = "9",
   r = "0",
  }
  local grade = args["grade"]
  grade = grade_replacements[grade] or grade

  local wikitext = {}
  local categories = {}

  local catsort = rs or pagename

  -- display the kanji itself at the top at 275% size
  table.insert(wikitext, '<div><span lang="ja" class="Jpan" style="font-size:275%; line-height: 100%;">' .. (args["head"] or pagename) .. '</span></div>')

  -- display information for the grade

  -- if grade was not specified, determine it now
  if not grade then
   grade = tostring(export.kanji_grade(pagename))
  end

  table.insert(wikitext, "(''")
  local grade_links = {
   ["1"] = "[[w:Kyōiku kanji|grade 1 “Kyōiku” kanji]]",
   ["2"] = "[[w:Kyōiku kanji|grade 2 “Kyōiku” kanji]]",
   ["3"] = "[[w:Kyōiku kanji|grade 3 “Kyōiku” kanji]]",
   ["4"] = "[[w:Kyōiku kanji|grade 4 “Kyōiku” kanji]]",
   ["5"] = "[[w:Kyōiku kanji|grade 5 “Kyōiku” kanji]]",
   ["6"] = "[[w:Kyōiku kanji|grade 6 “Kyōiku” kanji]]",
   ["7"] = "[[w:Jōyō kanji|common “Jōyō” kanji]]",
   ["8"] = "[[w:Jinmeiyō kanji|“Jinmeiyō” kanji used for names]]",
   ["9"] = "[[w:Hyōgai kanji|uncommon “Hyōgai” kanji]]",
   ["0"] = "[[w:Radical_(Chinese_character)|Radical]]",
  }
  table.insert(wikitext, grade_links[grade] or "[[Category:Japanese kanji missing grade]]")

  -- link to shinjitai if shinjitai was specified, and link to kyujitai if kyujitai was specified

  if kyu then
   table.insert(wikitext, ",&nbsp;")
   table.insert(wikitext, '[[shinjitai]] kanji, [[kyūjitai]] form <span lang="ja" class="Jpan">[[' .. kyu .. '#Japanese|' .. kyu .. ']]</span>')
  elseif shin then
   table.insert(wikitext, ",&nbsp;")
   table.insert(wikitext, '[[kyūjitai]] kanji, [[shinjitai]] form <span lang="ja" class="Jpan">[[' .. shin .. '#Japanese|' .. shin .. ']]</span>')
  end
  table.insert(wikitext, "'')")

  -- add categories
  table.insert(categories, "[[Category:Japanese Han characters|" .. catsort .. "]]")
  local grade_categories = {
   ["1"] = "[[Category:Grade 1 kanji|" .. catsort .. "]]",
   ["2"] = "[[Category:Grade 2 kanji|" .. catsort .. "]]",
   ["3"] = "[[Category:Grade 3 kanji|" .. catsort .. "]]",
   ["4"] = "[[Category:Grade 4 kanji|" .. catsort .. "]]",
   ["5"] = "[[Category:Grade 5 kanji|" .. catsort .. "]]",
   ["6"] = "[[Category:Grade 6 kanji|" .. catsort .. "]]",
   ["7"] = "[[Category:Common kanji|" .. catsort .. "]]",
   ["8"] = "[[Category:Kanji used for names|" .. catsort .. "]]",
   ["9"] = "[[Category:Uncommon kanji|" .. catsort .. "]]",
   ["0"] = "[[Category:CJKV radicals| ]]",
  }
  table.insert(categories, grade_categories[grade])

  -- error category
  if not rs then
   table.insert(categories, "[[Category:Japanese kanji missing radical and strokes]]")
  end

  return table.concat(wikitext, "") .. table.concat(categories, "\n")
 end
end

local grade1_pattern = ('[' .. data.grade1 .. ']')
local grade2_pattern = ('[' .. data.grade2 .. ']')
local grade3_pattern = ('[' .. data.grade3 .. ']')
local grade4_pattern = ('[' .. data.grade4 .. ']')
local grade5_pattern = ('[' .. data.grade5 .. ']')
local grade6_pattern = ('[' .. data.grade6 .. ']')
local secondary_pattern = ('[' .. data.secondary .. ']')
local jinmeiyo_kanji_pattern = ('[' .. data.jinmeiyo_kanji .. ']')
local hyogaiji_pattern = ('[^' .. data.joyo_kanji .. data.jinmeiyo_kanji .. ']')

function export.kanji_grade(kanji)
 if type(kanji) == "table" then
  kanji = kanji.args[1]
 end

 if find(kanji, hyogaiji_pattern) then return 9
 elseif find(kanji, jinmeiyo_kanji_pattern) then return 8
 elseif find(kanji, secondary_pattern) then return 7
 elseif find(kanji, grade6_pattern) then return 6
 elseif find(kanji, grade5_pattern) then return 5
 elseif find(kanji, grade4_pattern) then return 4
 elseif find(kanji, grade3_pattern) then return 3
 elseif find(kanji, grade2_pattern) then return 2
 elseif find(kanji, grade1_pattern) then return 1
 end

 return false
end

function export.new(frame)
 local args = frame:getParent().args

 local function waapuro_r_to_kana(text)
  return require("Module:typing-aids").replace{"ja", text}
 end

 local result = "==Japanese=="

 if args["defs"] then
  result = result .. "\n{{DEFAULTSORT:" .. args["defs"] .. "}}"
 end
 if args["wp"] then
  result = result .. "\n{{wp|lang=ja" .. (args["wp"] ~= "y" and "|" .. args["wp"] or "") .. "}}"
 end
 wp_count = 2
 while args["wp" .. wp_count] do
  result = result .. "\n{{wp|lang=ja|" .. args["wp" .. wp_count] .. "}}"
  wp_count = wp_count + 1
 end

 if args["swp"] then
  result = result .. "\n{{swp|lang=ja" .. (args["swp"] ~= "y" and "|" .. args["swp"] or "") .. "}}"
 end
 swp_count = 2
 while args["swp" .. swp_count] do
  result = result .. "\n{{swp|lang=ja|" .. args["swp" .. swp_count] .. "}}"
  swp_count = swp_count + 1
 end

 text = args[1] ~= "" and args[1] or pagename
 text = gsub(text, "%-", "|")
 text = waapuro_r_to_kana(text)

 local function make_tab(original, yomi)
  output_text = ""
  original = gsub(original, " ", "|")
  original = gsub(original, "%.", "|")
  original = gsub(original, "%^", "")
  if find(original, "<") then
   for word in gmatch(original, "<([^>]+)>") do
    output_text = output_text .. "|" .. word
   end
   yomi = yomi or "k"
  else
   output_text = gsub(original, ">([1-9])", "|k%1=")
   output_text = find(output_text, "|") and "|" .. output_text or false
  end
  yomi = yomi or "o"
  return "\n{{ja-kanjitab" .. (output_text or "") .. (yomi == "n" and "" or "|yomi=" .. yomi) .. (yomi == "irr" and "" or sortkey or "") .. (args["r"] and "|r=" .. args["r"] or "") .. "}}", yomi
 end
 if find(pagename, "[㐀-䶵一-鿌" .. compat_ideo .. "𠀀-𯨟]") then
  to_add, yomi = make_tab(text, args["yomi"])
  result = result .. to_add
 end

 if find(text, "<") then
  text = gsub(text, "[<>]", "")
 else
  text = gsub(text, "^[^>|]+>%d+([^>|]+)", "%1")
  text = gsub(text, "|[^>|]+>%d+([^>|]+)", "%1")
  text = gsub(text, "([あかがさざただなはばぱまやらわ])|(あ)", "%1.%2")
  text = gsub(text, "([いきぎしじちぢにひびぴみり])|(い)", "%1.%2")
  text = gsub(text, "([うくぐすずつづぬふむゆる])|(う)", "%1.%2")
  text = gsub(text, "([えけげせぜてでねへめれ])|([えい])", "%1.%2")
  text = gsub(text, "([おこごそぞとどのほぼぽもよろ])|([おう])", "%1.%2")
  text = gsub(text, "|", "")
 end

 if args["wpd"] then
  result = result .. "\n{{swp|lang=ja" .. (args["wpd"] ~= "y" and "|" .. args["wpd"] or "") .. "}}"
 end
 wpd_count = 2
 while args["wpd" .. wpd_count] do
  result = result .. "\n{{swp|lang=ja|" .. args["wpd" .. wpd_count] .. "}}"
  wpd_count = wpd_count + 1
 end

 local function other(class, title, args)
  local code, i = "", 2

  if args[class] then
   code = code .. "\n\n===" .. title .. "===\n* {{ja-l|" .. args[class] .. "}}"

   while args[class .. i] do
    code = code .. "\n* {{ja-l|" .. args[class .. i] .. "}}"
    i = i + 1
   end
  end

  code = gsub(code, "{{ja%-l\|([^%|%}]+)[::]", "{{ja-r|%1|") -- change something like "{{ja-l|辞典:じてん}}" to "{{ja-r|辞典|じてん}}"
  code = gsub(code, "{{ja%-l\|([ぁ-ー ^%%.]+)}}", "{{ja-r|%1}}") -- change something like "{{ja-l|じてん}}" to "{{ja-r|じてん}}"

  return code
 end

 result = result .. other("alt", "Alternative forms", args)

 sortkey = export.script(text) == "Kana" and export.sort(text) or false
 if sortkey and sortkey == "|sort=" .. text then
  sortkey = false
 end

 if args["d"] or args["e"] or args["we1"] or args["b"] or args["lb"] or args["co1"] or args["et"] or args["pr1"] or args["su1"] then
  result = result .. "\n\n{{Etimologio}}\n"
  if args["we1"] then
   result = result .. "{{waei|" .. args["we1"] .. (args["we2"] and "|" .. args["we2"] or "") .. (args["defs"] and "" or sortkey or "") .. "}}"
  else
   if args["pr1"] then
    result = result .. "{{pre|ja|" .. args["pr1"] .. "|" .. args["pr2"] .. (args["defs"] and "" or sortkey or "") .. (args["tr1"] and "|tr1=" .. args["tr1"] or "") .. (args["tr2"] and "|tr2=" .. args["tr2"] or "") .. (args["pos1"] and "|pos1=" .. args["pos1"] or "") .. (args["pos2"] and "|pos2=" .. args["pos2"] or "") .. (args["t1"] and "|t1=" .. args["t1"] or "") .. (args["t2"] and "|t2=" .. args["t2"] or "") .. "}}"
   else
    if args["su1"] then
     result = result .. "{{suf|ja|" .. args["su1"] .. "|" .. args["su2"] .. (args["defs"] and "" or sortkey or "") .. (args["tr1"] and "|tr1=" .. args["tr1"] or "") .. (args["tr2"] and "|tr2=" .. args["tr2"] or "") .. (args["pos1"] and "|pos1=" .. args["pos1"] or "") .. (args["pos2"] and "|pos2=" .. args["pos2"] or "") .. (args["t1"] and "|t1=" .. args["t1"] or "") .. (args["t2"] and "|t2=" .. args["t2"] or "") .. "}}"
    else
     if args["co1"] then
      result = result .. "{{com|ja|" .. args["co1"] .. "|" .. args["co2"] .. (args["co3"] and "|" .. args["co3"] or "") .. (args["co4"] and "|" .. args["co4"] or "") .. (args["co5"] and "|" .. args["co5"] or "") .. (args["co6"] and "|" .. args["co6"] or "") .. (args["tr1"] and "|tr1=" .. args["tr1"] or "") .. (args["tr2"] and "|tr2=" .. args["tr2"] or "") .. (args["tr3"] and "|tr3=" .. args["tr3"] or "") .. (args["tr4"] and "|tr4=" .. args["tr4"] or "") .. (args["tr5"] and "|tr5=" .. args["tr5"] or "") .. (args["tr6"] and "|tr6=" .. args["tr6"] or "") .. (args["pos1"] and "|pos1=" .. args["pos1"] or "") .. (args["pos2"] and "|pos2=" .. args["pos2"] or "") .. (args["pos3"] and "|pos3=" .. args["pos3"] or "") .. (args["pos4"] and "|pos4=" .. args["pos4"] or "") .. (args["pos5"] and "|pos5=" .. args["pos5"] or "") .. (args["pos6"] and "|pos6=" .. args["pos6"] or "") .. (args["t1"] and "|t1=" .. args["t1"] or "") .. (args["t2"] and "|t2=" .. args["t2"] or "") .. (args["t3"] and "|t3=" .. args["t3"] or "") .. (args["t4"] and "|t4=" .. args["t4"] or "") .. (args["t5"] and "|t5=" .. args["t5"] or "") .. (args["t6"] and "|t6=" .. args["t6"] or "") .. (args["defs"] and "" or sortkey or "") .. "}}"
     else
     if args["et"] then
      result = result .. "{{cal|ja|" .. ((args["el"] and "etyl lang=" .. args["el"]) or "etyl lang=en") .. "|etyl term=" .. args["et"] .. (args["nocap"] and "|nocap=" .. args["nocap"] or "") .. (args["ca1"] and "|" .. args["ca1"] or "") .. (args["ca2"] and "|" .. args["ca2"] or "") .. (args["ca3"] and "|" .. args["ca3"] or "") .. (args["ca4"] and "|" .. args["ca4"] or "") .. (args["ca5"] and "|" .. args["ca5"] or "") .. (args["ca6"] and "|" .. args["ca6"] or "") .. (args["tr1"] and "|tr1=" .. args["tr1"] or "") .. (args["tr2"] and "|tr2=" .. args["tr2"] or "") .. (args["tr3"] and "|tr3=" .. args["tr3"] or "") .. (args["tr4"] and "|tr4=" .. args["tr4"] or "") .. (args["tr5"] and "|tr5=" .. args["tr5"] or "") .. (args["tr6"] and "|tr6=" .. args["tr6"] or "") .. (args["pos1"] and "|pos1=" .. args["pos1"] or "") .. (args["pos2"] and "|pos2=" .. args["pos2"] or "") .. (args["pos3"] and "|pos3=" .. args["pos3"] or "") .. (args["pos4"] and "|pos4=" .. args["pos4"] or "") .. (args["pos5"] and "|pos5=" .. args["pos5"] or "") .. (args["pos6"] and "|pos6=" .. args["pos6"] or "") .. (args["t1"] and "|t1=" .. args["t1"] or "") .. (args["t2"] and "|t2=" .. args["t2"] or "") .. (args["t3"] and "|t3=" .. args["t3"] or "") .. (args["t4"] and "|t4=" .. args["t4"] or "") .. (args["t5"] and "|t5=" .. args["t5"] or "") .. (args["t6"] and "|t6=" .. args["t6"] or "") .. (args["defs"] and "" or sortkey or "") .. "}}"
     else
      if args["b"] then
       result = result .. "{{bor|ja|" .. (args["bl"] or "en") .. (args["b"] and "|" .. args["b"] or "") .. (args["tr"] and "|tr=" .. args["tr"] or "") .. (args["t"] and "||" .. args["t"] or "") .. (args["lit"] and "|lit=" .. args["lit"] or "") .. (args["pos"] and "|pos=" .. args["pos"] or "") .. (args["g"] and "|g=" .. args["g"] or "") .. (args["defs"] and "" or sortkey or "") .. "}}" .. (args["e_ref"] and args["e_ref"] or "")
      else
      if args["lb"] then
       result = result .. "{{lbor|ja|" .. (args["lbl"] or "grc") .. (args["lb"] and "|" .. args["lb"] or "") .. (args["tr"] and "|tr=" .. args["tr"] or "") .. (args["t"] and "||" .. args["t"] or "") .. (args["lit"] and "|lit=" .. args["lit"] or "") .. (args["pos"] and "|pos=" .. args["pos"] or "") .. (args["g"] and "|g=" .. args["g"] or "") .. (args["defs"] and "" or sortkey or "") .. "}}" .. (args["e_ref"] and args["e_ref"] or "")
      else
       result = result .. (args["e"] or
        ("From {{der|ja|" .. (args["dl"] or "en") .. (args["d"] and "|" .. args["d"] or "") .. (args["tr"] and "|tr=" .. args["tr"] or "") .. (args["t"] and "||" .. args["t"] or "") .. (args["lit"] and "|lit=" .. args["lit"] or "") .. (args["pos"] and "|pos=" .. args["pos"] or "") .. (args["g"] and "|g=" .. args["g"] or "") .. (args["defs"] and "" or sortkey or "") .. "}}" .. (args["e_ref"] and args["e_ref"] or "")))
      end
      end
      end
     end
    end
   end
  end
 end

 if not args["nop"] then
  result = result .. "\n\n===Pronunciation===\n{{ja-pron" .. (args[1] ~= "" and "|" .. gsub(text, '%^', '') or "")
  if args["y"] == "n" or yomi == "n" then
   result = result .. ""
  else if args["y"] and args["y"] ~= "n" then
   result = result .. "|y=" .. args["y"]
  else if yomi then
   if yomi == "irr" then
   result = result .. "|y=i"
   else
    result = result .. "|y=" .. yomi
   end
   end
  end
  end
  result = result .. (args["acc"] and "|acc=" .. args["acc"] or "") .. (args["acc2"] and "|acc2=" .. args["acc2"] or "") .. (args["acc3"] and "|acc3=" .. args["acc3"] or "") .. (args["acc4"] and "|acc4=" .. args["acc4"] or "") .. (args["acc_ref"] and "|acc_ref=" .. args["acc_ref"] or "") .. (args["acc2_ref"] and "|acc2_ref=" .. args["acc2_ref"] or "") .. (args["acc3_ref"] and "|acc3_ref=" .. args["acc3_ref"] or "") .. (args["acc4_ref"] and "|acc4_ref=" .. args["acc4_ref"] or "") .. (args["dev"] and "|dev=" .. args["dev"] or "") .. "}}"
        if args["hmp"] then
           if yomi and not (yomi == "n" or y == "n") then result = result .. "\n** "
           else result = result .. "\n* "
               end
               end
        result = result .. (args["hmp"] and "{{hmp|lang=ja|" .. args["hmp"] .. (args["hmp2"] and "|" .. args["hmp2"] or "") .. (args["hmp3"] and "|" .. args["hmp3"] or "") .. (args["hmp4"] and "|" .. args["hmp4"] or "") .. (args["hmp5"] and "|" .. args["hmp5"] or "") .. (args["hmp6"] and "|" .. args["hmp6"] or "") .. (args["defs"] and "" or sortkey or "") .. "}}" or "")
 end

 local pos = args[2] ~= "" and args[2] or "n"
 local pos_table = {
  [""] = { "Noun", "noun", true },
  ["n"] = { "Noun", "noun", true },
  ["s"] = { "Noun", "noun", true, "Verb", "verb-suru" },
  ["noun"] = { "Noun", "noun", true },
  ["suru"] = { "Noun", "noun", true, "Verb", "verb-suru" },
  ["an"] = { "Adjective", "adj", true, "Noun", "noun" },
  ["anoun"] = { "Adjective", "adj", true, "Noun", "noun" },
  ["v"] = { "Verb", "verb", true },
  ["verb"] = { "Verb", "verb", true },
  ["vform"] = { "Verb", "verb form", true },
  ["verb form"] = { "Verb", "verb form", true },
  ["a"] = { "Adjective", "adj", true },
  ["adj"] = { "Adjective", "adj", true },
  ["adjective"] = { "Adjective", "adj", true },
  ["adv"] = { "Adverb", "adverb", false },
  ["adverb"] = { "Adverb", "adverb", false },
  ["pron"] = { "Pronoun", "pronoun", false },
  ["pronoun"] = { "Pronoun", "pronoun", false },
  ["pn"] = { "Proper noun", "proper", false },
  ["propn"] = { "Proper noun", "proper", false },
  ["proper"] = { "Proper noun", "proper", false },
  ["proper noun"] = { "Proper noun", "proper", false },
  ["ph"] = { "Phrase", "phrase", true },
  ["phrase"] = { "Phrase", "phrase", true },
  ["interjection"] = { "Interjection", "interjection", false },
  ["intj"] = { "Interjection", "interjection", false },
  ["conj"] = { "Conjunction", "conjunction", false },
  ["part"] = { "Particle", "particle", false },
  ["prep"] = { "Preposition", "preposition", false },
  ["suf"] = { "Suffix", "suffix", false },
  ["suffix"] = { "Suffix", "suffix", false },
  ["pref"] = { "Prefix", "prefix", false },
  ["prefix"] = { "Prefix", "prefix", false },
  ["prov"] = { "Proverb", "proverb", false },
  ["idiom"] = { "Idiom", "idiom", false },
 }

 result = result .. "\n\n===" .. pos_table[pos][1] .. "===\n{{ja-" ..
  (not pos_table[pos][3] and "pos|" or "") .. pos_table[pos][2] ..
  (args[1] ~= "" and "|" .. text or "") ..
  (args["head"] and "|head=" .. args["head"] or "") ..
  (args["c"] and "|count=" .. args["c"] or "") ..
  (args["kyu"] and "|kyu=" .. args["kyu"] or "") ..
  (args["sin"] and "|shin=" .. args["sin"] or "") ..
  (args["ak"] and "|" .. waapuro_r_to_kana(args["ak"]) or "") ..
  (args["ak2"] and "|" .. waapuro_r_to_kana(args["ak2"]) or "") ..
  (args["ak3"] and "|" .. waapuro_r_to_kana(args["ak3"]) or "") ..
  (args["ro"] and "|rom=" .. args["ro"] or "") ..
  (args["hh"] and "|hhira=" .. waapuro_r_to_kana(args["hh"]) or "") ..
  (args["hk"] and "|hkata=" .. waapuro_r_to_kana(args["hk"]) or "")

 if pos_table[pos][1] == "Adjective" then
  result = result .. "|infl=" .. (args["infl"] and args["infl"] or "na")
 end

 result = result .. (args["type"] and "|type=" .. args["type"] or "") .. (args["tr"] and "|tr=" .. args["tr"] or "") .. "}}"
 result = result .. "\n\n# " .. (args[3] or "{{rfdef|ja}}")

 if pos_table[pos][1] == "Adjective" then
  result = result .. "\n\n====Inflection====\n"
  if args["infl"] == "i" or args["infl"] == "い" then
   result = result .. "{{ja-i" .. (args[1] ~= "" and "|" .. sub(text, 1, -2) or "") .. "}}"
  else
   result = result .. "{{ja-na" .. (args[1] ~= "" and "|" .. text or "") .. "}}"
  end
 end

 if pos_table[pos][2] == "verb" then
  result = result .. "\n\n====Conjugation====\n{{ja-"
  penul, cons = text, text
  penul, cons = sub(penul, -2, -2), sub(cons, -1, -1)
  penul, cons = export.hira_to_kata(penul), export.hira_to_kata(cons)
  penul, cons = gsub(penul, ".", data.kr), gsub(cons, ".", data.kr)
  penul, cons = sub(penul, -1, -1), sub(cons, 1, 1)
  if cons == "u" then
   cons = ""
  elseif cons == "c" then
   cons = "ts"
  end
  if cons == "r" and (penul == "i" or penul == "e") and args["type"] == "2" then
   result = result .. "ichi"
  else
   result = result .. "go-" .. cons .. "u"
  end

  result = result .. (args[1] ~= "" and "|" .. sub(text, 1, -2) or "") .. "}}"
 end

 if pos_table[pos][4] and args[4] ~= "" then
  result = result .. "\n\n===" .. pos_table[pos][4] .. "===\n{{ja-" .. pos_table[pos][5] .. (args[1] ~= "" and "|" .. text or "") ..
   (args["head"] and "|head=" .. args["head"] or "") ..
   (args["c"] and "|count=" .. args["c"] or "") ..
   (args["kyu"] and "|kyu=" .. args["kyu"] or "") ..
   (args["sin"] and "|shin=" .. args["sin"] or "") ..
   (args["ak"] and "|" .. waapuro_r_to_kana(args["ak"]) or "") ..
   (args["ak2"] and "|" .. waapuro_r_to_kana(args["ak2"]) or "") ..
   (args["ak3"] and "|" .. waapuro_r_to_kana(args["ak3"]) or "") ..
   (args["ro"] and "|rom=" .. args["ro"] or "") ..
   (args["hh"] and "|hhira=" .. waapuro_r_to_kana(args["hh"]) or "") ..
   (args["hk"] and "|hkata=" .. waapuro_r_to_kana(args["hk"]) or "") ..
   (args["type"] and "|type=" .. args["type"] or "") .. (args["tr"] and "|tr=" .. args["tr"] or "") .. "}}\n\n# " .. (args[4] or "{{rfdef|ja}}")

  if pos_table[pos][4] == "Verb" then
   result = result .. "\n\n====Conjugation====\n{{ja-suru" .. (args[1] ~= "" and "|" .. text or "") .. "}}"
  end
 end

 if args["use"] then
  result = result .. "\n\n====Usage notes====\n* " .. args["use"]
 end

 result = result .. other("syn", "=Synonyms=", args)
 result = result .. other("ant", "=Antonyms=", args)
 result = result .. other("der", "=Derived terms=", args)
 result = result .. other("rel", "=Related terms=", args)

 if args["dzh"] or args["den"] or args["dko"] or args["dvi"] then
  result = result .. "\n\n===Descendants===\n" .. (args["dzh"] and "* Chinese: {{zh-l|" .. args["dzh"] .. "}}" or "")
  if args["dzh"] then
   if args["den"] or args["dko"] or args["dvi"] then
    result = result .. "\n"
   end
  end
  result = result .. (args["den"] and "* English: {{l|en|" .. args["den"] .. "}}" or "")
  if args["den"] then
   if args["dko"] or args["dvi"] then
    result = result .. "\n"
   end
  end
  result = result .. (args["dko"] and "* Korean: {{ko-l|" .. args["dko"] .. "}}" or "")
  if args["dko"] then
   if args["dvi"] then
    result = result .. "\n"
   end
  end
  result = result .. (args["dvi"] and "* Vietnamese: {{vi-l|" .. args["dvi"] .. "}}" or "")
 end
 result = result .. other("ana", "Anagrams", args)
 result = result .. other("also", "See also", args)

 if args["acc_ref"] or args["acc2_ref"] or args["acc3_ref"] or args["e_ref"] then
  result = result .. "\n\n===References===\n<references/>"
 end

 if args["cn"] then
  result = result .. "\n\n{{cln|ja|" .. args["cn"] .. (args["cn2"] and "|" .. args["cn2"] or "") .. (args["cn3"] and "|" .. args["cn3"] or "") .. (args["cn4"] and "|" .. args["cn4"] or "") .. (args["cn5"] and "|" .. args["cn5"] or "") .. (args["cn6"] and "|" .. args["cn6"] or "") .. (args["defs"] and "" or sortkey or "") .. "}}"
 end
 if args["ct"] then
  result = result .. (args["cn"] and "\n" or "\n\n") .. "{{C|ja|" .. args["ct"] .. (args["ct2"] and "|" .. args["ct2"] or "") .. (args["ct3"] and "|" .. args["ct3"] or "") .. (args["ct4"] and "|" .. args["ct4"] or "") .. (args["ct5"] and "|" .. args["ct5"] or "") .. (args["ct6"] and "|" .. args["ct6"] or "") .. (args["defs"] and "" or sortkey or "") .. "}}"
 end

 if args["k"] then
  result = result .."\n\n----\n\n==Korean==\n{{ko-hanjatab}}\n\n===" .. pos_table[args["kp"] or "n"][1] ..
   "===\n{{ko-" .. pos_table[args["kp"] or "n"][2] .. "|hangeul=" .. args["k"] .. (args["mr"] and "|mr=" .. args["mr"] or "") .. (args["yl"] and "|y=" .. args["yl"] or "") .. "}}" ..
   "\n\n# {{hanja form of|" .. args["k"] .. "|" .. (args["kd"] or args[3]) .. "}}"
 end

 return result
end

return export