La documentation pour ce module peut être créée à Module:ja-kana/Documentation

local ja = {}

-- Il est à noter que mw.ustring.gsub ne peut pas utiliser
-- un tableau de remplacement importé par mw.loadData.
local kana = mw.loadData("Module:ja-kana/data")

-- Cette fonction change des hiraganas en katakanas correspondants
function ja.hira_en_kata(str)
	str = mw.ustring.gsub(str, "([ぁ-ゔ])",
	                      function (char)
	                          return mw.ustring.char(mw.ustring.codepoint(char) + 0x60)
	                      end)
	return str
end

-- Cette fonction change des katakanas en hiraganas correspondants.
function ja.kata_en_hira(str)
	str = mw.ustring.gsub(str, "([ァ-ヴ])",
	                      function (char)
	                          return mw.ustring.char(mw.ustring.codepoint(char) - 0x60)
	                      end)
	return str
end

-- Cette fonction romanise des kanas.
function ja.kana_en_roman(str, point, certain)
	local pref, suf
	str, pref = mw.ustring.gsub(str, "-$", "")
	str, suf = mw.ustring.gsub(str, "^-", "")
	str = mw.ustring.gsub(str, "([アイウエオい])", ".%1") -- on écrit ー pour les katakanas ; いい est devenu ii
	str = ja.hira_en_kata(str)
	str = mw.ustring.gsub(str, "[・= %-]", " ")
	str = mw.ustring.gsub(str, "、", ", ")
	str = mw.ustring.gsub(str, "([ァ-ヴヷ-ヺー])([ァィゥェォャュョヮ]?)",
	                      function (char1, char2)
	                          return kana.roman[char1 .. char2] or
	                                 (kana.roman[char1] .. kana.roman[char2])
	                      end)

	-- voyelles longues
	if certain and (mw.ustring.find(str, "([aiueo])%1") or mw.ustring.find(str, "ou")) then
		return nil
	end
	str = mw.ustring.gsub(str, "([aue]i)i", "%1.i") -- diphtongue
	str = mw.ustring.gsub(str, "o[ou]", "oˉ")
	str = mw.ustring.gsub(str, "([aiueo])%1", "%1ˉ")

	-- macrons
	str = mw.ustring.gsub(str, "([aiueo])ˉ",
	                      function (char)
	                          return kana.macron[char]
	                      end)
	if not point then
		str = mw.ustring.gsub(str, "%.", "") -- symbole pour séparer des syllabes
	end
	-- ん
	str = mw.ustring.gsub(str, "n’([^aiueoāīūēōy])", "n%1") -- y compris "n’."
	str = mw.ustring.gsub(str, "n’$", "n")
	-- っ
	str = mw.ustring.gsub(str, "q+%.?([^aiueoāīūēō])", "%1%1") -- devant une consonne
	str = mw.ustring.gsub(str, "cch", "tch")
	str = mw.ustring.gsub(str, "q+", "’") -- devant une voyelle ou à la fin

	if pref > 0 then str = str .. "-" end
	if suf > 0 then str = "-" .. str end
	return str
end

