feat(i18n): add localized content for game page

This commit is contained in:
neilchen0812 committed 2026-09-22 02:33:53 +09:30
1 parent 87df12ac85
commit 03fbae616d
9 files changed
+90 -20

No files matched your search

+10 -2
View File
@@ -1,7 +1,15 @@
<script setup lang="ts">
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
</script>
<template>
<font-awesome-icon :icon="['fas', 'list']" class="text-text-primary p-1" />
<h1 id="CommandBook" class="inline text-text-primary font-xl">指令之書</h1>
<h1 id="CommandBook" class="inline text-text-primary font-xl">
{{ t('game.command_book') }}
</h1>
<div id="cheatsheets">
<p class="text-text-secondary">-- --指令列表-- --</p>
<p class="text-text-secondary">-- --{{ t('game.cheatsheets') }}-- --</p>
</div>
</template>
@@ -1,4 +1,5 @@
<script setup lang="ts">
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
import { useToast } from 'vue-toastification'
import { ref } from 'vue'
@@ -6,6 +7,8 @@ import { Vue3Lottie } from 'vue3-lottie'
// import { surveyUrl } from '../config'
import CompleteAnimation from '../../img/complete-animation.json'
const { t } = useI18n()
const showModal = ref(true) // Controls whether the modal is visible or not
const router = useRouter()
const toast = useToast()
+25 -8
View File
@@ -1,9 +1,12 @@
<script setup lang="ts">
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
import { useToast } from 'vue-toastification'
import { useSession } from '../../store/session'
import { openBugReport, openQuestSurvey } from '../../utils/formbricks'
const { t } = useI18n()
const router = useRouter()
const sessionStore = useSession()
const reset = async () => {
@@ -14,16 +17,17 @@ const continuePlay = async () => {
try {
router.push({ name: 'map' })
} catch (err) {
useToast().error('無法讀取地圖')
useToast().error(t('quest_map.load_quest_map_failed'))
console.error(err)
}
}
</script>
<template>
<section class="mt-2 flex flex-row gap-3 justify-center">
<section class="mt-2 flex flex-row gap-2 justify-center">
<button
id="survey"
:title="t('game_nav.survey')"
class="bg-bg-secondary rounded-lg p-2"
@click="openQuestSurvey"
>
@@ -31,15 +35,18 @@ const continuePlay = async () => {
:icon="['fas', 'file-invoice']"
class="text-text-primary px-1"
/>
<p class="text-text-primary inline md:flex">問卷</p>
<p class="text-text-primary text-xs">{{ t('game_nav.survey') }}</p>
</button>
<button
title="Bug Report"
id="bug-report"
:title="t('game_nav.bug_report')"
@click="openBugReport"
class="bg-bg-secondary rounded-lg p-2"
>
<font-awesome-icon :icon="['fas', 'bug']" class="text-text-primary" />
<p class="text-text-primary inline ml-1 md:flex">回報</p>
<p class="text-text-primary text-xs">
{{ t('game_nav.bug_report') }}
</p>
</button>
<!-- <button disabled id="solution" class="bg-bg-disabled rounded-lg p-2">
<font-awesome-icon
@@ -48,15 +55,23 @@ const continuePlay = async () => {
/>
<p class="text-text-disabled inline ml-1 md:flex">解答</p>
</button> -->
<button id="reset" class="bg-bg-secondary rounded-lg p-2" @click="reset">
<button
id="reset"
:title="t('game.reset')"
class="bg-bg-secondary rounded-lg p-2"
@click="reset"
>
<font-awesome-icon
:icon="['fas', 'arrow-rotate-left']"
class="text-text-primary px-1"
/>
<p class="text-text-primary inline ml-1 md:flex">重來</p>
<p class="text-text-primary text-xs">
{{ t('game.reset') }}
</p>
</button>
<button
id="continue"
:title="t('game.continue')"
class="bg-bg-secondary rounded-lg p-2"
@click="continuePlay"
>
@@ -64,7 +79,9 @@ const continuePlay = async () => {
:icon="['far', 'circle-right']"
class="text-text-primary px-1"
/>
<p class="text-text-primary inline ml-1 md:flex">繼續</p>
<p class="text-text-primary text-xs">
{{ t('game.continue') }}
</p>
</button>
</section>
</template>
+7 -2
View File
@@ -1,8 +1,11 @@
<script setup lang="ts">
import { useI18n } from 'vue-i18n'
import { computed, ref, watch } from 'vue'
import { useSession } from '../../store/session'
import MarkdownText from '../MarkdownText.vue'
const { t } = useI18n()
const sessionStore = useSession()
const current = ref(-1)
const hintDetails = ref<HTMLDetailsElement | null>(null)
@@ -52,7 +55,9 @@ watch(response, () => {
:icon="['far', 'lightbulb']"
class="text-yellow-200 p-2 content-center"
/>
<h1 class="inline text-text w-1/2 font-xl p-2 m-1">說明</h1>
<h1 class="inline text-text w-1/2 font-xl p-2 m-1">
{{ t('game.description') }}
</h1>
<div v-if="current !== -1" class="flex w-full justify-end items-end">
<button
class="p-2 m-1 w-1/8"
@@ -98,7 +103,7 @@ watch(response, () => {
class="text-text-primary font-xl whitespace-pre-wrap"
>
<details ref="hintDetails">
<summary class="cursor-pointer">提示</summary>
<summary class="cursor-pointer">{{ t('game.hint') }}</summary>
<MarkdownText :content="response.hint" />
</details>
</li>
+5 -2
View File
@@ -1,8 +1,11 @@
<script setup lang="ts">
import { useI18n } from 'vue-i18n'
import { computed } from 'vue'
import MarkdownText from '../MarkdownText.vue'
import { useSession } from '../../store/session'
const { t } = useI18n()
const sessionStore = useSession()
const tasks = computed(() => {
console.log('session', sessionStore.session)
@@ -31,14 +34,14 @@ const tasks = computed(() => {
:content="sessionStore.quest.instruction"
/>
<br />
<p id="tasks" class="text-text">任務:</p>
<p id="tasks" class="text-text font-bold">{{ t('game.task') }}</p>
<ul v-if="sessionStore.session">
<li v-for="task in tasks" :key="task.id">
<MarkdownText :class="task.class" :content="task.content" />
</li>
</ul>
</div>
<p class="text-text" v-else>Loading...</p>
<p class="text-text" v-else>{{ t('game.loading') }}</p>
</div>
</div>
</template>
+4 -1
View File
@@ -1,8 +1,11 @@
<script setup lang="ts">
import { useI18n } from 'vue-i18n'
import { useToast } from 'vue-toastification'
import { computed } from 'vue'
import { useSession } from '../../store/session'
const { t } = useI18n()
const toast = useToast()
const sessionStore = useSession()
@@ -31,7 +34,7 @@ const startSession = async () => {
:icon="['fas', 'play']"
class="text-text-primary h-full px-3"
/>
<p class="text-text-primary inline ml-1 md:flex">開始</p>
<p class="text-text-primary inline ml-1 md:flex">{{ t('game.start') }}</p>
</button>
</div>
</template>
+6 -3
View File
@@ -1,7 +1,10 @@
<script setup lang="ts">
import { useI18n } from 'vue-i18n'
import { onMounted, onUnmounted, ref } from 'vue'
import { useSession, useTerminal } from '../../store/session'
const { t } = useI18n()
const sessionStore = useSession()
const terminal = ref<HTMLElement | null>(null)
@@ -24,14 +27,14 @@ onMounted(() => {
<div class="flex flex-col h-full">
<div class="flex items-center gap-2 p-1">
<font-awesome-icon :icon="['fas', 'terminal']" class="text-text" />
<button id="Terminal" class="text-text">終端機</button>
<p id="Terminal" class="text-text">{{ t('game.terminal') }}</p>
<a
class="text-sm font-normal text-gray-200 ml-10"
class="text-xs font-normal text-gray-200 ml-10 bg-bg-primary rounded-lg px-2 py-1 hover:bg-bg-primary-hover transition-colors duration-300"
:href="`/terminal/${sessionStore.session?.containerName}?token=${sessionStore.session?.token}`"
target="_blank"
>
Open Terminal in New Tab
{{ t('game.open_terminal_in_new_tab') }}
</a>
</div>
<div
+15 -1
View File
@@ -77,7 +77,8 @@
},
"quest_map": {
"title": "Quest Map",
"intro": "Embark on your Linux adventure!"
"intro": "Embark on your Linux adventure!",
"load_quest_map_failed": "Failed to Load Quest Map"
},
"quest": {
"start": "Start Adventure",
@@ -89,5 +90,18 @@
"score": "Score",
"completed_quests": "Completed Quests",
"load_leaderboard_failed": "Failed to Load Leaderboard"
},
"game": {
"start": "Start",
"task": "Task:",
"loading": "Loading...",
"description": "Description",
"hint": "Hint",
"terminal": "Terminal",
"open_terminal_in_new_tab": "Open Terminal in New Tab",
"reset": "Reset",
"continue": "Continue",
"command_book": "Command Book",
"cheatsheets": "Cheat Sheets"
}
}
+15 -1
View File
@@ -77,7 +77,8 @@
},
"quest_map": {
"title": "關卡地圖",
"intro": "踏上你的Linux冒險之旅吧!"
"intro": "踏上你的Linux冒險之旅吧!",
"load_quest_map_failed": "無法讀取關卡地圖"
},
"quest": {
"start": "開始冒險",
@@ -89,5 +90,18 @@
"score": "分數",
"completed_quests": "完成關卡",
"load_leaderboard_failed": "無法讀取排行榜"
},
"game": {
"start": "開始",
"task": "任務:",
"loading": "載入中...",
"description": "說明",
"hint": "提示",
"terminal": "終端機",
"open_terminal_in_new_tab": "在新分頁開啟終端機",
"reset": "重置",
"continue": "繼續",
"command_book": "指令之書",
"cheatsheets": "指令列表"
}
}