feat(l10n): 장비/아이템 동적 이름 한국어 번역 지원

- pq_logic.dart: 구조화된 결과 타입 (EquipResult, ItemResult) 추가
- pq_logic.dart: 구조화된 생성 함수 (winEquipStructured, winItemStructured 등) 추가
- GameDataL10n: 구조화된 결과 렌더링 함수 추가 (renderEquipResult, renderItemResult)
- GameDataL10n: 문자열 파싱 기반 번역 함수 추가 (translateEquipString, translateItemString)
- game_play_screen.dart: 장비/아이템 목록에 번역 함수 적용
This commit is contained in:
JiWoong Sul
2025-12-11 18:36:51 +09:00
parent ebb0e0dda6
commit d4acd3503b
3 changed files with 411 additions and 13 deletions

View File

@@ -1,4 +1,5 @@
import 'package:askiineverdie/data/game_translations_ko.dart';
import 'package:askiineverdie/src/core/util/pq_logic.dart';
import 'package:flutter/widgets.dart';
/// 게임 데이터 번역을 위한 헬퍼 클래스
@@ -169,4 +170,220 @@ class GameDataL10n {
}
return englishName;
}
/// 구조화된 장비 결과를 로컬라이즈된 문자열로 렌더링
/// 예: EquipResult(baseName: "Keyboard", modifiers: ["Optimized"], plusValue: 2)
/// → 영어: "+2 Optimized Keyboard"
/// → 한국어: "+2 최적화된 키보드"
static String renderEquipResult(
BuildContext context,
EquipResult result,
int slotIndex,
) {
if (_isKorean(context)) {
return _renderEquipResultKo(result, slotIndex);
}
return result.displayName;
}
/// 한국어 장비 렌더링 (내부 함수)
static String _renderEquipResultKo(EquipResult result, int slotIndex) {
// 기본 장비 이름 번역
String baseName;
if (slotIndex == 0) {
baseName = weaponTranslationsKo[result.baseName] ?? result.baseName;
} else if (slotIndex == 1) {
baseName = shieldTranslationsKo[result.baseName] ?? result.baseName;
} else {
baseName = armorTranslationsKo[result.baseName] ?? result.baseName;
}
// 수식어 번역 (공격용 vs 방어용)
final isWeapon = slotIndex == 0;
final translatedModifiers = result.modifiers.map((mod) {
if (isWeapon) {
// 공격 속성: offenseAttrib 또는 offenseBad
return offenseAttribTranslationsKo[mod] ??
offenseBadTranslationsKo[mod] ??
mod;
} else {
// 방어 속성: defenseAttrib 또는 defenseBad
return defenseAttribTranslationsKo[mod] ??
defenseBadTranslationsKo[mod] ??
mod;
}
}).toList();
// 조합: 수식어들 + 기본 이름
var name = baseName;
for (final mod in translatedModifiers) {
name = '$mod $name';
}
// +/- 수치 추가
if (result.plusValue != 0) {
final sign = result.plusValue > 0 ? '+' : '';
name = '$sign${result.plusValue} $name';
}
return name;
}
/// 구조화된 아이템 결과를 로컬라이즈된 문자열로 렌더링
/// 예: ItemResult(attrib: "Golden", special: "Iterator", itemOf: "Compilation")
/// → 영어: "Golden Iterator of Compilation"
/// → 한국어: "컴파일의 황금 이터레이터"
static String renderItemResult(BuildContext context, ItemResult result) {
if (_isKorean(context)) {
return _renderItemResultKo(result);
}
return result.displayName;
}
/// 한국어 아이템 렌더링 (내부 함수)
static String _renderItemResultKo(ItemResult result) {
// 단순 아이템 (boringItem)
if (result.boringItem != null) {
// boringItem은 별도 번역 맵이 필요할 수 있음
// 현재는 그대로 반환 (대부분 영어 그대로 사용)
return result.boringItem!;
}
// 복합 아이템: attrib + special + itemOf
final attrib = result.attrib != null
? (itemAttribTranslationsKo[result.attrib] ?? result.attrib!)
: null;
final special = result.special != null
? (specialTranslationsKo[result.special] ?? result.special!)
: null;
final itemOf = result.itemOf != null
? (itemOfsTranslationsKo[result.itemOf] ?? result.itemOf!)
: null;
// 한국어 어순: "X의 Y" 패턴
// "Golden Iterator of Compilation" → "컴파일의 황금 이터레이터"
if (attrib != null && special != null && itemOf != null) {
return '$itemOf의 $attrib $special';
}
// attrib + special만 있는 경우
if (attrib != null && special != null) {
return '$attrib $special';
}
return '';
}
/// 장비 이름 문자열 파싱 후 번역 (기존 저장 데이터 호환)
/// 예: "+2 Optimized GPU-Powered Keyboard" → "+2 최적화된 GPU 파워 키보드"
static String translateEquipString(
BuildContext context,
String equipString,
int slotIndex,
) {
if (!_isKorean(context) || equipString.isEmpty) return equipString;
// 1. +/- 값 추출
final plusMatch = RegExp(r'^([+-]?\d+)\s+').firstMatch(equipString);
String remaining = equipString;
String plusPart = '';
if (plusMatch != null) {
plusPart = plusMatch.group(1)!;
remaining = equipString.substring(plusMatch.end);
}
// 2. 기본 장비 이름 찾기 (가장 긴 매칭 우선)
final Map<String, String> baseMap;
if (slotIndex == 0) {
baseMap = weaponTranslationsKo;
} else if (slotIndex == 1) {
baseMap = shieldTranslationsKo;
} else {
baseMap = armorTranslationsKo;
}
String baseTranslated = remaining;
String modifierPart = '';
// 기본 장비 이름을 뒤에서부터 찾기
for (final entry in baseMap.entries) {
if (remaining.endsWith(entry.key)) {
baseTranslated = entry.value;
modifierPart = remaining.substring(
0,
remaining.length - entry.key.length,
).trim();
break;
}
}
// 3. 수식어 번역
final isWeapon = slotIndex == 0;
final modWords = modifierPart.split(' ').where((s) => s.isNotEmpty).toList();
final translatedMods = modWords.map((mod) {
if (isWeapon) {
return offenseAttribTranslationsKo[mod] ??
offenseBadTranslationsKo[mod] ??
mod;
} else {
return defenseAttribTranslationsKo[mod] ??
defenseBadTranslationsKo[mod] ??
mod;
}
}).toList();
// 4. 조합
var result = baseTranslated;
for (final mod in translatedMods.reversed) {
result = '$mod $result';
}
if (plusPart.isNotEmpty) {
result = '$plusPart $result';
}
return result;
}
/// 아이템 이름 문자열 파싱 후 번역 (기존 저장 데이터 호환)
/// 예: "Golden Iterator of Compilation" → "컴파일의 황금 이터레이터"
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);
if (ofMatch != null) {
final beforeOf = ofMatch.group(1)!; // "Golden Iterator"
final afterOf = ofMatch.group(2)!; // "Compilation"
// "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 없음)
final words = itemString.split(' ');
if (words.length == 2) {
final attrib = words[0];
final special = words[1];
final attribKo = itemAttribTranslationsKo[attrib] ?? attrib;
final specialKo = specialTranslationsKo[special] ?? special;
return '$attribKo $specialKo';
}
// 단일 단어 (boringItem 등)
return itemString;
}
}