From 8852f8c498400d1bfb13e6a91d44a44e6aa58323 Mon Sep 17 00:00:00 2001
From: JacobLinCool
Date: Mon, 24 Feb 2025 21:26:07 +0800
Subject: [PATCH] Revert "feat: I18npackage support for whole project (#148)"
This reverts commit 7f68d921b087d9723c191027517c1173edfc3bc7.
---
messages/en.json | 135 +-----------
messages/zh.json | 139 +-----------
src/lib/components/Chatroom.svelte | 33 ++-
src/lib/components/Navbar.svelte | 36 ++--
src/lib/components/QrScanner.svelte | 36 +++-
src/lib/components/SessionCard.svelte | 19 +-
src/lib/components/session/HostView.svelte | 101 +++++++--
.../components/session/ParticipantView.svelte | 155 +++++++++++---
.../components/session/StageProgress.svelte | 22 +-
src/routes/+page.svelte | 197 ++++++++++++++----
src/routes/dashboard/+page.svelte | 106 +++++++---
src/routes/join/+page.svelte | 29 ++-
src/routes/login/+page.svelte | 23 +-
src/routes/profile/+page.svelte | 61 ++++--
src/routes/session/[id]/+page.svelte | 13 +-
.../[id]/discussion/[userId]/+page.svelte | 21 +-
.../session/[id]/participant/+page.svelte | 12 +-
src/routes/session/[id]/status/+page.svelte | 22 +-
src/routes/template/[id]/+page.svelte | 97 +++++++--
src/routes/template/[id]/view/+page.svelte | 36 +++-
20 files changed, 808 insertions(+), 485 deletions(-)
diff --git a/messages/en.json b/messages/en.json
index 0db2f33..587a02a 100644
--- a/messages/en.json
+++ b/messages/en.json
@@ -40,138 +40,5 @@
"coreValues": "We chose the name Hinagiku because it reflects our core values: resilience, simplicity, and growth—much like the daisy flower itself, which flourishes in diverse conditions.",
"mission": "Our mission is to help participants and hosts connect meaningfully by providing tools that facilitate better communication and collaboration in classrooms.",
"openSource": "Open Source",
- "openSourceDesc": "Hinagiku is open source and available on GitHub. We welcome contributions from the community!",
- "confirmEndGroup": "Entering the Ended Stage is irreversible. Are you sure you want to proceed?",
- "confirm": "Confirm",
- "cancel": "Cancel",
- "confirmEndSummarize": "Are you sure you want to confirm the group summary?",
- "finishgroup": "Finish Group Discussion",
- "groupInfo": "Group Information",
- "groupManagement": "Group Management",
- "createNewGroup": "Create New Group",
- "jointGroup": "Join Group",
- "waitStart": "Waiting for session to start...",
- "end": "Please wait for the host to end the session.",
- "status": "Session Status",
- "individual": "Work on your individual contributions",
- "beforeGroup": "Get ready to collaborate with your group.",
- "group": "Collaborate with your group members.",
- "ended": "Ended",
- "member": "Members: ",
- "groupNum": "Group Number",
- "notInGroup": "You are not in a group yet.",
- "leaveGroup": "Leave Group",
- "hostBeginShortly": "The host will begin the session shortly.",
- "unknownUser": "Unknown User",
- "loadingProfile": "Loading...",
- "you": "You",
- "leader": " (Leader)",
- "creatingGroup": "Creating...",
- "joinExistingGroup": "Join Existing Group",
- "enterGroupNumber": "Enter group number (1-50)",
- "successCreateGroup": "Group created successfully",
- "failedCreateGroup": "Failed to create group",
- "successJoinGroup": "Joined group successfully",
- "failedJoinGroup": "Failed to join group",
- "successLeaveGroup": "Successfully left group",
- "failedLeaveGroup": "Failed to leave group",
- "invalidGroupNumber": "Please enter a valid group number (1-50)",
- "placeholder": "Type your message...(max 500 characters)",
- "send": "Send",
- "record": "Record",
- "stop": "Stop",
- "thinking": "Thinking",
- "waiting": "Waiting",
- "confirmEndStage": "Entering the Ended Stage is irreversible. Are you sure you want to proceed?",
- "stopCamera": "Stop Camera",
- "startCamera": "Start Camera",
- "uploadQrImage": "Upload QR Code Image",
- "errorScanning": "error during scanning: ",
- "errorStartingCamera": "error during starting camera: ",
- "errorScanningFile": "error during scanning file: ",
- "unableToFindQr": "Unable to find QR code in the image. Please ensure the image is clear and contains a valid QR code.",
- "hosteBy": "Hosted by",
- "viewSession": "View Session",
- "joinSessionHost": "Join Session",
- "joinSessionDescHost": "Please scan the QR code or enter the code to join the session.",
- "showCode": "Show Code",
- "showCodeDesc": "Show a 6-digit code for participants to join the session.",
- "finalSummary": "Final Summary",
- "mainTask": "Main Task",
- "subtasks": "Subtasks:",
- "resources": "Resources",
- "noResources": "No resources available",
- "waitingForParticipants": "Waiting for participants to join groups...",
- "noParticipants": "No participants",
- "removeParticipant": "Remove participant",
- "openChatHistory": "Open chat history",
- "openGroupChatHistory": "Open group chat history",
- "warningInappropriate": "Warning: inappropriate content detected.",
- "warningOffTopic": "Warning: many off-topic messages.",
- "hostedBy": "Hosted by",
- "sessionLabels": "Session Labels",
- "welcomeDashboard": "Welcome to your dashboard",
- "stats": "Statistics",
- "recentActivity": "Recent Activity",
- "createTemplate": "Create Template",
- "createTemplateDesc": "Create a new discussion template",
- "dashjoinSession": "Join Session",
- "dashjoinSessionDesc": "Join an existing discussion session",
- "editProfile": "Edit Profile",
- "editProfileDesc": "Update your profile settings",
- "publicTemplates": "Public Templates",
- "viewAll": "View All",
- "yourTemplates": "Your Templates",
- "noTemplates": "No templates created yet",
- "createFirstTemplate": "Create your first template to get started",
- "createTemplateButton": "Create Your First Template",
- "noSessions": "No sessions created yet",
- "createSession": "Create a new session with template",
- "JjoinSession": "Join Discussion Session",
- "scanQr": "Scan the QR code provided by your session host to join the discussion.",
- "enterCode": "Or please enter the 6-digits code to join.",
- "joinButton": "Join Session",
- "enterCodePlaceholder": "Enter code",
- "back": "← Back to home",
- "button": "Login",
- "Llogin": "Sign in with your Google account to get started",
- "profileSettings": "Profile Settings",
- "updateInfo": "Update your personal information and preferences",
- "successMessage": "Profile updated successfully!",
- "errorMessage": "Cannot update profile. Please try again later.",
- "displayName": "Display Name",
- "title": "Title",
- "titlePlaceholder": "e.g. Professor, Student, Teaching Assistant",
- "bio": "Bio",
- "bioPlaceholder": "Tell us about yourself",
- "bioHelp": "Write a short bio to help others know more about you",
- "Ttitle": "Title",
- "TsaveChanges": "Save Changes",
- "saving": "Saving...",
- "typeMessage": "Type your message...",
- "waitResponse": "Waiting for response...",
- "PwaitingForParticipants": "Please wait for other participants...",
- "editTemplate": "Edit Template",
- "startSession": "Start Session",
- "saveChanges": "Save changes before starting a session",
- "startDiscussion": "Start a discussion session with this template",
- "templateTitle": "Template title",
- "mainTaskDesc": "Main task description",
- "subtaskDesc": "Subtask description",
- "addSubtask": "Add Subtask",
- "makePublic": "Make template public",
- "backToDashboard": "Back to Dashboard",
- "unsavedChanges": "You have unsaved changes!",
- "deleteTemplate": "Delete this Template",
- "saveChangesButton": "Save Changes",
- "allChangesSaved": "All changes saved",
- "deleteConfirmation": "Are you sure you want to delete this template?",
- "yesDelete": "Yes, delete it",
- "noCancel": "No, cancel",
- "loadingTemplate": "Loading template...",
- "taskDescription": "Task Description",
- "Tsubtasks": "Subtasks",
- "Tpublic": "Public",
- "Tprivate": "Private",
- "lastUpdated": "Last Updated"
+ "openSourceDesc": "Hinagiku is open source and available on GitHub. We welcome contributions from the community!"
}
diff --git a/messages/zh.json b/messages/zh.json
index 0f8e26e..4523330 100644
--- a/messages/zh.json
+++ b/messages/zh.json
@@ -41,142 +41,5 @@
"mission": "我們的使命是通過提供促進更好溝通和協作的工具,幫助參與者和主持人有意義地聯繫。",
"openSource": "開源",
"openSourceDesc": "Hinagiku是開源的,並在GitHub上可用。我們歡迎社區的貢獻!",
- "confirmEndGroup": "進入 Ended Stage 將無法返回,確定要繼續嗎?",
- "confirm": "確定",
- "cancel": "取消",
- "confirmEndSummarize": "確定要確認群組總結嗎?",
- "finishgroup": "結束群組討論",
- "groupInfo": "群組資訊",
- "groupManagement": "群組管理",
- "createNewGroup": "建立新群組",
- "jointGroup": "加入群組",
- "waitStart": "等待會議開始...",
- "end": "請等待主持人結束會議。",
- "status": "會議狀態",
- "individual": "進行個人觀念發想",
- "beforeGroup": "準備好與您的小組合作",
- "group": "與您的小組成員合作討論",
- "ended": "已結束",
- "member": "成員:",
- "groupNum": "小組編號",
- "notInGroup": "您還沒有加入任何群組。",
- "leaveGroup": "退出群組",
- "hostBeginShortly": "主持人即將開始會議。",
- "unknownUser": "未知使用者",
- "loadingProfile": "載入中...",
- "you": "您",
- "leader": "(組長)",
- "creatingGroup": "正在建立...",
- "joinExistingGroup": "加入現有群組",
- "enterGroupNumber": "請輸入群組編號 (1-50)",
- "successCreateGroup": "成功建立群組",
- "failedCreateGroup": "無法建立群組",
- "successJoinGroup": "成功加入群組",
- "failedJoinGroup": "無法加入群組",
- "successLeaveGroup": "成功退出群組",
- "failedLeaveGroup": "無法退出群組",
- "invalidGroupNumber": "請輸入有效的群組編號 (1-50)",
- "placeholder": "手動輸入文字...(最多500個字元)",
- "send": "送出",
- "record": "錄音",
- "stop": "停止",
- "thinking": "正在思考",
- "waiting": "等待中",
- "confirmEndStage": "進入總結階段將無法返回,確定要繼續嗎?",
- "stopCamera": "停止相機",
- "startCamera": "啟動相機",
- "uploadQrImage": "上傳QRcode圖片",
- "errorScanning": "掃描過程中出錯:",
- "errorStartingCamera": "啟動相機過程中出錯:",
- "errorScanningFile": "掃描文件過程中出錯:",
- "unableToFindQr": "無法在圖像中找到QRcode。請確保圖像清晰並包含有效的QRcode。",
- "hosteBy": "主持人:",
- "viewSession": "查看會話",
- "joinSessionHost": "加入會話",
- "joinSessionDescHost": "請掃描QRcode或輸入代碼以加入會話。",
- "showCode": "顯示代碼",
- "showCodeDesc": "顯示一個6位數的代碼供參與者加入會話。",
- "finalSummary": "最終總結",
- "mainTask": "主要任務",
- "subtasks": "子任務:",
- "resources": "資源",
- "noResources": "沒有可用資源",
- "waitingForParticipants": "等待參與者加入小組...",
- "noParticipants": "沒有參與者",
- "removeParticipant": "移除參與者",
- "openChatHistory": "打開聊天記錄",
- "openGroupChatHistory": "打開小組聊天記錄",
- "warningInappropriate": "警告:檢測到不適當內容。",
- "warningOffTopic": "警告:許多離題訊息。",
- "hostedBy": "主持人:",
- "sessionLabels": "會話標籤",
- "welcomeDashboard": "歡迎來到您的儀表板",
- "stats": "統計數據",
- "recentActivity": "最近活動",
- "createTemplate": "創建模板",
- "createTemplateDesc": "創建一個新的討論模板",
- "dashjoinSession": "加入會話",
- "dashjoinSessionDesc": "加入現有的討論會話",
- "editProfile": "編輯個人資料",
- "editProfileDesc": "更新您的個人資料設置",
- "publicTemplates": "公開模板",
- "viewAll": "查看全部",
- "yourTemplates": "您的模板",
- "noTemplates": "尚未創建模板",
- "createFirstTemplate": "創建您的第一個模板以開始",
- "createTemplateButton": "創建您的第一個模板",
- "noSessions": "尚未創建會話",
- "createSession": "使用模板創建新會話",
- "JjoinSession": "加入討論會話",
- "scanQr": "掃描會話主持人提供的QRcode加入討論。",
- "enterCode": "或是輸入6位數代碼加入。",
- "joinButton": "加入會話",
- "enterCodePlaceholder": "輸入代碼",
- "back": "← 返回首頁",
- "button": "login",
- "Llogin": "使用您的Google帳戶登入",
- "profileSettings": "個人資料設定",
- "updateInfo": "更新您的個人資料和偏好",
- "successMessage": "個人資料更新成功!",
- "errorMessage": "無法更新個人資料。請稍後再試。",
- "displayName": "顯示名稱",
- "title": "標題",
- "titlePlaceholder": "例如:教授、學生、助教",
- "bio": "簡介",
- "bioPlaceholder": "介紹一下你自己",
- "bioHelp": "寫一個簡短的簡介,幫助其他人更好地了解你",
- "Ttitle": "職稱",
- "TsaveChanges": "保存更改",
- "saving": "保存中...",
- "typeMessage": "輸入您的訊息...",
- "waitResponse": "等待回應...",
- "PwaitingForParticipants": "請等待其他參與者...",
- "editTemplate": "編輯模板",
- "startSession": "開始會話",
- "saveChanges": "保存更改後再開始會話",
- "startDiscussion": "使用此模板開始討論會話",
- "templateTitle": "模板標題",
- "mainTaskDesc": "主要任務描述",
- "subtaskDesc": "子任務描述",
- "addSubtask": "添加子任務",
- "makePublic": "將模板設為公開",
- "backToDashboard": "返回儀表板",
- "unsavedChanges": "您有未保存的更改!",
- "deleteTemplate": "刪除此模板",
- "saveChangesButton": "保存更改",
- "allChangesSaved": "所有更改已保存",
- "deleteConfirmation": "您確定要刪除此模板嗎?",
- "yesDelete": "是的,刪除它",
- "noCancel": "不,取消",
- "loadingTemplate": "載入模板...",
- "taskDescription": "任務描述",
- "Tsubtasks": "子任務",
- "Tpublic": "公開",
- "Tprivate": "私人",
- "lastUpdated": "最後更新時間",
- "viewOnGitHub": "在GitHub上查看",
- "confirmEnd": "確定",
- "dashboardTitle": "參與者狀態儀表板",
- "loadingGroups": "載入群組...",
- "noParticipantsInGroup": "此群組中沒有參與者"
+ "viewOnGitHub": "在GitHub上查看"
}
diff --git a/src/lib/components/Chatroom.svelte b/src/lib/components/Chatroom.svelte
index 8019e42..b8b83de 100644
--- a/src/lib/components/Chatroom.svelte
+++ b/src/lib/components/Chatroom.svelte
@@ -3,7 +3,7 @@
import { Mic, Send, Square } from 'lucide-svelte'; // Added Square icon import
import AudioPlayer from './AudioPlayer.svelte';
import { renderMarkdown } from '$lib/utils/renderMarkdown';
- import * as m from '$lib/paraglide/messages.js';
+ import { language } from '$lib/stores/language'; // Import the global language store
interface Conversation {
name: string;
@@ -36,6 +36,25 @@
let messagesContainer: HTMLDivElement;
let dots = $state('...');
+ const translations = {
+ en: {
+ placeholder: 'Type your message...(max 500 characters)',
+ send: 'Send',
+ record: 'Record',
+ waiting: 'Waiting',
+ stop: 'Stop',
+ thinking: 'Thinking'
+ },
+ zh: {
+ placeholder: '手動輸入文字...(最多500個字元)',
+ send: '送出',
+ record: '錄音',
+ waiting: '等待',
+ stop: '停止',
+ thinking: '正在思考'
+ }
+ };
+
function scrollToBottom() {
if (!messagesContainer || !autoscroll) return;
@@ -148,7 +167,7 @@
- {#await renderMarkdown(`${m.thinking()}${dots}`)}
+ {#await renderMarkdown(`${translations[$language].thinking}${dots}`)}
Loading ...
{:then content}
@@ -168,7 +187,7 @@
{/if}
- {recording ? (operating ? m.waiting() : m.stop()) : m.record()}
+ {recording
+ ? operating
+ ? translations[$language].waiting
+ : translations[$language].stop
+ : translations[$language].record}
- {m.send()}
+ {translations[$language].send}
diff --git a/src/lib/components/Navbar.svelte b/src/lib/components/Navbar.svelte
index 4e13309..5846411 100644
--- a/src/lib/components/Navbar.svelte
+++ b/src/lib/components/Navbar.svelte
@@ -6,12 +6,28 @@
import { onMount } from 'svelte';
import { page } from '$app/state';
import { language } from '$lib/stores/language'; // Import the global language store
- import * as m from '$lib/paraglide/messages.js';
let hinagiku = $state('Hinagiku');
let highlight = $state(0);
let hydrated = $state(false);
+ const translations = {
+ en: {
+ welcome: 'Welcome to Hinagiku!',
+ profile: 'Profile',
+ dashboard: 'Dashboard',
+ signOut: 'Sign out',
+ login: 'Login'
+ },
+ zh: {
+ welcome: '歡迎來到 Hinagiku!',
+ profile: '個人資料',
+ dashboard: '儀表板',
+ signOut: '登出',
+ login: '登入'
+ }
+ };
+
import { derived } from 'svelte/store';
const flagSrc = derived(language, ($language) => {
@@ -34,20 +50,12 @@
function setLanguage(lang: 'en' | 'zh') {
language.set(lang);
- console.log('set language to', lang);
- console.log('current pathname:', window.location.pathname);
- if (lang === 'zh' && !window.location.pathname.startsWith('zh')) {
- window.location.assign('/zh' + window.location.pathname + window.location.search);
- } else if (lang === 'en' && window.location.pathname.startsWith('/zh')) {
- console.log('gotoen', window.location.pathname.replace('/zh', ''));
- window.location.assign(window.location.pathname.replace('/zh', '') + window.location.search);
- }
}
-
+
{#each hinagiku as c, i}{$user.email}
- {m.profile()}
+ {translations[$language].profile}
- {m.dashboard()}
+ {translations[$language].dashboard}
signOut()} class="flex items-center">
- {m.signOut()}
+ {translations[$language].signOut}
{:else if !page.url.pathname.startsWith('/login')}
- {m.login()}
+ {translations[$language].login}
{/if}
diff --git a/src/lib/components/QrScanner.svelte b/src/lib/components/QrScanner.svelte
index ba2d33c..f955058 100644
--- a/src/lib/components/QrScanner.svelte
+++ b/src/lib/components/QrScanner.svelte
@@ -2,7 +2,7 @@
import { notifications } from '$lib/stores/notifications';
import { Html5Qrcode } from 'html5-qrcode';
import { onMount, onDestroy } from 'svelte';
- import * as m from '$lib/paraglide/messages.js';
+ import { language } from '$lib/stores/language'; // Import the global language store
let { onScan } = $props<{
onScan: (code: string) => void;
@@ -12,6 +12,28 @@
let scanning = $state(false);
let fileInput: HTMLInputElement;
+ const translations = {
+ en: {
+ stopCamera: 'Stop Camera',
+ startCamera: 'Start Camera',
+ uploadQrImage: 'Upload QR Code Image',
+ errorScanning: 'error during scanning: ',
+ errorStartingCamera: 'error during starting camera: ',
+ errorScanningFile: 'error during scanning file: ',
+ unableToFindQr:
+ 'Unable to find QR code in the image. Please ensure the image is clear and contains a valid QR code.'
+ },
+ zh: {
+ stopCamera: '停止相機',
+ startCamera: '啟動相機',
+ uploadQrImage: '上傳QRcode圖片',
+ errorScanning: '掃描過程中出錯:',
+ errorStartingCamera: '啟動相機過程中出錯:',
+ errorScanningFile: '掃描文件過程中出錯:',
+ unableToFindQr: '無法在圖像中找到QRcode。請確保圖像清晰並包含有效的QRcode。'
+ }
+ };
+
onMount(() => {
scanner = new Html5Qrcode('qr-reader');
});
@@ -40,11 +62,11 @@
if ((err as unknown as Error).toString().includes('NotFoundException')) {
return;
}
- // notifications.error(`${m.errorScanning()} ${err}`);
+ // notifications.error(`${translations[$language].errorScanning} ${err}`);
}
);
} catch (err) {
- notifications.error(`${m.errorStartingCamera()} ${err}`);
+ notifications.error(`${translations[$language].errorStartingCamera} ${err}`);
scanning = false;
}
}
@@ -58,9 +80,9 @@
onScan(result);
} catch (err) {
if ((err as unknown as Error).toString().includes('NotFoundException')) {
- notifications.error(m.unableToFindQr());
+ notifications.error(translations[$language].unableToFindQr);
} else {
- notifications.error(`${m.errorScanningFile()} ${err}`);
+ notifications.error(`${translations[$language].errorScanningFile} ${err}`);
}
} finally {
fileInput.value = '';
@@ -77,7 +99,7 @@
onclick={scanning ? () => scanner.stop() : startCamera}
class="w-full rounded-lg border px-6 py-2 hover:bg-gray-50"
>
- {scanning ? m.stopCamera() : m.startCamera()}
+ {scanning ? translations[$language].stopCamera : translations[$language].startCamera}
@@ -93,7 +115,7 @@
for="qr-image-input"
class="block w-full cursor-pointer rounded-lg border px-6 py-2 text-center hover:bg-gray-50"
>
- {m.uploadQrImage()}
+ {translations[$language].uploadQrImage}
diff --git a/src/lib/components/SessionCard.svelte b/src/lib/components/SessionCard.svelte
index fb592c3..ac0cf6f 100644
--- a/src/lib/components/SessionCard.svelte
+++ b/src/lib/components/SessionCard.svelte
@@ -1,7 +1,7 @@
@@ -53,7 +64,9 @@
{#if host}
- {m.hostedBy()}
+ {translations[$language].hostedBy}
{/if}
@@ -61,7 +74,7 @@
{createdAt.toLocaleString()}
-
{m.viewSession()}
+
{translations[$language].viewSession}
diff --git a/src/lib/components/session/HostView.svelte b/src/lib/components/session/HostView.svelte
index e9ee122..7123cc9 100644
--- a/src/lib/components/session/HostView.svelte
+++ b/src/lib/components/session/HostView.svelte
@@ -34,7 +34,7 @@
import MostActiveGroups from './MostActiveGroups.svelte';
import LabelManager from './LabelManager.svelte';
import ResolveUsername from '../ResolveUsername.svelte';
- import * as m from '$lib/paraglide/messages.js';
+ import { language } from '$lib/stores/language'; // Import the global language store
let { session }: { session: Readable } = $props();
let code = $state('');
@@ -81,6 +81,45 @@
let conversationsData = $state>([]);
let keywordData = $state>({});
+ const translations = {
+ en: {
+ joinSession: 'Join Session',
+ joinSessionDesc: 'Please scan the QR code or enter the code to join the session.',
+ showCode: 'Show Code',
+ showCodeDesc: 'Show a 6-digit code for participants to join the session.',
+ finalSummary: 'Final Summary',
+ mainTask: 'Main Task',
+ subtasks: 'Subtasks:',
+ resources: 'Resources',
+ noResources: 'No resources available',
+ waitingForParticipants: 'Waiting for participants to join groups...',
+ noParticipants: 'No participants',
+ removeParticipant: 'Remove participant',
+ openChatHistory: 'Open chat history',
+ openGroupChatHistory: 'Open group chat history',
+ warningInappropriate: 'Warning: inappropriate content detected.',
+ warningOffTopic: 'Warning: many off-topic messages.'
+ },
+ zh: {
+ joinSession: '加入會話',
+ joinSessionDesc: '請掃描QRcode或輸入代碼以加入會話。',
+ showCode: '顯示代碼',
+ showCodeDesc: '顯示一個6位數的代碼供參與者加入會話。',
+ finalSummary: '最終總結',
+ mainTask: '主要任務',
+ subtasks: '子任務:',
+ resources: '資源',
+ noResources: '沒有可用資源',
+ waitingForParticipants: '等待參與者加入小組...',
+ noParticipants: '沒有參與者',
+ removeParticipant: '移除參與者',
+ openChatHistory: '打開聊天記錄',
+ openGroupChatHistory: '打開小組聊天記錄',
+ warningInappropriate: '警告:檢測到不適當內容。',
+ warningOffTopic: '警告:許多離題訊息。'
+ }
+ };
+
onMount(() => {
const unsubscribes: (() => void)[] = [];
const initializeSession = async () => {
@@ -192,6 +231,7 @@
limit(1)
);
const codeDoc = (await getDocs(codeQuery)).docs[0];
+ //console.log(codeDoc.data());
if (!codeDoc || Timestamp.now().toMillis() - codeDoc.data()?.createTime.toMillis() > 3600000) {
code = await genCode();
@@ -451,6 +491,7 @@
}
conversationsData = await response.json();
+ console.log(conversationsData);
} catch (error) {
console.error('無法加載對話資料:', error);
notifications.error('無法加載對話資料');
@@ -514,10 +555,10 @@
{#if $session?.status && $session.status === 'preparing'}
- {m.joinSessionHost()}
+ {$language === 'zh' ? translations.zh.joinSession : translations.en.joinSession}
- {m.joinSessionDescHost()}
+ {$language === 'zh' ? translations.zh.joinSessionDesc : translations.en.joinSessionDesc}
{#if $session?.status === 'preparing'}
@@ -529,10 +570,10 @@
{#if code === '' || code === 'Loading...'}
- {m.showCode()}
+ {$language === 'zh' ? translations.zh.showCode : translations.en.showCode}
- {m.showCodeDesc()}
+ {$language === 'zh' ? translations.zh.showCodeDesc : translations.en.showCodeDesc}
{:else}
@@ -548,7 +589,7 @@
{#if $session?.status === 'ended'}
- {m.finalSummary()}
+ {$language === 'zh' ? translations.zh.finalSummary : translations.en.finalSummary}
@@ -573,7 +614,11 @@
>
Groups
{#if $groups.length === 0}
-
{m.waitingForParticipants()}
+
{$language === 'zh'
+ ? translations.zh.waitingForParticipants
+ : translations.en.waitingForParticipants}
{:else}
{#each [...$groups].sort((a, b) => a.number - b.number) as group}
@@ -586,12 +631,18 @@
>
Group #{group.number}
- {m.openGroupChatHistory()}
+ {$language === 'zh'
+ ? translations.zh.openGroupChatHistory
+ : translations.en.openGroupChatHistory}
{#if group.participants.length === 0}
- {m.noParticipants()}
+ {$language === 'zh'
+ ? translations.zh.noParticipants
+ : translations.en.noParticipants}
{:else}
@@ -609,7 +660,11 @@
>
- {m.openChatHistory()}
+ {$language === 'zh'
+ ? translations.zh.openChatHistory
+ : translations.en.openChatHistory}
{#if participantProgress.has(participant)}
@@ -651,9 +706,17 @@
{/if}
{#if warning.moderation}
- {m.warningInappropriate()}
+ {$language === 'zh'
+ ? translations.zh.warningInappropriate
+ : translations.en.warningInappropriate}
{:else if warning.offTopic >= 3}
- {m.warningOffTopic()}
+ {$language === 'zh'
+ ? translations.zh.warningOffTopic
+ : translations.en.warningOffTopic}
{/if}
{/if}
{/if}
@@ -666,7 +729,11 @@
>
- {m.removeParticipant()}
+ {$language === 'zh'
+ ? translations.zh.removeParticipant
+ : translations.en.removeParticipant}
{/each}
@@ -681,14 +748,14 @@
- {m.mainTask()}
+ {$language === 'zh' ? translations.zh.mainTask : translations.en.mainTask}
{$session?.task}
{#if $session?.subtasks.length > 0}
- {m.subtasks()}
+ {$language === 'zh' ? translations.zh.subtasks : translations.en.subtasks}
{#each $session?.subtasks as subtask}
@@ -702,11 +769,11 @@
- {m.resources()}
+ {$language === 'zh' ? translations.zh.resources : translations.en.resources}
{#if $session?.resources.length === 0}
- {m.noResources()}
+ {$language === 'zh' ? translations.zh.noResources : translations.en.noResources}
{:else}
diff --git a/src/lib/components/session/ParticipantView.svelte b/src/lib/components/session/ParticipantView.svelte
index a305449..3cb25f1 100644
--- a/src/lib/components/session/ParticipantView.svelte
+++ b/src/lib/components/session/ParticipantView.svelte
@@ -19,7 +19,7 @@
import GroupSummary from '$lib/components/session/GroupSummary.svelte';
import { initFFmpeg, float32ArrayToWav, wav2mp3 } from '$lib/utils/wav2mp3';
import EndedView from '$lib/components/session/EndedView.svelte';
- import * as m from '$lib/paraglide/messages.js';
+ import { language } from '$lib/stores/language'; // Import the global language store
interface ChatroomConversation {
name: string;
@@ -133,15 +133,15 @@
if (!response.ok) {
await response.json(); // ignore result
- notifications.error(m.failedCreateGroup());
+ notifications.error(translations[$language].failedCreateGroup);
return;
}
- notifications.success(m.successCreateGroup());
+ notifications.success(translations[$language].successCreateGroup);
creating = false;
} catch (error) {
console.error('Error creating group:', error);
- notifications.error(m.failedCreateGroup());
+ notifications.error(translations[$language].failedCreateGroup);
} finally {
isCreatingGroup = false;
}
@@ -149,7 +149,9 @@
async function handleJoinGroup() {
if (!groupNumber || !/^(?:[1-9]|[1-4][0-9]|50)$/.test(groupNumber)) {
- notifications.error(m.invalidGroupNumber() || 'Please enter a valid group number (1-50)');
+ notifications.error(
+ translations[$language].invalidGroupNumber || 'Please enter a valid group number (1-50)'
+ );
return;
}
@@ -160,15 +162,15 @@
if (!response.ok) {
await response.json(); // ignore result
- notifications.error(m.failedJoinGroup());
+ notifications.error(translations[$language].failedJoinGroup);
return;
}
- notifications.success(m.successJoinGroup());
+ notifications.success(translations[$language].successJoinGroup);
creating = false;
} catch (error) {
console.error('Error joining group:', error);
- notifications.error(m.failedJoinGroup());
+ notifications.error(translations[$language].failedJoinGroup);
}
}
@@ -512,17 +514,95 @@
if (!response.ok) {
await response.json();
- throw new Error(m.failedLeaveGroup());
+ throw new Error(translations[$language].failedLeaveGroup);
}
- notifications.success(m.successLeaveGroup());
+ notifications.success(translations[$language].successLeaveGroup);
groupDoc = null;
conversationDoc = null;
} catch (error) {
console.error('Error leaving group:', error);
- notifications.error(m.failedLeaveGroup());
+ notifications.error(translations[$language].failedLeaveGroup);
}
}
+
+ const translations = {
+ en: {
+ confirmEndGroup:
+ 'Entering the Ended Stage is irreversible. Are you sure you want to proceed?',
+ confirm: 'Confirm',
+ cancel: 'Cancel',
+ confirmEndSummarize: 'Are you sure you want to confirm the group summary?',
+ finishgroup: 'Finish Group Discussion',
+ groupInfo: 'Group Information',
+ groupManagement: 'Group Management',
+ createNewGroup: 'Create New Group',
+ jointGroup: 'Join Group',
+ waitStart: 'Waiting for session to start...',
+ end: 'Please wait for the host to end the session.',
+ status: 'Session Status',
+ individual: 'Work on your individual contributions',
+ beforeGroup: 'Get ready to collaborate with your group.',
+ group: 'Collaborate with your group members.',
+ ended: 'Ended',
+ member: 'Members: ',
+ groupNum: 'Group Number',
+ notInGroup: 'You are not in a group yet.',
+ leaveGroup: 'Leave Group',
+ hostBeginShortly: 'The host will begin the session shortly.',
+ unknownUser: 'Unknown User',
+ loadingProfile: 'Loading...',
+ you: 'You',
+ leader: ' (Leader)',
+ creatingGroup: 'Creating...',
+ joinExistingGroup: 'Join Existing Group',
+ enterGroupNumber: 'Enter group number (1-50)',
+ successCreateGroup: 'Group created successfully',
+ failedCreateGroup: 'Failed to create group',
+ successJoinGroup: 'Joined group successfully',
+ failedJoinGroup: 'Failed to join group',
+ successLeaveGroup: 'Successfully left group',
+ failedLeaveGroup: 'Failed to leave group',
+ invalidGroupNumber: 'Please enter a valid group number (1-50)'
+ },
+ zh: {
+ confirmEndGroup: '進入 Ended Stage 將無法返回,確定要繼續嗎?',
+ confirm: '確定',
+ cancel: '取消',
+ finishgroup: '結束群組討論',
+ confirmEndSummarize: '確定要確認群組總結嗎?',
+ groupInfo: '群組資訊',
+ groupManagement: '群組管理',
+ createNewGroup: '建立新群組',
+ jointGroup: '加入群組',
+ waitStart: '等待會議開始...',
+ end: '請等待主持人結束會議。',
+ status: '會議狀態',
+ individual: '進行個人觀念發想',
+ beforeGroup: '準備好與您的小組合作',
+ group: '與您的小組成員合作討論',
+ ended: '已結束',
+ member: '成員:',
+ groupNum: '小組編號',
+ notInGroup: '您還沒有加入任何群組。',
+ leaveGroup: '退出群組',
+ hostBeginShortly: '主持人即將開始會議。',
+ unknownUser: '未知使用者',
+ loadingProfile: '載入中...',
+ you: '您',
+ leader: '(組長)',
+ creatingGroup: '正在建立...',
+ joinExistingGroup: '加入現有群組',
+ enterGroupNumber: '請輸入群組編號 (1-50)',
+ successCreateGroup: '成功建立群組',
+ failedCreateGroup: '無法建立群組',
+ successJoinGroup: '成功加入群組',
+ failedJoinGroup: '無法加入群組',
+ successLeaveGroup: '成功退出群組',
+ failedLeaveGroup: '無法退出群組',
+ invalidGroupNumber: '請輸入有效的群組編號 (1-50)'
+ }
+ };
@@ -532,15 +612,15 @@
{#if groupStatus === 'discussion'}
- {m.finishgroup()}
+ {translations[$language].finishgroup}
{:else if groupStatus === 'summarize'}
- {m.confirmEndSummarize()}
+ {translations[$language].confirmEndSummarize}
{:else if groupStatus === 'end'}
- {m.end()}
+ {translations[$language].end}
{/if}
{/if}
@@ -549,7 +629,7 @@
-
{m.status()}
+
{translations[$language].status}
@@ -567,18 +647,18 @@
{$session?.status}
{#if $session?.status === 'individual'}
-
{m.individual()}
+
{translations[$language].individual}
{:else if $session?.status === 'before-group'}
-
{m.beforeGroup()}
+
{translations[$language].beforeGroup}
{:else if $session?.status === 'group'}
-
{m.group()}
+
{translations[$language].group}
{/if}
-
{m.groupInfo()}
+
{translations[$language].groupInfo}
{#if groupDoc}
-
{m.member()}
+
{translations[$language].member}
{#each groupDoc.data.participants as participant, index}
{#if participant === user.uid}
- {m.you()}{index === 0 ? m.leader() : ''}
+ {translations[$language].you}{index === 0
+ ? translations[$language].leader
+ : ''}
{:else}
{#await getUser(participant)}
- {m.loadingProfile()}
+ {translations[$language].loadingProfile}
{:then profile}
- {profile.displayName}{index === 0 ? m.leader() : ''}
+ {profile.displayName}{index === 0 ? translations[$language].leader : ''}
{:catch}
- {m.unknownUser()}
+ {translations[$language].unknownUser}
{/await}
{/if}
@@ -619,7 +702,7 @@
onclick={() => groupDoc?.id && handleLeaveGroup(groupDoc.id, user.uid)}
>
- {m.leaveGroup()}
+ {translations[$language].leaveGroup}
{:else if $session?.status === 'preparing'}
-
{m.groupManagement()}
+
{translations[$language].groupManagement}
{#if creating}
{:else}
- {isCreatingGroup ? m.creatingGroup() : m.createNewGroup()}
+ {isCreatingGroup
+ ? translations[$language].creatingGroup
+ : translations[$language].createNewGroup}
{/if}
(creating = !creating)}>
- {creating ? m.cancel() : m.joinExistingGroup()}
+ {creating
+ ? translations[$language].cancel
+ : translations[$language].joinExistingGroup}
{:else}
- {m.notInGroup()}
+ {translations[$language].notInGroup}
{/if}
@@ -674,8 +761,8 @@
>
{#if $session?.status === 'preparing'}
-
{m.waitStart()}
-
{m.hostBeginShortly()}
+
{translations[$language].waitStart}
+
{translations[$language].hostBeginShortly}
{:else if $session?.status === 'individual'}
diff --git a/src/lib/components/session/StageProgress.svelte b/src/lib/components/session/StageProgress.svelte
index 1f52419..6e3a048 100644
--- a/src/lib/components/session/StageProgress.svelte
+++ b/src/lib/components/session/StageProgress.svelte
@@ -2,7 +2,7 @@
import { ArrowLeft, ArrowRight, Loader2 } from 'lucide-svelte';
import { Button, Modal } from 'flowbite-svelte';
import type { Session } from '$lib/schema/session';
- import * as m from '$lib/paraglide/messages.js';
+ import { language } from '$lib/stores/language'; // Import the global language store
const stages = [
{ id: 'preparing', name: '準備階段', color: 'bg-yellow-500' },
@@ -25,6 +25,20 @@
$: canGoNext = currentStageIndex < stages.length - 1 && !loadingNext;
$: nextStage = currentStageIndex < stages.length - 1 ? stages[currentStageIndex + 1] : null;
+ const translations = {
+ en: {
+ confirmEndStage:
+ 'Entering the Ended Stage is irreversible. Are you sure you want to proceed?',
+ confirm: 'Confirm',
+ cancel: 'Cancel'
+ },
+ zh: {
+ confirmEndStage: '進入總結階段將無法返回,確定要繼續嗎?',
+ confirm: '確定',
+ cancel: '取消'
+ }
+ };
+
async function handlePrevious() {
if (canGoPrevious) {
loadingPrevious = true;
@@ -118,7 +132,7 @@
- {m.confirmEndStage()}
+ {translations[$language].confirmEndStage}
- {m.confirm()}
+ {translations[$language].confirm}
(showEndedConfirmModal = false)}
- >{m.cancel()} {translations[$language].cancel}
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte
index d00f3e7..8e76dfe 100644
--- a/src/routes/+page.svelte
+++ b/src/routes/+page.svelte
@@ -3,10 +3,124 @@
import { Button, Card } from 'flowbite-svelte';
import { ArrowRight, Mic, Brain, GraduationCap, Github } from 'lucide-svelte';
import { onMount } from 'svelte';
- import * as m from '$lib/paraglide/messages.js';
+ import { language } from '$lib/stores/language'; // Import the global language store
let title = $state('Hinagiku');
let highlight = $state(0);
+ let lang = language; // Use the global language store directly
+
+ const translations = {
+ en: {
+ transcript: 'Real-time Transcription',
+ Itanalysis: 'Intelligent Analysis',
+ educational: 'Educational Focus',
+ tr_details:
+ 'Capture every valuable insight from your discussions with our advanced speech-to-text technology.',
+ ia_details:
+ 'Get AI-powered insights and suggestions to improve discussion quality and participation.',
+ ef_details:
+ 'Purpose-built for educational environments with features that support meaningful learning.',
+ Ai_describe:
+ 'Our AI-powered system emphasizes student-led discussions while providing structured guidance, making it easier for teachers to direct learning and analyze outcomes.',
+ intro:
+ 'Hinagiku helps educators facilitate more engaging and productive discussions through real-time transcription and intelligent analysis.',
+ welcome: 'Welcome to Hinagiku!',
+ profile: 'Profile',
+ dashboard: 'Go to Dashboard',
+ signOut: 'Sign out',
+ login: 'Login',
+ started: 'Get Started',
+ learn: 'Learn More',
+ whyChoose: 'Why Choose Hinagiku?',
+ whyChooseDesc:
+ 'Our platform combines cutting-edge technology with educational expertise to enhance learning outcomes.',
+ howItWorks: 'How It Works',
+ howItWorksDesc:
+ 'Hinagiku enhances the Think-Pair-Share learning technique with AI assistance throughout the entire process.',
+ preparation: '1. Preparation',
+ preparationDesc:
+ 'Teachers create discussion templates and set up dynamic timelines for structured learning sessions.',
+ joinSession: '2. Join Session',
+ joinSessionDesc:
+ 'Students easily join discussions by scanning a session QR code, then form or join groups.',
+ discussion: '3. Discussion',
+ discussionDesc:
+ 'AI assists in guiding individual reflection, group discussions, and helps maintain focus on the topic.',
+ analysis: '4. Analysis',
+ analysisDesc:
+ 'Get visual summaries and insights from discussions to understand class perspectives and engagement.',
+ communityDriven: 'Community-Driven Templates',
+ communityDrivenDesc:
+ 'Access and share discussion templates with educators worldwide. Build upon proven discussion frameworks or contribute your own.',
+ browseTemplates: 'Browse public templates from experienced educators',
+ forkTemplates: 'Fork and customize existing templates for your needs',
+ shareTemplates: 'Share your successful discussion formats with the community',
+ collaborateTemplates: 'Collaborate with other educators to improve templates',
+ exploreTemplates: 'Explore Templates',
+ storyBehind: 'The Story Behind Our Name',
+ storyBehindDesc:
+ 'Hinagiku (雛菊), or Daisy in English, is an intelligent system designed to support discussions in educational environments.',
+ realTime:
+ "One of Hinagiku's key features is its real-time voice transcription and analysis, which helps hosts provide timely and insightful feedback, setting it apart from other educational tools.",
+ coreValues:
+ 'We chose the name Hinagiku because it reflects our core values: resilience, simplicity, and growth—much like the daisy flower itself, which flourishes in diverse conditions.',
+ mission:
+ 'Our mission is to help participants and hosts connect meaningfully by providing tools that facilitate better communication and collaboration in classrooms.',
+ openSource: 'Open Source',
+ openSourceDesc:
+ 'Hinagiku is open source and available on GitHub. We welcome contributions from the community!',
+ viewOnGitHub: 'View on GitHub'
+ },
+ zh: {
+ transcript: '即時轉錄',
+ Itanalysis: '智慧分析',
+ educational: '教育焦點',
+ tr_details: '使用我們先進的語音轉文字技術捕獲討論中的每一個寶貴見解。',
+ ia_details: '獲取AI提供的見解和建議,以提高討論品質和參與度。',
+ ef_details: '專為教育環境而設計,具被助於有效學習的功能。',
+ Ai_describe:
+ '我們的AI系統強調學生主導的討論,同時提供結構化指導,使教師更容易引導學習並分析結果。',
+ intro: 'Hinagiku透過即時轉錄和智慧分析幫助教育工作者促進更具吸引力和生產力的討論。',
+ welcome: '歡迎來到Hinagiku!',
+ profile: '個人资料',
+ dashboard: '儀表板',
+ signOut: '登出',
+ login: '登入',
+ started: '開始使用',
+ learn: '了解更多',
+ whyChoose: '為什麼選擇Hinagiku?',
+ whyChooseDesc: '我們的平台結合了尖端技術和教育專業知識,以提高學習成果。',
+ howItWorks: '它是如何工作的',
+ howItWorksDesc: 'Hinagiku在整個過程中通過AI輔助增強了Think-Pair-Share學習技術。',
+ preparation: '1. 準備',
+ preparationDesc: '教師創建討論模板並設置動態時間表以進行結構化學習會話。',
+ joinSession: '2. 加入會話',
+ joinSessionDesc: '學生通過掃描會話QRcode輕鬆加入討論,然後組建或加入小組。',
+ discussion: '3. 討論',
+ discussionDesc: 'AI協助指導個人反思、小組討論,並幫助保持對主題的關注。',
+ analysis: '4. 分析',
+ analysisDesc: '從討論中獲取視覺摘要和見解,以了解班級的觀點和參與度。',
+ communityDriven: '社區驅動的模板',
+ communityDrivenDesc:
+ '訪問並分享來自全球教育工作者的討論模板。在經過驗證的討論框架上進行構建或貢獻您自己的模板。',
+ browseTemplates: '瀏覽來自經驗豐富的教育工作者的公共模板',
+ forkTemplates: '分叉並自定義現有模板以滿足您的需求',
+ shareTemplates: '與社區分享您成功的討論格式',
+ collaborateTemplates: '與其他教育工作者合作改進模板',
+ exploreTemplates: '探索模板',
+ storyBehind: '我們名字背後的故事',
+ storyBehindDesc:
+ 'Hinagiku(雛菊),或英文中的Daisy,是一個旨在支持教育環境中討論的智能系統。',
+ realTime:
+ 'Hinagiku的一個關鍵功能是其實時語音轉錄和分析,這有助於主持人提供及時和有見地的反饋,使其與其他教育工具區分開來。',
+ coreValues:
+ '我們選擇Hinagiku這個名字是因為它反映了我們的核心價值觀:韌性、簡單和成長——就像雛菊花一樣,在不同的條件下茁壯成長。',
+ mission: '我們的使命是通過提供促進更好溝通和協作的工具,幫助參與者和主持人有意義地聯繫。',
+ openSource: '開源',
+ openSourceDesc: 'Hinagiku是開源的,並在GitHub上可用。我們歡迎社區的貢獻!',
+ viewOnGitHub: '在GitHub上查看'
+ }
+ };
onMount(() => {
const interval = setInterval(() => {
@@ -19,6 +133,9 @@
return () => clearInterval(interval);
});
+ //for i18n support
+ // import * as m from '$lib/paraglide/messages.js'
+ // import { languageTag } from '$lib/paraglide/runtime.js'
@@ -44,20 +161,20 @@
- {m.intro()}
+ {translations[$lang].intro}
{#if $user}
- {m.dashboard()}
+ {translations[$lang].dashboard}
{:else}
- {m.started()}
+ {translations[$lang].started}
-
{m.learn()}
+
{translations[$lang].learn}
{/if}
@@ -72,9 +189,9 @@
-
{m.whyChoose()}
+
{translations[$lang].whyChoose}
- {m.whyChooseDesc()}
+ {translations[$lang].whyChooseDesc}
@@ -85,9 +202,9 @@
-
{m.transcript()}
+
{translations[$lang].transcript}
- {m.tr_details()}
+ {translations[$lang].tr_details}
@@ -97,9 +214,9 @@
-
{m.Itanalysis()}
+
{translations[$lang].Itanalysis}
- {m.ia_details()}
+ {translations[$lang].ia_details}
@@ -109,9 +226,9 @@
-
{m.educational()}
+
{translations[$lang].educational}
- {m.ef_details()}
+ {translations[$lang].ef_details}
@@ -122,45 +239,45 @@
-
{m.howItWorks()}
+
{translations[$lang].howItWorks}
- {m.howItWorksDesc()}
+ {translations[$lang].howItWorksDesc}
- {m.preparation()}
+ {translations[$lang].preparation}
- {m.preparationDesc()}
+ {translations[$lang].preparationDesc}
- {m.joinSession()}
+ {translations[$lang].joinSession}
- {m.joinSessionDesc()}
+ {translations[$lang].joinSessionDesc}
- {m.discussion()}
+ {translations[$lang].discussion}
- {m.discussionDesc()}
+ {translations[$lang].discussionDesc}
- {m.analysis()}
+ {translations[$lang].analysis}
- {m.analysisDesc()}
+ {translations[$lang].analysisDesc}
- {m.Ai_describe()}
+ {translations[$lang].Ai_describe}
@@ -172,21 +289,21 @@
- {m.communityDriven()}
+ {translations[$lang].communityDriven}
- {m.communityDrivenDesc()}
+ {translations[$lang].communityDrivenDesc}
- {m.browseTemplates()}
- {m.forkTemplates()}
- {m.shareTemplates()}
- {m.collaborateTemplates()}
+ {translations[$lang].browseTemplates}
+ {translations[$lang].forkTemplates}
+ {translations[$lang].shareTemplates}
+ {translations[$lang].collaborateTemplates}
- {m.exploreTemplates()}
+ {translations[$lang].exploreTemplates}
@@ -215,19 +332,21 @@
/>
-
{m.storyBehind()}
+
{translations[$lang].storyBehind}
- Hinagiku (雛菊) , {m.storyBehindDesc()}
+ Hinagiku (雛菊) , {translations[
+ $lang
+ ].storyBehindDesc}
- {m.realTime()}
+ {translations[$lang].realTime}
- {m.coreValues()}
+ {translations[$lang].coreValues}
- {m.mission()}
+ {translations[$lang].mission}
@@ -239,9 +358,9 @@
-
{m.openSource()}
+
{translations[$lang].openSource}
- {m.openSourceDesc()}
+ {translations[$lang].openSourceDesc}
- {m.viewOnGitHub()}
+ {translations[$lang].viewOnGitHub}
diff --git a/src/routes/dashboard/+page.svelte b/src/routes/dashboard/+page.svelte
index 3f4dac8..7d65a77 100644
--- a/src/routes/dashboard/+page.svelte
+++ b/src/routes/dashboard/+page.svelte
@@ -27,7 +27,7 @@
import TemplateCard from '$lib/components/TemplateCard.svelte';
import SessionCard from '$lib/components/SessionCard.svelte';
- import * as m from '$lib/paraglide/messages.js';
+ import { language } from '$lib/stores/language'; // Import the global language store
let { data } = $props();
@@ -142,6 +142,57 @@
unsubscribe2();
unsubscribe3();
});
+
+ const translations = {
+ en: {
+ welcome: 'Welcome to Hinagiku!',
+ profile: 'Profile',
+ dashboard: 'Dashboard',
+ signOut: 'Sign out',
+ login: 'Login',
+ welcomeDashboard: 'Welcome to your dashboard',
+ stats: 'Statistics',
+ recentActivity: 'Recent Activity',
+ createTemplate: 'Create Template',
+ createTemplateDesc: 'Create a new discussion template',
+ joinSession: 'Join Session',
+ joinSessionDesc: 'Join an existing discussion session',
+ editProfile: 'Edit Profile',
+ editProfileDesc: 'Update your profile settings',
+ publicTemplates: 'Public Templates',
+ viewAll: 'View All',
+ yourTemplates: 'Your Templates',
+ noTemplates: 'No templates created yet',
+ createFirstTemplate: 'Create your first template to get started',
+ createTemplateButton: 'Create Your First Template',
+ noSessions: 'No sessions created yet',
+ createSession: 'Create a new session with template'
+ },
+ zh: {
+ welcome: '歡迎來到Hinagiku!',
+ profile: '個人資料',
+ dashboard: '儀表板',
+ signOut: '登出',
+ login: '登入',
+ welcomeDashboard: '歡迎來到您的儀表板',
+ stats: '統計數據',
+ recentActivity: '最近活動',
+ createTemplate: '創建模板',
+ createTemplateDesc: '創建一個新的討論模板',
+ joinSession: '加入會話',
+ joinSessionDesc: '加入現有的討論會話',
+ editProfile: '編輯個人資料',
+ editProfileDesc: '更新您的個人資料設置',
+ publicTemplates: '公開模板',
+ viewAll: '查看全部',
+ yourTemplates: '您的模板',
+ noTemplates: '尚未創建模板',
+ createFirstTemplate: '創建您的第一個模板以開始',
+ createTemplateButton: '創建您的第一個模板',
+ noSessions: '尚未創建會話',
+ createSession: '使用模板創建新會話'
+ }
+ };
@@ -150,9 +201,9 @@
-
{m.dashboard()}
+
{translations[$language].dashboard}
- {m.welcomeDashboard()}, {$profile?.displayName || $user?.displayName}
+ {translations[$language].welcomeDashboard}, {$profile?.displayName || $user?.displayName}
@@ -164,9 +215,9 @@
- {m.createTemplate()}
+ {translations[$language].createTemplate}
-
{m.createTemplateDesc()}
+
{translations[$language].createTemplateDesc}
@@ -176,9 +227,9 @@
- {m.dashjoinSession()}
+ {translations[$language].joinSession}
-
{m.dashjoinSessionDesc()}
+
{translations[$language].joinSessionDesc}
@@ -188,9 +239,9 @@
- {m.editProfile()}
+ {translations[$language].editProfile}
-
{m.editProfileDesc()}
+
{translations[$language].editProfileDesc}
@@ -199,9 +250,10 @@
- {m.publicTemplates()}
+ {translations[$language].publicTemplates}
- {m.viewAll()}
+ {translations[$language].viewAll}
{#if $publicTemplates?.length}
@@ -222,9 +274,11 @@
Example Template
- {m.createFirstTemplate()}
+ {translations[$language].createFirstTemplate}
-
{m.createTemplateButton()}
+
{translations[$language].createTemplateButton}
{/each}
@@ -235,8 +289,8 @@
-
{m.yourTemplates()}
- {m.viewAll()}
+ {translations[$language].yourTemplates}
+ {translations[$language].viewAll}
{#if $templates?.length}
@@ -258,11 +312,11 @@
- {m.noTemplates()}
+ {translations[$language].noTemplates}
-
{m.createFirstTemplate()}
+
{translations[$language].createFirstTemplate}
{m.createTemplateButton()} {translations[$language].createTemplateButton}
@@ -273,8 +327,10 @@
-
{m.recentActivity()}
- {m.viewAll()}
+ {translations[$language].recentActivity}
+ {translations[$language].viewAll}
{#each $availableLabels as label}
@@ -306,9 +362,9 @@
- {m.noSessions()}
+ {translations[$language].noSessions}
-
{m.createSession()}
+
{translations[$language].createSession}
{/if}
@@ -320,9 +376,11 @@
- {m.recentActivity()}
+ {translations[$language].recentActivity}
- {m.viewAll()}
+ {translations[$language].viewAll}
{#each $sessions as [id, session]}
diff --git a/src/routes/join/+page.svelte b/src/routes/join/+page.svelte
index c0e8ebb..c7ee89a 100644
--- a/src/routes/join/+page.svelte
+++ b/src/routes/join/+page.svelte
@@ -4,7 +4,7 @@
import { notifications } from '$lib/stores/notifications';
import { getDoc, doc } from 'firebase/firestore';
import { db } from '$lib/firebase';
- import * as m from '$lib/paraglide/messages.js';
+ import { language } from '$lib/stores/language'; // Import the global language store
async function handleScan(code: string) {
try {
@@ -41,6 +41,23 @@
console.error(e);
}
}
+
+ const translations = {
+ en: {
+ joinSession: 'Join Discussion Session',
+ scanQr: 'Scan the QR code provided by your session host to join the discussion.',
+ enterCode: 'Or please enter the 6-digits code to join.',
+ joinButton: 'Join Session',
+ enterCodePlaceholder: 'Enter code'
+ },
+ zh: {
+ joinSession: '加入討論會話',
+ scanQr: '掃描會話主持人提供的QRcode以加入討論。',
+ enterCode: '或者請輸入6位數的代碼以加入。',
+ joinButton: '加入會話',
+ enterCodePlaceholder: '輸入代碼'
+ }
+ };
@@ -48,21 +65,21 @@
- {m.joinSession()}
+ {translations[$language].joinSession}
- {m.scanQr()}
+ {translations[$language].scanQr}
-
{m.enterCode()}
+
{translations[$language].enterCode}
- {m.joinButton()}
+ {translations[$language].joinButton}
diff --git a/src/routes/login/+page.svelte b/src/routes/login/+page.svelte
index e038253..b662d28 100644
--- a/src/routes/login/+page.svelte
+++ b/src/routes/login/+page.svelte
@@ -1,14 +1,29 @@
diff --git a/src/routes/profile/+page.svelte b/src/routes/profile/+page.svelte
index 75bd135..ee5fcc6 100644
--- a/src/routes/profile/+page.svelte
+++ b/src/routes/profile/+page.svelte
@@ -3,20 +3,53 @@
import { profile } from '$lib/stores/profile';
import { Button, Label, Input, Textarea, Card, Alert } from 'flowbite-svelte';
import { CheckCircle, XCircle } from 'lucide-svelte';
- import * as m from '$lib/paraglide/messages.js';
+ import { language } from '$lib/stores/language'; // Import the global language store
let { form, data } = $props();
let loading = $state(false);
+
+ const translations = {
+ en: {
+ profileSettings: 'Profile Settings',
+ updateInfo: 'Update your personal information and preferences',
+ successMessage: 'Profile updated successfully!',
+ errorMessage: 'Cannot update profile. Please try again later.',
+ displayName: 'Display Name',
+ title: 'Title',
+ titlePlaceholder: 'e.g. Professor, Student, Teaching Assistant',
+ bio: 'Bio',
+ bioPlaceholder: 'Tell us about yourself',
+ bioHelp: 'Write a short bio to help others know more about you',
+ cancel: 'Cancel',
+ saveChanges: 'Save Changes',
+ saving: 'Saving...'
+ },
+ zh: {
+ profileSettings: '個人資料設置',
+ updateInfo: '更新您的個人資料和偏好',
+ successMessage: '個人資料更新成功!',
+ errorMessage: '無法更新個人資料。請稍後再試。',
+ displayName: '顯示名稱',
+ title: '職稱',
+ titlePlaceholder: '例如:教授、學生、助教',
+ bio: '簡介',
+ bioPlaceholder: '介紹一下你自己',
+ bioHelp: '寫一個簡短的簡介,幫助其他人更好地了解你',
+ cancel: '取消',
+ saveChanges: '保存更改',
+ saving: '保存中...'
+ }
+ };
- {m.profileSettings()} | Hinagiku
+ {translations[$language].profileSettings} | Hinagiku
-
{m.profileSettings()}
-
{m.updateInfo()}
+
{translations[$language].profileSettings}
+
{translations[$language].updateInfo}
{#if form}
@@ -25,14 +58,14 @@
- {m.successMessage()}
+ {translations[$language].successMessage}
{:else}
- {m.errorMessage()}
+ {translations[$language].errorMessage}
{/if}
{/if}
@@ -51,7 +84,7 @@
class="space-y-6"
>
-
{m.displayName()}
+
{translations[$language].displayName}
- {m.title()}
+ {translations[$language].title}
-
{m.bio()}
+
{translations[$language].bio}
- {m.bioHelp()}
+ {translations[$language].bioHelp}
- {m.cancel()}
+ {translations[$language].cancel}
- {loading ? m.saving() : m.saveChanges()}
+ {loading ? translations[$language].saving : translations[$language].saveChanges}
diff --git a/src/routes/session/[id]/+page.svelte b/src/routes/session/[id]/+page.svelte
index d3248cf..46d9902 100644
--- a/src/routes/session/[id]/+page.svelte
+++ b/src/routes/session/[id]/+page.svelte
@@ -6,12 +6,21 @@
import ParticipantView from '$lib/components/session/ParticipantView.svelte';
import LabelManager from '$lib/components/session/LabelManager.svelte';
import { page } from '$app/stores';
- import * as m from '$lib/paraglide/messages.js';
+ import { language } from '$lib/stores/language'; // Import the global language store
let { data } = $props();
let session = getContext
>('session');
let isHost = $derived($session?.host === data.user.uid);
let sessionId = $page.params.id;
+
+ const translations = {
+ en: {
+ sessionLabels: 'Session Labels'
+ },
+ zh: {
+ sessionLabels: '會話標籤'
+ }
+ };
@@ -23,7 +32,7 @@
{#if $session.status === 'preparing' && isHost}
-
{m.sessionLabels()}
+ {translations[$language].sessionLabels}
{/if}
diff --git a/src/routes/session/[id]/discussion/[userId]/+page.svelte b/src/routes/session/[id]/discussion/[userId]/+page.svelte
index a066d24..8bb555f 100644
--- a/src/routes/session/[id]/discussion/[userId]/+page.svelte
+++ b/src/routes/session/[id]/discussion/[userId]/+page.svelte
@@ -1,5 +1,5 @@
@@ -47,15 +60,15 @@
class="inline-block"
type="text"
bind:value={inputText}
- placeholder={m.typeMessage()}
+ placeholder={translations[$language].typeMessage}
onkeyup={(e) => e.key === 'Enter' && sendMessage()}
/>
- {m.send()}
+ {translations[$language].send}
{#if messages[messages.length - 1].sender === 'me'}
- {m.waitResponse()}
+ {translations[$language].waitResponse}
{/if}
diff --git a/src/routes/session/[id]/participant/+page.svelte b/src/routes/session/[id]/participant/+page.svelte
index 73eaa8b..5140172 100644
--- a/src/routes/session/[id]/participant/+page.svelte
+++ b/src/routes/session/[id]/participant/+page.svelte
@@ -1,5 +1,13 @@
-
{m.PwaitingForParticipants()}
+
{translations[$language].waitResponse}
diff --git a/src/routes/session/[id]/status/+page.svelte b/src/routes/session/[id]/status/+page.svelte
index 2037f78..ac39be3 100644
--- a/src/routes/session/[id]/status/+page.svelte
+++ b/src/routes/session/[id]/status/+page.svelte
@@ -1,6 +1,7 @@
-
{m.dashboardTitle()}
+
{translations[$language].dashboardTitle}
{#if groups.length === 0}
-
{m.loadingGroups()}
+
{translations[$language].loadingGroups}
{:else}
{#each groups as group, index}
Group #{index + 1}
{#if group.participants.length === 0}
-
{m.noParticipantsInGroup()}
+
{translations[$language].noParticipants}
{:else}
{#each group.participants as participant}
diff --git a/src/routes/template/[id]/+page.svelte b/src/routes/template/[id]/+page.svelte
index 1ac147a..d5f4bdc 100644
--- a/src/routes/template/[id]/+page.svelte
+++ b/src/routes/template/[id]/+page.svelte
@@ -10,7 +10,7 @@
import { subscribe } from '$lib/firebase/store';
import { goto } from '$app/navigation';
import { notifications } from '$lib/stores/notifications';
- import * as m from '$lib/paraglide/messages';
+ import { language } from '$lib/stores/language'; // Import the global language store
let title = '';
let task = '';
@@ -128,56 +128,105 @@
notifications.error('Failed to delete template');
}
}
+
+ const translations = {
+ en: {
+ editTemplate: 'Edit Template',
+ startSession: 'Start Session',
+ saveChanges: 'Save changes before starting a session',
+ startDiscussion: 'Start a discussion session with this template',
+ title: 'Title',
+ templateTitle: 'Template title',
+ mainTask: 'Main Task',
+ mainTaskDesc: 'Main task description',
+ subtasks: 'Subtasks',
+ subtaskDesc: 'Subtask description',
+ addSubtask: 'Add Subtask',
+ makePublic: 'Make template public',
+ backToDashboard: 'Back to Dashboard',
+ unsavedChanges: 'You have unsaved changes!',
+ deleteTemplate: 'Delete this Template',
+ saveChangesButton: 'Save Changes',
+ allChangesSaved: 'All changes saved',
+ deleteConfirmation: 'Are you sure you want to delete this template?',
+ yesDelete: 'Yes, delete it',
+ noCancel: 'No, cancel',
+ loadingTemplate: 'Loading template...'
+ },
+ zh: {
+ editTemplate: '編輯模板',
+ startSession: '開始會話',
+ saveChanges: '保存更改後再開始會話',
+ startDiscussion: '使用此模板開始討論會話',
+ title: '標題',
+ templateTitle: '模板標題',
+ mainTask: '主要任務',
+ mainTaskDesc: '主要任務描述',
+ subtasks: '子任務',
+ subtaskDesc: '子任務描述',
+ addSubtask: '添加子任務',
+ makePublic: '將模板設為公開',
+ backToDashboard: '返回儀表板',
+ unsavedChanges: '您有未保存的更改!',
+ deleteTemplate: '刪除此模板',
+ saveChangesButton: '保存更改',
+ allChangesSaved: '所有更改已保存',
+ deleteConfirmation: '您確定要刪除此模板嗎?',
+ yesDelete: '是的,刪除它',
+ noCancel: '不,取消',
+ loadingTemplate: '加載模板...'
+ }
+ };
- {m.editTemplate()} | Hinagiku
+ {translations[$language].editTemplate} | Hinagiku
{#if template}
-
{m.editTemplate()}
+
{translations[$language].editTemplate}
- {m.startSession()}
+ {translations[$language].startSession}
{#if unsavedChanges}
- {m.saveChanges()}
+ {translations[$language].saveChanges}
{:else}
- {m.startDiscussion()}
+ {translations[$language].startDiscussion}
{/if}