Module:Th-rm
Appearance
| Main | Documentation | Usage |
This module implements {{th-rm}}.
local p = {}
local U = mw.ustring
local gsub = U.gsub
local find = U.find
local sub = U.sub
local match = U.match
local len = U.len
local INITIAL = {
['ก'] = 'k',
['ข'] = 'kh', ['ฃ'] = 'kh', ['ค'] = 'kh', ['ฅ'] = 'kh', ['ฆ'] = 'kh',
['ง'] = 'ng',
['จ'] = 'ch', ['ฉ'] = 'ch', ['ช'] = 'ch',
['ซ'] = 's',
['ฌ'] = 'ch',
['ญ'] = 'y',
['ฎ'] = 'd',
['ฏ'] = 't',
['ฐ'] = 'th', ['ฑ'] = 'th', ['ฒ'] = 'th',
['ณ'] = 'n',
['ด'] = 'd',
['ต'] = 't',
['ถ'] = 'th', ['ท'] = 'th', ['ธ'] = 'th',
['น'] = 'n',
['บ'] = 'b',
['ป'] = 'p',
['ผ'] = 'ph',
['ฝ'] = 'f',
['พ'] = 'ph',
['ฟ'] = 'f',
['ภ'] = 'ph',
['ม'] = 'm',
['ย'] = 'y',
['ร'] = 'r',
['ล'] = 'l',
['ว'] = 'w',
['ศ'] = 's', ['ษ'] = 's', ['ส'] = 's',
['ห'] = 'h',
['ฬ'] = 'l',
['ฮ'] = 'h'
}
local FINAL = {
['ก'] = 'k',
['ข'] = 'k', ['ฃ'] = 'k', ['ค'] = 'k', ['ฅ'] = 'k', ['ฆ'] = 'k',
['ง'] = 'ng',
['จ'] = 't', ['ฉ'] = 't', ['ช'] = 't', ['ฌ'] = '',
['ซ'] = 't',
['ญ'] = 'n',
['ฎ'] = 't', ['ฏ'] = 't',
['ฐ'] = 't', ['ฑ'] = 't', ['ฒ'] = 't',
['ณ'] = 'n',
['ด'] = 't',
['ต'] = 't',
['ถ'] = 't', ['ท'] = 't', ['ธ'] = 't',
['น'] = 'n',
['บ'] = 'p',
['ป'] = 'p',
['ผ'] = '', ['ฝ'] = '',
['พ'] = 'p', ['ฟ'] = 'p', ['ภ'] = 'p',
['ม'] = 'm',
['ย'] = '',
['ร'] = 'n',
['ล'] = 'n',
['ว'] = '',
['ศ'] = 't', ['ษ'] = 't', ['ส'] = 't',
['ห'] = '',
['ฬ'] = 'n',
['ฮ'] = ''
}
-- Vowel patterns are tested longest-first.
local VOWELS = {
{ 'เ' , 'ะ', 'e' },
{ 'เ' , '็', 'e' },
{ 'เ' , '', 'e' },
{ 'แ' , 'ะ', 'ae' },
{ 'แ' , '', 'ae' },
{ 'โ' , 'ะ', 'o' },
{ 'โ' , '', 'o' },
{ 'เ' , 'าะ', 'o' },
{ '' , 'อ', 'o' },
{ 'เ' , 'อะ', 'oe' },
{ 'เ' , 'ิ', 'oe' },
{ 'เ' , 'อ', 'oe' },
{ 'เ' , 'ียะ', 'ia' },
{ 'เ' , 'ีย', 'ia' },
{ 'เ' , 'ือะ', 'uea' },
{ 'เ' , 'ือ', 'uea' },
{ '' , 'ัวะ', 'ua' },
{ '' , 'ัว', 'ua' },
{ '' , 'ว', 'ua' },
{ 'ใ', '', 'ai' },
{ 'ไ', '', 'ai' },
{ '' , 'ัย', 'ai' },
{ 'ไ', 'ย', 'ai' },
{ '' , 'าย', 'ai' },
{ 'เ', 'า', 'ao' },
{ '' , 'าว', 'ao' },
{ '' , 'ุย', 'ui' },
{ 'โ', 'ย', 'oi' },
{ '' , 'อย', 'oi' },
{ 'เ', 'ย', 'oei' },
{ 'เ', 'ือย', 'ueai' },
{ '' , 'วย', 'uai' },
{ '' , 'ิว', 'io' },
{ 'เ', '็ว', 'eo' },
{ 'เ', 'ว', 'eo' },
{ 'แ', '็ว', 'aeo' },
{ 'แ', 'ว', 'aeo' },
{ 'เ', 'ียว', 'iao' },
{ '' , 'ะ', 'a' },
{ '' , 'ั', 'a' },
{ '' , 'า', 'a' },
{ '' , 'ำ', 'am' },
{ '' , 'ิ', 'i' },
{ '' , 'ี', 'i' },
{ '' , 'ึ', 'ue' },
{ '' , 'ื', 'ue' },
{ '' , 'ือ', 'ue' },
{ '' , 'ุ', 'u' },
{ '' , 'ู', 'u' },
{ '' , 'รร', 'a' }
}
local function isConsonant(c)
return c and INITIAL[c] ~= nil
end
local function isThai(c)
return c and match(c, '[ก-๛]') ~= nil
end
local function isToneMark(c)
return c and match(c, '[่้๊๋]') ~= nil
end
local function isSilentMark(c)
return c == '์'
end
local function clean(str)
str = mw.text.unstrip(str or '')
str = gsub(str, '<br%s*/?>', ' ')
str = gsub(str, '<[^>]+>', '')
return str
end
local function applyOrthographicExceptions(str)
-- Silent letters marked with ์.
-- Remove the marked letter and its ์ marker.
str = gsub(str, '([ก-ฮ])์', '')
-- Tone marks have no RTGS representation.
str = gsub(str, '[่้๊๋]', '')
-- ฤ
str = gsub(str, 'ฤกษ์', 'roe')
str = gsub(str, 'ฤิ', 'ri')
str = gsub(str, 'ฤ', 'rue')
str = gsub(str, 'ฤๅ', 'rue')
-- ฦ / ฦๅ
str = gsub(str, 'ฦๅ', 'lue')
str = gsub(str, 'ฦ', 'lue')
return str
end
local function getVowel(str, pos)
local remaining = sub(str, pos, -1)
-- Explicit special vowel patterns.
local patterns = {
{ 'เ', 'ื', 'อ', 'ย', 'ueai' },
{ 'เ', 'ี', 'ย', 'ว', 'iao' },
{ 'เ', 'ี', 'ย', 'ะ', 'ia' },
{ 'เ', 'ี', 'ย', '', 'ia' },
{ 'เ', 'ื', 'อ', 'ะ', 'uea' },
{ 'เ', 'ื', 'อ', '', 'uea' },
{ 'เ', '็', 'ว', '', 'eo' },
{ 'เ', 'ว', '', '', 'eo' },
{ 'แ', '็', 'ว', '', 'aeo' },
{ 'แ', 'ว', '', '', 'aeo' },
{ 'โ', 'ย', '', '', 'oi' },
{ '', 'อ', 'ย', '', 'oi' },
{ 'เ', 'ย', '', '', 'oei' },
{ '', 'ว', 'ย', '', 'uai' },
{ '', 'ุ', 'ย', '', 'ui' },
{ '', 'ิ', 'ว', '', 'io' },
{ 'เ', 'า', '', '', 'ao' },
{ '', 'า', 'ว', '', 'ao' },
{ '', 'ั', 'ว', 'ะ', 'ua' },
{ '', 'ั', 'ว', '', 'ua' },
{ '', 'ว', '', '', 'ua' },
{ 'ใ', '', '', '', 'ai' },
{ 'ไ', '', '', '', 'ai' },
{ '', 'ั', 'ย', '', 'ai' },
{ 'ไ', 'ย', '', '', 'ai' },
{ '', 'า', 'ย', '', 'ai' },
{ 'เ', 'า', 'ะ', '', 'ao' },
{ 'โ', 'ะ', '', '', 'o' },
{ 'เ', 'า', '', '', 'ao' },
{ 'เ', 'าะ', '', '', 'o' },
{ 'เ', 'อ', '', '', 'oe' },
{ 'เ', 'ิ', '', '', 'oe' },
{ 'เ', 'อะ', '', '', 'oe' },
{ 'แ', 'ะ', '', '', 'ae' },
{ 'แ', '', '', '', 'ae' },
{ 'เ', 'ะ', '', '', 'e' },
{ 'เ', '็', '', '', 'e' },
{ 'เ', '', '', '', 'e' },
{ 'โ', '', '', '', 'o' },
{ '', 'ำ', '', '', 'am' },
{ '', 'ะ', '', '', 'a' },
{ '', 'ั', '', '', 'a' },
{ '', 'า', '', '', 'a' },
{ '', 'ิ', '', '', 'i' },
{ '', 'ี', '', '', 'i' },
{ '', 'ึ', '', '', 'ue' },
{ '', 'ือ', '', '', 'ue' },
{ '', 'ื', '', '', 'ue' },
{ '', 'ุ', '', '', 'u' },
{ '', 'ู', '', '', 'u' },
{ '', 'อ', '', '', 'o' }
}
for _, v in ipairs(patterns) do
local a, b, c, d, roman = v
local pattern = a .. b .. c .. d
if pattern ~= '' and sub(remaining, 1, len(pattern)) == pattern then
return roman, len(pattern)
end
end
-- Implied a.
return 'a', 0
end
local function nextVowelPosition(str, pos)
local i = pos
while i <= len(str) do
local c = sub(str, i, i)
if c == 'เ' or c == 'แ' or c == 'โ' or c == 'ใ' or c == 'ไ' then
return i
end
if match(c, '[ะาิีึืุูัำอ]') then
return i
end
i = i + 1
end
return nil
end
local function romanizeSyllable(str, startPos)
local pos = startPos
local n = len(str)
-- Preposed vowel.
local preVowel = ''
local preLen = 0
local c = sub(str, pos, pos)
if c == 'เ' or c == 'แ' or c == 'โ' or c == 'ใ' or c == 'ไ' then
preVowel = c
preLen = 1
pos = pos + 1
end
-- Initial consonant.
local initial = sub(str, pos, pos)
if not isConsonant(initial) then
-- Non-Thai character.
return initial, 1
end
pos = pos + 1
local initialRom = INITIAL[initial] or ''
-- ห- exception.
if initial == 'ห' then
local second = sub(str, pos, pos)
if second and match(second, '[งนมยรลวญณฬ]') then
initialRom = INITIAL[second]
pos = pos + 1
initial = second
end
end
-- Consonant clusters where ร is not represented.
if (initial == 'จ' or initial == 'ซ' or initial == 'ศ' or initial == 'ส')
and sub(str, pos, pos) == 'ร' then
initialRom = INITIAL[initial]
pos = pos + 1
end
-- Find the vowel from the current position, including preposed vowels.
local vowelStart = startPos + preLen + 1
local vowelRoman
local vowelLen
if preLen > 0 then
vowelRoman, vowelLen = getVowel(str, startPos + 1)
else
vowelRoman, vowelLen = getVowel(str, pos)
end
-- If the vowel was explicitly consumed by getVowel, advance.
if vowelLen > 0 then
if preLen > 0 then
pos = startPos + preLen + 1 + vowelLen
else
pos = pos + vowelLen
end
end
-- Collect possible final consonant.
local final = sub(str, pos, pos)
local finalRom = ''
if isConsonant(final) then
local after = sub(str, pos + 1, pos + 1)
-- A consonant followed by a vowel-starting syllable is normally
-- treated as the next syllable's initial rather than this syllable's final.
local nextIsVowel = after and (
match(after, '[ะาิีึืุูัำอ]') or
match(after, '[เแโใไ]')
)
if not nextIsVowel then
finalRom = FINAL[final] or ''
pos = pos + 1
-- -ร following another final consonant is silent in RTGS.
if final == 'ร' then
finalRom = 'n'
end
end
end
-- ทร- exception.
if initial == 'ท' and sub(str, pos, pos) == 'ร' then
initialRom = 's'
pos = pos + 1
end
return initialRom .. vowelRoman .. finalRom, math.max(1, pos - startPos)
end
local function romanizeWord(word)
if word == '' then
return ''
end
-- Manual markers:
-- < > = force space
-- <-> = force hyphen
word = gsub(word, '< >', ' ')
word = gsub(word, '<->', '-')
local output = {}
local pos = 1
local n = len(word)
while pos <= n do
local c = sub(word, pos, pos)
if c == '<' then
local close = find(word, '>', pos + 1, true)
if close then
pos = close + 1
else
table.insert(output, c)
pos = pos + 1
end
elseif not isThai(c) then
table.insert(output, c)
pos = pos + 1
else
local roman, consumed = romanizeSyllable(word, pos)
table.insert(output, roman)
pos = pos + consumed
end
end
return table.concat(output)
end
local function capitalize(str, sentence)
local chars = mw.text.split(str, '')
local shouldCap = true
for i, c in ipairs(chars) do
if match(c, '%a') then
if shouldCap then
chars[i] = U.upper(c)
shouldCap = false
elseif sentence then
shouldCap = false
end
elseif c == ' ' then
if sentence then
shouldCap = true
else
shouldCap = true
end
elseif match(c, '[%.!?]') then
shouldCap = true
end
end
return table.concat(chars)
end
function p.main(frame)
local args = require('Module:Arguments').getArgs(frame, {
parentFirst = true,
wrappers = {'Template:Th-rm'}
})
if not args[1] or args[1] == '' then
return ''
end
local str = clean(args[1])
-- Don't romanize text which contains no Thai script.
if not match(str, '[ก-๛]') then
return ''
end
str = applyOrthographicExceptions(str)
local result = romanizeWord(str)
if args.capi then
result = capitalize(result, false)
elseif args.sent then
result = capitalize(result, true)
end
return result
end
return p