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

-- To do: add stress in words with >2 syllables (primary and secondary)
--- Reference: 'Research report on phonetic and phonological analysis of Khmer'
--- http://www.panl10n.net/english/Outputs%20Phase%202/CCs/Cambodia/ITC/Papers/2007/0701/phonetic-and-phonological-analysis.pdf
--- Algorithm is simple, though may be inaccurate when automatically applied to multisyllabic words, as some can be 'romanised' in dictionaries as if they are one word but have stress patterns indicating otherwise
--- e.g. [[ប្រជាប្រិយ]]

local export = {}

local gsub = mw.ustring.gsub
local find = mw.ustring.find
local match = mw.ustring.match
local pagename = mw.title.getCurrentTitle().text

local j = "្"
local c = "កខគឃងចឆជឈញដឋឌឍណតថទធនបផពភមយរលវឝឞសហឡអ"
local cMod = "៉៊"
local vDiac = "ាិីឹឺុូួើឿៀេែៃោៅំះៈ័"
local vPost = "់"
local apos = "'"

local kmChar = "ក-៹'"
local kmString = "[" .. kmChar .. "]+"
local recessive = "[ŋɲñnmjrlʋv]"

local cCapt, cUncapt = "([" .. c .. "][" .. cMod .. "]?)", "[" .. c .. "][" .. cMod .. "]?"
local cOptCapt = "([" .. c .. "]?[" .. cMod .. "]?)"
local cCaptClus = {
  "([" .. c .. "][" .. cMod .. "]?)",
  "([" .. c .. "][" .. cMod .. "]?" .. j .. cUncapt .. ")",
  "([" .. c .. "][" .. cMod .. "]?" .. j .. cUncapt .. j .. cUncapt .. ")",
  "([" .. c .. "][" .. cMod .. "]?" .. j .. cUncapt .. j .. cUncapt .. j .. cUncapt .. ")"
}
local vCaptB, vCaptM = "([" .. vDiac .. j .. "])", "([" .. vDiac .. "]*)"
local cvCapt = "([" .. c .. cMod .. vDiac .. "])"
local vPostCapt = "([" .. vPost .. "]?)"

local postInit = vCaptM .. cOptCapt .. vPostCapt .. "(" .. apos .. "?)"

