refactor: separate story locales (public/) from UI locales (src/), async fetch for story messages
This commit is contained in:
@@ -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<Lang>(
|
||||
(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<Record<string, Record<string, any>>>({})
|
||||
const storyLoading = new Set<Lang>()
|
||||
|
||||
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 }
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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": "引き返す"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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": "回头"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user