feat(l10n): 퀘스트 및 시네마틱 텍스트 번역 적용

- game_text_l10n.dart에 게임 데이터 번역 함수 추가
  - translateMonster, translateRace, translateKlass
  - translateTitle, translateImpressiveTitle
  - translateBoringItem, translateInterestingItem
- pq_logic.dart monsterTask에서 몬스터 이름 번역
- completeQuest에서 퀘스트 아이템/몬스터 번역
- impressiveGuy, namedMonster에서 NPC 이름 번역
- interplotCinematic에서 시네마틱 아이템 번역
This commit is contained in:
JiWoong Sul
2025-12-11 19:42:25 +09:00
parent b16ae6c2b8
commit 13198f9f1f
2 changed files with 82 additions and 14 deletions

View File

@@ -1,6 +1,8 @@
// 게임 텍스트 로컬라이제이션 (BuildContext 없이 사용)
// progress_service.dart, pq_logic.dart 등에서 사용
import 'package:askiineverdie/data/game_translations_ko.dart';
/// 현재 게임 로케일 설정 (전역)
String _currentLocale = 'en';
@@ -282,3 +284,52 @@ String namedMonsterFormat(String generatedName, String monsterType) =>
isKoreanLocale
? '$monsterType $generatedName'
: '$generatedName the $monsterType';
// ============================================================================
// 게임 데이터 번역 함수 (BuildContext 없이 사용)
// ============================================================================
/// 몬스터 이름 번역
String translateMonster(String englishName) =>
isKoreanLocale ? (monsterTranslationsKo[englishName] ?? englishName) : englishName;
/// 종족 이름 번역
String translateRace(String englishName) =>
isKoreanLocale ? (raceTranslationsKo[englishName] ?? englishName) : englishName;
/// 직업 이름 번역
String translateKlass(String englishName) =>
isKoreanLocale ? (klassTranslationsKo[englishName] ?? englishName) : englishName;
/// 칭호 이름 번역
String translateTitle(String englishName) =>
isKoreanLocale ? (titleTranslationsKo[englishName] ?? englishName) : englishName;
/// 인상적인 칭호 번역 (impressiveTitles용)
String translateImpressiveTitle(String englishName) =>
isKoreanLocale ? (impressiveTitleTranslationsKo[englishName] ?? englishName) : englishName;
/// 특수 아이템 이름 번역
String translateSpecial(String englishName) =>
isKoreanLocale ? (specialTranslationsKo[englishName] ?? englishName) : englishName;
/// 아이템 속성 이름 번역
String translateItemAttrib(String englishName) =>
isKoreanLocale ? (itemAttribTranslationsKo[englishName] ?? englishName) : englishName;
/// 아이템 "~의" 접미사 번역
String translateItemOf(String englishName) =>
isKoreanLocale ? (itemOfsTranslationsKo[englishName] ?? englishName) : englishName;
/// 단순 아이템 번역
String translateBoringItem(String englishName) =>
isKoreanLocale ? (boringItemTranslationsKo[englishName] ?? englishName) : englishName;
/// interestingItem 번역 (attrib + special 조합)
/// 예: "Golden Iterator" → "황금 이터레이터"
String translateInterestingItem(String attrib, String special) {
if (!isKoreanLocale) return '$attrib $special';
final translatedAttrib = itemAttribTranslationsKo[attrib] ?? attrib;
final translatedSpecial = specialTranslationsKo[special] ?? special;
return '$translatedAttrib $translatedSpecial';
}

View File