local consonants = {
  ["ក"] = { class = 1, ["ipa"] = { "k", "k" }, ["tc"] = { "k", "k" } },
  ["ខ"] = { class = 1, ["ipa"] = { "kʰ", "k" }, ["tc"] = { "kh", "k" } },
  ["គ"] = { class = 2, ["ipa"] = { "k", "k" }, ["tc"] = { "k", "k" } },
  ["ឃ"] = { class = 2, ["ipa"] = { "kʰ", "k" }, ["tc"] = { "kh", "k" } },
  ["ង"] = { class = 2, ["ipa"] = { "ŋ", "ŋ" }, ["tc"] = { "ng", "ng" } },
  ["ង៉"] = { class = 1, ["ipa"] = { "ŋ", "ŋ" }, ["tc"] = { "ng", "ng" } },
  ["ច"] = { class = 1, ["ipa"] = { "c", "c" }, ["tc"] = { "c", "c" } },
  ["ឆ"] = { class = 1, ["ipa"] = { "cʰ", "c" }, ["tc"] = { "ch", "c" } },
  ["ជ"] = { class = 2, ["ipa"] = { "c", "c" }, ["tc"] = { "c", "c" } },
  ["ឈ"] = { class = 2, ["ipa"] = { "cʰ", "c" }, ["tc"] = { "ch", "c" } },
  ["ញ"] = { class = 2, ["ipa"] = { "ɲ", "ɲ" }, ["tc"] = { "ñ", "ñ" } },
  ["ញ៉"] = { class = 1, ["ipa"] = { "ɲ", "ɲ" }, ["tc"] = { "ñ", "ñ" } },
  ["ដ"] = { class = 1, ["ipa"] = { "ɗ", "t" }, ["tc"] = { "d", "t" } },
  ["ឋ"] = { class = 1, ["ipa"] = { "tʰ", "t" }, ["tc"] = { "th", "t" } },
  ["ឌ"] = { class = 2, ["ipa"] = { "ɗ", "t" }, ["tc"] = { "d", "t" } },
  ["ឍ"] = { class = 2, ["ipa"] = { "tʰ", "t" }, ["tc"] = { "th", "t" } },
  ["ណ"] = { class = 1, ["ipa"] = { "n", "n" }, ["tc"] = { "n", "n" } },
  ["ត"] = { class = 1, ["ipa"] = { "t", "t" }, ["tc"] = { "t", "t" } },
  ["ថ"] = { class = 1, ["ipa"] = { "tʰ", "t" }, ["tc"] = { "th", "t" } },
  ["ទ"] = { class = 2, ["ipa"] = { "t", "t" }, ["tc"] = { "t", "t" } },
  ["ធ"] = { class = 2, ["ipa"] = { "tʰ", "t" }, ["tc"] = { "th", "t" } },
  ["ន"] = { class = 2, ["ipa"] = { "n", "n" }, ["tc"] = { "n", "n" } },
  ["ន៉"] = { class = 1, ["ipa"] = { "n", "n" }, ["tc"] = { "n", "n" } },
  ["ប"] = { class = 1, ["ipa"] = { "ɓ", "p" }, ["tc"] = { "b", "p" } },
  ["ប៉"] = { class = 1, ["ipa"] = { "p", "p" }, ["tc"] = { "p", "p" } },
  ["ប៊"] = { class = 2, ["ipa"] = { "ɓ", "p" }, ["tc"] = { "b", "p" } },
  ["ផ"] = { class = 1, ["ipa"] = { "pʰ", "p" }, ["tc"] = { "ph", "p" } },
  ["ព"] = { class = 2, ["ipa"] = { "p", "p" }, ["tc"] = { "p", "p" } },
  ["ភ"] = { class = 2, ["ipa"] = { "pʰ", "p" }, ["tc"] = { "ph", "p" } },
  ["ម"] = { class = 2, ["ipa"] = { "m", "m" }, ["tc"] = { "m", "m" } },
  ["ម៉"] = { class = 1, ["ipa"] = { "m", "m" }, ["tc"] = { "m", "m" } },
  ["យ"] = { class = 2, ["ipa"] = { "j", "j" }, ["tc"] = { "y", "y" } },
  ["យ៉"] = { class = 1, ["ipa"] = { "j", "j" }, ["tc"] = { "y", "y" } },
  ["រ"] = { class = 2, ["ipa"] = { "r", "" }, ["tc"] = { "r", "" } },
  ["រ៉"] = { class = 1, ["ipa"] = { "r", "" }, ["tc"] = { "r", "" } },
  ["ល"] = { class = 2, ["ipa"] = { "l", "l" }, ["tc"] = { "l", "l" } },
  ["ល៉"] = { class = 1, ["ipa"] = { "l", "l" }, ["tc"] = { "l", "l" } },
  ["វ"] = { class = 2, ["ipa"] = { "ʋ", "w" }, ["tc"] = { "v", "w" } },
  ["វ៉"] = { class = 1, ["ipa"] = { "ʋ", "w" }, ["tc"] = { "v", "w" } },
  ["ឝ"] = { class = 1, ["ipa"] = { "s", "h" }, ["tc"] = { "s", "h" } },
  ["ឞ"] = { class = 1, ["ipa"] = { "s", "h" }, ["tc"] = { "s", "h" } },
  ["ស"] = { class = 1, ["ipa"] = { "s", "h" }, ["tc"] = { "s", "h" } },
  ["ស៊"] = { class = 2, ["ipa"] = { "s", "h" }, ["tc"] = { "s", "h" } },
  ["ហ"] = { class = 1, ["ipa"] = { "h", "h" }, ["tc"] = { "h", "h" } },
  ["ហ៊"] = { class = 2, ["ipa"] = { "h", "h" }, ["tc"] = { "h", "h" } },
  ["ឡ"] = { class = 1, ["ipa"] = { "l", "l" }, ["tc"] = { "l", "l" } },
  ["អ"] = { class = 1, ["ipa"] = { "ʔ", "" }, ["tc"] = { "ʾ", "ʾ" } },
  ["អ៊"] = { class = 2, ["ipa"] = { "ʔ", "" }, ["tc"] = { "ʾ", "ʾ" } },

  ["ហគ"] = { class = 1, ["ipa"] = { "ɡ", "k" }, ["tc"] = { "g", "k" } },
  ["ហគ៊"] = { class = 2, ["ipa"] = { "ɡ", "k" }, ["tc"] = { "g", "k" } },
  ["ហ៊គ"] = { class = 2, ["ipa"] = { "ɡ", "k" }, ["tc"] = { "g", "k" } },
  ["ហន"] = { class = 1, ["ipa"] = { "n", "" }, ["tc"] = { "n", "n" } },
  ["ហម"] = { class = 1, ["ipa"] = { "m", "" }, ["tc"] = { "m", "m" } },
  ["ហល"] = { class = 1, ["ipa"] = { "l", "" }, ["tc"] = { "l", "l" } },
  ["ហវ"] = { class = 1, ["ipa"] = { "f", "f" }, ["tc"] = { "f", "f" } },
  ["ហវ៊"] = { class = 2, ["ipa"] = { "f", "f" }, ["tc"] = { "f", "f" } },
  ["ហ៊វ"] = { class = 2, ["ipa"] = { "f", "f" }, ["tc"] = { "f", "f" } },
  ["ហស"] = { class = 1, ["ipa"] = { "z", "z" }, ["tc"] = { "z", "z" } },
  ["ហស៊"] = { class = 2, ["ipa"] = { "z", "z" }, ["tc"] = { "z", "z" } },
  ["ហ៊ស"] = { class = 2, ["ipa"] = { "z", "z" }, ["tc"] = { "z", "z" } },

  [""] = { class = 1, ["ipa"] = { "", "" }, ["tc"] = { "", "" } },
}

