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:
@@ -1,6 +1,8 @@
|
|||||||
// 게임 텍스트 로컬라이제이션 (BuildContext 없이 사용)
|
// 게임 텍스트 로컬라이제이션 (BuildContext 없이 사용)
|
||||||
// progress_service.dart, pq_logic.dart 등에서 사용
|
// progress_service.dart, pq_logic.dart 등에서 사용
|
||||||
|
|
||||||
|
import 'package:askiineverdie/data/game_translations_ko.dart';
|
||||||
|
|
||||||
/// 현재 게임 로케일 설정 (전역)
|
/// 현재 게임 로케일 설정 (전역)
|
||||||
String _currentLocale = 'en';
|
String _currentLocale = 'en';
|
||||||
|
|
||||||
@@ -282,3 +284,52 @@ String namedMonsterFormat(String generatedName, String monsterType) =>
|
|||||||
isKoreanLocale
|
isKoreanLocale
|
||||||
? '$monsterType $generatedName'
|
? '$monsterType $generatedName'
|
||||||
: '$generatedName the $monsterType';
|
: '$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';
|
||||||
|
}
|
||||||
|
|||||||
@@ -510,15 +510,18 @@ MonsterTaskResult monsterTask(
|
|||||||
|
|
||||||
// 원본 Main.pas:537-547: 가끔 NPC를 몬스터로 사용
|
// 원본 Main.pas:537-547: 가끔 NPC를 몬스터로 사용
|
||||||
if (rng.nextInt(25) == 0) {
|
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) {
|
if (rng.nextInt(2) == 0) {
|
||||||
// 'passing Race Class' 형태
|
// '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');
|
monster = l10n.modifierPassing('$race $klass');
|
||||||
} else {
|
} else {
|
||||||
// 'Title Name the Race' 형태 (원본은 PickLow(Titles) 사용)
|
// 'Title Name the Race' 형태 (원본은 PickLow(Titles) 사용)
|
||||||
final title = pickLow(config.titles, rng);
|
final titleEn = pickLow(config.titles, rng);
|
||||||
monster = '$title ${generateName(rng)} the $race';
|
final title = l10n.translateTitle(titleEn);
|
||||||
|
monster = l10n.namedMonsterFormat(generateName(rng), '$title $race');
|
||||||
definite = true;
|
definite = true;
|
||||||
}
|
}
|
||||||
monsterLevel = targetLevel;
|
monsterLevel = targetLevel;
|
||||||
@@ -553,7 +556,8 @@ MonsterTaskResult monsterTask(
|
|||||||
// Adjust quantity and adjectives based on level delta.
|
// Adjust quantity and adjectives based on level delta.
|
||||||
var qty = 1;
|
var qty = 1;
|
||||||
final levelDiff = targetLevel - monsterLevel;
|
final levelDiff = targetLevel - monsterLevel;
|
||||||
var name = baseName;
|
// 몬스터 이름 번역 (l10n 지원)
|
||||||
|
var name = l10n.translateMonster(baseName);
|
||||||
|
|
||||||
if (levelDiff > 10) {
|
if (levelDiff > 10) {
|
||||||
qty =
|
qty =
|
||||||
@@ -669,7 +673,8 @@ QuestResult completeQuest(PqConfig config, DeterministicRandom rng, int level) {
|
|||||||
bestIndex = monsterIndex;
|
bestIndex = monsterIndex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
final name = best.split('|').first;
|
final nameEn = best.split('|').first;
|
||||||
|
final name = l10n.translateMonster(nameEn);
|
||||||
return QuestResult(
|
return QuestResult(
|
||||||
caption: l10n.questPatch(l10n.definiteL10n(name, 2)),
|
caption: l10n.questPatch(l10n.definiteL10n(name, 2)),
|
||||||
reward: reward,
|
reward: reward,
|
||||||
@@ -678,19 +683,24 @@ QuestResult completeQuest(PqConfig config, DeterministicRandom rng, int level) {
|
|||||||
monsterIndex: bestIndex,
|
monsterIndex: bestIndex,
|
||||||
);
|
);
|
||||||
case 1:
|
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(
|
return QuestResult(
|
||||||
caption: l10n.questLocate(l10n.definiteL10n(item, 1)),
|
caption: l10n.questLocate(l10n.definiteL10n(item, 1)),
|
||||||
reward: reward,
|
reward: reward,
|
||||||
);
|
);
|
||||||
case 2:
|
case 2:
|
||||||
final item = boringItem(config, rng);
|
final itemEn = boringItem(config, rng);
|
||||||
|
final item = l10n.translateBoringItem(itemEn);
|
||||||
return QuestResult(
|
return QuestResult(
|
||||||
caption: l10n.questTransfer(item),
|
caption: l10n.questTransfer(item),
|
||||||
reward: reward,
|
reward: reward,
|
||||||
);
|
);
|
||||||
case 3:
|
case 3:
|
||||||
final item = boringItem(config, rng);
|
final itemEn = boringItem(config, rng);
|
||||||
|
final item = l10n.translateBoringItem(itemEn);
|
||||||
return QuestResult(
|
return QuestResult(
|
||||||
caption: l10n.questDownload(l10n.indefiniteL10n(item, 1)),
|
caption: l10n.questDownload(l10n.indefiniteL10n(item, 1)),
|
||||||
reward: reward,
|
reward: reward,
|
||||||
@@ -708,7 +718,8 @@ QuestResult completeQuest(PqConfig config, DeterministicRandom rng, int level) {
|
|||||||
bestLevel = l;
|
bestLevel = l;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
final name = best.split('|').first;
|
final nameEn = best.split('|').first;
|
||||||
|
final name = l10n.translateMonster(nameEn);
|
||||||
// Stabilize는 fQuest.Caption := '' 로 비움 → monsterIndex 미저장
|
// Stabilize는 fQuest.Caption := '' 로 비움 → monsterIndex 미저장
|
||||||
return QuestResult(
|
return QuestResult(
|
||||||
caption: l10n.questStabilize(l10n.definiteL10n(name, 2)),
|
caption: l10n.questStabilize(l10n.definiteL10n(name, 2)),
|
||||||
@@ -926,11 +937,13 @@ String _pick(String pipeSeparated, DeterministicRandom rng) {
|
|||||||
/// NPC 이름 생성 (ImpressiveGuy, Main.pas:514-521)
|
/// NPC 이름 생성 (ImpressiveGuy, Main.pas:514-521)
|
||||||
/// 인상적인 타이틀 + 종족 또는 이름 조합 (l10n 지원)
|
/// 인상적인 타이틀 + 종족 또는 이름 조합 (l10n 지원)
|
||||||
String impressiveGuy(PqConfig config, DeterministicRandom rng) {
|
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)) {
|
switch (rng.nextInt(2)) {
|
||||||
case 0:
|
case 0:
|
||||||
// "the King of the Elves" / "엘프들의 왕" 형태
|
// "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);
|
return l10n.impressiveGuyPattern1(title, race);
|
||||||
case 1:
|
case 1:
|
||||||
// "King Vrognak of Zoxzik" / "Zoxzik의 왕 Vrognak" 형태
|
// "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" 형태
|
// "GeneratedName the MonsterType" / "몬스터타입 GeneratedName" 형태
|
||||||
return l10n.namedMonsterFormat(generateName(rng), best);
|
return l10n.namedMonsterFormat(generateName(rng), translatedMonster);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 플롯 간 시네마틱 이벤트 생성 (InterplotCinematic, Main.pas:456-495)
|
/// 플롯 간 시네마틱 이벤트 생성 (InterplotCinematic, Main.pas:456-495)
|
||||||
@@ -1021,9 +1036,11 @@ List<QueueEntry> interplotCinematic(
|
|||||||
case 2:
|
case 2:
|
||||||
// 시나리오 3: 내부자 위협 발견
|
// 시나리오 3: 내부자 위협 발견
|
||||||
final guy = impressiveGuy(config, rng);
|
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, 2, l10n.cinematicBetrayal1(guy));
|
||||||
q(QueueKind.task, 3, l10n.cinematicBetrayal2(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.cinematicBetrayal4());
|
||||||
q(QueueKind.task, 2, l10n.cinematicBetrayal5(guy));
|
q(QueueKind.task, 2, l10n.cinematicBetrayal5(guy));
|
||||||
q(QueueKind.task, 3, l10n.cinematicBetrayal6());
|
q(QueueKind.task, 3, l10n.cinematicBetrayal6());
|
||||||
|
|||||||
Reference in New Issue
Block a user