-- Cette fonction génère la prononciation de la romanisation donnée.
function ja.roman_en_pron(str, certain, sans_diphtongue, acc)
	-- syllabation simple
	str = mw.ustring.lower(str)
	str = mw.ustring.gsub(str, "[^a-zāīūēō’]", ".")
	if sans_diphtongue then
		str = mw.ustring.gsub(str, "([aiueoāīūēō])", "%1.")
	else
		str = mw.ustring.gsub(str, "([aiueoāīūēō]i?)", "%1.") -- ai, ui, ei
		str = mw.ustring.gsub(str, "([ioāīūēō])i", "%1.i")
		if certain and mw.ustring.find(str, "[aue]i") then
			return nil
		end
	end
	-- points
	str = mw.ustring.gsub("." .. str, "%.+", ".")
	str = mw.ustring.gsub(str, "%.$", "")
	-- ん
	str = mw.ustring.gsub(str, "%.n[’%.]", "n.")
	str = mw.ustring.gsub(str, "%.n([^aiueoāīūēōy])", "n.%1")
	str = mw.ustring.gsub(str, "%.n$", "n")
	-- っ
	str = mw.ustring.gsub(str, "%.tch", "q.ch")
	str = mw.ustring.gsub(str, "%.([^aiueoāīūēō])%1", "q.%1")
	str = mw.ustring.gsub(str, "%.’$", "q")
	str = mw.ustring.gsub(str, "%.’", "q.")
	-- palatalisation
	str = mw.ustring.gsub(str, "%.([kgnh])([iī])", ".%1y%2")
	-- consonnes et voyelles
	local erreur = false
	str = mw.ustring.gsub(str, "([^%.aiueoāīūēō]*)([aiueoāīūēō]i?)",
		                  function (consonne, voyelle)
		                  		if kana.consonne[consonne] and kana.voyelle[voyelle] then
		                    	  return kana.consonne[consonne] .. kana.voyelle[voyelle]
		                    	else
		                    		erreur = true
		                			return nil
		                		end
	                  end)

	if erreur then
		return "romanisation erronée"
	end

	-- っ
	str = mw.ustring.gsub(str, "q(%.?)([^aiɯeo]?)",
		                  function (point, consonne)
		                      return (kana.geminee[consonne] or consonne) .. point .. consonne
		                  end)
	-- ん
	str = mw.ustring.gsub(str, "n%.(.)",
		                  function (consonne)
		                      return (kana.nasale[consonne] or "ɴ") .. "." .. consonne
		                  end)
	str = mw.ustring.gsub(str, "n$", "ɴ")
	str = mw.ustring.gsub(str, "^%.", "")

	if acc then
		str = ja.pron_accent(str, acc)
	end
	return str
end

function ja.pron_accent(str, acc)
	str = mw.ustring.toNFD(str) .. '.◌'
	if acc == 0 then acc = 1000000 end -- assez grand
	local syllabes = 1
	str = mw.ustring.gsub(str, kana.rime,
	                      function (rime)
	                          local len = mw.ustring.len(mw.ustring.gsub(rime, kana.nombre_more, "%1%2%3"))
	                          if syllabes <= acc and not (syllabes == 1 and acc > len) then 
	                              rime = rime .. '˦'
	                              if len >= 2 and syllabes + len > acc then
	                                  rime = rime .. '˨'
	                              end
	                          else
	                              rime = rime .. '˨'
	                              if len >= 2 and syllabes + len <= acc then
	                                  rime = rime .. '˦'
	                              end
	                          end

	                          syllabes = syllabes + len
	                          return rime
	                      end)
	return str
end

function ja.kana_accent(str, acc)
	str = mw.ustring.gsub(str, "([アイウエオ])", ".%1") -- on écrit ー pour les katakanas
	if acc == 0 then acc = 1000000 end -- assez grand
	str = ja.hira_en_kata(str)
	str = mw.ustring.gsub(str, "[・=、]", " ")
	str = mw.ustring.gsub(str, "([アイウエオカ-モヤユヨラ-ロワ-ヴヷ-ヺ]?)([ァィゥェォャュョヮ])",
	                      function (char1, char2)
	                          if char1 == "" or not kana.roman[char1 .. char2] then
	                              return char1 .. mw.ustring.char(mw.ustring.codepoint(char2) + 1)
	                          end
	                      end)

	-- voyelles longues
	str = mw.ustring.gsub(str, "([" .. kana.katakana_a .. kana.katakana_u .. "]イ)イ", "%1.イ")
	str = mw.ustring.gsub(str, "([" .. kana.katakana_e .. "])[イエ]", "%1ー")
	str = mw.ustring.gsub(str, "([" .. kana.katakana_o .. "])[ウオ]", "%1ー")
	str = mw.ustring.gsub(str, "([" .. kana.katakana_u .. "])ウ", "%1ー")
	str = mw.ustring.gsub(str, "([" .. kana.katakana_a .. "])ア", "%1ー")
	str = mw.ustring.gsub(str, "([" .. kana.katakana_i .. "])イ", "%1ー")

	str = mw.ustring.gsub(str, "[^ァ-ヴヷ-ヺー]", "")

	-- accent
	local mores = 1
	local span = false
	str = mw.ustring.gsub(str, "([ァ-ヴヷ-ヺ][ァィゥェォャュョヮ]?)(ー?[ッン]?)",
	                      function (char, coda)
	                          local coda_len = mw.ustring.len(coda)
	                          if (acc <= 1 + coda_len and mores == 1) or (acc ~= 1 and mores == 2) then
	                              char = '<span style="border-top: 1px solid black; position: relative; padding: 1px;">' .. char
	                              span = true
	                          elseif coda_len > 0 and mores == 1 then
	                              coda = '<span style="border-top: 1px solid black; position: relative; padding: 1px;">' .. coda
	                              span = true
	                          end
	                          if mores <= acc and mores + coda_len >= acc then
	                              char = char .. '<span style="position: absolute; top: 0; bottom: 67%; right: 0%; border-right: 1px solid black;">&#8203;</span></span>'
	                              span = false
	                          end
	                          mores = mores + 1 + coda_len
	                          return char .. coda
	                      end)
	if span then
		str = str .. '</span>'
	end

	return str
