feat(l10n): 몬스터 드롭 아이템 번역 로직 개선

- dropItemTranslationsKo 추가 (250+ 드롭 아이템 번역)
- translateItemString 함수 리팩터링:
  - specialItem 형식 정확히 감지 (itemOfs 검증)
  - 몬스터 드롭 형식 지원 ("{monster} {drop}" → "{몬스터}의 {드롭}")
- 인벤토리 아이템이 올바르게 한글로 표시됨
This commit is contained in:
JiWoong Sul
2025-12-11 19:30:49 +09:00
parent 071ac5f1e3
commit b16ae6c2b8
2 changed files with 413 additions and 23 deletions

View File

@@ -344,32 +344,20 @@ class GameDataL10n {
/// 아이템 이름 문자열 파싱 후 번역 (기존 저장 데이터 호환)
/// 예: "Golden Iterator of Compilation" → "컴파일의 황금 이터레이터"
/// 예: "index out of bounds Array fragment" → "인덱스 초과의 배열 조각"
static String translateItemString(BuildContext context, String itemString) {
if (!_isKorean(context) || itemString.isEmpty) return itemString;
// "X Y of Z" 패턴 파싱
final ofMatch = RegExp(r'^(.+)\s+of\s+(.+)$').firstMatch(itemString);
// 1. specialItem 형식 체크: "Attrib Special of ItemOf"
// itemOfs에 있는 값으로 끝나는지 확인
final specialItemResult = _tryTranslateSpecialItem(itemString);
if (specialItemResult != null) return specialItemResult;
if (ofMatch != null) {
final beforeOf = ofMatch.group(1)!; // "Golden Iterator"
final afterOf = ofMatch.group(2)!; // "Compilation"
// 2. 몬스터 드롭 형식 체크: "{monster_lowercase} {drop_ProperCase}"
final monsterDropResult = _tryTranslateMonsterDrop(itemString);
if (monsterDropResult != null) return monsterDropResult;
// "X Y" 분리 (마지막 단어가 special)
final words = beforeOf.split(' ');
if (words.length >= 2) {
final attrib = words.sublist(0, words.length - 1).join(' ');
final special = words.last;
final attribKo = itemAttribTranslationsKo[attrib] ?? attrib;
final specialKo = specialTranslationsKo[special] ?? special;
final itemOfKo = itemOfsTranslationsKo[afterOf] ?? afterOf;
// 한국어 어순: "ItemOf의 Attrib Special"
return '$itemOfKo의 $attribKo $specialKo';
}
}
// "X Y" 패턴 (of 없음)
// 3. interestingItem 형식: "Attrib Special" (2단어)
final words = itemString.split(' ');
if (words.length == 2) {
final attrib = words[0];
@@ -381,7 +369,92 @@ class GameDataL10n {
return '$attribKo $specialKo';
}
// 단일 단어 (boringItem 등) - 잡템 번역 시도
return boringItemTranslationsKo[itemString] ?? itemString;
// 4. 단일 단어 (boringItem 등) - 잡템 번역 시도
return boringItemTranslationsKo[itemString] ??
dropItemTranslationsKo[itemString.toLowerCase()] ??
itemString;
}
/// specialItem 형식 번역 시도
/// "Attrib Special of ItemOf" → "ItemOf의 Attrib Special"
static String? _tryTranslateSpecialItem(String itemString) {
// "of" 뒤의 부분이 itemOfs에 있는지 확인
final ofMatch = RegExp(r'^(.+)\s+of\s+(.+)$').firstMatch(itemString);
if (ofMatch == null) return null;
final beforeOf = ofMatch.group(1)!;
final afterOf = ofMatch.group(2)!;
// afterOf가 itemOfs에 있어야 specialItem 형식
if (!itemOfsTranslationsKo.containsKey(afterOf)) return null;
// beforeOf를 Attrib + Special로 분리
final words = beforeOf.split(' ');
if (words.length < 2) return null;
final attrib = words.sublist(0, words.length - 1).join(' ');
final special = words.last;
// Attrib와 Special이 유효한지 확인
if (!itemAttribTranslationsKo.containsKey(attrib) &&
!specialTranslationsKo.containsKey(special)) {
return null;
}
final attribKo = itemAttribTranslationsKo[attrib] ?? attrib;
final specialKo = specialTranslationsKo[special] ?? special;
final itemOfKo = itemOfsTranslationsKo[afterOf] ?? afterOf;
return '$itemOfKo의 $attribKo $specialKo';
}
/// 몬스터 드롭 형식 번역 시도
/// "{monster_lowercase} {drop_ProperCase}" → "{몬스터}의 {드롭아이템}"
static String? _tryTranslateMonsterDrop(String itemString) {
// dropItemTranslationsKo에서 매칭되는 드롭 아이템 찾기
// (대소문자 무시, 아이템 문자열 끝에서 매칭)
for (final entry in dropItemTranslationsKo.entries) {
final dropItem = entry.key;
final dropItemProperCase = _properCase(dropItem);
// 아이템 문자열이 드롭 아이템으로 끝나는지 확인
if (itemString.endsWith(dropItemProperCase) ||
itemString.endsWith(dropItem)) {
// 드롭 아이템 앞 부분이 몬스터 이름
String monsterPart;
if (itemString.endsWith(dropItemProperCase)) {
monsterPart =
itemString.substring(0, itemString.length - dropItemProperCase.length).trim();
} else {
monsterPart =
itemString.substring(0, itemString.length - dropItem.length).trim();
}
if (monsterPart.isEmpty) continue;
// 몬스터 이름 번역 (소문자를 원래 형태로 변환하여 찾기)
final monsterNameKey = _toTitleCase(monsterPart);
final monsterKo =
monsterTranslationsKo[monsterNameKey] ?? monsterPart;
final dropKo = entry.value;
return '$monsterKo의 $dropKo';
}
}
return null;
}
/// 첫 글자만 대문자로 (나머지는 그대로)
static String _properCase(String s) {
if (s.isEmpty) return s;
return s[0].toUpperCase() + s.substring(1);
}
/// 각 단어의 첫 글자를 대문자로 (Title Case)
static String _toTitleCase(String s) {
return s.split(' ').map((word) {
if (word.isEmpty) return word;
return word[0].toUpperCase() + word.substring(1);
}).join(' ');
}
}