local vowels = {
  [""] = { ["ipa"] = { "ɑː", "ɔː" }, ["tc"] = { "ɑɑ", "ɔɔ" } },
  ["់"] = { ["ipa"] = { "ɑ", "ŭə" }, ["tc"] = { "ɑ", "ŭə" } },
  ["់2"] = { ["ipa"] = { "ɑ", "u" }, ["tc"] = { "ɑ", "u" } }, --before labial finals
  ["័"] = { ["ipa"] = { "a", "ŏə" }, ["tc"] = { "a", "ŏə" } },
  ["័2"] = { ["ipa"] = { "a", "ĕə" }, ["tc"] = { "a", "ĕə" } }, --before velar finals
  ["័យ"] = { ["ipa"] = { "aj", "ɨj" }, ["tc"] = { "ay", "ɨy" } },
  ["័រ"] = { ["ipa"] = { "", "ɔə" }, ["tc"] = { "", "ɔə" } },
  ["ា"] = { ["ipa"] = { "aː", "iə" }, ["tc"] = { "aa", "iə" } },
  ["ា់"] = { ["ipa"] = { "a", "ŏə" }, ["tc"] = { "a", "ŏə" } },
  ["ា់2"] = { ["ipa"] = { "a", "ĕə" }, ["tc"] = { "a", "ĕə" } }, --before velar finals
  ["ិ"] = { ["ipa"] = { "eʔ", "iʔ" }, ["tc"] = { "eʾ", "iʾ" } }, --glottal coda only in stressed syllables
  ["ិ2"] = { ["ipa"] = { "ə", "ɨ" }, ["tc"] = { "ə", "ɨ" } }, --with non-glottal coda
  ["ិយ"] = { ["ipa"] = { "əj", "iː" }, ["tc"] = { "əy", "ii" } },
  ["ិះ"] = { ["ipa"] = { "eh", "ih" }, ["tc"] = { "eh", "ih" } }, --["tc"] inferred
  ["ី"] = { ["ipa"] = { "əj", "iː" }, ["tc"] = { "əy", "ii" } },
  ["ឹ"] = { ["ipa"] = { "ə", "ɨ" }, ["tc"] = { "ə", "ɨ" } },
  ["ឹះ"] = { ["ipa"] = { "əh", "ɨh" }, ["tc"] = { "əh", "ɨh" } },
  ["ឺ"] = { ["ipa"] = { "əɨ", "ɨː" }, ["tc"] = { "əɨ", "ɨɨ" } },
  ["ុ"] = { ["ipa"] = { "oʔ", "uʔ" }, ["tc"] = { "oʾ", "uʾ" } }, --glottal coda only in stressed syllables
  ["ុ2"] = { ["ipa"] = { "o", "u" }, ["tc"] = { "o", "u"  }}, --with non-glottal coda
  ["ុះ"] = { ["ipa"] = { "oh", "uh" }, ["tc"] = { "oh", "uh" } },
  ["ូ"] = { ["ipa"] = { "ou", "uː" }, ["tc"] = { "ou", "uu" } },
  ["ូវ"] = { ["ipa"] = { "əw", "ɨw" }, ["tc"] = { "əw", "ɨw" } },
  ["ួ"] = { ["ipa"] = { "uə", "uə" }, ["tc"] = { "uə", "uə" } },
  ["ើ"] = { ["ipa"] = { "aə", "əː" }, ["tc"] = { "aə", "əə" } },
  ["ើះ"] = { ["ipa"] = { "əh", "" }, ["tc"] = { "əh", "" } },
  ["ឿ"] = { ["ipa"] = { "ɨə", "ɨə" }, ["tc"] = { "ɨə", "ɨə" } },
  ["ៀ"] = { ["ipa"] = { "iə", "iə" }, ["tc"] = { "iə", "iə" } },
  ["េ"] = { ["ipa"] = { "eː", "ei" }, ["tc"] = { "ee", "ei" } },
  ["េ2"] = { ["ipa"] = { "ə", "ɨ" }, ["tc"] = { "ə", "ɨ" } }, --before palatals
  ["េះ"] = { ["ipa"] = { "eh", "ih" }, ["tc"] = { "eh", "ih" } },
  ["ែ"] = { ["ipa"] = { "ae", "ɛː" }, ["tc"] = { "ae", "ɛɛ" } },
  ["ែះ"] = { ["ipa"] = { "eh", "" }, ["tc"] = { "eh", "" } },
  ["ៃ"] = { ["ipa"] = { "aj", "ɨj" }, ["tc"] = { "ay", "ɨy" } },
  ["ោ"] = { ["ipa"] = { "ao", "oː" }, ["tc"] = { "ao", "oo" } },
  ["ោះ"] = { ["ipa"] = { "ɑh", "ŭəh" }, ["tc"] = { "ɑh", "ŭəh" } },
  ["ៅ"] = { ["ipa"] = { "aw", "ɨw" }, ["tc"] = { "aw", "ɨw" } },
  ["ុំ"] = { ["ipa"] = { "om", "um" }, ["tc"] = { "om", "um" } },
  ["ំ"] = { ["ipa"] = { "ɑm", "um" }, ["tc"] = { "ɑm", "um" } },
  ["ាំ"] = { ["ipa"] = { "am", "ŏəm" }, ["tc"] = { "am", "ŏəm" } },
  ["ាំង"] = { ["ipa"] = { "aŋ", "ĕəŋ" }, ["tc"] = { "ang", "ĕəng" } },
  ["ះ"] = { ["ipa"] = { "ah", "ĕəh" }, ["tc"] = { "ah", "ĕəh" } },
  ["ៈ"] = { ["ipa"] = { "aʔ", "ĕəʔ" }, ["tc"] = { "aʾ", "ĕəʾ" } },
  ["'"] = { ["ipa"] = { "ə", "ə" }, ["tc"] = { "ə", "ə" } },
}

