I built a Korean dialect converter that runs entirely in the browser and uses no LLM. Type a standard Korean sentence and it rewrites it the way people speak in Gyeongsang, Jeolla, Chungcheong, Gangwon or Jeju. Paste a dialect sentence and it converts back to standard Korean. Nothing leaves the browser.
Try it: https://toolnjoy.com/dialect-converter (Korean UI)
The hard part was not producing dialect. It was not breaking standard Korean. Every time I widened a rule, a perfectly normal sentence got mangled somewhere. Here is what I ended up with. Most of it applies to any rule-based text rewriter (honorific converters, profanity filters, spelling normalizers).
Shape of the thing
Four modules:
| file | job |
|---|---|
| core | sentence splitting, sentence-type detection, ending replacement |
| endings | per-region ending tables (declarative / interrogative / propositive / imperative × politeness) |
| words | per-region vocabulary |
| reverse | dialect → standard |
Forward conversion goes "decide the sentence type, then pick the region's ending". Reverse goes the other way: "look at the ending, infer the sentence type". You cannot just invert the forward table, so reverse has its own table. Each reverse rule carries a type, which removes the detection step entirely.
In reverse mode the user does not pick a region. I run all five and keep the one with the highest score (4 points per matching ending + syllable count of matched words). People who paste dialect usually do not know which region it is from. That is why they came.
Eight guards, each from a real failure
1. Never reverse a form that appears on the standard side of any table. The Gyeongsang table had 함께 → 같이 (together). Reverse flipped it and rewrote "같이 보게" into "함께 보게", except 같이 is perfectly standard. Fix: anything that ever appears in a standard column anywhere is excluded from reverse candidates.
2. Question endings only count when there is a question mark or a wh-word. Gyeongsang -나 / -노 mark questions. But 하나 ("one") also ends in -나. Without the guard, "하나 주세요" (give me one) became "하니 주세요".
3. Count the prefix in words, not in the sentence. In "왜 그랴?" the ending 그랴 has a one-character stem. Counted at sentence level it looked like two characters ("왜 그") and passed a minimum-length check it should have failed.
4. Leave single characters alone. I added the particle 와 as vocabulary and "PDF와 이미지" became "PDF왜 이미지". Particles attach to Latin letters and digits too, and a single character gives you no boundary to anchor on. Only two single-character entries survived, both nouns.
5. Nouns matched mid-word. 벼락 (lightning) became 나락락 because 벼 → 나락 (rice plant) matched inside it. Same story for 오이소박이, 부담, 벽지. Noun entries get a marker and their own boundary check.
6. Single-character particles punch through boundaries. 도 was in the particle list, so 가재도구 (household goods) was split as 가재 + 도. Rule: if more Hangul follows a one-character particle, it is not a particle. Also, particles must agree with the final consonant: the 이 in 가재이 cannot be a subject particle because 가재 has no final consonant (it would need 가). That single check stopped the propositive 가재이 from turning into 까재이.
7. -으니까 is not a sentence end. Treating 니까 as a terminal ending turned "왔으니까 드세요" (since you came, please eat) into a question-then-imperative in Gyeongsang. 니까 terminates only after a ㅂ final.
8. Greetings before endings. If the ending rule hits 감사합니다 first you get 감사합니더 and the real Gyeongsang 고맙심더 never appears. Vocabulary of four or more characters is applied before endings. Four, because anything shorter at the end of a sentence would swallow the ending and the ending rule would never fire.
The biggest hole was punctuation
All my test sentences had periods. A real user typed "안녕하세요 저는 백수입니다 돈을 주세요" with none, and not a single character changed. The whole thing was one sentence so only the last ending matched.
Now sentences split on polite terminal endings followed by whitespace (습니다, 세요, 어요…). Casual one-character endings (-다, -어) are deliberately not in the list: in "이거 다 먹어" the 다 would become a sentence end.
Using the whole repository as a regression corpus
This tool never throws. It produces quietly wrong output. So the only safety net is a corpus check: scrape every Korean string literal and every JSX prose node from the codebase (about 9,400 snippets, all assumed to be standard Korean), run reverse over all of them, and assert that not one character changes. Every rule widening gets caught here. Jeju -게, Chungcheong -유, Gyeongsang -니라 were all caught this way.
Scraping only string literals misses things. "오직 → 오죽" only showed up in JSX prose.
Importing 8,886 dialect headwords from the national dictionary
I widened the reverse vocabulary with the National Institute of Korean Language's open dictionary (Urimalsaem). You cannot import it blindly: many dialect forms are homographs of standard words.
- 패기: 覇氣 (spirit) in standard Korean, "hiccup" in one dialect
- 새똥: bird droppings, or "dawn"
- 영신: a given name, listed as "shaman"
A corpus filter cannot catch these because 패기 does not appear in my codebase. So the import is two-layered:
- rewrite layer: exactly one standard equivalent, and the dictionary API confirms there is no non-dialect sense
- annotate layer: text untouched, the meaning is only listed ("this word also means X in region Y")
Two-syllable homographs were the danger zone. I first demoted every two-syllable entry, then queried the dictionary API for 7,766 headwords asking "does a standard sense exist", which restored 1,340 of them. Two things the dictionary cannot catch: personal names (handled with a name list) and real-world text (I ran 890k Korean sentences through reverse and dropped any entry that changed even one character).
Still weak
- Jeju changes both endings and vocabulary, so a round trip (standard → Jeju → standard) does not return the original
- Jeju propositive
-게(먹게 = let's eat) is identical to standard-게, so it is not in. Adding it turns 유일하게 (uniquely) into 유일하자 - Splitting only looks at polite endings and ㅆ past-tense forms; "이거 다 먹어 그리고 자" is still one sentence
Everything above came from actually using the thing, not from the test suite. If you read Korean and get a weird result, tell me the sentence.
- Converter: https://toolnjoy.com/dialect-converter
- Regional pages: Gyeongsang · Jeolla · Chungcheong · Gangwon · Jeju
- Dictionary lookup only: https://toolnjoy.com/dialect-dictionary
Top comments (1)
The no-LLM choice is refreshing here. For dialect conversion, explicit guards can be easier to audit and safer to tune than a model that produces fluent output but hides why it changed a phrase.