diff --git a/public/locales/en.json b/public/locales/en.json new file mode 100644 index 0000000..6c51236 --- /dev/null +++ b/public/locales/en.json @@ -0,0 +1,56 @@ +{ + "intro": { + "choice": { + "left_door": "Walk toward the glowing door on the left", + "right_door": "Walk toward the ordinary door on the right", + "search": "Search the room carefully", + "stay": "Stay where you are, do nothing" + } + }, + "left_door": { + "choice": { + "handshake": "Shake hands with the stranger", + "reject": "Refuse to shake, stay alert" + } + }, + "right_door": { + "choice": { + "continue": "Keep moving forward", + "back": "Turn back" + } + }, + "trust_ending": { + "choice": { + "journey": "Embark on a journey of trust (requires trust >= 80)", + "leave": "Leave this place" + } + }, + "investigation_site": { + "choice": { + "leave": "Leave the room" + } + }, + "desk_detail": { + "choice": { + "return": "Return to the crime scene", + "leave": "Leave" + } + }, + "stay": { + "choice": { + "stand": "Stand up and leave" + } + }, + "qte_success": { + "choice": { + "continue": "Keep moving forward", + "back": "Turn back" + } + }, + "qte_fail": { + "choice": { + "continue": "Keep moving forward", + "back": "Turn back" + } + } +} \ No newline at end of file diff --git a/public/locales/ja.json b/public/locales/ja.json new file mode 100644 index 0000000..87253fe --- /dev/null +++ b/public/locales/ja.json @@ -0,0 +1,56 @@ +{ + "intro": { + "choice": { + "left_door": "左にある光るドアへ向かう", + "right_door": "右にある普通のドアへ向かう", + "search": "部屋を念入りに調べる", + "stay": "その場に留まる" + } + }, + "left_door": { + "choice": { + "handshake": "見知らぬ人と握手する", + "reject": "握手を断り、警戒を続ける" + } + }, + "right_door": { + "choice": { + "continue": "先へ進む", + "back": "引き返す" + } + }, + "trust_ending": { + "choice": { + "journey": "信頼の旅に出る(trust >= 80 が必要)", + "leave": "ここを去る" + } + }, + "investigation_site": { + "choice": { + "leave": "部屋を出る" + } + }, + "desk_detail": { + "choice": { + "return": "調査現場に戻る", + "leave": "立ち去る" + } + }, + "stay": { + "choice": { + "stand": "立ち上がって去る" + } + }, + "qte_success": { + "choice": { + "continue": "先へ進む", + "back": "引き返す" + } + }, + "qte_fail": { + "choice": { + "continue": "先へ進む", + "back": "引き返す" + } + } +} \ No newline at end of file diff --git a/public/locales/zh.json b/public/locales/zh.json new file mode 100644 index 0000000..30bc673 --- /dev/null +++ b/public/locales/zh.json @@ -0,0 +1,56 @@ +{ + "intro": { + "choice": { + "left_door": "走向左边那扇发光的门", + "right_door": "走向右边那扇普通的门", + "search": "仔细搜索房间", + "stay": "留在原地,什么也不做" + } + }, + "left_door": { + "choice": { + "handshake": "与陌生人握手", + "reject": "拒绝握手,保持警惕" + } + }, + "right_door": { + "choice": { + "continue": "继续前进", + "back": "回头" + } + }, + "trust_ending": { + "choice": { + "journey": "开启信任的旅程(需要 trust >= 80)", + "leave": "离开这里" + } + }, + "investigation_site": { + "choice": { + "leave": "离开房间" + } + }, + "desk_detail": { + "choice": { + "return": "返回调查现场", + "leave": "离开" + } + }, + "stay": { + "choice": { + "stand": "站起来离开" + } + }, + "qte_success": { + "choice": { + "continue": "继续前进", + "back": "回头" + } + }, + "qte_fail": { + "choice": { + "continue": "继续前进", + "back": "回头" + } + } +} \ No newline at end of file diff --git a/src/composables/useI18n.ts b/src/composables/useI18n.ts index 58886dd..d386d69 100644 --- a/src/composables/useI18n.ts +++ b/src/composables/useI18n.ts @@ -1,29 +1,62 @@ import { ref } from 'vue' -import zh from '@/locales/zh.json' -import en from '@/locales/en.json' -import ja from '@/locales/ja.json' +import zhUI from '@/locales/zh.json' +import enUI from '@/locales/en.json' +import jaUI from '@/locales/ja.json' -const messages = { zh, en, ja } as const +const uiMessages = { zh: zhUI, en: enUI, ja: jaUI } as const type Lang = 'zh' | 'en' | 'ja' const currentLang = ref( - (localStorage.getItem('lang') as Lang) || 'zh' + (localStorage.getItem('lang') as Lang) || 'zh', ) -export function useI18n() { - function t(key: string): string { - const parts = key.split('.') - let result: any = messages[currentLang.value] - for (const p of parts) { - result = result?.[p] +const storyMessages = ref>>({}) +const storyLoading = new Set() + +async function loadStoryMessages(lang: Lang) { + if (storyMessages.value[lang] || storyLoading.has(lang)) return + storyLoading.add(lang) + try { + const resp = await fetch(`/locales/${lang}.json`) + if (resp.ok) { + storyMessages.value = { + ...storyMessages.value, + [lang]: await resp.json(), + } } - return typeof result === 'string' ? result : key + } catch { + /* story locales optional */ + } finally { + storyLoading.delete(lang) } +} - function setLang(lang: Lang) { - currentLang.value = lang - localStorage.setItem('lang', lang) +loadStoryMessages(currentLang.value) + +function t(key: string): string { + const parts = key.split('.') + + let result: any = storyMessages.value[currentLang.value] + for (const p of parts) { + result = result?.[p] } + if (typeof result === 'string') return result + let fallback: any = uiMessages[currentLang.value] + for (const p of parts) { + fallback = fallback?.[p] + } + if (typeof fallback === 'string') return fallback + + return key +} + +async function setLang(lang: Lang) { + await loadStoryMessages(lang) + currentLang.value = lang + localStorage.setItem('lang', lang) +} + +export function useI18n() { return { t, currentLang, setLang } } diff --git a/src/locales/en.json b/src/locales/en.json index 536b695..1de726b 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -19,61 +19,5 @@ "speed": "Speed", "noAutoSave": "No auto save yet", "autoSaveHint": "Game auto-saves to slot 0 at each scene change" - }, - "scene": { - "intro": { - "choice": { - "left_door": "Walk toward the glowing door on the left", - "right_door": "Walk toward the ordinary door on the right", - "search": "Search the room carefully", - "stay": "Stay where you are, do nothing" - } - }, - "left_door": { - "choice": { - "handshake": "Shake hands with the stranger", - "reject": "Refuse to shake, stay alert" - } - }, - "right_door": { - "choice": { - "continue": "Keep moving forward", - "back": "Turn back" - } - }, - "trust_ending": { - "choice": { - "journey": "Embark on a journey of trust (requires trust >= 80)", - "leave": "Leave this place" - } - }, - "investigation_site": { - "choice": { - "leave": "Leave the room" - } - }, - "desk_detail": { - "choice": { - "return": "Return to the crime scene", - "leave": "Leave" - } - }, - "stay": { - "choice": { - "stand": "Stand up and leave" - } - }, - "qte_success": { - "choice": { - "continue": "Keep moving forward", - "back": "Turn back" - } - }, - "qte_fail": { - "choice": { - "continue": "Keep moving forward", - "back": "Turn back" - } - } } -} +} \ No newline at end of file diff --git a/src/locales/ja.json b/src/locales/ja.json index 0b7041a..8adef8f 100644 --- a/src/locales/ja.json +++ b/src/locales/ja.json @@ -19,61 +19,5 @@ "speed": "倍速", "noAutoSave": "オートセーブがありません", "autoSaveHint": "シーン切替時にスロット0に自動保存されます" - }, - "scene": { - "intro": { - "choice": { - "left_door": "左にある光るドアへ向かう", - "right_door": "右にある普通のドアへ向かう", - "search": "部屋を念入りに調べる", - "stay": "その場に留まる" - } - }, - "left_door": { - "choice": { - "handshake": "見知らぬ人と握手する", - "reject": "握手を断り、警戒を続ける" - } - }, - "right_door": { - "choice": { - "continue": "先へ進む", - "back": "引き返す" - } - }, - "trust_ending": { - "choice": { - "journey": "信頼の旅に出る(trust >= 80 が必要)", - "leave": "ここを去る" - } - }, - "investigation_site": { - "choice": { - "leave": "部屋を出る" - } - }, - "desk_detail": { - "choice": { - "return": "調査現場に戻る", - "leave": "立ち去る" - } - }, - "stay": { - "choice": { - "stand": "立ち上がって去る" - } - }, - "qte_success": { - "choice": { - "continue": "先へ進む", - "back": "引き返す" - } - }, - "qte_fail": { - "choice": { - "continue": "先へ進む", - "back": "引き返す" - } - } } -} +} \ No newline at end of file diff --git a/src/locales/zh.json b/src/locales/zh.json index 6205b53..705e0f5 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -19,61 +19,5 @@ "speed": "倍速", "noAutoSave": "暂无自动存档", "autoSaveHint": "游戏会在每次场景切换时自动保存到槽位 0" - }, - "scene": { - "intro": { - "choice": { - "left_door": "走向左边那扇发光的门", - "right_door": "走向右边那扇普通的门", - "search": "仔细搜索房间", - "stay": "留在原地,什么也不做" - } - }, - "left_door": { - "choice": { - "handshake": "与陌生人握手", - "reject": "拒绝握手,保持警惕" - } - }, - "right_door": { - "choice": { - "continue": "继续前进", - "back": "回头" - } - }, - "trust_ending": { - "choice": { - "journey": "开启信任的旅程(需要 trust >= 80)", - "leave": "离开这里" - } - }, - "investigation_site": { - "choice": { - "leave": "离开房间" - } - }, - "desk_detail": { - "choice": { - "return": "返回调查现场", - "leave": "离开" - } - }, - "stay": { - "choice": { - "stand": "站起来离开" - } - }, - "qte_success": { - "choice": { - "continue": "继续前进", - "back": "回头" - } - }, - "qte_fail": { - "choice": { - "continue": "继续前进", - "back": "回头" - } - } } -} +} \ No newline at end of file