| Latest revision |
Your text |
| Line 1: |
Line 1: |
| local p = {} | | local p = {} |
|
| |
|
| local arguments = require('Module:Arguments') | | local U = mw.ustring |
| local tt = require('Module:Tt') | | local gsub = U.gsub |
| | local match = U.match |
| | local sub = U.sub |
| | local len = U.len |
|
| |
|
| local AUTO_TOOLTIP = 'This romanization was generated automatically. Please add a user-curated romanization as soon as possible.' | | local INITIAL = { |
| | |
| local THAI = '[ก-๛]'
| |
| | |
| -- Initial consonants.
| |
| local consonants = {
| |
| ['ก'] = 'k', | | ['ก'] = 'k', |
| ['ข'] = 'kh', | | ['ข'] = 'kh', ['ฃ'] = 'kh', ['ค'] = 'kh', ['ฅ'] = 'kh', ['ฆ'] = 'kh', |
| ['ฃ'] = 'kh',
| |
| ['ค'] = 'kh',
| |
| ['ฅ'] = 'kh',
| |
| ['ฆ'] = 'kh',
| |
| ['ง'] = 'ng', | | ['ง'] = 'ng', |
| ['จ'] = 'ch', | | ['จ'] = 'ch', |
| ['ฉ'] = 'ch', | | ['ฉ'] = 'ch', ['ช'] = 'ch', |
| ['ช'] = 'ch',
| |
| ['ซ'] = 's', | | ['ซ'] = 's', |
| ['ฌ'] = 'ch', | | ['ฌ'] = 'ch', |
| Line 25: |
Line 18: |
| ['ฎ'] = 'd', | | ['ฎ'] = 'd', |
| ['ฏ'] = 't', | | ['ฏ'] = 't', |
| ['ฐ'] = 'th', | | ['ฐ'] = 'th', ['ฑ'] = 'th', ['ฒ'] = 'th', |
| ['ฑ'] = 'th',
| |
| ['ฒ'] = 'th',
| |
| ['ณ'] = 'n', | | ['ณ'] = 'n', |
| ['ด'] = 'd', | | ['ด'] = 'd', |
| ['ต'] = 't', | | ['ต'] = 't', |
| ['ถ'] = 'th', | | ['ถ'] = 'th', ['ท'] = 'th', ['ธ'] = 'th', |
| ['ท'] = 'th',
| |
| ['ธ'] = 'th',
| |
| ['น'] = 'n', | | ['น'] = 'n', |
| ['บ'] = 'b', | | ['บ'] = 'b', |
| Line 47: |
Line 36: |
| ['ล'] = 'l', | | ['ล'] = 'l', |
| ['ว'] = 'w', | | ['ว'] = 'w', |
| ['ศ'] = 's', | | ['ศ'] = 's', ['ษ'] = 's', ['ส'] = 's', |
| ['ษ'] = 's',
| |
| ['ส'] = 's',
| |
| ['ห'] = 'h', | | ['ห'] = 'h', |
| ['ฬ'] = 'l', | | ['ฬ'] = 'l', |
| ['ฮ'] = 'h', | | ['ฮ'] = 'h' |
| ['อ'] = ''
| |
| } | | } |
|
| |
|
| -- Final consonants.
| | local FINAL = { |
| local finals = { | |
| ['ก'] = 'k', | | ['ก'] = 'k', |
| ['ข'] = 'k', | | ['ข'] = 'k', ['ฃ'] = 'k', ['ค'] = 'k', ['ฅ'] = 'k', ['ฆ'] = 'k', |
| ['ฃ'] = 'k',
| | ['ง'] = 'ng', |
| ['ค'] = 'k',
| |
| ['ฅ'] = 'k',
| |
| ['ฆ'] = 'k',
| |
| | |
| ['จ'] = 't', | | ['จ'] = 't', |
| ['ฉ'] = 't', | | ['ฉ'] = 't', ['ช'] = 't', ['ซ'] = 't', |
| ['ช'] = 't',
| | ['ฌ'] = '', |
| ['ซ'] = 't',
| | ['ญ'] = 'n', |
| ['ฌ'] = 't', | | ['ฎ'] = 't', ['ฏ'] = 't', |
| ['ฎ'] = 't', | | ['ฐ'] = 't', ['ฑ'] = 't', ['ฒ'] = 't', |
| ['ฏ'] = 't',
| | ['ณ'] = 'n', |
| ['ฐ'] = 't', | |
| ['ฑ'] = 't',
| |
| ['ฒ'] = 't', | |
| ['ด'] = 't', | | ['ด'] = 't', |
| ['ต'] = 't', | | ['ต'] = 't', |
| ['ถ'] = 't', | | ['ถ'] = 't', ['ท'] = 't', ['ธ'] = 't', |
| ['ท'] = 't',
| | ['น'] = 'n', |
| ['ธ'] = 't',
| |
| ['ศ'] = 't', | |
| ['ษ'] = 't',
| |
| ['ส'] = 't',
| |
| | |
| ['บ'] = 'p', | | ['บ'] = 'p', |
| ['ป'] = 'p', | | ['ป'] = 'p', |
| ['ผ'] = 'p', | | ['ผ'] = '', ['ฝ'] = '', |
| ['ฝ'] = 'p',
| | ['พ'] = 'p', ['ฟ'] = 'p', ['ภ'] = 'p', |
| ['พ'] = 'p', | | ['ม'] = 'm', |
| ['ฟ'] = 'p',
| | ['ย'] = '', |
| ['ภ'] = 'p',
| |
| | |
| ['ง'] = 'ng',
| |
| | |
| ['ญ'] = 'n',
| |
| ['ณ'] = 'n', | |
| ['น'] = 'n', | |
| ['ร'] = 'n', | | ['ร'] = 'n', |
| ['ล'] = 'n', | | ['ล'] = 'n', |
| | ['ว'] = '', |
| | ['ศ'] = 't', ['ษ'] = 't', ['ส'] = 't', |
| | ['ห'] = '', |
| ['ฬ'] = 'n', | | ['ฬ'] = 'n', |
| | | ['ฮ'] = '' |
| ['ม'] = 'm' | |
| } | | } |
|
| |
|
| -- Vowels.
| | local CONSONANTS = 'กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรลวศษสหฬฮ' |
| local vowels = { | |
| ['เียะ'] = 'ia',
| |
| ['เีย'] = 'ia',
| |
| ['เือะ'] = 'uea',
| |
| ['เือ'] = 'uea',
| |
| ['เือย'] = 'ueai',
| |
| ['เียว'] = 'iao',
| |
|
| |
|
| ['ัวะ'] = 'ua',
| | local function isConsonant(c) |
| ['ัว'] = 'ua', | | return c and match(c, '[' .. CONSONANTS .. ']') ~= nil |
| | |
| ['เิ็ย'] = 'oei',
| |
| ['เิย'] = 'oei',
| |
| ['เอย'] = 'oei',
| |
| | |
| ['็อย'] = 'oi',
| |
| ['อย'] = 'oi',
| |
| | |
| ['ุย'] = 'ui',
| |
| ['วย'] = 'uai',
| |
| | |
| ['ัย'] = 'ai',
| |
| ['ไย'] = 'ai',
| |
| ['าย'] = 'ai',
| |
| ['ใ'] = 'ai',
| |
| ['ไ'] = 'ai',
| |
| | |
| ['เ็ว'] = 'eo',
| |
| ['แ็ว'] = 'aeo',
| |
| ['เา'] = 'ao',
| |
| ['าว'] = 'ao',
| |
| ['แว'] = 'aeo',
| |
| ['เว'] = 'eo',
| |
| | |
| ['ิว'] = 'io',
| |
| ['ีว'] = 'io',
| |
| | |
| ['ำ'] = 'am',
| |
| | |
| ['ือ'] = 'ue',
| |
| ['ื'] = 'ue',
| |
| ['ึ'] = 'ue',
| |
| | |
| ['ิ'] = 'i',
| |
| ['ี'] = 'i',
| |
| | |
| ['ุ'] = 'u',
| |
| ['ู'] = 'u',
| |
| | |
| ['เ็'] = 'e',
| |
| ['เ'] = 'e',
| |
| | |
| ['แ็'] = 'ae',
| |
| ['แ'] = 'ae',
| |
| | |
| ['โะ'] = 'o',
| |
| ['โ'] = 'o',
| |
| | |
| ['เาะ'] = 'o',
| |
| ['อ'] = 'o',
| |
| | |
| ['เอะ'] = 'oe',
| |
| ['เอ'] = 'oe',
| |
| ['เะ'] = 'e',
| |
| | |
| ['ะ'] = 'a',
| |
| ['ั'] = 'a',
| |
| ['า'] = 'a'
| |
| }
| |
| | |
| -- Longest vowel forms must be checked first.
| |
| local vowel_order = {
| |
| 'เือย',
| |
| 'เียว',
| |
| 'เือะ',
| |
| 'เียะ',
| |
| 'เีย',
| |
| 'เือ',
| |
| 'ัวะ',
| |
| 'ัว',
| |
| 'เิ็ย',
| |
| 'เิย',
| |
| '็อย',
| |
| 'เ็ว',
| |
| 'แ็ว',
| |
| 'เอย',
| |
| 'เา',
| |
| 'าว',
| |
| 'เว',
| |
| 'แว',
| |
| 'าย',
| |
| 'ไย',
| |
| 'ัย',
| |
| 'ุย',
| |
| 'วย',
| |
| 'อย',
| |
| 'ำ',
| |
| 'ิว',
| |
| 'ีว',
| |
| 'ิ',
| |
| 'ี',
| |
| 'ึ',
| |
| 'ื',
| |
| 'ุ',
| |
| 'ู',
| |
| 'เ็',
| |
| 'แ็',
| |
| 'เาะ',
| |
| 'เอะ',
| |
| 'เอ',
| |
| 'เะ',
| |
| 'ะ',
| |
| 'ั',
| |
| 'า',
| |
| 'เ',
| |
| 'แ',
| |
| 'โ',
| |
| 'อ',
| |
| 'ใ',
| |
| 'ไ'
| |
| }
| |
| | |
| local consonant_set = {}
| |
| | |
| for c in pairs(consonants) do
| |
| consonant_set[c] = true
| |
| end | | end |
|
| |
|
| -- Thai consonant clusters.
| | local function isThai(c) |
| local valid_clusters = {
| | return c and match(c, '[ก-๛]') ~= nil |
| ['กร'] = true,
| |
| ['กล'] = true,
| |
| ['กว'] = true,
| |
| ['ขร'] = true,
| |
| ['ขล'] = true,
| |
| ['ขว'] = true,
| |
| ['คร'] = true,
| |
| ['คล'] = true,
| |
| ['คว'] = true,
| |
| ['ฆร'] = true,
| |
| ['ฆล'] = true,
| |
| ['จร'] = true,
| |
| ['ตร'] = true,
| |
| ['ปร'] = true,
| |
| ['ปล'] = true,
| |
| ['พร'] = true,
| |
| ['พล'] = true,
| |
| ['ผล'] = true,
| |
| ['ทร'] = true,
| |
| ['ธร'] = true,
| |
| ['ศร'] = true,
| |
| ['สร'] = true,
| |
| ['สล'] = true,
| |
| ['สว'] = true,
| |
| ['ซร'] = true,
| |
| ['ฝร'] = true,
| |
| ['ฟร'] = true
| |
| }
| |
| | |
| -- ห is silent in these clusters when followed by a vowel.
| |
| local h_silent_clusters = {
| |
| ['หง'] = true,
| |
| ['หน'] = true,
| |
| ['หม'] = true,
| |
| ['หย'] = true,
| |
| ['หร'] = true,
| |
| ['หล'] = true,
| |
| ['หว'] = true,
| |
| ['หญ'] = true
| |
| }
| |
| | |
| -- ร is silent in these clusters when followed by a vowel.
| |
| local r_silent_clusters = {
| |
| ['จร'] = true,
| |
| ['ซร'] = true,
| |
| ['ศร'] = true,
| |
| ['สร'] = true
| |
| }
| |
| | |
| local function chars(text) | |
| local result = {} | |
| | |
| for c in mw.text.gsplit(text, '') do
| |
| table.insert(result, c)
| |
| end
| |
| | |
| return result
| |
| end | | end |
|
| |
|
| local function isConsonant(c) | | local function isVowelMark(c) |
| return c and consonant_set[c] == true | | return c and match(c, '[ะาิีึืุูัำเแโใไอ]') ~= nil |
| end | | end |
|
| |
|
| local function isThai(c) | | local function clean(str) |
| return c and mw.ustring.find(c, THAI) ~= nil | | str = mw.text.unstrip(str or '') |
| | str = gsub(str, '<br%s*/?>', ' ') |
| | str = gsub(str, '<[^>]+>', '') |
| | return str |
| end | | end |
|
| |
|
| local function isFrontVowel(c) | | local function removeMarks(str) |
| return c == 'เ' or c == 'แ' or c == 'โ' or c == 'ใ' or c == 'ไ' | | -- Tone marks are not represented in RTGS. |
| end
| | str = gsub(str, '[่้๊๋]', '') |
|
| |
|
| local function skipMarks(c, pos)
| | -- Mai thanthakhat makes the marked consonant silent. |
| while c[pos] and ( | | str = gsub(str, '([ก-ฮ])์', '') |
| c[pos] == '็' or
| |
| c[pos] == '่' or
| |
| c[pos] == '้' or
| |
| c[pos] == '๊' or
| |
| c[pos] == '๋' or
| |
| c[pos] == 'ํ' or
| |
| c[pos] == '๎'
| |
| ) do
| |
| pos = pos + 1
| |
| end
| |
|
| |
|
| return pos | | return str |
| end | | end |
|
| |
|
| local function removeSilent(chars_) | | local function getVowel(str, pos) |
| local result = {} | | local s = sub(str, pos, -1) |
| local i = 1
| |
|
| |
|
| while chars_[i] do | | -- Longest / most specific patterns first. |
| if chars_[i] == '์' then | | local patterns = { |
| -- ์ makes the following consonant(s) silent.
| | { 'เ', 'ื', 'อ', 'ย', 'ueai' }, |
| i = i + 1
| | { 'เ', 'ี', 'ย', 'ว', 'iao' }, |
| | { 'เ', 'ี', 'ย', 'ะ', 'ia' }, |
| | { 'เ', 'ี', 'ย', '', 'ia' }, |
| | { 'เ', 'ื', 'อ', 'ะ', 'uea' }, |
| | { 'เ', 'ื', 'อ', '', 'uea' }, |
|
| |
|
| while chars_[i] and (
| | { '', 'ั', 'ว', 'ะ', 'ua' }, |
| isConsonant(chars_[i]) or
| | { '', 'ั', 'ว', '', 'ua' }, |
| chars_[i] == '็' or
| |
| chars_[i] == '่' or
| |
| chars_[i] == '้' or
| |
| chars_[i] == '๊' or
| |
| chars_[i] == '๋'
| |
| ) do
| |
| i = i + 1
| |
| end
| |
| else | |
| table.insert(result, chars_[i])
| |
| i = i + 1
| |
| end
| |
| end
| |
|
| |
|
| return result
| | { 'แ', '็', 'ว', '', 'aeo' }, |
| end
| | { 'แ', 'ว', '', '', 'aeo' }, |
| | { 'เ', '็', 'ว', '', 'eo' }, |
| | { 'เ', 'ว', '', '', 'eo' }, |
|
| |
|
| local function getVowel(c, pos)
| | { '', 'ิ', 'ว', '', 'io' }, |
| local remaining = table.concat(c, '', pos)
| | { '', 'ุ', 'ย', '', 'ui' }, |
| | { '', 'ว', 'ย', '', 'uai' }, |
|
| |
|
| for _, form in ipairs(vowel_order) do
| | { 'เ', 'ื', 'อ', '', 'ue' }, |
| local length = mw.ustring.len(form) | | { 'เ', 'ื', '', '', 'ue' }, |
|
| |
|
| if mw.ustring.sub(remaining, 1, length) == form then | | { 'เ', 'า', '', '', 'ao' }, |
| return form, vowels[form]
| | { '', 'า', 'ว', '', 'ao' }, |
| end | |
| end
| |
|
| |
|
| return nil, nil
| | { 'โ', 'ย', '', '', 'oi' }, |
| end
| | { '', 'อ', 'ย', '', 'oi' }, |
|
| |
|
| local function getInitial(c, pos)
| | { 'เ', 'ย', '', '', 'oei' }, |
| local c1 = c[pos]
| |
|
| |
|
| if not isConsonant(c1) then
| | { 'ใ', '', '', '', 'ai' }, |
| return '', pos | | { 'ไ', '', '', '', 'ai' }, |
| end
| | { '', 'ั', 'ย', '', 'ai' }, |
| | | { '', 'า', 'ย', '', 'ai' }, |
| local c2 = c[pos + 1]
| |
| | |
| -- ห is not pronounced in หง หน หม หย หร หล หว หญ.
| |
| if c1 == 'ห' and c2 and h_silent_clusters[c1 .. c2] then
| |
| return consonants[c2], pos + 2, true | |
| end
| |
|
| |
|
| -- ทร is pronounced s in the relevant Sanskrit-derived words.
| | { 'เ', 'าะ', '', '', 'o' }, |
| -- This is the broad automatic fallback; exceptions can be manually
| | { 'โ', 'ะ', '', '', 'o' }, |
| -- supplied with th_rm.
| | { 'โ', '', '', '', 'o' }, |
| if c1 == 'ท' and c2 == 'ร' then
| | { '', 'อ', '', '', 'o' }, |
| return 's', pos + 2, false | |
| end
| |
|
| |
|
| if c2 and valid_clusters[c1 .. c2] then
| | { 'เ', 'อะ', '', '', 'oe' }, |
| -- ร is silent in these clusters. | | { 'เ', 'ิ', '', '', 'oe' }, |
| if r_silent_clusters[c1 .. c2] then | | { 'เ', 'อ', '', '', 'oe' }, |
| return consonants[c1], pos + 2, false
| |
| end | |
|
| |
|
| return consonants[c1] .. consonants[c2], pos + 2, false | | { 'แ', 'ะ', '', '', 'ae' }, |
| end
| | { 'แ', '', '', '', 'ae' }, |
|
| |
|
| return consonants[c1], pos + 1, false
| | { 'เ', 'ะ', '', '', 'e' }, |
| end
| | { 'เ', '็', '', '', 'e' }, |
| | { 'เ', '', '', '', 'e' }, |
|
| |
|
| local function getCoda(c, pos)
| | { '', 'ำ', '', '', 'am' }, |
| local current = c[pos]
| |
|
| |
|
| if not current or not isConsonant(current) then
| | { '', 'ะ', '', '', 'a' }, |
| return '', pos | | { '', 'ั', '', '', 'a' }, |
| end
| | { '', 'า', '', '', 'a' }, |
|
| |
|
| -- These do not act as ordinary codas here.
| | { '', 'ิ', '', '', 'i' }, |
| if current == 'อ' or current == 'ห' or current == 'ฮ' then
| | { '', 'ี', '', '', 'i' }, |
| return '', pos | |
| end
| |
|
| |
|
| -- ย and ว are generally part of the vowel/diphthong.
| | { '', 'ึ', '', '', 'ue' }, |
| if current == 'ย' or current == 'ว' then
| | { '', 'ื', '', '', 'ue' }, |
| return '', pos | |
| end
| |
|
| |
|
| if finals[current] then
| | { '', 'ุ', '', '', 'u' }, |
| return finals[current], pos + 1 | | { '', 'ู', '', '', 'u' } |
| end | | } |
|
| |
|
| return '', pos + 1 | | for _, v in ipairs(patterns) do |
| end
| | local a = v[1] or '' |
| | local b = v[2] or '' |
| | local c = v[3] or '' |
| | local d = v[4] or '' |
| | local roman = v[5] or '' |
|
| |
|
| local function startsNextSyllable(c, pos) | | local pattern = a .. b .. c .. d |
| if not c[pos] or not isConsonant(c[pos]) then
| |
| return false
| |
| end
| |
|
| |
|
| if isFrontVowel(c[pos + 1]) then
| | if pattern ~= '' and sub(s, 1, len(pattern)) == pattern then |
| return true | | return roman, len(pattern) |
| | end |
| end | | end |
|
| |
|
| local _, vowel = getVowel(c, pos + 1) | | -- Implied a. |
| | | return 'a', 0 |
| return vowel ~= nil | |
| end | | end |
|
| |
|
| local function parseSyllable(c, start) | | local function romanizeSyllable(str, start) |
| local pos = start | | local pos = start |
| | | local n = len(str) |
| if not c[pos] then | |
| return nil, pos
| |
| end
| |
|
| |
|
| local initial = '' | | local initial = '' |
| local cluster = false | | local initialRom = '' |
|
| |
|
| -- Preposed vowels. | | -- Preposed vowels are written before the initial consonant. |
| local front = '' | | local pre = sub(str, pos, pos) |
|
| |
|
| if isFrontVowel(c[pos]) then | | if pre == 'เ' or pre == 'แ' or pre == 'โ' or pre == 'ใ' or pre == 'ไ' then |
| front = c[pos]
| |
| pos = pos + 1 | | pos = pos + 1 |
| end | | end |
|
| |
|
| local initialPos = pos | | local first = sub(str, pos, pos) |
| | |
| initial, pos, _, cluster =
| |
| getInitial(c, pos)
| |
|
| |
|
| if initial == '' and not isConsonant(c[initialPos]) then | | if not isConsonant(first) then |
| return nil, start + 1 | | return first, 1 |
| end | | end |
|
| |
|
| local vowel | | initial = first |
| local vowelForm | | initialRom = INITIAL[first] or '' |
| | pos = pos + 1 |
|
| |
|
| if front ~= '' then | | -- ห + sonorant consonant: ห is not romanized. |
| -- Handle the vowel forms which contain a preposed vowel.
| | local second = sub(str, pos, pos) |
| if front == 'เ' and c[pos] == 'ี' and c[pos + 1] == 'ย' then
| |
| vowel = 'ia'
| |
| vowelForm = 'เีย'
| |
| pos = pos + 2
| |
|
| |
|
| elseif front == 'เ' and c[pos] == 'ื' and c[pos + 1] == 'อ' then
| | if initial == 'ห' and second and match(second, '[งนมยรลวญณฬ]') then |
| vowel = 'uea'
| | initial = second |
| vowelForm = 'เือ'
| | initialRom = INITIAL[second] |
| pos = pos + 2
| | pos = pos + 1 |
| | end |
|
| |
|
| elseif front == 'เ' and c[pos] == 'อ' then
| | -- จร-, ซร-, ศร-, สร-: ร is not romanized. |
| vowel = 'oe'
| | second = sub(str, pos, pos) |
| vowelForm = 'เอ'
| |
| pos = pos + 1
| |
|
| |
|
| elseif front == 'เ' and c[pos] == 'า' then
| | if second == 'ร' |
| vowel = 'ao'
| | and (initial == 'จ' or initial == 'ซ' or initial == 'ศ' or initial == 'ส') then |
| vowelForm = 'เา'
| | pos = pos + 1 |
| pos = pos + 1
| | end |
| | |
| elseif front == 'เ' and c[pos] == '็' then | |
| vowel = 'e'
| |
| vowelForm = 'เ็'
| |
| pos = pos + 1
| |
| | |
| elseif front == 'แ' and c[pos] == '็' then
| |
| vowel = 'ae'
| |
| vowelForm = 'แ็'
| |
| pos = pos + 1
| |
| | |
| elseif front == 'โ' and c[pos] == 'ะ' then
| |
| vowel = 'o'
| |
| vowelForm = 'โะ'
| |
| pos = pos + 1
| |
|
| |
|
| elseif front == 'ใ' or front == 'ไ' then
| | -- Read the vowel. |
| vowel = 'ai'
| | local vowelPos |
| vowelForm = front
| |
|
| |
|
| else
| | if pre == 'เ' or pre == 'แ' or pre == 'โ' or pre == 'ใ' or pre == 'ไ' then |
| vowel = vowels[front]
| | vowelPos = start |
| vowelForm = front
| |
| end | |
| else | | else |
| vowelForm, vowel = getVowel(c, pos) | | vowelPos = pos |
| | |
| if vowelForm then
| |
| pos = pos + mw.ustring.len(vowelForm)
| |
| end
| |
| end
| |
| | |
| -- Implied vowels.
| |
| if not vowel then
| |
| if c[pos] and isConsonant(c[pos]) then
| |
| if startsNextSyllable(c, pos) then
| |
| vowel = 'a'
| |
| else
| |
| vowel = 'o'
| |
| end
| |
| else
| |
| vowel = 'a'
| |
| end
| |
| end | | end |
|
| |
|
| pos = skipMarks(c, pos) | | local vowel, vowelLen = getVowel(str, vowelPos) |
| | |
| -- Final consonant.
| |
| local coda = ''
| |
| local codaPos = pos
| |
|
| |
|
| if c[pos] and isConsonant(c[pos]) then | | -- Advance over the vowel. |
| -- If another consonant begins the next syllable, don't consume
| | if vowelLen > 0 then |
| -- it as a coda.
| | pos = vowelPos + vowelLen |
| if startsNextSyllable(c, pos) then
| |
| coda = ''
| |
| else | |
| coda, codaPos = getCoda(c, pos)
| |
| end
| |
| end | | end |
|
| |
|
| pos = codaPos | | -- If there was a preposed vowel, getVowel consumed from its position, |
| pos = skipMarks(c, pos) | | -- so the resulting position already includes it. |
| | | if vowelLen == 0 and (pre == 'เ' or pre == 'แ' or pre == 'โ' or pre == 'ใ' or pre == 'ไ') then |
| return {
| | pos = start + 1 |
| initial = initial,
| |
| vowel = vowel,
| |
| coda = coda,
| |
| next = pos
| |
| }, pos
| |
| end
| |
| | |
| local function shouldHyphen(previous, current)
| |
| if not previous or not current then
| |
| return false | |
| end | | end |
|
| |
|
| -- Before a succeeding syllable beginning with a vowel. | | -- Final consonant. |
| if current.initial == '' and current.vowel ~= '' then | | local final = sub(str, pos, pos) |
| return true
| | local finalRom = '' |
| end
| |
|
| |
|
| -- Before ng when the preceding syllable ends in a vowel.
| | if isConsonant(final) then |
| if previous.coda == '' and current.initial == 'ng' then | | local after = sub(str, pos + 1, pos + 1) |
| return true | |
| end
| |
|
| |
|
| return false
| | -- If another vowel begins immediately afterward, this consonant |
| end
| | -- belongs to the following syllable. |
| | | if not isVowelMark(after) and not (after == 'เ' or after == 'แ' or after == 'โ' or after == 'ใ' or after == 'ไ') then |
| local function romanizeWord(word)
| | finalRom = FINAL[final] or '' |
| local c = chars(word)
| | pos = pos + 1 |
| c = removeSilent(c)
| |
| | |
| local syllables = {}
| |
| local pos = 1
| |
| local safety = 0
| |
| | |
| while pos <= #c and safety < 1000 do
| |
| safety = safety + 1
| |
| | |
| if isConsonant(c[pos]) or isFrontVowel(c[pos]) then
| |
| local syllable, nextPos = parseSyllable(c, pos) | |
|
| |
|
| if syllable then | | -- Final -ร after another final consonant. |
| table.insert(syllables, syllable)
| | if final == 'ร' then |
| | | finalRom = 'n' |
| if nextPos <= pos then
| |
| pos = pos + 1
| |
| else
| |
| pos = nextPos
| |
| end
| |
| else
| |
| -- Fail safely rather than looping forever.
| |
| pos = pos + 1 | |
| end | | end |
| else
| |
| -- Thai punctuation/marks that aren't part of a syllable.
| |
| pos = pos + 1
| |
| end | | end |
| end | | end |
|
| |
|
| local output = {} | | -- ทร- exception. |
| | if initial == 'ท' and sub(str, pos, pos) == 'ร' then |
| | initialRom = 's' |
| | pos = pos + 1 |
| | end |
|
| |
|
| for i, syllable in ipairs(syllables) do | | -- -รร- |
| if shouldHyphen(syllables[i - 1], syllable) then | | -- With a final consonant, รร = a. |
| table.insert(output, '-') | | -- Without a final consonant, รร = an. |
| | if sub(str, vowelPos, vowelPos + 1) == 'รร' then |
| | if finalRom ~= '' then |
| | vowel = 'a' |
| | else |
| | vowel = 'an' |
| end | | end |
|
| |
| table.insert(
| |
| output,
| |
| (syllable.initial or '') ..
| |
| (syllable.vowel or '') ..
| |
| (syllable.coda or '')
| |
| )
| |
| end | | end |
|
| |
|
| return table.concat(output) | | return initialRom .. vowel .. finalRom, math.max(1, pos - start) |
| end | | end |
|
| |
|
| -- Parse the text while treating <> and <-> as hard boundaries.
| | local function romanizeWord(word) |
| --
| |
| -- <> = literal space
| |
| -- <-> = literal hyphen
| |
| --
| |
| -- Because each Thai section is romanized independently, automatic
| |
| -- hyphenation can never cross one of these markers.
| |
| local function romanizeThaiText(text) | |
| local result = {} | | local result = {} |
| local pos = 1 | | local pos = 1 |
| local length = mw.ustring.len(text) | | local n = len(word) |
|
| |
|
| while pos <= length do | | while pos <= n do |
| -- Manual hyphen. | | local c = sub(word, pos, pos) |
| if mw.ustring.sub(text, pos, pos + 2) == '<->' then
| |
| table.insert(result, '-')
| |
| pos = pos + 3
| |
|
| |
|
| -- Manual space. | | if c == '<' then |
| elseif mw.ustring.sub(text, pos, pos + 1) == '<>' then
| | local close = mw.ustring.find(word, '>', pos + 1, true) |
| table.insert(result, ' ') | |
| pos = pos + 2
| |
|
| |
|
| -- Thai run.
| | if close then |
| elseif isThai(mw.ustring.sub(text, pos, pos)) then
| | local marker = sub(word, pos, close) |
| local start = pos
| |
|
| |
|
| while pos <= length do
| | if marker == '< >' then |
| local character = mw.ustring.sub(text, pos, pos)
| | table.insert(result, ' ') |
| | | elseif marker == '<->' then |
| if not isThai(character) then
| | table.insert(result, '-') |
| break
| | else |
| end
| | table.insert(result, marker) |
| | |
| -- Stop before a manual control marker. | |
| if mw.ustring.sub(text, pos, pos + 2) == '<->' then
| |
| break | |
| end | |
| | |
| if mw.ustring.sub(text, pos, pos + 1) == '<>' then
| |
| break
| |
| end | | end |
|
| |
|
| | pos = close + 1 |
| | else |
| | table.insert(result, c) |
| pos = pos + 1 | | pos = pos + 1 |
| end | | end |
| | | elseif isThai(c) then |
| local word = mw.ustring.sub(text, start, pos - 1) | | local roman, consumed = romanizeSyllable(word, pos) |
| | | table.insert(result, roman) |
| table.insert(result, romanizeWord(word)) | | pos = pos + consumed |
| | |
| -- Everything else is preserved.
| |
| else | | else |
| table.insert( | | table.insert(result, c) |
| result,
| |
| mw.ustring.sub(text, pos, pos)
| |
| )
| |
| | |
| pos = pos + 1 | | pos = pos + 1 |
| end | | end |
| Line 690: |
Line 336: |
| end | | end |
|
| |
|
| local function convertThaiDigits(text) | | local function capitalizeWords(str) |
| local digits = { | | local chars = mw.text.split(str, '') |
| ['๐'] = '0',
| | local capitalize = true |
| ['๑'] = '1',
| |
| ['๒'] = '2',
| |
| ['๓'] = '3',
| |
| ['๔'] = '4',
| |
| ['๕'] = '5',
| |
| ['๖'] = '6',
| |
| ['๗'] = '7',
| |
| ['๘'] = '8',
| |
| ['๙'] = '9'
| |
| }
| |
|
| |
|
| for thai, latin in pairs(digits) do | | for i, c in ipairs(chars) do |
| text = mw.ustring.gsub(text, thai, latin) | | if match(c, '[A-Za-z]') then |
| | if capitalize then |
| | chars[i] = U.upper(c) |
| | capitalize = false |
| | end |
| | elseif c == ' ' or c == '-' then |
| | capitalize = true |
| | end |
| end | | end |
|
| |
|
| return text | | return table.concat(chars) |
| end
| |
| | |
| local function hasThai(text)
| |
| return mw.ustring.find(text, THAI) ~= nil
| |
| end | | end |
|
| |
|
| local function validateRomanization(text) | | local function sentenceCase(str) |
| -- Never allow Thai characters to leak into the final result. | | local chars = mw.text.split(str, '') |
| if hasThai(text) then
| | local capitalize = true |
| return false
| |
| end | |
|
| |
|
| return true | | for i, c in ipairs(chars) do |
| end
| | if match(c, '[A-Za-z]') then |
| | | if capitalize then |
| local function capitalizeWords(text)
| | chars[i] = U.upper(c) |
| return mw.ustring.gsub(
| | capitalize = false |
| text,
| | end |
| '(%a)([%a]*)',
| | elseif match(c, '[.!?]') then |
| function(first, rest) | | capitalize = true |
| return mw.ustring.upper(first) .. rest | |
| end | | end |
| )
| |
| end
| |
|
| |
| local function capitalizeSentence(text)
| |
| local prefix, first = mw.ustring.match(
| |
| text,
| |
| '^([%s%p]*)(%a)'
| |
| )
| |
|
| |
| if not first then
| |
| return text
| |
| end | | end |
|
| |
|
| return prefix .. | | return table.concat(chars) |
| mw.ustring.upper(first) ..
| |
| mw.ustring.sub(text, mw.ustring.len(prefix) + 2)
| |
| end
| |
| | |
| function p.romanize(text)
| |
| if not text or mw.text.trim(text) == '' then
| |
| return ''
| |
| end
| |
| | |
| -- Do not automatically romanize Latin-only text.
| |
| if not hasThai(text) then
| |
| return ''
| |
| end
| |
| | |
| text = convertThaiDigits(text)
| |
| | |
| local result = romanizeThaiText(text)
| |
| | |
| result = mw.ustring.gsub(result, '%s+', ' ')
| |
| result = mw.text.trim(result)
| |
| | |
| if not validateRomanization(result) then
| |
| return ''
| |
| end
| |
| | |
| return result
| |
| end | | end |
|
| |
|
| function p.main(frame) | | function p.main(frame) |
| local args = arguments.getArgs(frame, { | | local args = require('Module:Arguments').getArgs(frame, { |
| parentFirst = true, | | parentFirst = true, |
| wrappers = {'Template:Th-rm'} | | wrappers = {'Template:Th-rm'} |
| }) | | }) |
|
| |
|
| local text = args[1] | | if not args[1] or args[1] == '' then |
| | |
| if not text or mw.text.trim(text) == '' then
| |
| return '' | | return '' |
| end | | end |
|
| |
|
| if not hasThai(text) then | | local str = clean(args[1]) |
| | |
| | -- Nothing to romanize. |
| | if not match(str, '[ก-๛]') then |
| return '' | | return '' |
| end | | end |
|
| |
|
| local result = p.romanize(text) | | str = removeMarks(str) |
|
| |
|
| if result == '' then | | local result = romanizeWord(str) |
| return ''
| |
| end
| |
|
| |
|
| if args.capi then | | if args.capi then |
| result = capitalizeWords(result) | | result = capitalizeWords(result) |
| | elseif args.sent then |
| | result = sentenceCase(result) |
| end | | end |
|
| |
|
| if args.sent then
| | return result |
| result = capitalizeSentence(result)
| |
| end
| |
| | |
| -- Automatically generated romanizations get a Tt warning.
| |
| return tt._main({ | |
| [1] = result,
| |
| [2] = AUTO_TOOLTIP,
| |
| ['class'] = args.class
| |
| })
| |
| end | | end |
|
| |
|
| return p | | return p |