local tl = {
  ["ក"] = "k", ["ខ"] = "kʰ", ["គ"] = "g", ["ឃ"] = "gʰ", ["ង"] = "ṅ",
  ["ច"] = "c", ["ឆ"] = "cʰ", ["ជ"] = "j", ["ឈ"] = "jʰ", ["ញ"] = "ñ",
  ["ដ"] = "ṭ", ["ឋ"] = "ṭʰ", ["ឌ"] = "ḍ", ["ឍ"] = "ḍʰ", ["ណ"] = "ṇ",
  ["ត"] = "t", ["ថ"] = "tʰ", ["ទ"] = "d", ["ធ"] = "dʰ", ["ន"] = "n",
  ["ប"] = "p", ["ផ"] = "pʰ", ["ព"] = "b", ["ភ"] = "bʰ", ["ម"] = "m",
  ["យ"] = "y", ["រ"] = "r", ["ល"] = "l", ["វ"] = "v",
  ["ឝ"] = "ś", ["ឞ"] = "ṣ", ["ស"] = "s",
  ["ហ"] = "h", ["ឡ"] = "ḷ", ["អ"] = "ʾ",

  ["ឣ"] = "a", ["ឤ"] = "ā", ["ឥ"] = "i", ["ឦ"] = "ī",
  ["ឧ"] = "u", ["ឨ"] = "🤷", ["ឩ"] = "ū", ["ឪ"] = "ýu",
  ["ឫ"] = "ṛ", ["ឬ"] = "ṝ", ["ឭ"] = "ḷ", ["ឮ"] = "ḹ",
  ["ឯ"] = "ae", ["ឰ"] = "ai", ["ឱ"] = "o", ["ឲ"] = "o", ["ឳ"] = "au",

  ["ា"] = "ā", ["ិ"] = "i", ["ី"] = "ī", ["ឹ"] = "ẏ", ["ឺ"] = "ȳ",
  ["ុ"] = "u", ["ូ"] = "ū", ["ួ"] = "ua",
  ["ើ"] = "oe", ["ឿ"] = "ẏa", ["ៀ"] = "ia",
  ["េ"] = "e", ["ែ"] = "ae", ["ៃ"] = "ai", ["ោ"] = "o", ["ៅ"] = "au",
  ["ំ"] = "ṃ", ["ះ"] = "ḥ", ["ៈ"] = "`",

  ["៉"] = "″", ["៊"] = "′", ["់"] = "´", ["៌"] = "ŕ", ["៍"] = "̊",
  ["៎"] = "⸗", ["៏"] = "ʿ", ["័"] = "˘", ["៑"] = "̑", ["្"] = "̥",
  ["៓"] = "🤷", ["។"] = "ǂ", ["៕"] = "ǁ", ["៖"] = "🤷", ["ៗ"] = "«",
  ["៘"] = "🤷", ["៙"] = "§", ["៚"] = "»", ["៛"] = "",
  ["ៜ"] = "🤷", ["៝"] = "🤷",

  ["០"] = "0", ["១"] = "1", ["២"] = "2", ["៣"] = "3", ["៤"] = "4",
  ["៥"] = "5", ["៦"] = "6", ["៧"] = "7", ["៨"] = "8", ["៩"] = "9",
  ["៰"] = "🤷", ["៱"] = "🤷", ["៲"] = "🤷", ["៳"] = "🤷", ["៴"] = "🤷",
  ["៵"] = "🤷", ["៶"] = "🤷", ["៷"] = "🤷", ["៸"] = "🤷", ["៹"] = "🤷",
}

