From 13198f9f1f0eeb6ae93f57d2520d3500bd0fb41c Mon Sep 17 00:00:00 2001 From: JiWoong Sul Date: Thu, 11 Dec 2025 19:42:25 +0900 Subject: [PATCH] =?UTF-8?q?feat(l10n):=20=ED=80=98=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EB=B0=8F=20=EC=8B=9C=EB=84=A4=EB=A7=88=ED=8B=B1=20=ED=85=8D?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EB=B2=88=EC=97=AD=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - game_text_l10n.dart에 게임 데이터 번역 함수 추가 - translateMonster, translateRace, translateKlass - translateTitle, translateImpressiveTitle - translateBoringItem, translateInterestingItem - pq_logic.dart monsterTask에서 몬스터 이름 번역 - completeQuest에서 퀘스트 아이템/몬스터 번역 - impressiveGuy, namedMonster에서 NPC 이름 번역 - interplotCinematic에서 시네마틱 아이템 번역 --- lib/data/game_text_l10n.dart | 51 +++++++++++++++++++++++++++++++++ lib/src/core/util/pq_logic.dart | 45 ++++++++++++++++++++--------- 2 files changed, 82 insertions(+), 14 deletions(-) diff --git a/lib/data/game_text_l10n.dart b/lib/data/game_text_l10n.dart index 0f943f2..5a7492f 100644 --- a/lib/data/game_text_l10n.dart +++ b/lib/data/game_text_l10n.dart @@ -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'; +} diff --git a/lib/src/core/util/pq_logic.dart b/lib/src/core/util/pq_logic.dart index 4cc59da..7d21852 100644 --- a/lib/src/core/util/pq_logic.dart +++ b/lib/src/core/util/pq_logic.dart @@ -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 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());