translation and little format fixture

This commit is contained in:
Annie Huang committed 2023-11-20 22:49:46 +08:00
1 parent f6c3b33da9
commit 9da23b5dc1
14 files changed
+155 -54

No files matched your search

+66 -14
View File
@@ -108,7 +108,10 @@ const handleChange = () => {
:icon="['fab', 'google']" :icon="['fab', 'google']"
class="text-text-secondary px-3 text-lg" class="text-text-secondary px-3 text-lg"
/> />
<span class="font-medium">Continue with Google</span> <span class="font-medium">
<!-- Continue with Google -->
以Google繼續
</span>
</a> </a>
<a <a
v-if="availableMethods.github" v-if="availableMethods.github"
@@ -119,17 +122,21 @@ const handleChange = () => {
:icon="['fab', 'github']" :icon="['fab', 'github']"
class="text-text-secondary px-3 text-lg" class="text-text-secondary px-3 text-lg"
/> />
<span class="font-medium">Continue with GitHub</span> <span class="font-medium">
<!-- Continue with GitHub -->
以Github繼續
</span>
</a> </a>
<p <p
class="text-text-secondary flex justify-center mt-3" class="text-text-secondary flex justify-center mt-3"
v-if="type === 'login' || type === 'register'" v-if="type === 'login' || type === 'register'"
> >
or <!-- or -->
或
</p> </p>
</div> </div>
<div class="mb-6"> <div class="mb-6">
<input <!-- <input
type="text" type="text"
id="username" id="username"
ref="usernameInput" ref="usernameInput"
@@ -139,6 +146,17 @@ const handleChange = () => {
required required
@input="handleChange()" @input="handleChange()"
autocomplete="username" autocomplete="username"
/> -->
<input
type="text"
id="username"
ref="usernameInput"
class="my-4 bg-bg-primary text-text-primary bg- rounded-md block w-full px-3 h-10 shadow-sm focus:outline-none placeholder:text-text-line focus:ring-2 focus:ring-text-primary ring-1 ring-bg-secondary"
:placeholder="type === 'login' ? '電子郵件 / 帳號名稱' : '帳號名稱'"
v-model="username"
required
@input="handleChange()"
autocomplete="username"
/> />
<label <label
class="text-sm font-normal text-text-secondary" class="text-sm font-normal text-text-secondary"
@@ -149,7 +167,7 @@ const handleChange = () => {
</label> </label>
</div> </div>
<div class="mb-6" v-if="type === 'register'"> <div class="mb-6" v-if="type === 'register'">
<input <!-- <input
type="email" type="email"
id="email" id="email"
ref="emailInput" ref="emailInput"
@@ -159,10 +177,21 @@ const handleChange = () => {
required required
@input="handleChange()" @input="handleChange()"
autocomplete="email" autocomplete="email"
/> -->
<input
type="email"
id="email"
ref="emailInput"
class="my-4 bg-bg-primary text-text-primary bg- rounded-md block w-full px-3 h-10 shadow-sm focus:outline-none placeholder:text-text-line focus:ring-2 focus:ring-text-primary ring-1 ring-bg-secondary"
placeholder="電子郵件"
v-model="email"
required
@input="handleChange()"
autocomplete="email"
/> />
</div> </div>
<div class="mb-6" v-if="type === 'login' || type === 'register'"> <div class="mb-6" v-if="type === 'login' || type === 'register'">
<input <!-- <input
type="password" type="password"
id="password" id="password"
class="my-4 bg-bg-primary text-text-primary bg- rounded-md block w-full px-3 h-10 shadow-sm focus:outline-none placeholder:text-text-line focus:ring-2 focus:ring-text-primary ring-1 ring-bg-secondary" class="my-4 bg-bg-primary text-text-primary bg- rounded-md block w-full px-3 h-10 shadow-sm focus:outline-none placeholder:text-text-line focus:ring-2 focus:ring-text-primary ring-1 ring-bg-secondary"
@@ -170,6 +199,15 @@ const handleChange = () => {
v-model="password" v-model="password"
required required
@input="handleChange()" @input="handleChange()"
/> -->
<input
type="password"
id="password"
class="my-4 bg-bg-primary text-text-primary bg- rounded-md block w-full px-3 h-10 shadow-sm focus:outline-none placeholder:text-text-line focus:ring-2 focus:ring-text-primary ring-1 ring-bg-secondary"
placeholder="密碼"
v-model="password"
required
@input="handleChange()"
/> />
</div> </div>
<p class="text-error flex justify-center" v-if="errorMessage"> <p class="text-error flex justify-center" v-if="errorMessage">
@@ -179,21 +217,35 @@ const handleChange = () => {
class="inline-flex justify-center rounded-lg font-black py-2 bg-text-primary text-bg w-full" class="inline-flex justify-center rounded-lg font-black py-2 bg-text-primary text-bg w-full"
type="submit" type="submit"
> >
<span v-if="type === 'login'">Log In</span> <span v-if="type === 'login'">
<span v-else>Sign Up</span> <!-- Log In -->
登入
</span>
<span v-else>
<!-- Sign Up -->
註冊
</span>
</button> </button>
<p class="text-text flex justify-center mt-3"> <p class="text-text flex justify-center mt-3">
<span v-if="type === 'register'" <span v-if="type === 'register'">
>Already have an account? <!-- Already have an account? -->
已經有帳號了嗎?
<RouterLink class="text-text-primary font-bold" to="/login"> <RouterLink class="text-text-primary font-bold" to="/login">
<u>Log in</u> <u>
<!-- Log in -->
登入
</u>
</RouterLink></span </RouterLink></span
> >
<span v-else-if="type === 'login'" <span v-else-if="type === 'login'">
>Don't have an account? <!-- Don't have an account? -->
還沒有帳號嗎?
<RouterLink class="text-text-primary font-bold" to="/register"> <RouterLink class="text-text-primary font-bold" to="/register">
<u>Sign up</u> <u>
<!-- Sign up -->
註冊
</u>
</RouterLink></span </RouterLink></span
> >
</p> </p>
+8 -2
View File
@@ -2,8 +2,14 @@
<template> <template>
<font-awesome-icon :icon="['fas', 'list']" class="text-text-primary p-1" /> <font-awesome-icon :icon="['fas', 'list']" class="text-text-primary p-1" />
<h1 class="inline text-text-primary font-xl">Command Cheatsheets</h1> <h1 class="inline text-text-primary font-xl">
<!-- Command Cheatsheets -->
指令之書
</h1>
<div id="cheatsheets"> <div id="cheatsheets">
<p class="text-text-secondary">--Command List--</p> <p class="text-text-secondary">
<!-- --Command List-- -->
-- --指令列表-- --
</p>
</div> </div>
</template> </template>
+2 -1
View File
@@ -19,7 +19,8 @@ setTimeout(() => {
<div class="modal-content bg-bg-secondary rounded-lg"> <div class="modal-content bg-bg-secondary rounded-lg">
<span class="close text-text" @click="closeModal">&times;</span> <span class="close text-text" @click="closeModal">&times;</span>
<h2 class="text-2xl text-text mb-5 w-full inline-flex justify-center"> <h2 class="text-2xl text-text mb-5 w-full inline-flex justify-center">
Quest Completed! <!-- Quest Completed! -->
關卡完成!
</h2> </h2>
<p class="text-base text-text"> <p class="text-base text-text">
您已成功完成挑戰,真的非常感謝您的參與!<br /> 您已成功完成挑戰,真的非常感謝您的參與!<br />
+12 -3
View File
@@ -5,21 +5,30 @@
:icon="['far', 'circle-question']" :icon="['far', 'circle-question']"
class="text-text-disabled" class="text-text-disabled"
/> />
<p class="text-text-disabled inline ml-1">Solution</p> <p class="text-text-disabled inline ml-1">
<!-- Solution -->
解答
</p>
</button> </button>
<button id="reset" class="bg-bg-secondary rounded-lg p-2" @click="reset"> <button id="reset" class="bg-bg-secondary rounded-lg p-2" @click="reset">
<font-awesome-icon <font-awesome-icon
:icon="['fas', 'arrow-rotate-left']" :icon="['fas', 'arrow-rotate-left']"
class="text-text-primary" class="text-text-primary"
/> />
<p class="text-text-primary inline ml-1">Reset</p> <p class="text-text-primary inline ml-1">
<!-- Reset -->
重來
</p>
</button> </button>
<button id="continue" class="bg-bg-disabled rounded-lg p-2"> <button id="continue" class="bg-bg-disabled rounded-lg p-2">
<font-awesome-icon <font-awesome-icon
:icon="['far', 'circle-right']" :icon="['far', 'circle-right']"
class="text-text-disabled" class="text-text-disabled"
/> />
<p class="text-text-disabled inline ml-1">Continue</p> <p class="text-text-disabled inline ml-1">
<!-- Continue -->
繼續
</p>
</button> </button>
</section> </section>
</template> </template>
+1
View File
@@ -39,6 +39,7 @@ const handleLogout = async () => {
</button> --> </button> -->
<p <p
class="text-text-primary inline-block font-bold whitespace-nowrap" class="text-text-primary inline-block font-bold whitespace-nowrap"
style="font-size: 2vh"
v-if="sessionStore.quest" v-if="sessionStore.quest"
> >
{{ sessionStore.quest.title }} {{ sessionStore.quest.title }}
+4 -1
View File
@@ -30,7 +30,10 @@ const right = () => {
:icon="['far', 'lightbulb']" :icon="['far', 'lightbulb']"
class="text-yellow-200 p-2 content-center" class="text-yellow-200 p-2 content-center"
/> />
<h1 class="inline text-text font-xl p-2 m-1">Hint</h1> <h1 class="inline text-text font-xl p-2 m-1">
<!-- Hint -->
提示
</h1>
<div v-if="current !== -1" class="w-full flex justify-end"> <div v-if="current !== -1" class="w-full flex justify-end">
<button <button
class="p-2 m-1 w-1/8" class="p-2 m-1 w-1/8"
+10 -6
View File
@@ -14,6 +14,7 @@ const marginX = 100
const marginY = 65 const marginY = 65
const chartContainer = ref(null) const chartContainer = ref(null)
let chartInstance = null let chartInstance = null
const fullwidth = window.screen.width
use([GraphChart, SVGRenderer, TitleComponent, TooltipComponent]) use([GraphChart, SVGRenderer, TitleComponent, TooltipComponent])
@@ -49,11 +50,11 @@ const genOption = (nodes, edges) => ({
symbol: () => { symbol: () => {
return NodeImage return NodeImage
}, },
symbolSize: [200, 100], symbolSize: [fullwidth / 10, fullwidth / 20],
roam: true, roam: false,
label: { label: {
show: true, show: true,
fontSize: 22, fontSize: fullwidth / 90,
}, },
edgeSymbol: ['pin', 'arrow'], edgeSymbol: ['pin', 'arrow'],
edgeSymbolSize: [2, 15], edgeSymbolSize: [2, 15],
@@ -126,7 +127,8 @@ function initChart(option) {
if (unlocked) { if (unlocked) {
router.push({ name: 'game', params: { questId: id } }) router.push({ name: 'game', params: { questId: id } })
} else { } else {
toast.warning('You have not completed the previous quest yet!') // toast.warning('You have not completed the previous quest yet!')
toast.warning('你還沒完成前一個關卡!')
} }
}) })
} }
@@ -147,9 +149,11 @@ onMounted(async () => {
class="p-2 stroke-2 scale-y-120 justify-center items-center" class="p-2 stroke-2 scale-y-120 justify-center items-center"
/> />
<h1 <h1
class="text-text-primary text-xl p-2.5 absolute w-fit z-2 font-mono font-bold flex flax-wrap" class="p-4 absolute w-fit z-2 font-mono flex flax-wrap text-xl"
style="width: 30%; height: 6%; font-size: 3vh; color: #00ff00"
> >
Get through your linux journey! <!-- Get through your linux journey! -->
踏上你的Linux冒險之旅吧!
</h1> </h1>
<div class="flex flex-wrap absolute w-full h-full z-1"> <div class="flex flex-wrap absolute w-full h-full z-1">
<div ref="chartContainer" class="w-full h-full flex flex-wrap"></div> <div ref="chartContainer" class="w-full h-full flex flex-wrap"></div>
+4 -1
View File
@@ -12,7 +12,10 @@ import sessionStore from '../store/session'
<div v-if="sessionStore.quest" class="whitespace-pre-wrap"> <div v-if="sessionStore.quest" class="whitespace-pre-wrap">
<p class="text-text">{{ sessionStore.quest.instruction }}</p> <p class="text-text">{{ sessionStore.quest.instruction }}</p>
<br /> <br />
<p class="text-text">Tasks:</p> <p class="text-text">
<!-- Tasks: -->
任務:
</p>
<ul v-if="sessionStore.session"> <ul v-if="sessionStore.session">
<li v-for="task in sessionStore.session.tasks" :key="task.id"> <li v-for="task in sessionStore.session.tasks" :key="task.id">
<p v-if="task.completed" class="text-text-primary"> <p v-if="task.completed" class="text-text-primary">
+4 -1
View File
@@ -1,6 +1,9 @@
<template> <template>
<font-awesome-icon :icon="['fas', 'terminal']" class="text-text p-1" /> <font-awesome-icon :icon="['fas', 'terminal']" class="text-text p-1" />
<button class="text-text h-[5%]">Terminal</button> <button class="text-text h-[5%]">
<!-- Terminal -->
終端機
</button>
<div <div
id="terminal" id="terminal"
ref="terminal" ref="terminal"
+8 -4
View File
@@ -8,21 +8,24 @@ import { checkUsername, chooseUsername } from '../utils/auth'
async function check({ username, error }) { async function check({ username, error }) {
if (username) { if (username) {
if (!isValidUsername(username)) { if (!isValidUsername(username)) {
error('Invalid username.') // error('Invalid username.')
error('無效的帳號名稱')
return return
} }
try { try {
await checkUsername(username) await checkUsername(username)
} catch (err) { } catch (err) {
if (err instanceof TooManyRequestsError) { if (err instanceof TooManyRequestsError) {
error('Too many requests. Try again in 2 minutes.') // error('Too many requests. Try again in 2 minutes.')
error('太多請求,兩分鐘後再試一次。')
return return
} }
if (err instanceof ValidationError) { if (err instanceof ValidationError) {
error(err.message) error(err.message)
return return
} }
error('Something went wrong. Please try again later.') // error('Something went wrong. Please try again later.')
error('出了點問題,請再試一次。')
} }
} }
} }
@@ -33,7 +36,8 @@ async function handleSubmit({ username, success, error }) {
success() success()
} catch (err) { } catch (err) {
if (err instanceof TooManyRequestsError) { if (err instanceof TooManyRequestsError) {
error('Too many requests. Try again in 2 minutes.') // error('Too many requests. Try again in 2 minutes.')
error('太多請求,兩分鐘後再試一次。')
return return
} }
if (err instanceof ValidationError) { if (err instanceof ValidationError) {
+6 -4
View File
@@ -30,13 +30,15 @@ onMounted(async () => {
await init(props.questId) await init(props.questId)
} catch (err) { } catch (err) {
if (err instanceof LoadQuestError) { if (err instanceof LoadQuestError) {
toast.error(`Failed to load quest: ${err.questId}. Please check the URL.`) // toast.error(`Failed to load quest: ${err.questId}. Please check the URL.`)
toast.error(`無法讀取關卡: ${err.questId},請確認網頁連結。`)
return return
} }
if (err instanceof LoadSessionError) { if (err instanceof LoadSessionError) {
toast.error( // toast.error(
`Failed to create session for quest: ${err.questId}. Please re-login and try again.` // `Failed to create session for quest: ${err.questId}. Please re-login and try again.`
) // )
toast.error(`無法建立工作階段: ${err.questId},請重新登入再試一次。`)
return return
} }
console.error(err) console.error(err)
+8 -4
View File
@@ -15,19 +15,23 @@ const handleLogin = async ({ username, password, success, error }) => {
if (isSuccess) success() if (isSuccess) success()
} catch (err) { } catch (err) {
if (err instanceof TooManyRequestsError) { if (err instanceof TooManyRequestsError) {
error('Too many requests. Try again in 2 minutes.') // error('Too many requests. Try again in 2 minutes.')
error('太多請求,兩分鐘後再試一次。')
return return
} }
if (err instanceof UnauthorizedError) { if (err instanceof UnauthorizedError) {
error('Wrong username or password.') // error('Wrong username or password.')
error('錯誤的帳號名稱或密碼。')
return return
} }
if (err instanceof ValidationError) { if (err instanceof ValidationError) {
error('Invalid username or password.') // error('Invalid username or password.')
error('無效的帳號名稱或密碼。')
return return
} }
console.error(err) console.error(err)
error('Something went wrong.') // error('Something went wrong.')
error('出了點問題。')
} }
} }
</script> </script>
+4 -3
View File
@@ -7,10 +7,11 @@ import GameHeaderPart from '../components/GameHeaderPart.vue'
<div class="h-[6vh] w-full"> <div class="h-[6vh] w-full">
<GameHeaderPart /> <GameHeaderPart />
<p <p
class="text-text-primary absolute z-1 font-bold inline-block" class="text-text-primary absolute z-1 font-bold inline-block tracking-widest"
style="left: 1.2%; top: 1.5%; height: 100%" style="left: 1.2%; top: 1.5%; height: 100%; font-size: 2vh"
> >
Quest Map <!-- Quest Map -->
Unix編年史
</p> </p>
</div> </div>
<div class="flex flex-col justify-center items-center w-full h-[94vh]"> <div class="flex flex-col justify-center items-center w-full h-[94vh]">
+18 -10
View File
@@ -17,11 +17,13 @@ function handleRegister({ username, email, password, success, error }) {
.then(success) .then(success)
.catch((err) => { .catch((err) => {
if (err instanceof TooManyRequestsError) { if (err instanceof TooManyRequestsError) {
error('Too many requests. Try again in 2 minutes.') // error('Too many requests. Try again in 2 minutes.')
error('太多請求,兩分鐘後再試一次。')
return return
} }
if (err instanceof UnauthorizedError) { if (err instanceof UnauthorizedError) {
error('Invalid username or password.') // error('Invalid username or password.')
error('無效的帳號名稱或密碼。')
return return
} }
if (err instanceof ValidationError) { if (err instanceof ValidationError) {
@@ -29,23 +31,27 @@ function handleRegister({ username, email, password, success, error }) {
return return
} }
console.error(err) console.error(err)
error('Something went wrong.') // error('Something went wrong.')
error('出了點問題。')
}) })
} }
async function check({ username, email, password, error }) { async function check({ username, email, password, error }) {
if (username && !isValidUsername(username)) { if (username && !isValidUsername(username)) {
error('Invalid username.') // error('Invalid username.')
error('無效的帳號名稱')
return return
} }
if (email && !validator.isEmail(email)) { if (email && !validator.isEmail(email)) {
error('Invalid email.') // error('Invalid email.')
error('無效的電子郵件')
return return
} }
if (password && !validator.isStrongPassword(password, passwordPolicy)) { if (password && !validator.isStrongPassword(password, passwordPolicy)) {
error( // error(
'Your password must be 8+ characters with at least one number, one upper and one lower case letter.' // 'Your password must be 8+ characters with at least one number, one upper and one lower case letter.'
) // )
error('密碼必須超過8個字元,至少包含一個數字、一個大寫及一個小寫。')
return return
} }
try { try {
@@ -56,11 +62,13 @@ async function check({ username, email, password, error }) {
return return
} }
if (err instanceof TooManyRequestsError) { if (err instanceof TooManyRequestsError) {
error('Too many requests. Try again in 2 minutes.') // error('Too many requests. Try again in 2 minutes.')
error('太多請求,兩分鐘後再試一次。')
return return
} }
console.error(err) console.error(err)
error('Something went wrong.') // error('Something went wrong.')
error('出了點問題。')
} }
} }
</script> </script>