end

local function voyelle_longue(char)
	local roman = kana.roman[char]
	if roman then
		if mw.ustring.match(char, "[ぁ-ぢつ-をゔ]$") then -- exclure っ et ん
			return char .. kana.hiragana_voyelle[mw.ustring.sub(roman, -1)]
		elseif mw.ustring.match(char, "[ァ-ヂツ-ヲヴヷ-ヺ]$") then -- exclure ッ et ン
			return char .. kana.katakana_voyelle[mw.ustring.sub(roman, -1)]
		end
	end
	return char
end

-- Cette fonction supprime les voyelles longues pour générer une clé de tri.
function ja.kana_sans_longue(str)
	str = mw.ustring.gsub(str, "([ぁ-ゔァ-ヴヷ-ヺ])ー", voyelle_longue)
	str = mw.ustring.gsub(str, "[・=  %-]", "")
	return str
end

-- Cette function compte les mores.
function ja.compter_mores(str)
	str = ja.hira_en_kata(str)
	str = mw.ustring.gsub(str, "([アイウエオカ-モヤユヨラ-ロワ-ヴヷ-ヺ]?)([ァィゥェォャュョヮ])",
	                      function (char1, char2)
	                          if char1 == "" or not kana.roman[char1 .. char2] then
	                              return char1 .. mw.ustring.char(mw.ustring.codepoint(char2) + 1)
	                          end
	                      end)

	local mores = 0
	for i in mw.ustring.gmatch(str, "[ァ-ヴヷ-ヺー][ァィゥェォャュョヮ]?") do
		mores = mores + 1
	end
	return mores
end

-- Cette fonction combine un numéral et un compteur.
function ja.combiner_compteur(num, compteur, consonne, hira, kata)
	local numeral = kana.numeraux[num]
	local euphonie = hira or (not kata and mw.ustring.match(compteur, "^[ぁ-ゔ]"))
	compteur = mw.ustring.gsub(compteur, "^([あいうえおアイウエオ])", ".%1") -- initiale vocalique
	if consonne == nil or consonne == "" then
		consonne = mw.ustring.sub(ja.kana_en_roman(compteur), 1, 1)
		if consonne == "f" then consonne = "h" end
	end

	-- modification du numéral
	if (consonne == "k" and (num == 6 or num == 10 or (euphonie and (num == 1 or num == 8))))
	   or (consonne == "s" and (num == 1 or num == 8 or num == 10))
	   or ((consonne == "t" or consonne == "c") and (num == 10 or (euphonie and (num == 1 or num == 8))))
	   or (consonne == "hp" and (num == 1 or num == 6 or num == 8 or num == 10))
	   or (consonne == "h" and (num == 10 or (euphonie and (num == 1 or num == 6 or num == 8)))) then
		numeral = mw.ustring.gsub(numeral, ".$", "っ")
	elseif consonne == "n" and num == 4 then
		numeral = mw.ustring.gsub(numeral, ".$", "")
	end

	-- modification du compteur
	if (consonne == "h" and euphonie and (num == 1 or num == 6 or num == 8 or num == 10))
	   or (consonne == "hp" and (num == 1 or num == 3 or num == 6 or num == 8 or num == 10)) then
		compteur = mw.ustring.gsub(compteur, "^.",
	                               function (char) -- handakuon
	                                  return mw.ustring.char(mw.ustring.codepoint(char) + 2)
	                               end)
	elseif consonne == "h" and euphonie and num == 3 then
		compteur = mw.ustring.gsub(compteur, "^.",
	                               function (char) -- dakuon
	                                  return mw.ustring.char(mw.ustring.codepoint(char) + 1)
	                               end)
	end
	return numeral .. compteur