@@ -510,15 +510,18 @@ MonsterTaskResult monsterTask(
// 원본 Main.pas:537-547: 가끔 NPC를 몬스터로 사용
if (rng.nextInt(25) == 0) {
final race = pick(config.races, rng).split('|').first;
final raceEn = pick(config.races, rng).split('|').first;
final race = l10n.translateRace(raceEn);
if (rng.nextInt(2) == 0) {
// 'passing Race Class' 형태
final klass = pick(config.klasses, rng).split('|').first;
final klassEn = pick(config.klasses, rng).split('|').first;
final klass = l10n.translateKlass(klassEn);
monster = l10n.modifierPassing('$race $klass');
} else {
// 'Title Name the Race' 형태 (원본은 PickLow(Titles) 사용)
final title = pickLow(config.titles, rng);
monster = '$title ${generateName(rng)} the $race';
final titleEn = pickLow(config.titles, rng);
final title = l10n.translateTitle(titleEn);
monster = l10n.namedMonsterFormat(generateName(rng), '$title $race');
definite = true;
}
monsterLevel = targetLevel;
@@ -553,7 +556,8 @@ MonsterTaskResult monsterTask(
// Adjust quantity and adjectives based on level delta.
var qty = 1;
final levelDiff = targetLevel - monsterLevel;
var name = baseName;
// 몬스터 이름 번역 (l10n 지원)
var name = l10n.translateMonster(baseName);
if (levelDiff > 10) {
qty =
@@ -669,7 +673,8 @@ QuestResult completeQuest(PqConfig config, DeterministicRandom rng, int level) {
bestIndex = monsterIndex;
}
}
final name = best.split('|').first;
final nameEn = best.split('|').first;
final name = l10n.translateMonster(nameEn);
return QuestResult(
caption: l10n.questPatch(l10n.definiteL10n(name, 2)),
reward: reward,
@@ -678,19 +683,24 @@ QuestResult completeQuest(PqConfig config, DeterministicRandom rng, int level) {
monsterIndex: bestIndex,
);
case 1:
final item = interestingItem(config, rng);
// interestingItem: attrib + special 조합 후 번역
final attr = pick(config.itemAttrib, rng);
final special = pick(config.specials, rng);
final item = l10n.translateInterestingItem(attr, special);
return QuestResult(
caption: l10n.questLocate(l10n.definiteL10n(item, 1)),
reward: reward,
);
case 2:
final item = boringItem(config, rng);
final itemEn = boringItem(config, rng);
final item = l10n.translateBoringItem(itemEn);
return QuestResult(
caption: l10n.questTransfer(item),
reward: reward,
);
case 3:
final item = boringItem(config, rng);
final itemEn = boringItem(config, rng);
final item = l10n.translateBoringItem(itemEn);
return QuestResult(
caption: l10n.questDownload(l10n.indefiniteL10n(item, 1)),
reward: reward,
@@ -708,7 +718,8 @@ QuestResult completeQuest(PqConfig config, DeterministicRandom rng, int level) {
bestLevel = l;
}
}
final name = best.split('|').first;
final nameEn = best.split('|').first;
final name = l10n.translateMonster(nameEn);
// Stabilize는 fQuest.Caption := '' 로 비움 → monsterIndex 미저장
return QuestResult(
caption: l10n.questStabilize(l10n.definiteL10n(name, 2)),
@@ -926,11 +937,13 @@ String _pick(String pipeSeparated, DeterministicRandom rng) {
/// NPC 이름 생성 (ImpressiveGuy, Main.pas:514-521)
/// 인상적인 타이틀 + 종족 또는 이름 조합 (l10n 지원)
String impressiveGuy(PqConfig config, DeterministicRandom rng) {
final title = pick(config.impressiveTitles, rng);
final titleEn = pick(config.impressiveTitles, rng);
final title = l10n.translateImpressiveTitle(titleEn);
switch (rng.nextInt(2)) {
case 0:
// "the King of the Elves" / "엘프들의 왕" 형태
final race = pick(config.races, rng).split('|').first;
final raceEn = pick(config.races, rng).split('|').first;
final race = l10n.translateRace(raceEn);
return l10n.impressiveGuyPattern1(title, race);
case 1:
// "King Vrognak of Zoxzik" / "Zoxzik의 왕 Vrognak" 형태
@@ -961,8 +974,10 @@ String namedMonster(PqConfig config, DeterministicRandom rng, int level) {
}
}
// 몬스터 이름 번역
final translatedMonster = l10n.translateMonster(best);
// "GeneratedName the MonsterType" / "몬스터타입 GeneratedName" 형태
return l10n.namedMonsterFormat(generateName(rng), best);
return l10n.namedMonsterFormat(generateName(rng), translatedMonster);
}
/// 플롯 간 시네마틱 이벤트 생성 (InterplotCinematic, Main.pas:456-495)
@@ -1021,9 +1036,11 @@ List<QueueEntry> interplotCinematic(
case 2:
// 시나리오 3: 내부자 위협 발견
final guy = impressiveGuy(config, rng);
final itemEn = boringItem(config, rng);
final item = l10n.translateBoringItem(itemEn);
q(QueueKind.task, 2, l10n.cinematicBetrayal1(guy));
q(QueueKind.task, 3, l10n.cinematicBetrayal2(guy));
q(QueueKind.task, 2, l10n.cinematicBetrayal3(boringItem(config, rng)));
q(QueueKind.task, 2, l10n.cinematicBetrayal3(item));
q(QueueKind.task, 2, l10n.cinematicBetrayal4());
q(QueueKind.task, 2, l10n.cinematicBetrayal5(guy));
q(QueueKind.task, 3, l10n.cinematicBetrayal6());