local glottify = {
  ["a"] = 1, ["aː"] = 1, ["ɑ"] = 1, ["ɑː"] = 1, ["ɔ"] = 1, ["ɔː"] = 1,
  ["ĕə"] = 1, ["ŭə"] = 1, ["iə"] = 1, ["ɨə"] = 1, ["uə"] = 1
}

local err = {
  ["័"] = 1, ["័រ"] = 1,
}

local ambig = {
  ["k%-h"] = "k​h", ["c%-h"] = "c​h", ["t%-h"] = "t​h", ["p%-h"] = "p​h",
  ["n%-g"] = "n​g",
}

function export.syllabify(text)
  text = gsub(text, "([%'់])([^,%- ])", "%1-%2")
  local seq1 = cvCapt .. cCapt .. vCaptB
  while find(text, seq1) do text = gsub(text, seq1, "%1-%2%3") end
  return text
end

function export.syl_analysis(syllable)
  for ind = 4, 1, -1 do
    if match(syllable, "^" .. cCaptClus[ind] .. postInit .. "$") then
      return match(syllable, "^" .. cCaptClus[ind] .. postInit .. "$")
    end
  end
  return nil
end

local function sylRedist(text, block)
  for word in mw.ustring.gmatch(text, "[" .. kmChar .. "%-]+") do
    local originalWord = word
    local allSyl, syls, newWord = {}, mw.text.split(word, "%-"), {}
    for sylId = 1, #syls do
      if syls[sylId] == "" then table.insert(allSyl, {})
      else
        local set = export.syl_analysis(syls[sylId])
        if not set or set == "" then return nil end
        table.insert(allSyl, { export.syl_analysis(syls[sylId]) })
        if sylId ~= 1 and allSyl[sylId - 1][3] == "" and find(allSyl[sylId][1], j) and not block then
          allSyl[sylId - 1][3], allSyl[sylId][1] =
          match(allSyl[sylId][1], "^([^" .. j .. "]+)"),
          match(allSyl[sylId][1], "^[^" .. j .. "]+" .. j .. "(.+)")
        end
        if #syls == 2 and sylId == 2 and allSyl[sylId - 1][2] .. allSyl[sylId - 1][4] == "" then
          allSyl[sylId - 1][4] = vPost
        end
      end
    end
    for sylId = 1, #syls do
      table.insert(newWord, table.concat(allSyl[sylId]))
    end
    text = gsub(text, (gsub(originalWord, "%-", "%-")), table.concat(newWord, "%-"), 1)
  end
  return text
