Module:Ko-translit/sandbox
Appearance
| This is the module sandbox page for Module:Ko-translit (diff). |
| This module is rated as beta. It is considered ready for widespread use, but as it is still relatively new, it should be applied with some caution to ensure results are as expected. |
| This module depends on the following other modules: |
This module automatically romanizes Korean text (Hangul). It supports the Revised Romanization (RR) and McCune–Reischauer (MR) romanization systems.
- Example: 독립 (pronounced [동닙]) → dongnip (RR), tongnip (MR)
Due to the nature of both systems, users will sometimes need to input symbols to instruct the system on how to correctly handle various edge cases. These input symbols do not appear in the output; they are only used to output the correct romanization per the rules of each system.
See the Module FAQ for some questions.
Quick guide
[edit]- Personal names
- For names with one-character surnames, prepend
%to the full name (e.g.%홍길동).- If the surname is longer than one syllable, insert
_or space between the surname and the given name (e.g.%선우_진or%선우 진).
- If the surname is longer than one syllable, insert
- If there is no surname (e.g. a mononym), add
%_to the start of the name (e.g.%_복남).
- For names with one-character surnames, prepend
- Capitalization for proper nouns
- Prepend
^to the proper noun (e.g.^압구정).
- Prepend
- Mandatory hyphens in RR (for separating an administrative unit)
- Place
*where the hyphen should go (e.g.^양주*군).
- Place
- Unpredictable pronunciation (cannot be predicted from the Hangul spelling and regular pronunciation rules)
- Use
$for neutralization + resyllabification (절음 법칙; e.g.웃$어른[우더른]). - Otherwise, use
@(e.g.손@등[손뜽],색@연필[생년필], etc.).
- Use
Full guide
[edit]- Note: When adding
@,$or`, it is recommend that you state why the symbol is needed.- Example:
물@고기<!--pronounced [물꼬기]-->
- Example:
| Symbol | RR | MR | Without symbol | Note |
|---|---|---|---|---|
@ |
N/A | unpredictable tensification (e.g. 손@등 [손뜽] sontŭng) |
전등 [전등] chŏndŭng | 싫증 [실쯩] siljeung/silchŭng is automatically handled (i.e. does not need @).
|
| written epenthic ㅅ + ㄱ/ㅂ (e.g. 바닷@가 [바다까~바닫까] padakka) |
옷걸이 [옫꺼리] otkŏri | |||
| ㄴ-addition (e.g. 색@연필 [생년필] saengnyeonpil/saengnyŏnp'il, 물@엿 [물렫] mullyeot/mullyŏt) |
입양 [이뱡] ibyang | ㄴ-addition before 윷 and 잎 is automatically applied. For example, words like 가락윷 [가랑뉻] garangnyut/karangnyut and 깻잎 [깬닙] kkaennip do not need @.
| ||
| ㄴㄹ pronounced [ㄴㄴ] (e.g. 음운@론 [으문논] eumunnon/ŭmunnon) |
난리 [날리] nalli | |||
| k/t/p for ㄱㅎ/ㄷㅎ/ㅂㅎ in words that are not nouns, pronouns, and numerals (e.g. 잡@혀 [자펴] japyeo) |
N/A | 낙하산 nakhasan | ||
$ |
neutralization + resyllabification ("N+R"; 절음법칙) (e.g. 웃$어른 [우더른] udeoreun/udŏrŭn) |
웃어라 [우서라] useora/usŏra | Most N+R is automatically applied. For example, words like 맛없다 [마덥따] madeopda/madŏpta do not need $. Manual N+R is needed only before the syllabic blocks 아, 어, 에, 엔, 엘, 여, 요, 으, 은, 을, 음, 읍, 의, 이, 인, 일, 임, and 입.
| |
` |
N/A | voicing of syllable-initial ㄱ/ㄷ/ㅂ/ㅈ after a non-Hangul character (e.g. 1`번 [일번/한번] 1bŏn) |
6번 [육뻔/여섣뻔] 6pŏn | only between a non-Hangul character and a Hangul syllabic block beginning with ㄱ/ㄷ/ㅂ/ㅈ Currently, the "non-Hangul character" is limited to ASCII digits and alphabet letters ( [0-9A-Za-z]). This can be broadened later if needed.
|
_ |
1. adds an additional space not in Hangul text 2. in the personal name mode (see % below), used when the length of the surname is not one syllable |
|||
* |
adds an additional hyphen not in Hangul text (e.g. ^양주*군 Yangju-gun) |
needed for a mandatory hyphen (for separating an administrative unit) in RR | ||
^ |
capitalizes the following letter | must be immediately followed by a Hangul syllabic block | ||
% |
personal name mode | 1. By default, this mode assumes that the first syllabic block is the surname and the remaining syllabic blocks are the given name. (e.g. %홍길동 → surname 홍, given name 길동)2. To specify a different segmentation, insert _ or space between the components.(e.g. surname 선우 + given name 진: %선우_진 or %선우 진)3. For a mononym (personal name that does not have a surname, or any case where only the given name is needed), precede the name with %_.(e.g. %_복남)4. For a surname-only name (personal name that only consists of a surname, or any case where only the surname is needed), if it is 4.1. two or more syllables long, _ is needed after it.(e.g. %남궁_)4.2. just one syllable long, do not add _ after it.(e.g. %최)5. If a personal name is followed by non–personal name text, put the personal name between two %.(e.g. 국립 %홍길동% 기념관)6. Names of obviously non-Korean origin: for such family names, do not use a %; instead place a ^ before them. For such given names, insert a %_ before them. (e.g. "Esther Smith" → %_에스더% ^스미스)Note: This mode does not support 1. the "given name + surname" order (e.g. 제임스 홍). 2. a name consisting of three or more components. | ||
\ |
escape character (e.g. \@ → @) |
for outputting literal @, $, etc. | ||
local p = {}
local get_args = require('Module:Arguments').getArgs
local m_data = require('Module:Ko-translit/data/sandbox')
local m_clean = require('Module:Ko-translit/clean')
local m_utils = require('Module:Ko-utils')
local find = mw.ustring.find
local gsub = mw.ustring.gsub
--[[
IMPORTANT NOTE before editing this module:
1. Make sure that you use a font that displays the following characters differently, and that you know the differences of them:
ᄀ (U+1100)
ᆨ (U+11A8)
ㄱ (U+3131)
2. When dealing with decomposed Hangul,
a. [ᄀ-ᄒ] should not be directly followed by [ᅡ-ᅵ] because MediaWiki uses Unicode Normalization Form C (NFC), which converts any sequence of [ᄀ-ᄒ][ᅡ-ᅵ] into a precomposed character; write ᄀ[ᅡ] or ᄀ(ᅡ)
b. ᄀ[ᅡ] or ᄀ(ᅡ) at the end of a pattern is equivalent to not just 가 but [가-갛] in precomposed form. To match a syllabic block without a final consonant at the end of a pattern, use both vowel + [^ᆨ-ᇂ] and vowel + $
For example, to only match 가 (and not [각-갛]) at the end of a pattern, use both ᄀ[ᅡ][^ᆨ-ᇂ] and ᄀ[ᅡ]$
--]]
-- _ replaced with additional space that appears in romanization only
local function roman_only_space(text)
return gsub(text, "_", " ")
end
-- * replaced with additional hyphen that appears in romanization only
local function roman_only_hyphen(text)
return gsub(text, "%*", "-")
end
local function parse_names(text)
-- Processing people names
-- Note: internally uses 3 non-characters
-- (U+FDD0): used to interrupt and prevent euphonic changes between syllables; mostly for given name in RR
-- (U+FDD1): marks beginning of name
-- (U+FDD2): marks end of name
local hanja_readings_final_L = "갈걸결골괄굴궐귤글길날녈놜눌닐달돌랄렬률말멸몰물밀발벌별불살설솔술슬실알얼열올왈울월율을일절졸줄즐질찰철촬출칠탈팔필할헐혈홀활훌휼흘힐"
local hanja_readings_init_DSJ = "다단달담답당대댁덕도독돈돌동두둔득등사삭산살삼삽상새색생서석선설섬섭성세소속손솔송쇄쇠수숙순술숭쉬슬습승시식신실심십자작잔잠잡장재쟁저적전절점접정제조족존졸종좌죄주죽준줄중즉즐즙증지직진질짐집징"
-- Change % to U+FDD1 and U+FDD2
text = gsub(text, "%%([^%%]*)%%", "%1") -- When name is surrounded by %
text = gsub(text, "%%([^%%]*)$", "%1") -- When no end %; end of string also terminates name mode
-- Validate person name syntax
if find(text, "_?") then
error("Name cannot be empty")
elseif find(text, "[^]*[^가-힣_ ][^]*") then
error("Invalid character in name")
elseif find(text, " ") then
error("Name cannot begin with space")
elseif find(text, " ") then
error("Name cannot end with space")
elseif find(text, "[^]*[ _][^]*[ _][^]*") then
error("No more than two components in name")
elseif find(text, "[가-힣]_") then
error("No _ after one-syllable surname")
elseif find(text, "[^]*[" .. hanja_readings_final_L .. "]@[" .. hanja_readings_init_DSJ .. "][^]*") then
error("Contains unnecessary @ in name") -- See below
end
-- Separate surname and given name
-- If input contains _ or space, separate there
text = gsub(text, "([가-힣%$@]+)_", "^%1_") -- for surname-only string
text = gsub(text, "_([가-힣%$@]+)", "_^%1") -- for mononym
text = gsub(text, "([가-힣%$@]+)[ _]([가-힣%$@]+)", "^%1_^%2")
-- Otherwise, separate after first syllabic block
text = gsub(text, "([가-힣])", "^%1_") -- for surname-only string
text = gsub(text, "([가-힣])([가-힣%$@]+)", "^%1_^%2")
-- Check invalid input after separating surname and given name
if find(text, "[^]*_%^[%$@][^]*") then
error("No @ or $ between surname and given name")
end
-- Tensification of ㄹ + {ㄷ, ㅅ, ㅈ} (needed for MR; e.g. 홍길동 [홍길똥], 을지문덕 [을찌문덕])
-- Does not occur when same syllable is repeated (e.g. 구구절절 [구구절절], not [구구절쩔]); just using U+FDD0 here too
for i = 1, mw.ustring.len(text) do
text = gsub(text, "([^]*)([달돌살설솔술슬실절졸줄즐질])%2([^]*)", "%1%2%2%3")
end
-- Now apply tensification
for i = 1, mw.ustring.len(text) do
text = gsub(text, "([^]*)([" .. hanja_readings_final_L .. "])([" .. hanja_readings_init_DSJ .. "])([^]*)", "%1%2@%3%4")
end
-- Insert U+FDD0 in given name
-- (needed for RR; e.g. 한복남 Han Boknam, not Han Bongnam)
-- Will be removed after romanization is complete
for i = 1, mw.ustring.len(text) do
text = gsub(text, "([^]*)_%^([^]*)([가-힣%$@])([가-힣%$@])([^]*)", "%1_^%2%3%4%5")
end
-- Remove _ which was needed for surname-only string and mononym
text = gsub(text, "_", "")
text = gsub(text, "_%^", "^")
text = gsub(text, "[]", "") -- Remove U+FDD1 and U+FDD2
return text
end
-- Convert to Revised Romanization
function p.rr(frame)
return p._rr(get_args(frame))
end
function p._rr(args)
local text = args[1]
-- Preprocessing (input cleaning, parsing people names, validation)
text = m_clean.escaped_to_html(text) -- replacing escaped special chars with placeholders
m_clean.validate_input(text)
text = parse_names(text) -- Parse people names (marked with %)
text = m_clean.remove_links_and_markup(text)
m_clean.validate_composed(text)
text = gsub(text, "`", "") -- ` is not needed (used for voicing in MR)
text = m_clean.unwrap_enclosed_hangul(text)
text = m_utils.decompose_hangul(text) -- decompose Hangul syllables to jamo
m_clean.validate_decomposed(text)
text = roman_only_hyphen(text) -- *->hyphen in romanization only
text = roman_only_space(text) -- _->space in romanization only
-- Convert input to phonemic spelling per standard Korean pronunciation rules
text = m_data.respell(text, "rr")
-- Romanize the phonemic spelling
text = m_data.romanize_rr(text)
-- Postprocessing
text = gsub(text, "", "") -- now remove U+FDD0 (was used for name mode)
-- ^ for capitalization
text = gsub(text, "%^[a-eg-km-pr-uwy]", mw.ustring.upper)
text = gsub(text, "%^", "")
text = m_clean.final_processing(text)
return text
end
-- Convert to McCune–Reischauer
function p.mr(frame)
return p._mr(get_args(frame))
end
function p._mr(args)
local text = args[1]
-- Preprocessing (input cleaning, parsing people names, validation)
text = m_clean.escaped_to_html(text) -- replacing escaped special chars with placeholders
m_clean.validate_input(text)
text = parse_names(text)
text = gsub(text, "", "") -- remove U+FDD0 (for RR only)
text = m_clean.remove_links_and_markup(text)
m_clean.validate_composed(text)
text = m_clean.unwrap_enclosed_hangul(text)
text = m_utils.decompose_hangul(text) -- decompose Hangul to jamo
m_clean.validate_decomposed(text)
text = roman_only_space(text) -- _ for additional space in romanization only
-- Convert input to phonemic spelling
text = m_data.respell(text, "mr")
-- Romanize the phonemic spelling
text = m_data.romanize_mr(text)
-- Postprocessing
text = roman_only_hyphen(text) -- * for additional hyphen in romanization only
-- replace ' with ' when followed by another ' or at end of string (to avoid possible clashes with bold/italic markup)
text = gsub(text, "([hkpt])''", "%1''")
text = gsub(text, "([hkpt])'$", "%1'")
-- ^ for capitalization
text = gsub(text, "%^[acehikm-pr-uwyŏŭ]", mw.ustring.upper)
text = gsub(text, "%^", "")
text = m_clean.final_processing(text)
return text
end
-- Removing special chars (except for escaped ones)
function p.clean_hangul(frame)
return p._clean_hangul(get_args(frame))
end
function p._clean_hangul(args)
local text = args[1]
-- Validate input
if not m_utils.contains_hangul(text) then
error("Input must contain Hangul")
elseif m_utils.contains_reference(text) then
error("Input cannot contain references")
elseif find(text, "[ᄀ-ᅟꥠ-ꥼ][%$%%%*@%^_`][ᅠ-ᆧힰ-ퟆ]") or find(text, "[ᅠ-ᆧ가개갸걔거게겨계고과괘괴교구궈궤귀규그긔기까깨꺄꺠꺼께껴꼐꼬꽈꽤꾀꾜꾸꿔꿰뀌뀨끄끠끼나내냐냬너네녀녜노놔놰뇌뇨누눠눼뉘뉴느늬니다대댜댸더데뎌뎨도돠돼되됴두둬뒈뒤듀드듸디따때땨떄떠떼뗘뗴또똬뙈뙤뚀뚜뚸뛔뛰뜌뜨띄띠라래랴럐러레려례로롸뢔뢰료루뤄뤠뤼류르릐리마매먀먜머메며몌모뫄뫠뫼묘무뭐뭬뮈뮤므믜미바배뱌뱨버베벼볘보봐봬뵈뵤부붜붸뷔뷰브븨비빠빼뺘뺴뻐뻬뼈뼤뽀뽜뽸뾔뾰뿌뿨쀄쀠쀼쁘쁴삐사새샤섀서세셔셰소솨쇄쇠쇼수숴쉐쉬슈스싀시싸쌔쌰썌써쎄쎠쎼쏘쏴쐐쐬쑈쑤쒀쒜쒸쓔쓰씌씨아애야얘어에여예오와왜외요우워웨위유으의이자재쟈쟤저제져졔조좌좨죄죠주줘줴쥐쥬즈즤지짜째쨔쨰쩌쩨쪄쪠쪼쫘쫴쬐쬬쭈쭤쮀쮜쮸쯔쯰찌차채챠챼처체쳐쳬초촤쵀최쵸추춰췌취츄츠츼치카캐캬컈커케켜켸코콰쾌쾨쿄쿠쿼퀘퀴큐크킈키타태탸턔터테텨톄토톼퇘퇴툐투퉈퉤튀튜트틔티파패퍄퍠퍼페펴폐포퐈퐤푀표푸풔풰퓌퓨프픠피하해햐햬허헤혀혜호화홰회효후훠훼휘휴흐희히ힰ-ퟆ][%$%%%*@%^_`][ᆨ-ᇿퟋ-ퟻ]") then
-- symbol should not appear within single syllabic block
error("Do not insert symbol within single syllabic block")
end
-- Remove special chars
text = m_clean.escaped_to_html(text) -- replacing escaped special chars with placeholders
text = gsub(text, "[%$%%%*@%^_`]", "") -- removing non-escaped special chars
text = m_clean.html_to_ascii(text) -- convert HTML encodings back to ASCII
text = mw.text.unstrip(text)
return text
end
return p