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

@@ -323,7 +323,7 @@ onMounted(async () => {
});
</script>
<style>
/*為了讓 radio 按鈕可以置中,所以讓欄位的文字也置中 */
/* Center column text so that radio buttons are also centered */
.header-center .p-column-header-content{
justify-content: center;
}

View File

@@ -52,7 +52,7 @@
:class="{
'h-[100px]': whichCurrentModal !== MODAL_CREATE_NEW,
}"
> <!-- 這裡會根據究竟是create彈窗還是edit彈窗來改變Password的排版 -->
> <!-- Layout of Password field changes depending on whether this is the create or edit modal -->
<span class="align-right-span flex h-full w-[122px] justify-end"
:class="{
'pt-[12px]': whichCurrentModal !== MODAL_CREATE_NEW,
@@ -100,7 +100,7 @@
</div>
</div>
<div v-show="false" id="confirm_pwd_row" class="input-row w-full grid grid-cols-2 grid-cols-[122px_1fr] gap-x-4
mb-4 flex items-center"> <!-- 2-by-2 的格子其中左下角是一個dummy格子其沒有內容 -->
mb-4 flex items-center"> <!-- 2-by-2 grid; the bottom-left cell is a dummy cell with no content -->
<span v-show="false" class="field-label w-[122px] text-sm flex items-center justify-end text-right font-medium mr-4 whitespace-nowrap"
:class="{
'text-[#000000]': isPwdMatched,
@@ -125,14 +125,14 @@
@click="togglePwdConfirmEyeBtn" alt="eye"/>
<img v-else src='@/assets/icon-eye-hide.svg' class="absolute right-[8px] cursor-pointer" @click="togglePwdConfirmEyeBtn" alt="eye"/>
</div>
<div class="dummy-grid h-[24px]"></div> <!-- 透過 dummy-grid 來撐起高度-->
<div class="dummy-grid h-[24px]"></div> <!-- Use dummy-grid to maintain the height -->
<div class="error-msg-section flex justify-start">
<img v-show="!isPwdMatched" src="@/assets/icon-alert.svg" alt="!" class="exclamation-img flex mr-2">
<span class="error-msg-text flex text-[#FF3366] h-[24px]">
{{ isPwdMatched ? "" : i18next.t("AcctMgmt.PwdNotMatch") }}
</span>
</div>
<div class="dummy-grid h-[24px]"></div> <!-- 透過 dummy-grid 來撐起高度-->
<div class="dummy-grid h-[24px]"></div> <!-- Use dummy-grid to maintain the height -->
<div class="error-msg-section flex justify-start">
<img v-show="!isPwdLengthValid" src="@/assets/icon-alert.svg" alt="!" class="exclamation-img flex mr-2">
<span class="error-msg-text flex text-[#FF3366] h-[24px]">
@@ -142,7 +142,7 @@
</div>
<div v-if="whichCurrentModal === MODAL_CREATE_NEW" class="checkbox-row w-full grid-cols-[122px_1fr] gap-x-4 flex py-2 h-[40px] my-4 items-center">
<div class="dummy field-label flex items-center w-[122px]">
</div> <!--這裡也使用了dummy欄位去撐起空間-->
</div> <!-- A dummy field is also used here to maintain spacing -->
<section id="account_create_checkboxes_section" class="flex flex-col">
<div class="checkbox-and-text flex">
<IconChecked :isChecked="isSetAsAdminChecked" @click="toggleIsAdmin"/>
@@ -229,7 +229,7 @@ const inputPwd = ref("");
const isAccountUnique = ref(true);
const isEditable = ref(true);
// 自從加入這段 watch 之後,填寫密碼欄位之時,就不會胡亂清空掉 account 或是 full name 欄位了。
// Since adding this watch, filling in the password field no longer clears the account or full name fields.
watch(whichCurrentModal, (newVal) => {
if (newVal === MODAL_CREATE_NEW) {
inputUserAccount.value = '';
@@ -253,7 +253,7 @@ const validatePwdLength = () => {
}
const onInputDoubleClick = () => {
// 允許編輯模式
// Enable edit mode
isEditable.value = true;
}
@@ -288,8 +288,8 @@ const onConfirmBtnClick = async () => {
if(!isAccountUnique.value) {
return;
}
// 要注意的是舊的username跟新的username可以是不同的
// 區分有無傳入密碼的情況
// Note that the old username and new username can be different
// Distinguish between cases with and without a password
if(isResetPwdSectionShow.value) {
await acctMgmtStore.editAccount(
currentViewingUser.value.username, {
@@ -315,7 +315,7 @@ const onConfirmBtnClick = async () => {
}
const checkAccountIsUnique = async() => {
// 如果使用者沒有更動過欄位那就不用調用任何後端的API
// If the user has not modified the field, no backend API call is needed
if(inputUserAccount.value === username.value) {
return true;
}
@@ -344,14 +344,14 @@ const onInputNameFocus = () => {
const onResetPwdButtonClick = () => {
isResetPwdSectionShow.value = !isResetPwdSectionShow.value;
// 必須清空密碼欄位輸入的字串
// Must clear the password input field
inputPwd.value = '';
}
watch(
[inputPwd, inputUserAccount, inputName],
([newPwd, newAccount, newName]) => {
// 只要[確認密碼]或[密碼]欄位有更動且所有欄位都不是空的confirm 按鈕就可點選
// Enable the confirm button when all fields are non-empty
if(newAccount.length > 0 && newName.length > 0) {
isConfirmDisabled.value = false;
}
@@ -374,6 +374,6 @@ function onCancelBtnClick(){
<style>
#modal_account_edit {
background-color: #ffffff;
backdrop-filter: opacity(1); /*防止子元件繼承父元件的透明度 */
backdrop-filter: opacity(1); /* Prevent child elements from inheriting parent's opacity */
}
</style>

View File

@@ -273,7 +273,7 @@ function handleClick(tagId) {
if (isSafeTagId(tagId)) {
window.location.href = tagId;
} else {
console.warn("不安全的 tagId: ", tagId);
console.warn("Unsafe tagId: ", tagId);
}
}
@@ -1258,7 +1258,7 @@ function getAvgProcessTimeHorizontalBarChart(chartData, content, isSingle, xUnit
{ [secondaryTypeParam]: secondaryId }
];
// 取得 Compare Data
// Fetch Compare Data
await compareStore.getCompare(queryParams);
avgProcessTimeByTaskHeight.value = getHorizontalBarHeight(compareDashboardData.value.time.avg_process_time_by_task);
if(compareDashboardData.value.time.avg_waiting_time_by_edge !== null) {

View File

@@ -42,19 +42,19 @@ x: {
},
ticks: {
display: true,
maxRotation: 0, // 不旋轉 lable 0~50
maxRotation: 0, // Do not rotate labels (range: 0~50)
color: '#64748b',
source: 'labels', // 依比例彈性顯示 label 數量
source: 'labels', // Dynamically display label count proportionally
},
border: {
color: '#64748b',
},
grid: {
tickLength: 0, // 網格是否超過邊線
tickLength: 0, // Prevent grid lines from extending beyond the axis
}
},
y: {
beginAtZero: true, // scale 包含 0
beginAtZero: true, // Scale includes 0
title: {
display: true,
color: '#334155',
@@ -71,7 +71,7 @@ y: {
color: '#64748b',
},
border: {
display: false, // 隱藏左側多出來的線
display: false, // Hide the extra border line on the left side
},
},
};
@@ -128,7 +128,7 @@ const customizeScaleChartOptionTicks = (scaleObjectToAlter, ticksOfXAxis) => {
ticks: {
...scaleObjectToAlter.x.ticks,
callback: function(value, index) {
// 根據不同的級距客製化 x 軸的時間刻度
// Customize x-axis time ticks based on different intervals
return ticksOfXAxis[index];
},
},
@@ -188,8 +188,8 @@ const getLineChartPrimeVueSetting = (chartData, content, pageName) => {
let primeVueSetData = {};
let primeVueSetOption = {};
// 考慮 chartData.data 的dimension
// 當我們遇到了 Compare 頁面的案例
// Consider the dimension of chartData.data
// When handling the Compare page case
if(pageName === "Compare"){
datasetsPrimary = chartData.data[0].data;
datasetsSecondary = chartData.data[1].data;
@@ -199,7 +199,7 @@ const getLineChartPrimeVueSetting = (chartData, content, pageName) => {
label: chartData.data[0].label,
data: datasetsPrimary,
fill: false,
tension: 0, // 貝茲曲線張力
tension: 0, // Bezier curve tension
borderColor: colorPrimary,
pointBackgroundColor: colorPrimary,
},
@@ -207,7 +207,7 @@ const getLineChartPrimeVueSetting = (chartData, content, pageName) => {
label: chartData.data[1].label,
data: datasetsSecondary,
fill: false,
tension: 0, // 貝茲曲線張力
tension: 0, // Bezier curve tension
borderColor: colorSecondary,
pointBackgroundColor: colorSecondary,
}
@@ -220,7 +220,7 @@ const getLineChartPrimeVueSetting = (chartData, content, pageName) => {
label: content.title,
data: datasets,
fill: false,
tension: 0, // 貝茲曲線張力
tension: 0, // Bezier curve tension
borderColor: '#0099FF',
}
];
@@ -254,17 +254,17 @@ const getLineChartPrimeVueSetting = (chartData, content, pageName) => {
}
},
plugins: {
legend: false, // 圖例
legend: false, // Legend
tooltip: {
displayColors: true,
titleFont: {weight: 'normal'},
callbacks: {
label: function(tooltipItem) {
// 取得數據
// Get the data
const label = tooltipItem.dataset.label || '';
// 建立一個小方塊顯示顏色
return `${label}: ${tooltipItem.parsed.y}`; // 使用 Unicode 方塊表示顏色
// Build the tooltip label with dataset color indicator
return `${label}: ${tooltipItem.parsed.y}`; // Use Unicode block to represent color
},
},
},
@@ -275,9 +275,9 @@ const getLineChartPrimeVueSetting = (chartData, content, pageName) => {
scales: customizedScaleOption,
};
primeVueSetOption.scales.y.ticks.precision = 0; // y 軸顯示小數點後 0 位
primeVueSetOption.scales.y.ticks.precision = 0; // Show 0 decimal places on y-axis
primeVueSetOption.scales.y.ticks.callback = function (value, index, ticks) {
return value; //這裡的Y軸刻度沒有後綴代表時間的英文字母
return value; // Y-axis ticks here have no time unit suffix
};
primeVueSetDataState.value = primeVueSetData;
primeVueSetOptionsState.value = primeVueSetOption;

View File

@@ -269,7 +269,7 @@ function handleClick(tagId) {
if (isSafeTagId(tagId)) {
window.location.href = tagId;
} else {
console.warn("不安全的 tagId: ", tagId);
console.warn("Unsafe tagId: ", tagId);
}
}
@@ -918,7 +918,7 @@ function getAvgWaitingTimeLineChart(chartData, content, yUnit) {
id = file.parent.id;
}
// 取得 Performance Data
// Fetch Performance Data
await performanceStore.getPerformance(type, id);
if(!performanceData.value?.time) {
return;

View File

@@ -77,7 +77,7 @@
<!-- Recently Used -->
<section v-else>
<h2 class="h-12 font-bold py-4 mb-4 border-b border-neutral-500">Recently Used</h2>
<!-- card group 最多六個-->
<!-- card group, up to six items -->
<ul class="flex justify-start items-center gap-4 overflow-x-auto w-full h-[184px] scrollbar pb-4">
<!-- card item v-for -->
<li class="w-[216px] min-w-[216px] h-full p-4 border rounded border-neutral-300 hover:bg-primary/10 hover:border-primary duration-300 flex flex-col justify-between cursor-pointer" v-for="(file, index) in recentlyUsedFiles.slice(0, 6)" :key="file.id" @dblclick="enterDiscover(file)" :title="file.name" @contextmenu="onRightClick($event, file)" >
@@ -485,13 +485,13 @@
async function deleteFile(type, id, name, source) {
let srt = '';
let data = [];
// 判斷是否來自 hover icon 選單
// Check if the action comes from the hover icon menu
if(type && id && name && source === 'list-hover') {
selectedType.value = type;
selectedId.value = id;
selectedName.value = name;
}
// 取得相依性檔案
// Fetch dependent files
await store.getDependents(selectedType.value, selectedId.value);
if(dependentsData.value.length !== 0) {
data = [...dependentsData.value];
@@ -591,7 +591,7 @@
function getGridSortData(event) {
const code = event.value.code;
// 文字排序: 將 name 字段轉換為小寫進行比較,使用 localeCompare() 方法進行字母順序比較
// Text sorting: convert the name field to lowercase for comparison using localeCompare()
switch (code) {
case 'nameAscending':
compareData.value = compareData.value.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()));
@@ -628,7 +628,7 @@
const clickedLi = e.target.closest('li');
if(!clickedLi || !clickedLi.id.startsWith('li')) isActive.value = null;
});
// 為 DataTable tbody 加入 .scrollbar 選擇器
// Add the .scrollbar class to the DataTable tbody
const tbodyElement = document.querySelector('.p-datatable-tbody');
tbodyElement?.classList.add('scrollbar');
isLoading.value = false;

View File

@@ -102,8 +102,8 @@ function onInputPwdFocus(){
}
// Created logic
// 考慮到使用者可能在未登入的情況下貼入一個頁面網址連結過來瀏覽器
// btoa: 對字串進行 Base64 編碼
// Handle the case where a user pastes a page URL into the browser without being logged in
// btoa: Base64-encode the string
if(route.query['return-to']) {
setRememberedReturnToUrl(route.query['return-to']);
}

View File

@@ -33,7 +33,7 @@ import { leaveFilter, leaveConformance } from "@/module/alertModal.js";
import emitter from "@/utils/emitter";
export default {
// 重新整理畫面以及第一次進入網頁時beforeRouteEnter這個hook會被執行然而beforeRouteUpdate不會被執行
// When the page is refreshed or entered for the first time, beforeRouteEnter is executed, but beforeRouteUpdate is not
// PSEUDOCODE
// if (not logged in) {
// if (has refresh token) {
@@ -85,9 +85,9 @@ export default {
pageAdminStore.setPreviousPage(from.name);
// 離開 Map 頁時判斷是否有無資料和需要存檔
// When leaving the Map page, check if there is unsaved data
if ((from.name === 'Map' || from.name === 'CheckMap') && allMapDataStore.tempFilterId) {
// 傳給 Map,通知 Sidebar 要關閉。
// Notify the Map's Sidebar to close
emitter.emit('leaveFilter', false);
leaveFilter(next, allMapDataStore.addFilterId, to.path)
} else if((from.name === 'Conformance' || from.name === 'CheckConformance')

View File

@@ -94,7 +94,7 @@ import { useFilesStore } from '@/stores/files';
export default {
beforeRouteEnter(to, from, next){
// 要有 uploadID 才能進來
// An uploadID is required to enter this page
next(vm => {
const filesStore = useFilesStore();
if(filesStore.uploadId === null) {
@@ -128,7 +128,7 @@ const tooltipUpload = {
4. Timestamp: The time of occurrence of a particular event, such as the start or end of an activity.
5. Status: Activity status, such as Start or Complete.
6. Attribute: A property that can be associated with a case to provide additional information about that case.`,
// 暫時沒有 Resource
// Resource is not available yet
// 7. Resource: A resource refers to any entity that is required to carry out a business process. This can include people, equipment, software, or any other type of asset.
class: '!max-w-[400px] !text-[10px] !opacity-80',
autoHide: false,
@@ -141,7 +141,7 @@ const columnType = [
{ name: 'Activity*', code: 'name', color: '!text-secondary', value: '', label: 'Activity', required: true },
{ name: 'Activity Instance ID*', code: 'instance', color: '!text-secondary', value: '', label: 'Activity Instance ID', required: true },
{ name: 'Case Attribute', code: 'case_attributes', color: '!text-primary', value: '', label: 'Case Attribute', required: false },
// { name: 'Resource', code: '', color: '', value: '', label: 'Resource', required: false }, // 現階段沒有,未來可能有
// { name: 'Resource', code: '', color: '', value: '', label: 'Resource', required: false }, // Not available yet; may be added in the future
{ name: 'Not Assigned', code: '', color: '!text-neutral-700', value: '', label: 'Not Assigned', required: false },
];
@@ -153,8 +153,8 @@ const showEdit = ref(false);
// Computed
const isDisabled = computed(() => {
// 1. 長度一樣,強制每一個都要選
// 2. 不為 null undefind
// 1. Length must match; every column must be assigned
// 2. Must not be null or undefined
const hasValue = !selectedColumns.value.includes(undefined);
const result = !(selectedColumns.value.length === uploadDetail.value?.columns.length
&& informData.value.length === 0 && repeatedData.value.length === 0 && hasValue);
@@ -203,7 +203,7 @@ function onInput(e) {
* @returns {number} The text width in pixels.
*/
function getTextWidth(text, e) {
// 替換空格為不斷行的空格
// Replace spaces with non-breaking spaces
const processedText = text.replace(/ /g, '\u00a0');
const hiddenSpan = document.createElement('span');
@@ -223,24 +223,24 @@ function getTextWidth(text, e) {
*/
function updateValidationData(data) {
const nameOccurrences = {};
const noSortedRepeatedData = []; // 未排序的重複選擇的 data
const selectedData = [] // 已經選擇的 data
const noSortedRepeatedData = []; // Unsorted duplicate selections
const selectedData = [] // Already selected data
informData.value = []; // 尚未選擇的 data
repeatedData.value = []; // 重複選擇的 data
informData.value = []; // Not yet selected data
repeatedData.value = []; // Duplicate selections
data.forEach(item => {
const { name, code } = item;
if(nameOccurrences[name]) {
// 'Not Assigned''Case Attribute' 不列入驗證
// 'Not Assigned' and 'Case Attribute' are excluded from validation
if(!code || code === 'case_attributes') return;
nameOccurrences[name]++;
// 重複的選項只出現一次
// Each duplicate option should only appear once
if(nameOccurrences[name] === 2){
noSortedRepeatedData.push(item)
}
// 要按照選單的順序排序
// Sort according to the dropdown menu order
repeatedData.value = columnType.filter(column => noSortedRepeatedData.includes(column));
}else {
nameOccurrences[name] = 1;
@@ -252,13 +252,13 @@ function updateValidationData(data) {
/** Resets all column selections to default. */
function reset() {
// 路徑不列入歷史紀錄
// Do not add to browser history
selectedColumns.value = [];
}
/** Navigates back to the Files page without uploading. */
function cancel() {
// 路徑不列入歷史紀錄
// Do not add to browser history
router.push({name: 'Files', replace: true});
}
@@ -273,7 +273,7 @@ async function submit() {
status: '',
case_attributes: []
};
// 給值
// Assign values
const haveValueData = selectedColumns.value.map((column, i) => {
if (column && uploadDetail.value.columns[i]) {
return {
@@ -285,9 +285,9 @@ async function submit() {
}
});
// 取得欲更改的檔名,
// Get the desired file name to change
uploadFileName.value = fileName.value;
// 設定第二階段上傳的 data
// Set the data for the second-stage upload
haveValueData.forEach(column => {
if(column !== undefined) {
switch (column.code) {
@@ -319,7 +319,7 @@ async function submit() {
// Mounted
onMounted(async () => {
// 只監聯第一次
// Watch only once
const unwatch = watch(fileName, (newValue) => {
if (newValue) {
const inputElement = document.getElementById('fileNameInput');
@@ -332,13 +332,13 @@ onMounted(async () => {
);
showEdit.value = true;
if(uploadId.value) await filesStore.getUploadDetail();
selectedColumns.value = Array.from({ length: uploadDetail.value.columns.length }, () => columnType[columnType.length - 1]); // 預設選 Not Assigned
selectedColumns.value = Array.from({ length: uploadDetail.value.columns.length }, () => columnType[columnType.length - 1]); // Default to "Not Assigned"
unwatch();
isLoading.value = false;
});
onBeforeUnmount(() => {
// 離開頁面要刪 uploadID
// Clear uploadID when leaving the page
uploadId.value = null;
uploadFileName.value = null;
});