slang-i18n Templates
Internationalization translation pattern templates.
Internationalization translation pattern templates.
Template A: Basic Translation Structure#
ko.i18n.json (Korean, base)#
{
"common": {
"app_name": "Unibook",
"ok": "확인",
"cancel": "취소",
"save": "저장",
"delete": "삭제",
"edit": "수정",
"close": "닫기",
"loading": "로딩 중...",
"error": "오류가 발생했습니다",
"retry": "다시 시도",
"no_data": "데이터가 없습니다"
},
"auth": {
"login": "로그인",
"logout": "로그아웃",
"sign_up": "회원가입",
"email": "이메일",
"password": "비밀번호",
"forgot_password": "비밀번호를 잊으셨나요?"
},
"navigation": {
"home": "홈",
"search": "검색",
"mypage": "마이페이지",
"settings": "설정"
}
}
en.i18n.json (English)#
{
"common": {
"app_name": "Unibook",
"ok": "OK",
"cancel": "Cancel",
"save": "Save",
"delete": "Delete",
"edit": "Edit",
"close": "Close",
"loading": "Loading...",
"error": "An error occurred",
"retry": "Retry",
"no_data": "No data available"
},
"auth": {
"login": "Login",
"logout": "Logout",
"sign_up": "Sign Up",
"email": "Email",
"password": "Password",
"forgot_password": "Forgot password?"
},
"navigation": {
"home": "Home",
"search": "Search",
"mypage": "My Page",
"settings": "Settings"
}
}
Template B: Parameter Patterns#
User Greetings#
{
"user": {
"greeting": "{name}님, 안녕하세요!",
"welcome_back": "{name}님, 다시 오셨군요!",
"member_since": "{date}에 가입",
"points": "{count} 포인트 보유",
"level_info": "{level} 등급 회원입니다"
}
}
Dart Usage Example#
// User greeting
Text(context.i10n.user.greeting(name: user.displayName))
// Points display
Text(context.i10n.user.points(count: user.points.toString()))
// Join date display
Text(context.i10n.user.member_since(date: DateFormat('yyyy-MM-dd').format(user.createdAt)))
Template C: Pluralization Patterns#
Item Counts#
{
"items": {
"count(param=n)": {
"zero": "항목 없음",
"one": "항목 1개",
"other": "항목 {n}개"
}
},
"cart": {
"item_count(param=count)": {
"zero": "장바구니가 비어있습니다",
"one": "장바구니에 상품 1개",
"other": "장바구니에 상품 {count}개"
}
},
"notifications": {
"unread(param=count)": {
"zero": "읽지 않은 알림 없음",
"one": "읽지 않은 알림 1개",
"other": "읽지 않은 알림 {count}개"
}
},
"messages": {
"count(param=n)": {
"zero": "메시지 없음",
"one": "메시지 1개",
"other": "메시지 {n}개"
}
},
"followers": {
"count(param=n)": {
"zero": "팔로워 없음",
"one": "팔로워 1명",
"other": "팔로워 {n}명"
}
}
}
Dart Usage Example#
// Cart item count
Text(context.i10n.cart.item_count(count: cartItems.length))
// Notification badge
Badge(
label: Text(context.i10n.notifications.unread(count: unreadCount)),
)
// Follower count
Text(context.i10n.followers.count(n: followerCount))
Template D: Context-Based Patterns#
Enum-Based Translation#
{
"pet": {
"type(context=PetType)": {
"dog": "강아지",
"cat": "고양이",
"bird": "새",
"fish": "물고기",
"hamster": "햄스터",
"other": "기타"
}
},
"order": {
"status(context=OrderStatus)": {
"pending": "대기 중",
"confirmed": "확인됨",
"shipping": "배송 중",
"delivered": "배송 완료",
"cancelled": "취소됨"
}
},
"payment": {
"method(context=PaymentMethod)": {
"card": "카드 결제",
"bank": "계좌이체",
"kakao": "카카오페이",
"naver": "네이버페이"
}
}
}
Dart Enum Definition#
// lib/domain/entity/pet_type.dart
enum PetType {
dog,
cat,
bird,
fish,
hamster,
other,
}
// Usage
Text(context.i10n.pet.type(context: pet.type))
Text(context.i10n.order.status(context: order.status))
Template E: Error Message Patterns#
Form Validation Errors#
{
"validation": {
"required": "필수 입력 항목입니다",
"email": {
"invalid": "올바른 이메일 형식이 아닙니다",
"exists": "이미 사용 중인 이메일입니다"
},
"password": {
"too_short": "비밀번호는 최소 8자 이상이어야 합니다",
"too_weak": "비밀번호가 너무 약합니다",
"mismatch": "비밀번호가 일치하지 않습니다"
},
"phone": {
"invalid": "올바른 전화번호 형식이 아닙니다"
}
},
"error": {
"network": "네트워크 연결을 확인해주세요",
"server": "서버 오류가 발생했습니다",
"timeout": "요청 시간이 초과되었습니다",
"unauthorized": "로그인이 필요합니다",
"forbidden": "접근 권한이 없습니다",
"not_found": "요청한 정보를 찾을 수 없습니다",
"unknown": "알 수 없는 오류가 발생했습니다"
}
}
Dart Usage Example#
// Form validation
TextFormField(
validator: (value) {
if (value == null || value.isEmpty) {
return context.i10n.validation.required;
}
if (!EmailValidator.validate(value)) {
return context.i10n.validation.email.invalid;
}
return null;
},
)
// Error handling
result.fold(
(failure) => ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(context.i10n.error.network)),
),
(success) => navigateToHome(),
);
Template F: Date/Time Patterns#
Relative Time#
{
"time": {
"just_now": "방금 전",
"minutes_ago": "{minutes}분 전",
"hours_ago": "{hours}시간 전",
"days_ago": "{days}일 전",
"weeks_ago": "{weeks}주 전",
"months_ago": "{months}개월 전",
"years_ago": "{years}년 전",
"in_minutes": "{minutes}분 후",
"in_hours": "{hours}시간 후",
"in_days": "{days}일 후"
}
}
Dart Usage Example#
String getRelativeTime(BuildContext context, DateTime dateTime) {
final now = DateTime.now();
final difference = now.difference(dateTime);
if (difference.inMinutes < 1) {
return context.i10n.time.just_now;
} else if (difference.inMinutes < 60) {
return context.i10n.time.minutes_ago(minutes: difference.inMinutes.toString());
} else if (difference.inHours < 24) {
return context.i10n.time.hours_ago(hours: difference.inHours.toString());
} else if (difference.inDays < 7) {
return context.i10n.time.days_ago(days: difference.inDays.toString());
} else if (difference.inDays < 30) {
return context.i10n.time.weeks_ago(weeks: (difference.inDays ~/ 7).toString());
} else if (difference.inDays < 365) {
return context.i10n.time.months_ago(months: (difference.inDays ~/ 30).toString());
} else {
return context.i10n.time.years_ago(years: (difference.inDays ~/ 365).toString());
}
}
Template G: Rich Text Patterns#
Text with Links#
{
"legal": {
"terms_agreement": "이용약관에 동의합니다. <link>자세히 보기</link>",
"privacy_agreement": "개인정보 처리방침에 동의합니다. <link>자세히 보기</link>"
},
"marketing": {
"promotion": "<highlight>특별 할인</highlight> 이벤트가 진행 중입니다!",
"new_feature": "새로운 기능이 추가되었습니다. <link>확인하기</link>"
}
}
Dart Usage Example#
// Rich text builder
Text.rich(
context.i10n.legal.terms_agreement(
link: (text) => TextSpan(
text: text,
style: TextStyle(
color: AppColors.primary,
decoration: TextDecoration.underline,
),
recognizer: TapGestureRecognizer()
..onTap = () => Navigator.pushNamed(context, '/terms'),
),
),
)
// Highlighted text
Text.rich(
context.i10n.marketing.promotion(
highlight: (text) => TextSpan(
text: text,
style: TextStyle(
color: AppColors.accent,
fontWeight: FontWeight.bold,
),
),
),
)
Slang Configuration Template#
slang.yaml#
# Slang configuration file
base_locale: ko
fallback_strategy: base_locale
# Input settings
input_directory: lib/src/json
input_file_pattern: .i18n.json
# Output settings
# slang 4 writes translations.g.dart plus one file per locale
# (translations_ko.g.dart, translations_en.g.dart, ...).
# output_format was removed in slang 4.0.0.
output_directory: lib/src/translations
output_file_name: translations.g.dart
# Case settings
key_case: snake
key_map_case: snake
param_case: snake
# Other settings
string_interpolation: braces
flat_map: false
timestamp: true
statistics: true
translate_var: translations
# Interface generation (if needed)
interfaces:
PageData:
paths:
- home
- settings
- profile
GPT Translation Hint Template#
JSON does not support inline comments, so translation hints for slang_gpt
are provided via the reserved @ metadata keys (for example @app_name)
rather than comments.
{
"common": {
"app_name": "Unibook",
"@app_name": {
"description": "App name - do not translate, keep as Unibook"
}
},
"buttons": {
"submit": "제출",
"@submit": { "description": "Button text - keep short and clear (Submit)" },
"confirm": "확인",
"@confirm": { "description": "Button text - Confirm / OK" }
},
"pet": {
"type(context=PetType)": {
"dog": "강아지",
"cat": "고양이"
},
"@type": {
"description": "Pet species - maintain friendly tone; dog=dog, cat=cat"
}
}
}