Translate all Chinese comments and strings to English

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-07 20:03:19 +08:00
parent 7d5918837b
commit 1d621bf304
57 changed files with 499 additions and 499 deletions

View File

@@ -47,9 +47,9 @@ export const useAcctMgmtStore = defineStore('acctMgmtStore', {
response: {
deleteAccount: null,
},
isOneAccountJustCreate: false, //如果有一個帳號剛剛建立,則會在列表上的第一列顯示這個帳號,並且右置一個徽章
isOneAccountJustCreate: false, // If an account was just created, display it at the top of the list with a badge
justCreateUsername: '', // unique username
shouldUpdateList: false, // 控制是否該刷新列表
shouldUpdateList: false, // Controls whether the list should be refreshed
}),
getters: {},
actions: {
@@ -113,9 +113,9 @@ export const useAcctMgmtStore = defineStore('acctMgmtStore', {
await loginStore.getUserData();
const loginUserData:User = loginStore.userData;
return rawResponseData.map(user => ({
...user, // 保留後端傳來的欄位
...user, // Preserve fields from the backend response
isCurrentLoggedIn: loginUserData.username === user.username,
isDeleteHovered: false, // 針對前端顯示而額外增加的欄位
isDeleteHovered: false, // Additional fields for frontend display state
isRowHovered: false,
isEditHovered: false,
}));
@@ -267,7 +267,7 @@ export const useAcctMgmtStore = defineStore('acctMgmtStore', {
this.currentViewingUser.is_admin = response.data.roles.some(role => role.code === 'admin');
return response.status === 200;
} catch (error) {
//不需要跳出錯誤,因為如果是錯誤反而是好事,表示帳號是獨一的
// No need to show an error, because an error here means the account is unique
return false;
}
},

View File

@@ -55,17 +55,17 @@ export const useAllMapDataStore = defineStore('allMapDataStore', {
allFilterTimeframe: {},
allFilterTrace: [],
allFilterAttrs: [],
hasResultRule: null, // click Apply 後檢查是否有 Data
temporaryData: [], // 沒被 apply all 的 Data
postRuleData: [], // has-result API & temp-filters API 的 Data
hasResultRule: null, // Whether any data remains after clicking Apply
temporaryData: [], // Data not yet applied via Apply All
postRuleData: [], // Data for the has-result API and temp-filters API
ruleData: [], // Funnle view's data
isRuleData: [], // toggle button data
allFunnelData: [],
isUpdateFilter: false, // 是否成功儲存 Filter 檔
isUpdateFilter: false, // Whether the filter file was saved successfully
selectTimeFrame: [], // user select time start and end
infinite404: null, // 無限滾動式是否到底要換頁
infiniteStart: 0, // 無限滾動 case 開始的數字
baseInfiniteStart: 0, // 無限滾動 case 開始的數字
infinite404: null, // Whether infinite scroll has reached the last page
infiniteStart: 0, // Starting index for infinite scroll cases
baseInfiniteStart: 0, // Starting index for base infinite scroll cases
}),
getters: {
processMap: state => {
@@ -273,10 +273,10 @@ export const useAllMapDataStore = defineStore('allMapDataStore', {
const min = this.allFilterTimeframe.x_axis.min;
const max = this.allFilterTimeframe.x_axis.max;
// 給 Chart.js 原始資料,格式不同的畫會錯誤輸出
// Preserve raw data for Chart.js; incorrect format causes wrong output
this.allFilterTimeframe.x_axis.min_base = min;
this.allFilterTimeframe.x_axis.max_base = max;
// 轉成無秒的時間格式
// Convert to a time format without seconds
this.allFilterTimeframe.x_axis.min = min !== null ? moment(min).format('YYYY/MM/DD HH:mm') : null;
this.allFilterTimeframe.x_axis.max = max !== null ? moment(max).format('YYYY/MM/DD HH:mm') : null;
} catch(error) {

View File

@@ -29,7 +29,7 @@ export const useCompareStore = defineStore('compareStore', {
* @param { array } queryParams
*/
async getCompare(queryParams) {
// encodeURIComponent 函數用於將字串中的特殊字符進行編碼,以確保 URL 的正確性。
// encodeURIComponent encodes special characters in the string to ensure URL correctness.
const queryString = JSON.stringify(queryParams);
const api = `/api/compare?datasets=${encodeURIComponent(queryString)}`;

View File

@@ -49,12 +49,12 @@ function getFileTypeApi(state) {
/** Pinia store for conformance checking and rule management. */
export const useConformanceStore = defineStore('conformanceStore', {
state: () => ({
conformanceLogId: null, // log
conformanceFilterId: null, // filter
conformanceLogTempCheckId: null, // log 檔存檔前的 check Id
conformanceFilterTempCheckId: null, // Filter 檔存檔前的 check Id
conformanceLogCreateCheckId: null, // log 檔存檔後的 check Id(rule)
conformanceFilterCreateCheckId: null, // Filter 檔存檔後的 check Id(rule)
conformanceLogId: null, // Log file
conformanceFilterId: null, // Filter file
conformanceLogTempCheckId: null, // Temporary check ID for log (before saving)
conformanceFilterTempCheckId: null, // Temporary check ID for filter (before saving)
conformanceLogCreateCheckId: null, // Created check ID for log (rule, after saving)
conformanceFilterCreateCheckId: null, // Created check ID for filter (rule, after saving)
allConformanceTask: [],
allCfmSeqStart: [],
allCfmSeqEnd: [],
@@ -76,11 +76,11 @@ export const useConformanceStore = defineStore('conformanceStore', {
selectedActSeqMore: 'All', // radio
selectedActSeqFromTo: 'From', // radio
infinite404: null,
isStartSelected: null, // start & end 連動先選擇 start
isEndSelected: null, // start & end 連動先選擇 end
isStartSelected: null, // Whether start is selected in linked start & end selection
isEndSelected: null, // Whether end is selected in linked start & end selection
conformanceRuleData: null, // create checkId's data to save
isUpdateConformance: false, // 成功儲存後要跳 Modal
conformanceFileName: null, // 儲存成功的 Modal
isUpdateConformance: false, // Show modal after successful save
conformanceFileName: null, // File name displayed in the save success modal
}),
getters: {
conformanceAllTasks: state => {

View File

@@ -20,7 +20,7 @@ import moment from 'moment';
export const useConformanceInputStore = defineStore('conformanceInputStore', {
state: () => ({
inputDataToSave: {
inputStart: null, // 有待釐清start 是活動的開始,還是時間的開始?
inputStart: null, // TODO: clarify whether "start" means activity start or time start
inputEnd: null,
min: null,
max: null,

View File

@@ -51,7 +51,7 @@ export const useCytoscapeStore = defineStore('cytoscapeStore', {
if (!this.nodePositions[this.currentGraphId][direction]) {
this.nodePositions[this.currentGraphId][direction] = [];
}
// 若是資訊曾經存在這張圖於localStorage
// If this graph's data was previously stored in localStorage
if (localStorage.getItem(SAVE_KEY_NAME)) {
const nodeToSave = this.nodePositions[this.currentGraphId][direction]
.find(node => node.id === nodeId);

View File

@@ -147,13 +147,13 @@ export const useFilesStore = defineStore('filesStore', {
},
};
uploadloader(); // 進度條
uploadloader(); // Show loading progress bar
try {
const response = await apiClient.post(api, fromData, config);
this.uploadId = response.data.id;
this.$router.push({name: 'Upload'});
Swal.close(); // 關閉進度條
Swal.close(); // Close the loading progress bar
} catch(error) {
if(error.response?.status === 422) {
// msg: 'not in UTF-8' | 'insufficient columns' | 'the csv file is empty' | 'the filename does not ends with .csv'
@@ -162,7 +162,7 @@ export const useFilesStore = defineStore('filesStore', {
uploadFailedFirst(detail[0].type, detail[0].msg, detail[0].loc[2]);
} else {
Swal.close(); // 關閉進度條
Swal.close(); // Close the loading progress bar
apiError(error, 'Failed to upload the files.');
}
}
@@ -189,13 +189,13 @@ export const useFilesStore = defineStore('filesStore', {
const uploadId = this.uploadId;
const api = `/api/logs/csv-uploads/${uploadId}`;
uploadloader(); // 進度條
uploadloader(); // Show loading progress bar
try {
const response = await apiClient.post(api, data);
this.uploadLogId = response.data.id;
Swal.close(); // 關閉進度條
await this.rename(); // 改檔名
Swal.close(); // Close the loading progress bar
await this.rename(); // Rename the file
await uploadSuccess();
this.$router.push({name: 'Files'});
} catch(error) {
@@ -204,7 +204,7 @@ export const useFilesStore = defineStore('filesStore', {
uploadFailedSecond(detail);
} else {
Swal.close(); // 關閉進度條
Swal.close(); // Close the loading progress bar
apiError(error, 'Failed to upload the log files.');
}
}
@@ -216,7 +216,7 @@ export const useFilesStore = defineStore('filesStore', {
* @param {string} name - The file name.
*/
async rename(type, id, fileName) {
// 先判斷有沒有 uploadLogId,有就設定 id 和 type再判斷檔案型別。
// If uploadLogId exists, set id and type accordingly; then check the file type.
if(this.uploadId && this.uploadFileName) {
type = 'log';
id = this.uploadLogId;

View File

@@ -39,7 +39,7 @@ export const useLoginStore = defineStore('loginStore', {
const api = '/api/oauth/token';
const config = {
headers: {
// http post 預設的 url 編碼,非 json 格式
// Default URL-encoded format for HTTP POST, not JSON
'Content-Type':'application/x-www-form-urlencoded',
},
};
@@ -48,7 +48,7 @@ export const useLoginStore = defineStore('loginStore', {
const response = await axios.post(api, this.auth, config);
const accessToken = response.data.access_token;
const refresh_token = response.data.refresh_token;
// token 儲存在 cookie
// Store the token in a cookie
setCookieWithoutExpiration("luciaToken", accessToken);
const expiryDate = new Date();
expiryDate.setMonth(expiryDate.getMonth() + 6);
@@ -57,9 +57,9 @@ export const useLoginStore = defineStore('loginStore', {
this.isLoggedIn = true;
setCookie("isLuciaLoggedIn", "true");
// 大部分的情況下,預設導向至 FILES 頁面
// 然而有一種情況是使用者在沒有登入的情況下貼上了某一個頁面的網址,
// 則在此情況下時,我們會在使用者稍後登入後,把使用者帶到剛才記住的 return-to 網址
// By default, redirect to the FILES page.
// However, if the user pasted a URL while not logged in,
// redirect them to the remembered return-to URL after login.
if(this.rememberedReturnToUrl !== "") {
const decodedUrl = atob(this.rememberedReturnToUrl);
// Only allow relative paths to prevent open redirect attacks

View File

@@ -92,7 +92,7 @@ export const useMapPathStore = defineStore('mapPathStore', {
nodes: [],
}; // second layer index
let curGraphNode, prevGraphNode, curEdge; // 配對 curGraphNode 與 nodeIndex 指向的 node
let curGraphNode, prevGraphNode, curEdge; // Match curGraphNode with the node at nodeIndex
for (let nodeIndex = 0; nodeIndex < curButton[listIndex].length; nodeIndex++) {
if (nodeIndex === 0) { // special case, initialize curGraphNode
curGraphNode = this.startNode.outgoers('node').filter(neighborOfStart =>
@@ -109,7 +109,7 @@ export const useMapPathStore = defineStore('mapPathStore', {
}
this.insightWithPath[INSIGHTS_FIELDS_AND_LABELS[i][0]][listIndex].nodes.push(curGraphNode);
this.insightWithPath[INSIGHTS_FIELDS_AND_LABELS[i][0]][listIndex].edges.push(curEdge);
// 特殊狀況在for迴圈之外額外插入最後一條線段
// Special case: append the last edge segment outside the loop
if (nodeIndex === curButton[listIndex].length - 1) {
const endNode = curGraphNode.outgoers('node').filter(neighbor =>
neighbor.data('label').toLocaleLowerCase() === 'end'
@@ -142,14 +142,14 @@ export const useMapPathStore = defineStore('mapPathStore', {
depthFirstSearchCreatePath(node, currentPathByNode, curPathByEdge) {
const outgoingEdges = node.outgoers('edge');
if (outgoingEdges.length === 0) {
// 表示已經遇到尾聲
// Reached the end node
this.allPaths.push([...currentPathByNode]);
this.allPathsByEdge.push([...curPathByEdge])
} else {
outgoingEdges.targets().forEach((targetNode) => {
if (!currentPathByNode.includes(targetNode)) {
const connectingEdge = targetNode.edgesWith(currentPathByNode[currentPathByNode.length - 1]);
// 避免loop只有當目標節點不在當前路徑中之時才繼續
// Avoid loops: only continue if the target node is not already in the current path
this.depthFirstSearchCreatePath(targetNode, [...currentPathByNode, targetNode],
[...curPathByEdge, connectingEdge]
);
@@ -165,13 +165,13 @@ export const useMapPathStore = defineStore('mapPathStore', {
for (let whichPath = 0; whichPath < this.allPaths.length; whichPath++) {
const curPath = this.allPaths[whichPath];
const curPathByEdge = this.allPathsByEdge[whichPath];
// 針對這個path的第一個節點找到它在insights中是對應到哪一個起點
// For the first node in this path, find which insights starting point it corresponds to
for (let i = 0; i < INSIGHTS_FIELDS_AND_LABELS.length; i++) {
const curButton = this.insights[INSIGHTS_FIELDS_AND_LABELS[i][0]];
for (let listIndex = 0; listIndex < curButton.length; listIndex++) {
for (let nodeIndex = 0; nodeIndex < curButton[listIndex].length; nodeIndex++) {
if (curPath[1].data('label') === curButton[listIndex][nodeIndex]) {
// 從 1 開始而不是從 0 開始是因為 0 的label是start字串
// Start from index 1 instead of 0 because index 0 is the "start" label
const matchResult = this.depthFirstSearchMatchTwoPaths(curPath, 1, curButton, listIndex, nodeIndex)
if (matchResult) {
this.mapGraphPathToInsight[i] = {
@@ -189,39 +189,39 @@ export const useMapPathStore = defineStore('mapPathStore', {
} // end first for
},
depthFirstSearchMatchTwoPaths(curPath, curPathIndex, curButton, listIndex, nodeIndex) {
if (listIndex >= curButton.length) { // 邊界條件檢查,防止超出範圍
return; // nodeIndex表示是當選訂了五顆按鈕之一之後清單上的第幾個path
if (listIndex >= curButton.length) { // Bounds check to prevent out-of-range access
return; // nodeIndex is the path index in the list after selecting one of the five buttons
}
if (nodeIndex >= curButton[listIndex]) { // 邊界條件檢查,防止超出範圍
return; // 表示清單上這個path上的第幾個節點
if (nodeIndex >= curButton[listIndex]) { // Bounds check to prevent out-of-range access
return; // The node index within this path in the list
}
// 如果 `curPath` `curButton[listIndex]` 完全匹配
// If `curPath` and `curButton[listIndex]` fully match
if (curPathIndex === curPath.length || nodeIndex === curButton[listIndex].length) {
return true;
}
// 邊界條件檢查,防止超出範圍
// Bounds check to prevent out-of-range access
if (curPathIndex >= curPath.length || nodeIndex >= curButton[listIndex].length) {
return;
}
const nodeLabel = curPath[curPathIndex].data('label');
// 如果當前節點匹配
// If the current node matches
if (nodeLabel === curButton[listIndex][nodeIndex]) {
if (nodeIndex === curButton[listIndex].length - 1) {
return true; // Reach
}
//從以下兩個選項選出答案可能是true的。但也可能答案都是false
// 選項一是遞增insights的第一層的指標。這裡必須遞增path的指標
// Pick the option that may return true from the two below; both could be false.
// Option 1: increment the insights first-level index; must also increment the path index.
if (this.depthFirstSearchMatchTwoPaths(curPath, curPathIndex + 1, curButton, listIndex + 1, nodeIndex)) {
return true;
}
// 選項二是遞增insights的第一層的指標。這裡必須遞增path的指標
// Option 2: increment the insights second-level index; must also increment the path index.
if (this.depthFirstSearchMatchTwoPaths(curPath, curPathIndex + 1, curButton, listIndex, nodeIndex + 1)) {
return true;
}
}
return false; // 當前節點不匹配時返回 false
return false; // Return false when the current node does not match
},
highlightClickedPath(clickedActiveTraceIndex: number, clickedPathListIndex: number) {
this.insightWithPath[INSIGHTS_FIELDS_AND_LABELS[clickedActiveTraceIndex][0]][clickedPathListIndex].edges.forEach(edgeToHighlight => {