This is a Layer 2 document. See CLAUDE.md for project overview.
Voice & Tone Guide (Korean UX Writing)
Status: Active Applies to: All user-facing strings in src/config/i18n/ko.ts and en.ts
Current vocabulary override: top-level navigation is
홈 / 클럽 / 경기 / 기록 / 프로필. Use일정for scheduled sessions,경기for played/scored matches,클럽for clubs, and번개/번개 찾기for pickup discovery. Older examples in this historical guide that use모임for scheduled sessions should be interpreted as일정unless the text is describing an umbrella concept.
Current rules (the language pass, 2026-10-28). The pass over every flat v2 page set ten rules and one word per thing; its record is
copy-plans/2026-10-28-flat-v2-language-pass.mdand the words are the component library's § 1.19. Where this guide's older examples differ, the pass wins. It follows Toss's current rules for partner apps (앱인토스 UI/UX 가이드), which tightened three points: a question asks ~할까요? with no 시-honorific on the user (가입을 그만둘까요?, never 그만두시겠어요?); a result the user caused is in the active voice (신청했어요, 연습을 기록했어요; the passive only for what happened to them: 일정이 취소됐어요); a dialog's dismiss is 닫기. On the page boards the lint'scopy-wordsrule holds the retired words and forms.
1. Dabusi's Voice Identity
Dabusi speaks like a reliable club mate (믿음직한 클럽 동료) — someone who played with you last Saturday, remembers your schedule, and texts you reminders without being annoying. Not a corporate announcer. Not a chatbot trying too hard to be cute.
Brand voice (2026-10-14): the voice now derives from 다붓이 (close together, warm, familiar). A fuller voice pass is owed.
Voice Attributes
| Attribute | What it means | What it does NOT mean |
|---|---|---|
| 친근한 (Warm) | Conversational, approachable | Overly casual, slangy, using 해체 |
| 명확한 (Clear) | One meaning per sentence, no ambiguity | Verbose, over-explaining |
| 간결한 (Concise) | Every word earns its place | Terse, robotic, dropping needed context |
| 존중하는 (Respectful) | Acknowledges user's time and autonomy | Patronizing, commanding, guilt-tripping |
| 공감하는 (Empathetic) | Matches the user's emotional state | Fake cheerfulness during errors |
2. Korean Speech Level: 해요체 (Polite Conversational)
The Standard: 해요체 Throughout
Dabusi uses 해요체 as the primary speech level for all user-facing text. This matches the industry standard set by Toss, KakaoTalk, 당근마켓, and Naver.
해요체 (Dabusi default): "모임이 만들어졌어요"
합니다체 (too formal): "모임이 생성되었습니다"
해체 (too casual): "모임 만들었어"Why 해요체
| App | Speech Level | Rationale |
|---|---|---|
| 토스 (Toss) | 해요체 | "금융이나 IT 업계에서 쓰던 어려운 용어와 딱딱한 뉘앙스를 쉽고 친절하게 고쳐" |
| 카카오톡 | 해요체 | Messaging context demands conversational warmth |
| 카카오뱅크 | 해요체 (financial confirmations mix 합니다체) | Banking trust needs warmth without losing reliability |
| 당근마켓 | 해요체 | Neighborhood-friendly, "이웃과 대화하는 느낌" |
| 배달의민족 | 해요체 + playful wordplay | Humor lives in word choice, not formality level |
| 네이버 | 해요체 (Naver Pay, Maps) / 합니다체 (legal, settings) | Modern products are 해요체; legacy pages still 합니다체 |
When 합니다체 Is Acceptable
Only in these narrow contexts:
| Context | Example | Reason |
|---|---|---|
| Accessibility hints (screenreader) | "설정 화면으로 이동합니다" | VoiceOver convention; formal = predictable |
| Legal text (terms, privacy) | "개인정보를 수집합니다" | Legal requirement |
| System-level alerts | "OTA 업데이트는 빌드된 앱에서만 동작합니다" | Platform-level, not conversational |
Never mix 합니다체 into conversational UI. A single 합니다 in a 해요체 flow feels jarring.
3. Sentence Patterns by Context
3.1 Confirmations & Success (완료/성공)
Pattern: Declarative past tense — ~었어요 in the active voice when the user did it (신청했어요, 저장했어요); ~됐어요 for what happened to them (일정이 취소됐어요)
GOOD: "모임이 만들어졌어요!" (It's been created!)
GOOD: "참석 완료!" (RSVP confirmed!)
GOOD: "점수가 저장됐어요" (Score saved)
GOOD: "코드가 복사됐어요!" (Code copied!)
BAD: "모임이 성공적으로 생성되었습니다" (합니다체 + 한자어 '성공적')
BAD: "저장 완료되었습니다" (합니다체)Toss's principle: Find Hidden Emotion — celebrate completions. A loan repayment cleared? "축하해요!" A session created? Add the ! to share the excitement.
3.2 Calls to Action (CTA Buttons)
Pattern: ~하기 (verb stem + 하기) for primary actions
PRIMARY BUTTONS:
"시작하기" (Start)
"참석하기" (Attend)
"모임 만들기" (Create session)
"공유하기" (Share)
"기록하기" (Log it)
SECONDARY/SHORT BUTTONS:
"확인" (Confirm)
"취소" (Cancel)
"저장" (Save)
"삭제" (Delete)
"다음" (Next)
"완료" (Done)Rules:
- Primary CTA (bottom of screen, main action): use
~하기form — "참석하기", "모임 만들기" - Secondary/inline buttons: bare noun — "확인", "취소", "저장"
- Destructive actions: bare noun, never
~하기— "삭제", "나가기" (exception: 나가기 is a verb phrase) - Never use
~하세요(imperative) on buttons — it sounds like a command - Never use
~해요on buttons — it sounds like narration, not an action
Industry comparison:
| App | Primary CTA style | Example |
|---|---|---|
| Toss | ~하기 | "송금하기", "확인하기" |
| KakaoBank | ~하기 | "계좌 개설하기" |
| 당근마켓 | ~하기 | "글쓰기", "채팅하기" |
| 배민 | ~하기 (+ playful) | "주문하기", "찜하기" |
| Naver Pay | ~하기 | "결제하기" |
3.3 Suggestions & Guidance
Pattern: ~해 보세요 (try doing ~) or ~해 볼까요? (shall we try ~?)
GOOD: "클럽에 가입하고 테니스를 시작해 보세요!" (gentle suggestion)
GOOD: "첫 모임을 만들어 보세요" (encouraging nudge)
GOOD: "초대코드로 가입하거나 모집 중인 번개를 찾아보세요"
BAD: "클럽에 가입하세요" (too commanding — 하세요 = imperative)
BAD: "모임을 만드세요" (sounds like an order)
BAD: "클럽에 가입해야 해요" (obligation — 해야 해요 = "must")Toss's principle: Suggest Over Force — "권유와 강요는 다릅니다" (suggestion and coercion are different). Offer choices; never guilt or scare.
3.4 Confirmation Dialogs (할까요?)
Pattern: ~할까요? (shall we ~?) for destructive or important actions
GOOD: "클럽을 나갈까요?" (Leave the club?)
GOOD: "클럽을 삭제할까요?" (Delete the club?)
GOOD: "라운드 2를 생성할까요?" (Generate round 2?)
GOOD: "8명으로 3라운드를 생성할까요?" (Generate 3 rounds with 8 players?)
BAD: "삭제하시겠습니까?" (합니다체, too formal)
BAD: "나가시겠어요?" (시-honorific on the user; Toss 2026: ask ~할까요?)
BAD: "삭제해요" (too casual for a destructive dialog)
BAD: "정말 삭제하시겠어요? 되돌릴 수 없어요!" (fear-mongering)For destructive actions, state the consequence neutrally:
GOOD: "클럽을 삭제하면 모든 데이터가 사라져요. 정말 삭제할까요?"
BAD: "주의! 이 작업은 되돌릴 수 없습니다!!"3.5 Empty States
Pattern: Declarative situation + suggestive CTA
STRUCTURE:
Title: "아직 [thing]이(가) 없어요" (There's no [thing] yet)
Body: "[action]하면 여기에 표시돼요" (When you [action], it'll show here)
CTA: "[action]하기" or "~해 보세요"
EXAMPLES:
Title: "예정된 모임이 없어요"
Body: "클럽에서 모임을 만들거나 공개 모임에 참가해 보세요!"
CTA: [모임 찾기] [모임 만들기]
Title: "아직 경기 기록이 없어요"
Body: "경기 결과가 쌓이면 여기에 표시돼요. 첫 모임에 참가해 보세요!"
CTA: [모임 참가하기]
BAD:
Title: "데이터 없음" (robotic)
Body: "표시할 항목이 없습니다" (합니다체 + generic)당근마켓's pattern: empty states feel like a friendly nudge from a neighbor, not a system message. "아직 동네 이웃이 없어요. 관심사를 설정하면 이웃을 추천해 드려요!" — same energy Dabusi should have.
3.6 Error Messages
Pattern: Empathize + Explain + Guide
STRUCTURE:
"앗, [what went wrong]. [what to do next]."
EXAMPLES:
"앗, 모임을 만들지 못했어요. 다시 시도해 주세요."
"인터넷 연결을 확인해 주세요."
"초대 코드가 맞지 않아요. 다시 확인해 주세요."
BAD:
"Error: Session creation failed" (English, technical)
"세션 생성에 실패했습니다 (500)" (합니다체, error code leak)
"오류가 발생했습니다. 관리자에게 문의하세요" (unhelpful, formal)Toss's 6 Error Principles applied to Dabusi:
| Principle | Dabusi application |
|---|---|
| Prevention is best | Disable buttons when action is impossible; gray out past dates |
| Appropriate component | Toast for transient errors; Dialog for blocking errors |
| Self-directed solutions | Always include what the user can do next |
| User-friendly language | "모임을 만들지 못했어요" not "세션 생성 실패" |
| Reduce friction | Provide a retry button, not just text |
| Minimize negative emotion | "앗" softens the blow; never blame the user |
Error verb choices:
| Pattern | Tone | When to use |
|---|---|---|
~지 못했어요 | Soft, empathetic | Default — the system couldn't do it |
~에 실패했어요 | Slightly harder | Acceptable for auth failures where user might have caused it |
~할 수 없었어요 | Neutral | General inability — "불러올 수 없었어요" |
~이(가) 안 됐어요 | Too colloquial | Avoid in production copy |
Preferred: ~지 못했어요 (couldn't do X) as the default error verb. It puts responsibility on the system, not the user.
3.7 Notifications & Alerts
Pattern: Informative ~드려요 or factual ~이에요
GOOD: "새 모임이 열리면 알려 드려요"
GOOD: "모임 24시간·2시간 전에 알려 드려요"
GOOD: "미납 회비가 있으면 알려 드려요"
BAD: "새 세션 생성 시 알림을 전송합니다" (합니다체, 한자어)
BAD: "모임 알림 ON" (English mixed in)3.8 Loading States
Pattern: ~는 중... (in progress) or ~고 있어요
GOOD: "불러오는 중..."
GOOD: "새 버전을 다운로드하고 있어요"
BAD: "로딩 중..." (English loanword 로딩 is acceptable but 불러오는 is warmer)
BAD: "데이터 로드 중" (too technical)4. Word Choice Rules
4.1 Pure Korean Over 한자어
Following Toss's Easy to Speak principle — if you can't say it naturally in conversation, rewrite it.
| Avoid (한자어/technical) | Prefer (순우리말/conversational) |
|---|---|
| 생성 | 만들기 |
| 삭제하시겠습니까 | 삭제할까요 |
| 성공적으로 완료 | ~됐어요 |
| 일시적 오류 발생 | 앗, 잠깐 문제가 생겼어요 |
| 세션 | 모임 |
| 인원 | 명/사람 |
| 등록 | 추가/넣기 |
| 조회 | 보기/찾기 |
| 설정 완료 | 설정됐어요 |
| 전송 | 보내기 |
| 변경사항 적용 | 바뀐 내용 저장 |
4.2 Toss's Universal Words Principle
Avoid words that depend on age, education, or industry background:
BAD: "RSVP 마감" → GOOD: "참석 신청 마감" (but RSVP is known in tennis culture — acceptable)
BAD: "Elo 레이팅 변동" → GOOD: "실력 점수 변화" (for general users; Elo is fine for tennis enthusiasts)
BAD: "로테이션 알고리즘" → GOOD: "경기 배정 방식"Domain exception: Tennis-specific terms (Elo, RSVP, 복식, 단식, 타이브레이크) are acceptable because the audience knows them. But always provide context on first encounter.
4.3 Weed Cutting (잡초 제거)
Toss's principle: remove words that add no meaning on small screens.
BAD: "앞으로 이 기능은 여기서 이용하실 수 있어요"
GOOD: "이 기능은 여기서 이용할 수 있어요"
BAD: "현재 진행 중인 모임이 없습니다"
GOOD: "진행 중인 모임이 없어요"
BAD: "성공적으로 참석 처리가 완료되었습니다"
GOOD: "참석 완료!"5. Emoji Policy
Dabusi: Minimal, Purposeful Emoji
| Context | Emoji allowed? | Example |
|---|---|---|
| Celebrations (toast, success) | Limited | "참석 완료!" (no emoji needed; ! suffices) |
| Empty states | No | Use illustration/icon, not emoji |
| Error messages | No | "앗" is the softener, not an emoji |
| Notifications | No | Clean text only |
| Informal nudges | Sparingly | Achievement milestones may use sport emoji |
| Marketing / onboarding | Sparingly | Tagline can have subtle accent |
Industry reference:
| App | Emoji usage |
|---|---|
| Toss | Almost zero — relies on illustration + animation |
| KakaoBank | Zero in-app; emoticons in KakaoTalk integration |
| 당근마켓 | Minimal — uses warm illustration style instead |
| 배민 | Uses custom 배민체 font for personality, not emoji |
| Naver | Zero in serious products (Pay, Maps); some in Cafe |
Rule: If you need an emoji to convey tone, the copy itself is weak. Rewrite the text instead.
6. Korean / English Tone Mapping
How Dabusi Korean maps to English equivalents
| Dabusi Korean tone | English equivalent app | English tone |
|---|---|---|
| 해요체 conversational warmth | Stripe (clear, respectful) | "Your payment was successful" |
| Concise, one-task-per-screen | Notion (minimal, functional) | "No pages here yet. Create your first page." |
| Empathetic errors | Slack (friendly acknowledgment) | "Something went wrong. Try again?" |
| Celebratory completions | Duolingo (encouraging) | "Nice! Session created." |
| Suggestive CTAs | Airbnb (inviting) | "Try hosting a game" |
English i18n tone rules
When writing en.ts, the English should feel like Stripe meets Notion — clean, respectful, slightly warm but never corporate:
GOOD: "Session created!" (matches "모임이 만들어졌어요!")
GOOD: "Couldn't create the session. Try again." (matches "앗, 모임을 만들지 못했어요")
GOOD: "No upcoming sessions" (matches "예정된 모임이 없어요")
GOOD: "Join a session to get started" (matches "모임에 참가해 보세요!")
BAD: "Session creation successful" (too formal, passive)
BAD: "Error: Failed to create session" (too technical)
BAD: "Oopsie! Something broke!" (too cutesy)
BAD: "There are currently no sessions" (verbose)English button labels:
| Korean | English | Notes |
|---|---|---|
| ~하기 (primary CTA) | Verb phrase | "Create session", "Join club", "Start" |
| Bare noun (secondary) | Bare noun or short verb | "Save", "Cancel", "Delete" |
| ~해 보세요 (suggestion) | "Try ~ing" or infinitive | "Try creating a session" |
| ~할까요? (confirmation) | "~?" question | "Delete this club?" |
7. Codebase Audit: Current Issues
Analysis of src/config/i18n/ko.ts (as of 2026-03-28):
7.1 Inconsistent Error Verb Forms
The codebase mixes two error patterns:
// Pattern A: ~지 못했어요 (PREFERRED — system couldn't do it)
sessionCreateFailed: '앗, 모임을 만들지 못했어요. 다시 시도해 주세요.';
sessionStartFailed: '앗, 모임을 시작하지 못했어요. 다시 시도해 주세요.';
// Pattern B: ~에 실패했어요 (LESS PREFERRED — sounds like a failure report)
rsvpFailed: '앗, 참석 응답에 실패했어요. 다시 시도해 주세요.';
joinClubFailed: '앗, 클럽 가입에 실패했어요. 다시 시도해 주세요.';
leaveClubFailed: '앗, 클럽 탈퇴에 실패했어요. 다시 시도해 주세요.';
profileSaveFailed: '앗, 프로필 저장에 실패했어요. 다시 시도해 주세요.';Fix: Standardize all errors to Pattern A (~지 못했어요):
rsvpFailed-> "앗, 참석 응답을 처리하지 못했어요. 다시 시도해 주세요."joinClubFailed-> "앗, 클럽에 가입하지 못했어요. 다시 시도해 주세요."leaveClubFailed-> "앗, 클럽을 나가지 못했어요. 다시 시도해 주세요."profileSaveFailed-> "앗, 프로필을 저장하지 못했어요. 다시 시도해 주세요."
7.2 합니다체 Leaking Into Conversational UI
// These are accessibility hints — 합니다체 is CORRECT here:
prevMonthHint: '이전 달로 이동합니다'; // OK (a11y)
settingsHint: '설정 화면으로 이동합니다'; // OK (a11y)
// But this is a user-facing message, NOT an a11y hint — should be 해요체:
devModeOtaOnly: 'OTA 업데이트는 빌드된 앱에서만 동작합니다.';
// FIX: "OTA 업데이트는 빌드된 앱에서만 동작해요."
pinMax: '최대 4개까지 선택 가능합니다';
// FIX: "최대 4개까지 선택할 수 있어요"
// Mixed in a single message — jarring:
staleDataMessage: '최신 정보를 불러오지 못했어요. 이전 데이터를 표시합니다.';
// FIX: "최신 정보를 불러오지 못했어요. 이전 데이터를 보여 드릴게요."7.3 Repetitive Error Template
All errors follow an identical template: "앗, [X]하지 못했어요. 다시 시도해 주세요." While consistent, this becomes meaningless when users see it multiple times. Where possible, add specific guidance:
// Current (generic):
scoreSubmitFailed: '앗, 점수를 등록하지 못했어요. 다시 시도해 주세요.';
// Better (specific):
scoreSubmitFailed: '앗, 점수를 등록하지 못했어요. 인터넷 연결을 확인하고 다시 시도해 주세요.';
// Current (generic):
invalidInviteCode: '초대 코드가 맞지 않아요. 다시 확인해 주세요.';
// Better (specific):
invalidInviteCode: '초대 코드가 맞지 않아요. 관리자에게 코드를 다시 받아 보세요.';8. Quick Reference Card
Sentence Ending Cheat Sheet
| Context | Ending | Example |
|---|---|---|
| Success toast | ~됐어요 / ~완료! | "저장됐어요", "참석 완료!" |
| Primary CTA button | ~하기 | "참석하기", "모임 만들기" |
| Secondary button | Bare noun | "확인", "취소", "저장" |
| Suggestion (empty state body) | ~해 보세요 | "첫 모임을 만들어 보세요" |
| Confirmation dialog | ~할까요? | "삭제할까요?", "나갈까요?" |
| Error title | ~지 못했어요 | "불러오지 못했어요" |
| Error guidance | ~해 주세요 | "다시 시도해 주세요" |
| Status label | Noun / ~중 | "진행 중", "모집 중", "대기" |
| Notification description | ~드려요 | "알려 드려요" |
| Loading | ~는 중... | "불러오는 중..." |
| A11y hint | ~합니다 | "설정 화면으로 이동합니다" |
Do / Don't Summary
| DO | DON'T |
|---|---|
| 해요체 for all conversational UI | 합니다체 in user-facing messages |
| "~지 못했어요" for errors | "~에 실패했습니다" |
| "~해 보세요" for suggestions | "~하세요" (imperative commands) |
| "~할까요?" for confirmations | "~하시겠습니까?" |
| Pure Korean where possible | Unnecessary 한자어 |
| State consequence neutrally | Fear-based warnings ("주의!", "경고!") |
| Guide toward next action | Dead-end error messages |
! for celebrations | Emoji for emphasis |
| Concise (remove every unnecessary word) | Filler words ("현재", "앞으로", "성공적으로") |
9. Writing Checklist (for every new i18n string)
Before adding a string to ko.ts, verify:
- [ ] Speech level: Is it 해요체? (Only a11y hints use 합니다체)
- [ ] Speakable: Can you say it naturally in conversation?
- [ ] Weed-free: Is every word necessary? Remove filler.
- [ ] User-centered: Written from user's perspective, not system's?
- [ ] Next step: Does it tell the user what to do next?
- [ ] Positive framing: "~할 수 있어요" not "~할 수 없어요" (where possible)?
- [ ] Consistent pattern: Matches the sentence ending chart above?
- [ ] No 합니다 leak: Double-check no formal endings snuck in
- [ ] English parity: Does
en.tsentry match the tone? (Stripe/Notion feel)
10. Reference: App-by-App Deep Dive
Toss (토스) — The Benchmark
Core Values: Clear, Concise, Casual, Respectful, Emotional
8 Writing Principles:
- Predictable Hint — text hints at what happens next
- Weed Cutting — remove words that add no meaning
- Remove Empty Sentences — no decorative/redundant text
- Focus on Key Message — sometimes omitting info is kinder
- Easy to Speak — avoid 한자어 and 문어체; use words you'd say aloud
- Suggest Over Force — offer choices, never guilt or scare
- Universal Words — no jargon, slang, memes, or industry terms
- Find Hidden Emotion — go beyond info delivery; empathize with the user's feeling
Error philosophy: "좋은 에러 메시지 = 상황 설명 + 이유 + 해결책". Reframe negatively: "~할 수 있어요" instead of "안 돼요".
Dabusi takeaway: We already follow Toss closely. Key gap is error verb consistency and removing filler words.
카카오 (KakaoTalk, KakaoBank)
KakaoTalk: Pure 해요체. "메시지를 보냈어요", "사진이 저장됐어요". Minimal, functional — the message content is the hero, not the UI text.
KakaoBank: 해요체 for most interactions, but financial confirmations sometimes use 합니다체 for gravitas: "출금 이체가 완료되었습니다" alongside "이체할까요?" — the confirmation question is 해요체, the result is 합니다체. This mix works in finance but is NOT recommended for Dabusi (we are not a financial app).
KakaoBank error pattern: "이체하지 못했어요. [specific reason]." Always tells you why and what to do.
당근마켓 (Karrot)
Tone: "이웃" (neighbor) — the warmest of all major Korean apps. Every interaction feels like chatting with someone in your apartment complex.
Patterns:
- Empty states: "아직 동네 이웃이 없어요" (neighborly)
- Notifications: "회원님의 글에 댓글이 달렸어요" (uses 회원님, polite but warm)
- Listings: minimal text, photo-first, location chip
- Success: "글이 올라갔어요!" (conversational past tense)
Dabusi takeaway: We can adopt 당근's neighborhood warmth for club-internal communication. When a member RSVPs, it should feel like a friend confirming — "참석 완료!" not "참석이 등록되었습니다".
배달의민족 (Baemin)
Famous for: Quirky, witty Korean copywriting that makes mundane food delivery feel delightful. They commission custom fonts (배민체) and write copy that people screenshot and share.
How they balance humor + clarity:
- Humor lives in marketing, splash screens, and push notifications — never in critical flows
- Order confirmation, payment errors, and status updates are straightforward 해요체
- They never sacrifice clarity for a joke during a transaction
Example pattern:
- Marketing: "오늘 뭐 먹지?" (playful 해체 in headline only)
- Order flow: "주문이 접수됐어요" (standard 해요체)
- Error: "주문에 실패했어요. 다시 시도해 주세요." (empathetic 해요체)
Dabusi takeaway: We can add personality in onboarding, splash screens, and achievement celebrations. But session creation, score entry, and dues management must remain clear and straightforward.
네이버 (Naver)
Mixed approach: Naver's modern products (Naver Pay, Naver Maps, Naver Cafe) use 해요체. Legacy products and settings pages still have 합니다체. They are migrating toward 해요체 across the board.
Naver Pay: "결제가 완료됐어요" (해요체), "포인트가 적립됐어요" Naver Maps: "목적지까지 30분 걸려요" (해요체, conversational) Naver Cafe: "게시글이 등록됐어요" (해요체)
Dabusi takeaway: Naver's trajectory confirms that 해요체 is the future standard. We should never introduce 합니다체 into new strings.
11. Templates for Common Scenarios
Session Lifecycle
Created: "모임이 만들어졌어요!"
RSVP open: "참석 신청을 받고 있어요"
RSVP closed: "참석 신청이 마감됐어요"
Started: "모임이 시작됐어요!"
In progress: "지금 진행 중"
Ended: "모임이 종료됐어요. 수고했어요!"
Cancelled: "모임이 취소됐어요"Member Actions
Joined club: "환영해요! 클럽에 가입됐어요"
Left club: "클럽을 나갔어요"
RSVP yes: "참석 완료!"
RSVP cancel: "참석을 취소했어요"
Score saved: "점수가 저장됐어요"Admin Actions
Dues created: "회비가 생성됐어요"
Member removed: "멤버를 내보냈어요"
Matches generated: "경기 배정 완료!"
Settings saved: "설정이 저장됐어요"Last updated: 2026-03-28Based on: Toss tech blog principles, KakaoBank/KakaoTalk patterns, 당근마켓 tone analysis, 배민 copywriting analysis, Naver product audit, and Dabusi codebase audit of ko.ts