end

local function getCons(c1Set)
  local c1l, i, consSet = #c1Set, 1, {}
  while i < c1l + 1 do
    for k = 3, 1, -1 do
      local conss = i + k - 1 > c1l and "a" or table.concat(c1Set, "", i, i + k - 1)
      if consonants[conss] then
        table.insert(consSet, conss)
        i = i + k
        break
      end
      if k == 1 then return nil end
    end
  end
  return consSet
end

local function initClus(c1, mode)
  local fittest, init, pos = "", {}, 1
  c1 = gsub(c1, j, "")
  if consonants[c1] then
    local cData = consonants[c1]
    c1, fittest = cData[mode][pos], cData.class
  else
    local consSet = getCons(mw.text.split(c1, ""))
    if not consSet then return error("Erreur à l’initiale " .. c1 .. ".") end
    for seq, ch in ipairs(consSet) do
      local cData = consonants[ch]
      fittest = (not find(cData[mode][pos], recessive) and not find(cData[mode][pos], "ng")
          or (fittest == "" and seq == #consSet))
          and cData.class or fittest
      table.insert(init, cData[mode][pos])
    end
    c1 = table.concat(init)
  end

  c1 = gsub(c1, "[ɓb](.)", "p%1")
  c1 = gsub(c1, "[ɗd](.)", "t%1")
  if mode == "ipa" then
    c1 = gsub(c1, "p([knŋcɲdtnjls])", "pʰ%1")
    c1 = gsub(c1, "pʰ([^knŋcɲdtnjls])", "p%1")
    c1 = gsub(c1, "t([kŋnmjlʋ])", "tʰ%1")
    c1 = gsub(c1, "tʰ([^kŋnmjlʋ])", "t%1")
    c1 = gsub(c1, "k([ctnbmlʋs])", "kʰ%1")
    c1 = gsub(c1, "kʰ([^ctnbmlʋs])", "k%1")
    c1 = gsub(c1, "c([kŋnmlʋʔ])", "cʰ%1")
    c1 = gsub(c1, "cʰ([^kŋnmlʋʔ])", "c%1")
  end
  return c1, fittest
end

local function rime(v1, c2, fittest, red, mode)
  if red == apos then v1 = red end
  if vowels[v1 .. c2] then return vowels[v1 .. c2][mode][fittest] end
  c2 = consonants[c2][mode][2] or c2
  if ((v1 == "័" or v1 == "ា់") and (find(c2, "[kŋ]") or c2 == "ng")) or
      (v1 == "េ" and (find(c2, "[cɲ]") or c2 == "ñ")) or
      (v1 == "់" and find(c2, "[mp]")) or
      ((v1 == "ិ" or v1 == "ុ") and c2 ~= "") then
    v1 = v1 .. "2"
  end
  v1 = vowels[v1] and vowels[v1][mode][fittest] or v1
  if (glottify[v1] and mode == "ipa") and c2 == "k" then c2 = "ʡ" end --proxy
  return v1 .. c2
end

function export.convert(text, mode, source)
  local block = find(text, "%-")
  text = sylRedist(export.syllabify(text), block)
  if not text then return nil end

  for syllable in mw.ustring.gmatch(text, kmString) do
    local unchanged = syllable
    local c1, v1, c2, bantak, red = export.syl_analysis(syllable)
    if not c1 then return nil end
    local fittest
    c1, fittest = initClus(c1, mode)
    local v1c2 = rime(v1 .. bantak, c2, fittest, red, mode)
    if not v1c2 then return nil end
    text = gsub(text, unchanged, c1 .. v1c2, 1)
  end

  text = gsub(text, "(.%%%-.)", ambig)
  text = gsub(text, "%%", "")
  text = gsub(text, "%-", ".")
  text = gsub(text, "​", "-")
  text = gsub(text, "ʔ([ptkhlɲŋmnjw])", "%1")
  text = gsub(text, "ŭə%.", "ɔ.")
  text = gsub(text, "([eiou])[ʔʾ]%.", "%1.")
  text = gsub(text, "ʡ%.s", "k.s")
  text = gsub(text, "ʡ", "ʔ")
  if mode == "tc" then
    text = gsub(text, "%.%.%.", "…")
    text = gsub(text, "%.", "")
  else
    text = gsub(text, "%-", ".")
    local readings = {}
    for reading in mw.text.gsplit(text, ", ") do
      table.insert(readings, (gsub(reading, "^([^%.]+)%.([^%.]+)$", "%1.ˈ%2")))
    end
    text = table.concat(readings, ", ")
    text = gsub(text, "^([^%. ]+) ([^%. ]+)$", "%1 ˈ%2")
  end
  return text
end

local function return_error()
  return error("Le titre de la page contient un espace sans chasse (zero width space). Veuillez le retirer.")
end

function export.make(frame)
  local params = {
    [1] = { list = true },
    ["a"] = {},
    ["audio"] = { alias_of = "a" },
    ["word"] = { default = pagename },
  }
  local args = require("Module:parameters").process(frame:getParent().args, params)
  local output_text, respellings, transcriptions, ipas = {}, {}, {}, {}

  if find(pagename, "​") then return_error() end
  if #args[1] == 0  then args[1] = { args.word } end
  for _, param in ipairs(args[1]) do
    if find(param, "​") then return_error() end
    table.insert(respellings, export.syllabify(param))
    table.insert(transcriptions, export.convert(param, "tc", "temp"))
    table.insert(ipas, export.convert(param, "ipa"))
  end
  local separate = (gsub(table.concat(respellings), "[%- ]", "")) ~= args.word
  local respelling = table.concat(respellings, " / ")

  local function row(a, b)
    return "\n<tr>" ..

        tostring( mw.html.create( "td" )
                    :css( "padding-right", "0.8em" )
                    :css( "padding-left", "0.7em" )
                    :css( "font-size", "10.5pt" )
                    :css( "font-family", "sans-serif" )
                    :css( "color", "#555" )
                    :css( "font-weight", "bold" )
                    :css( "background-color", "#F8F9F8" )
                    :wikitext(a)) .. "\n" ..

        tostring( mw.html.create( "td" )
                    :css( "padding-left", "0.8em" )
                    :css( "padding-right", "0.8em" )
                    :css( "padding-top", ".4em" )
                    :css( "padding-bottom", ".4em" )
                    :wikitext(b)) ..

        "</tr>"
  end

  local function textFormat(text, class, size, lang)
    return tostring( mw.html.create( "span" )
                       :attr( "class", class or "Khmr" )
                       :css( "font-size", size or (class == "IPA" and "95%" or "130%") )
                       :attr( "lang", lang or (class == "IPA" and nil or "km") )
                       :wikitext(text))
  end

  table.insert(output_text,
      [=[{| style="margin: 0 .4em .4em .4em"
      |
      <table cellpadding=1 style="border: 1px solid #DFDFDF; text-align: center; line-height: 25pt; padding: .1em .3em .1em .3em">]=] ..

          row(separate
              and "''[[w:Alphasyllabaire khmer|Orthographique]]''"
              or "''[[w:Alphasyllabaire khmer|Orthographique et phonémique]]''",
              textFormat(args.word) .. "<br>" .. textFormat(gsub(gsub(args.word, ".", tl), "ʰ̥", "̥ʰ"), "IPA")
          ) ..

          (separate
              and row("''[[w:Alphasyllabaire khmer|Phonémique]]''",
              textFormat(respelling) .. "<br>" ..
                  textFormat(gsub(gsub(respelling, ".", tl), "ʰ̥", "̥ʰ"), "IPA"))
              or "") ..

          row("''Romanisation''",
              textFormat(table.concat(transcriptions, ", "), "IPA", "100%")
          ) ..

          row(
              "(''[[w:Alphasyllabaire khmer|standard]]'') [[w:Alphabet phonétique international|API]]",
              textFormat("/" .. table.concat(ipas, "/ ~ /") .. "/", "IPA", "110%")
          ) ..

          (args.a
              and row("Audio", mw.getCurrentFrame():expandTemplate{
            title = "Modèle:écouter",
            args = { args.a == "y" and "Km-" .. args.word .. ".ogg" or args.a, lang = "km" }} )
              or ""
          ) ..

          "</table>\n|}")

  return table.concat(output_text)
end

return export