end

-- Fonctions pour des modèles
function ja.katakana(frame)
	local str = frame.args[1] or ""
	return ja.hira_en_kata(str)
end

function ja.hiragana(frame)
	local str = frame.args[1] or ""
	return ja.kata_en_hira(str)
end

-- Cette fonction retourne true si la chaîne donnée comprend seulement des kanas.
function ja.tous_kana(frame)
	local str = frame.args[1] or ""
	if not mw.ustring.find(str, "[^" .. kana.tous_hiragana .. kana.tous_katakana .. "]") then
		return str
	else
		return nil
	end
end

-- Cette fonction retourne true si la chaîne donnée comprend seulement des hiraganas.
function ja.tous_hira(frame)
	local str = frame.args[1] or ""
	if not mw.ustring.find(str, "[^" .. kana.tous_hiragana .. "]") then
		return str
	else
		return nil
	end
end

-- Cette fonction retourne true si la chaîne donnée comprend seulement des katakanas.
function ja.tous_kata(frame)
	local str = frame.args[1] or ""
	if not mw.ustring.find(str, "[^" .. kana.tous_katakana .. "]") then
		return str
	else
		return nil
	end
end

function ja.normaliser_kana(frame)
	local str = frame.args[1] or ""
	str = mw.ustring.gsub(str, "[%.  ]", "")
	str = mw.ustring.gsub(str, "-$", "") -- préfixe
	str = mw.ustring.gsub(str, "^-", "") -- suffixe
	return str
end

function ja.roman(frame)
	local str = frame.args[1] or ""
	local certain = ((frame.args["certain"] or "") ~= "")
	return ja.kana_en_roman(str, false, certain)
end

function ja.pron(frame)
	local str = frame.args[1] or ""
	local certain = ((frame.args["certain"] or "") ~= "")
	local sans_diphtongue = ((frame.args["sans_diph"] or "") ~= "")
	local acc = tonumber(frame.args["acc"])
	if mw.ustring.find(str, "[ぁ-ゔァ-ヴヷ-ヺー]") then
		sans_diphtongue = sans_diphtongue or not mw.ustring.find(str, "[^ァ-ヴヷ-ヺー・=、 %.]") -- tous katakanas
		str = ja.kana_en_roman(str, true, certain) or ""
	end
	return ja.roman_en_pron(str, certain, sans_diphtongue, acc)
end

function ja.accent(frame)
	local str = frame.args[1] or ""
	local acc = tonumber(frame.args["acc"]) or 0
	return ja.kana_accent(str, acc)
end

function ja.cle_de_tri(frame)
	local str = frame.args[1] or ""
	return ja.kana_sans_longue(str)
end

function ja.compteur(frame)
	local num = tonumber(frame.args[1]) or 1
	local str = frame.args[2] or ""
	local consonne = frame.args[3] or ""
	local hira = frame.args["hira"] and frame.args["hira"] ~= ""
	local kata = frame.args["kata"] and frame.args["kata"] ~= ""
	return ja.combiner_compteur(num, str, consonne, hira, kata)
end

function ja.mores(frame)
	local str = frame.args[1] or ""
	return ja.compter_mores(str)
end

return ja