Kaynağa Gözat

feat: 提取硬编码

Jack Zhou 2 hafta önce
ebeveyn
işleme
9641c81039
100 değiştirilmiş dosya ile 2370 ekleme ve 1097 silme
  1. 0 12
      src/App.vue
  2. 7 4
      src/components/AIRobot/src/AIRobot.vue
  3. 55 53
      src/components/AddRules/src/AddRules.vue
  4. 14 12
      src/components/AddRules/src/components/DelayedType.vue
  5. 16 14
      src/components/AddRules/src/components/ETDShipments.vue
  6. 31 27
      src/components/AddRules/src/components/NotiFrequency.vue
  7. 12 8
      src/components/AddRules/src/components/NotiMethods.vue
  8. 15 12
      src/components/AddRules/src/components/ShipmentRange.vue
  9. 7 5
      src/components/AutoComplete/src/AutoComplete.vue
  10. 4 1
      src/components/AutoSelect/src/AutoSelect.vue
  11. 5 3
      src/components/ContainerStatus/src/ContainerStatus.vue
  12. 70 68
      src/components/CreateAddRules/src/CreateAddRules.vue
  13. 14 12
      src/components/CreateAddRules/src/components/DelayedType.vue
  14. 16 14
      src/components/CreateAddRules/src/components/ETDShipments.vue
  15. 30 26
      src/components/CreateAddRules/src/components/NotiFrequency.vue
  16. 13 9
      src/components/CreateAddRules/src/components/NotiMethods.vue
  17. 72 64
      src/components/CreateAddRules/src/components/ShipmentRange.vue
  18. 14 18
      src/components/CustomizeColumns/src/CustomizeColumns.vue
  19. 11 8
      src/components/DateRange/src/DateRange.vue
  20. 10 8
      src/components/DateRange/src/components/CalendarDate.vue
  21. 12 10
      src/components/DateRange/src/components/QuickCalendarDate.vue
  22. 4 2
      src/components/DateRange/src/components/QuickMonth.vue
  23. 10 8
      src/components/DateRange/src/components/VCalendarDate.vue
  24. 16 1
      src/components/FliterTags/src/FilterTags.vue
  25. 31 14
      src/components/MoreFilters/src/MoreFilters.vue
  26. 20 4
      src/components/MoreFilters/src/components/PartiesView.vue
  27. 16 3
      src/components/MoreFilters/src/components/PlacesView.vue
  28. 23 8
      src/components/MoreFilters/src/components/SelectValue.vue
  29. 5 2
      src/components/NotificationMessageCard/src/NotificationMessageCard.vue
  30. 11 8
      src/components/NotificationMessageCard/src/components/EventCard.vue
  31. 6 3
      src/components/NotificationMessageCard/src/components/FeatureUpdateCard.vue
  32. 3 1
      src/components/NotificationMessageCard/src/components/PasswordCard.vue
  33. 4 2
      src/components/ScoringGrade/components/DialogColorful.vue
  34. 21 4
      src/components/ScoringGrade/components/DialogUe.vue
  35. 23 21
      src/components/ScoringGrade/src/ScoringGrade.vue
  36. 3 1
      src/components/SeeAllIcon/src/SeeAllIcon.vue
  37. 9 7
      src/components/SelectTable/src/SelectTable.vue
  38. 7 4
      src/components/SelectTableSelect/src/SelectTableSelect.vue
  39. 3 1
      src/components/ShipmentStatus/src/ShipmentStatus.vue
  40. 3 1
      src/components/TableImgEmpty/TableImgEmpty.vue
  41. 11 8
      src/components/TransportMode/src/TransportMode.vue
  42. 4 2
      src/components/VBox/src/VBox.vue
  43. 3 1
      src/components/VBox_Dashboard/src/VBox_Dashboard.vue
  44. 7 5
      src/components/VBreadcrumb/src/VBreadcrumb.vue
  45. 5 2
      src/components/VDriverGuide/src/VDriverGuide.vue
  46. 5 3
      src/components/VEmpty/src/VEmpty.vue
  47. 3 1
      src/components/VLoading/src/VLoading.vue
  48. 8 6
      src/components/VSliderVerification/src/VSliderVerification.vue
  49. 7 4
      src/components/selectAutoSelect/src/selectAutoSelect.vue
  50. 3 0
      src/directive/VLoading.ts
  51. 908 2
      src/locales/en.json
  52. 5 3
      src/locales/index.ts
  53. 21 0
      src/locales/zh-cn.json
  54. 2 0
      src/main.ts
  55. 13 10
      src/views/AIApiLog/src/AIApiLog.vue
  56. 6 3
      src/views/AIApiLog/src/components/LogDialog.vue
  57. 8 5
      src/views/AIApiLog/src/components/TableView/src/TableView.vue
  58. 10 7
      src/views/AIApiLog/src/components/TableView/src/components/DownloadDialog.vue
  59. 17 14
      src/views/AIRobotChat/src/AIRobotChat.vue
  60. 4 1
      src/views/AIRobotChat/src/components/AIQuestions.vue
  61. 13 10
      src/views/Booking/src/BookingView.vue
  62. 12 10
      src/views/Booking/src/components/BookingDetail/src/BookingDetail.vue
  63. 21 8
      src/views/Booking/src/components/BookingDetail/src/components/AddReferenceDialog.vue
  64. 36 33
      src/views/Booking/src/components/BookingDetail/src/components/BasicInformation.vue
  65. 4 1
      src/views/Booking/src/components/BookingDetail/src/components/ContainersView.vue
  66. 10 7
      src/views/Booking/src/components/BookingDetail/src/components/EmailView.vue
  67. 18 17
      src/views/Booking/src/components/BookingGuide.vue
  68. 12 10
      src/views/Booking/src/components/BookingTable/src/BookingTable.vue
  69. 10 7
      src/views/Booking/src/components/BookingTable/src/components/DownloadDialog.vue
  70. 34 19
      src/views/ChatLog/src/ChatLog.vue
  71. 8 5
      src/views/ChatLog/src/components/TableView/src/TableView.vue
  72. 10 7
      src/views/ChatLog/src/components/TableView/src/components/DownloadDialog.vue
  73. 21 21
      src/views/Dashboard/src/DashboardView.vue
  74. 12 9
      src/views/Dashboard/src/components/CustomerFilter.vue
  75. 11 8
      src/views/Dashboard/src/components/DashFiters.vue
  76. 9 6
      src/views/Dashboard/src/components/DashboardGuide.vue
  77. 9 6
      src/views/Dashboard/src/components/RecentStatus.vue
  78. 3 1
      src/views/Dashboard/src/components/RevenueChart.vue
  79. 49 41
      src/views/Dashboard/src/components/ScoringSystem.vue
  80. 7 5
      src/views/DestinationDelivery/src/DestinationDelivery.vue
  81. 11 6
      src/views/DestinationDelivery/src/components/CalendarTagDetailDialog.vue
  82. 22 22
      src/views/DestinationDelivery/src/components/CalendarView.vue
  83. 8 6
      src/views/DestinationDelivery/src/components/ConfiguRations/src/ConfiguRations.vue
  84. 9 9
      src/views/DestinationDelivery/src/components/ConfiguRations/src/components/ConfigurationsTable.vue
  85. 27 29
      src/views/DestinationDelivery/src/components/ConfiguRations/src/components/CreateNewRule.vue
  86. 29 26
      src/views/DestinationDelivery/src/components/ConfiguRations/src/components/RecommendDate.vue
  87. 6 3
      src/views/DestinationDelivery/src/components/ConfiguRations/src/components/SelectStation.vue
  88. 4 1
      src/views/DestinationDelivery/src/components/ConfiguRations/src/components/SelectValue.vue
  89. 39 37
      src/views/DestinationDelivery/src/components/ConfiguRations/src/components/SetBookingWindow.vue
  90. 77 72
      src/views/DestinationDelivery/src/components/CreateNewBooking/src/CreateNewbooking.vue
  91. 16 13
      src/views/DestinationDelivery/src/components/ListView.vue
  92. 8 5
      src/views/DestinationDelivery/src/components/TableView/src/TableView.vue
  93. 12 10
      src/views/DestinationDelivery/src/components/TableView/src/components/BookingDetailDialog.vue
  94. 12 9
      src/views/DestinationDelivery/src/components/TableView/src/components/DownloadDialog.vue
  95. 11 8
      src/views/DestinationDelivery/src/components/TableView/src/components/EmailDialog.vue
  96. 24 13
      src/views/DestinationDelivery/src/components/TableView/src/components/TipsDialog.vue
  97. 8 5
      src/views/Layout/src/components/Header/HeaderView.vue
  98. 20 17
      src/views/Layout/src/components/Header/components/ChangePasswordDialog.vue
  99. 8 4
      src/views/Layout/src/components/Header/components/DownloadKLNPortal.vue
  100. 14 11
      src/views/Layout/src/components/Header/components/KAMMapping.vue

+ 0 - 12
src/App.vue

@@ -29,18 +29,6 @@ const languageChangetest = (label: string) => {
 }
 
 languageChangetest(langValue.value)
-
-const langData = ref({})
-onMounted(async () => {
-  try {
-    const response = await fetch('./locales/en.json')
-    console.log('Fetch response:', response)
-    langData.value = await response.json()
-    console.log('Fetch response:', langData.value)
-  } catch (error) {
-    console.error('Error fetching the JSON file:', error)
-  }
-})
 </script>
 
 <template>

+ 7 - 4
src/components/AIRobot/src/AIRobot.vue

@@ -1,4 +1,7 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
 import { ref, onMounted } from 'vue'
 import normalPng from '../image/icon_ai_robot24_b@2x.png'
 import emitter from '@/utils/bus'
@@ -155,14 +158,14 @@ defineExpose({
       <div class="AIAvator">
         <img width="40px" src="../image/icon_ai_robot36_b@2x.png" />
       </div>
-      <div class="dialogue_title">Hi! I'm your Freight Assistant, always on call</div>
+      <div class="dialogue_title">{{ t('aiRobot.greeting') }}</div>
     </div>
     <div class="flex_end">
       <div class="dialogue_content" style="box-shadow: -10px 10px 24px rgba(58, 0, 78, 0.15)">
         <div class="dialogue_content_title">
           <div class="dialogue_title_left">
             <img src="../image/icon_faq_b@2x.png" width="24px" />
-            Frequently Asked Questions
+            {{ t('aiRobot.frequentlyAskedQuestions') }}
           </div>
         </div>
         <el-carousel class="carousel" :autoplay="false" height="190px" style="width: 452px">
@@ -194,7 +197,7 @@ defineExpose({
       </div>
     </div>
     <div class="dialogue_title" style="margin-bottom: 0">
-      Hi! I'm your Freight Assistant, always on call
+      {{ t('aiRobot.greeting') }}
     </div>
   </div>
   <!-- 悬浮icon -->
@@ -212,7 +215,7 @@ defineExpose({
         />
       </template>
       <!-- hover时显示的对话框 -->
-      <div v-if="AIRobotHoverVisible" class="AIRobot_dialog">Continue the conversation</div>
+      <div v-if="AIRobotHoverVisible" class="AIRobot_dialog">{{ t('aiRobot.continueConversation') }}</div>
     </el-popover>
   </div>
 </template>

+ 55 - 53
src/components/AddRules/src/AddRules.vue

@@ -1,5 +1,6 @@
 <script lang="ts" setup>
 import { ref, watch } from 'vue'
+import { useI18n } from 'vue-i18n'
 import RulesShipments from './components/RulesShipments.vue'
 import AddedrluesTag from './components/AddedrluesTag.vue'
 import DelayedType from './components/DelayedType.vue'
@@ -8,6 +9,7 @@ import NotiFrequency from './components/NotiFrequency.vue'
 import NotiMethods from './components/NotiMethods.vue'
 import submitsucessful from './images/icon_success_big@2x.png'
 import moment from 'moment-timezone'
+const { t } = useI18n()
 interface CheckboxItem {
   value: string
   label: string
@@ -394,13 +396,13 @@ const Savesubscribe = () => {
       MilMethodsList.value.length == 0
     ) {
       if (OceanCheckList.value.length == 0 && AirCheckList.value.length == 0) {
-        missingmessage.value += 'Select Milestone, '
+        missingmessage.value += t('notificationRules.selectMilestone') + ', '
       }
       if (MilFrequencyList.value.length == 0) {
-        missingmessage.value += 'Notification Frequency, '
+        missingmessage.value += t('notificationRules.notificationFrequency') + ', '
       }
       if (MilMethodsList.value.length == 0 || MilMethodsList.value == undefined) {
-        missingmessage.value += 'Notification Method, '
+        missingmessage.value += t('notificationRules.notificationMethod') + ', '
       }
       missingmessage.value = missingmessage.value.substring(0, missingmessage.value.length - 2)
       UnableSaveVisible.value = true
@@ -431,13 +433,13 @@ const Savesubscribe = () => {
       ConMethodsList.value.length == 0
     ) {
       if (ContainerOceanList.value.length == 0) {
-        missingmessage.value += 'Container Status, '
+        missingmessage.value += t('notificationRules.containerStatus') + ', '
       }
       if (ConFrequencyList.value.length == 0) {
-        missingmessage.value += 'Notification Frequency, '
+        missingmessage.value += t('notificationRules.notificationFrequency') + ', '
       }
       if (ConMethodsList.value.length == 0 || ConMethodsList.value == undefined) {
-        missingmessage.value += 'Notification Method, '
+        missingmessage.value += t('notificationRules.notificationMethod') + ', '
       }
       missingmessage.value = missingmessage.value.substring(0, missingmessage.value.length - 2)
       UnableSaveVisible.value = true
@@ -456,13 +458,13 @@ const Savesubscribe = () => {
       DepMethodsList.value.length == 0
     ) {
       if (DelayedDeparturedList.value.length == 0 && DelayedAirdList.value.length == 0) {
-        missingmessage.value += 'Select Delayed Type, '
+        missingmessage.value += t('notificationRules.selectDelayedType') + ', '
       }
       if (DepFrequencyList.value.length == 0) {
-        missingmessage.value += 'Notification Frequency, '
+        missingmessage.value += t('notificationRules.notificationFrequency') + ', '
       }
       if (DepMethodsList.value.length == 0 || DepMethodsList.value == undefined) {
-        missingmessage.value += 'Notification Method, '
+        missingmessage.value += t('notificationRules.notificationMethod') + ', '
       }
       missingmessage.value = missingmessage.value.substring(0, missingmessage.value.length - 2)
       UnableSaveVisible.value = true
@@ -491,13 +493,13 @@ const Savesubscribe = () => {
       ETDMethodsList.value.length == 0
     ) {
       if (ETDOceanList.value.length == 0 && ETDAirList.value.length == 0) {
-        missingmessage.value += 'Select Time Type, '
+        missingmessage.value += t('notificationRules.selectTimeType') + ', '
       }
       if (ETDFrequencyList.value.length == 0) {
-        missingmessage.value += 'Notification Frequency, '
+        missingmessage.value += t('notificationRules.notificationFrequency') + ', '
       }
       if (ETDMethodsList.value.length == 0 || ETDMethodsList.value == undefined) {
-        missingmessage.value += 'Notification Method, '
+        missingmessage.value += t('notificationRules.notificationMethod') + ', '
       }
       missingmessage.value = missingmessage.value.substring(0, missingmessage.value.length - 2)
       UnableSaveVisible.value = true
@@ -593,12 +595,12 @@ defineExpose({
                     ></use>
                   </svg>
                 </span>
-                <span class="stars_red">*</span>Select Milestone
+                <span class="stars_red">*</span>{{ t('notificationRules.selectMilestone') }}
               </div>
             </template>
             <div>
               <RulesShipments
-                Title="Ocean Shipments"
+                :Title="t('notificationRules.oceanShipments')"
                 ref="MilOceanref"
                 @ChangeCheckRules="ChangeCheckOceanRules"
                 :CheckboxList="MilestoneOceanListInit"
@@ -607,7 +609,7 @@ defineExpose({
             </div>
             <div>
               <RulesShipments
-                Title="Air Shipments"
+                :Title="t('notificationRules.airShipments')"
                 ref="MilAirref"
                 @ChangeCheckRules="ChangeCheckAirRules"
                 :CheckboxList="MilestoneAirListInit"
@@ -631,12 +633,12 @@ defineExpose({
                     ></use>
                   </svg>
                 </span>
-                <span class="stars_red">*</span>Select Container Status
+                <span class="stars_red">*</span>{{ t('notificationRules.selectContainerStatus') }}
               </div>
             </template>
             <div>
               <RulesShipments
-                Title="Ocean Shipments"
+                :Title="t('notificationRules.oceanShipments')"
                 ref="ContainerOcean"
                 @ChangeCheckRules="ChangeContainerRules"
                 :CheckboxList="ContainerOceanListInit"
@@ -660,12 +662,12 @@ defineExpose({
                     ></use>
                   </svg>
                 </span>
-                <span class="stars_red">*</span>Select Delayed Type
+                <span class="stars_red">*</span>{{ t('notificationRules.selectDelayedType') }}
               </div>
             </template>
             <div>
               <DelayedType
-                Title="Ocean Shipments"
+                :Title="t('notificationRules.oceanShipments')"
                 ref="OceanDelayed"
                 :DelayedData="DelayedDataInit"
                 @ChangeCheckRules="ChangeDeayedRules"
@@ -673,7 +675,7 @@ defineExpose({
             </div>
             <div>
               <DelayedType
-                Title="Air Shipments"
+                :Title="t('notificationRules.airShipments')"
                 ref="AirDelayed"
                 :DelayedData="DelayedDataInitAir"
                 @ChangeCheckRules="ChangeAirRules"
@@ -696,12 +698,12 @@ defineExpose({
                     ></use>
                   </svg>
                 </span>
-                <span class="stars_red">*</span>Select Time Type
+                <span class="stars_red">*</span>{{ t('notificationRules.selectTimeType') }}
               </div>
             </template>
             <div>
               <ETDShipments
-                Title="Ocean Shipments"
+                :Title="t('notificationRules.oceanShipments')"
                 ref="OceanETD"
                 :ETDData="OceanETDInit"
                 @ChangeCheckRules="ChangeETDOceanRules"
@@ -709,7 +711,7 @@ defineExpose({
             </div>
             <div>
               <ETDShipments
-                Title="Air Shipments"
+                :Title="t('notificationRules.airShipments')"
                 ref="AirETD"
                 :ETDData="AirETDInit"
                 @ChangeCheckRules="ChangeETDAirRules"
@@ -732,7 +734,7 @@ defineExpose({
                     ></use>
                   </svg>
                 </span>
-                <span class="stars_red">*</span>Notification Frequency
+                <span class="stars_red">*</span>{{ t('notificationRules.notificationFrequency') }}
               </div>
             </template>
             <NotiFrequency
@@ -776,7 +778,7 @@ defineExpose({
                     ></use>
                   </svg>
                 </span>
-                <span class="stars_red">*</span>Notification Method
+                <span class="stars_red">*</span>{{ t('notificationRules.notificationMethod') }}
               </div>
             </template>
             <NotiMethods
@@ -808,125 +810,125 @@ defineExpose({
       </div>
     </div>
     <div class="Rules_right">
-      <div class="right_Title">Added Rules</div>
+      <div class="right_Title">{{ t('notificationRules.addedRules') }}</div>
       <AddedrluesTag
         :CheckedList="DelayedDeparturedList"
         v-if="props.TitleType == 'Departure'"
         @handleCloseRadio="handleCloseDelayed"
-        Title="Ocean Shipments"
+        :Title="t('notificationRules.oceanShipments')"
       ></AddedrluesTag>
       <AddedrluesTag
         v-if="props.TitleType == 'Milestone'"
         :CheckedList="OceanCheckList"
         @handleCloseRadio="handleCloseMilestoneOcean"
-        Title="Ocean Shipments"
+        :Title="t('notificationRules.oceanShipments')"
       ></AddedrluesTag>
       <AddedrluesTag
         v-if="props.TitleType == 'Container'"
         :CheckedList="ContainerOceanList"
         @handleCloseRadio="handleCloseContainer"
-        Title="Ocean Shipments"
+        :Title="t('notificationRules.oceanShipments')"
       ></AddedrluesTag>
       <AddedrluesTag
         :CheckedList="ETDOceanList"
         v-if="props.TitleType == 'ETDChange'"
-        Title="Ocean Shipments"
+        :Title="t('notificationRules.oceanShipments')"
         @handleCloseRadio="closeOceanETD"
       ></AddedrluesTag>
       <AddedrluesTag
         :CheckedList="ETDAirList"
         v-if="props.TitleType == 'ETDChange'"
         @handleCloseRadio="closeAirETD"
-        Title="Air Shipments"
+        :Title="t('notificationRules.airShipments')"
       ></AddedrluesTag>
       <AddedrluesTag
         :CheckedList="DelayedAirdList"
         v-if="props.TitleType == 'Departure'"
         @handleCloseRadio="handleCloseAirDelayed"
-        Title="Air Shipments"
+        :Title="t('notificationRules.airShipments')"
       ></AddedrluesTag>
       <AddedrluesTag
         v-if="props.TitleType == 'Milestone'"
         @handleCloseRadio="handleCloseMilestoneAir"
         :CheckedList="AirCheckList"
-        Title="Air Shipments"
+        :Title="t('notificationRules.airShipments')"
       ></AddedrluesTag>
       <AddedrluesTag
         v-if="props.TitleType == 'Milestone'"
         :CheckedList="MilFrequencyList"
-        Title="Notification Frequency"
+        :Title="t('notificationRules.notificationFrequency')"
         @handleCloseRadio="handleCloseRadio('Mil')"
       ></AddedrluesTag>
       <AddedrluesTag
         v-if="props.TitleType == 'Container'"
         :CheckedList="ConFrequencyList"
-        Title="Notification Frequency"
+        :Title="t('notificationRules.notificationFrequency')"
         @handleCloseRadio="handleCloseRadio('Con')"
       ></AddedrluesTag>
       <AddedrluesTag
         v-if="props.TitleType == 'Departure'"
         :CheckedList="DepFrequencyList"
-        Title="Notification Frequency"
+        :Title="t('notificationRules.notificationFrequency')"
         @handleCloseRadio="handleCloseRadio('Dep')"
       ></AddedrluesTag>
       <AddedrluesTag
         v-if="props.TitleType == 'ETDChange'"
         :CheckedList="ETDFrequencyList"
-        Title="Notification Frequency"
+        :Title="t('notificationRules.notificationFrequency')"
         @handleCloseRadio="handleCloseRadio('ETD')"
       ></AddedrluesTag>
       <AddedrluesTag
         v-if="props.TitleType == 'Milestone'"
         :CheckedList="MilMethodsList"
-        Title="Notification Method"
+        :Title="t('notificationRules.notificationMethod')"
       ></AddedrluesTag>
       <AddedrluesTag
         v-if="props.TitleType == 'Container'"
         :CheckedList="ConMethodsList"
-        Title="Notification Method"
+        :Title="t('notificationRules.notificationMethod')"
       ></AddedrluesTag>
       <AddedrluesTag
         v-if="props.TitleType == 'Departure'"
         :CheckedList="DepMethodsList"
-        Title="Notification Method"
+        :Title="t('notificationRules.notificationMethod')"
       ></AddedrluesTag>
       <AddedrluesTag
         v-if="props.TitleType == 'ETDChange'"
         :CheckedList="ETDMethodsList"
-        Title="Notification Method"
+        :Title="t('notificationRules.notificationMethod')"
       ></AddedrluesTag>
     </div>
   </div>
   <div class="Rules_buttom">
-    <el-button class="el-button--dark rules_button" @click="Savesubscribe">Save</el-button>
+    <el-button class="el-button--dark rules_button" @click="Savesubscribe">{{ t('common.save') }}</el-button>
     <el-button @click="CancelRulesVisible = true" class="rules_button" type="default"
-      >Cancel</el-button
+      >{{ t('common.cancel') }}</el-button
     >
     <!-- 取消保存 -->
     <el-dialog v-model="CancelRulesVisible" width="480">
-      <div>You have unsaved changes.</div>
-      <div>Are you sure you want to leave this page?</div>
+      <div>{{ t('destinationDelivery.unsavedChanges') }}</div>
+      <div>{{ t('destinationDelivery.confirmLeavePage') }}</div>
       <template #footer>
         <div class="dialog-footer">
           <el-button type="default" @click="CancelRulesVisible = false" style="width: 100px"
-            >Cancel</el-button
+            >{{ t('common.cancel') }}</el-button
           >
           <el-button class="el-button--warning" @click="UnsavedCollapse" style="width: 100px">
-            OK
+            {{ t('common.ok') }}
           </el-button>
         </div>
       </template>
       <template #header>
         <div class="warning-header dialog-header">
           <span class="font_family icon-icon_fail_fill_b"></span>
-          Unsaved Changes
+          {{ t('destinationDelivery.unsavedChangesTitle') }}
         </div>
       </template>
     </el-dialog>
     <!-- 保存失败 -->
     <el-dialog v-model="UnableSaveVisible" width="480">
-      <div>{{ missingmessage }} missing.</div>
-      <div>Please complete all required fields.</div>
+      <div>{{ missingmessage }} {{ t('notificationRules.missing') }}.</div>
+      <div>{{ t('destinationDelivery.completeRequiredFields') }}</div>
       <template #footer>
         <div class="dialog-footer">
           <el-button
@@ -934,21 +936,21 @@ defineExpose({
             @click="UnableSaveVisible = false"
             style="width: 100px"
           >
-            OK
+            {{ t('common.ok') }}
           </el-button>
         </div>
       </template>
       <template #header>
         <div class="unable-save-header dialog-header">
           <span class="font_family icon-icon_fail_fill_b"></span>
-          Unable to Save
+          {{ t('destinationDelivery.unableToSave') }}
         </div>
       </template>
     </el-dialog>
     <!-- 保存成功 -->
     <el-dialog v-model="SaveedVisible" width="320" style="height: 212px">
       <div style="text-align: center"><el-image :src="submitsucessful" style="width: 64px" /></div>
-      <div style="text-align: center; margin-top: 20px">Saved successfully</div>
+      <div style="text-align: center; margin-top: 20px">{{ t('common.saveSuccess') }}</div>
     </el-dialog>
   </div>
 </template>

+ 14 - 12
src/components/AddRules/src/components/DelayedType.vue

@@ -1,5 +1,7 @@
 <script lang="ts" setup>
 import { ref, computed, watch } from 'vue'
+import { useI18n } from 'vue-i18n'
+const { t } = useI18n()
 
 const props = defineProps({
   Title: String,
@@ -216,9 +218,9 @@ defineExpose({
         <div class="oceanCheckbox">
           <el-checkbox-group @change="CheckChange" v-model="OceanCheckedList">
             <el-checkbox @click="handleCheckboxClick($event)" class="delayedType" value="Departure Delayed">
-              <div class="titlecheckbox">Departure Delayed (ATD-ETD)</div>
+              <div class="titlecheckbox">{{ t('notificationRules.departureDelayedAtdEtd') }}</div>
               <div v-if="isDeparture" class="flex" style="margin-top: 16px">
-                <span class="delayedTitle">Delayed Time</span>
+                <span class="delayedTitle">{{ t('notificationRules.delayedTime') }}</span>
                 <span class="delayedIcon">≥</span>
                 <el-input
                   v-model="clampedValue"
@@ -229,12 +231,12 @@ defineExpose({
                   <template #append>
                     <el-select
                       v-model="DepartureSelect"
-                      placeholder="Select"
+                      :placeholder="t('destinationDelivery.select')"
                       class="arrivalselect"
                       @change="changedeparture('Departure')"
                     >
-                      <el-option label="Day(s)" value="Day(s)" />
-                      <el-option label="Hour(s)" value="Hour(s)" />
+                      <el-option :label="t('notificationRules.days')" value="Day(s)" />
+                      <el-option :label="t('notificationRules.hours')" value="Hour(s)" />
                     </el-select>
                   </template>
                 </el-input>
@@ -246,13 +248,13 @@ defineExpose({
                 >
                 </el-input>
                 <div
-                v-if="props.Title != 'Air Shipments'" class="Days">Day(s)</div>
+                v-if="props.Title != 'Air Shipments'" class="Days">{{ t('notificationRules.days') }}</div>
               </div>
             </el-checkbox>
             <el-checkbox class="delayedType" @click="handleCheckboxClick($event)"  value="Arrival Delayed (ATA-ETA)">
-              <div class="titlecheckbox">Arrival Delayed (ATA-ETA)</div>
+              <div class="titlecheckbox">{{ t('notificationRules.arrivalDelayedAtaEta') }}</div>
               <div v-if="isArrival" class="flex" style="margin-top: 16px">
-                <span class="delayedTitle">Delayed Time</span>
+                <span class="delayedTitle">{{ t('notificationRules.delayedTime') }}</span>
                 <span class="delayedIcon">≥</span>
                 <el-input
                   v-model="clampedArrivalValue"
@@ -263,12 +265,12 @@ defineExpose({
                   <template #append>
                     <el-select
                       v-model="ArrivalSelect"
-                      placeholder="Select"
+                      :placeholder="t('destinationDelivery.select')"
                       class="arrivalselect"
                       @change="changedeparture('Arrival')"
                     >
-                      <el-option label="Day(s)" value="Day(s)" />
-                      <el-option  label="Hour(s)" value="Hour(s)" />
+                      <el-option :label="t('notificationRules.days')" value="Day(s)" />
+                      <el-option :label="t('notificationRules.hours')" value="Hour(s)" />
                     </el-select>
                   </template>
                 </el-input>
@@ -279,7 +281,7 @@ defineExpose({
                   class="input-with-select1"
                 >
                 </el-input>
-                <div v-if="props.Title != 'Air Shipments'" class="Days">Day(s)</div>
+                <div v-if="props.Title != 'Air Shipments'" class="Days">{{ t('notificationRules.days') }}</div>
               </div>
             </el-checkbox>
           </el-checkbox-group>

+ 16 - 14
src/components/AddRules/src/components/ETDShipments.vue

@@ -1,5 +1,7 @@
 <script lang="ts" setup>
 import { ref, computed, watch } from 'vue'
+import { useI18n } from 'vue-i18n'
+const { t } = useI18n()
 
 const props = defineProps({
   Title: String,
@@ -311,13 +313,13 @@ const clampedETAValue = computed({
         <div class="oceanCheckbox">
           <el-checkbox-group @change="CheckChange" v-model="OceanCheckedList">
             <el-checkbox @click="handleCheckboxClick($event)" class="delayedType" value="ETD">
-              <div class="titlecheckbox">ETD</div>
+              <div class="titlecheckbox">{{ t('common.etd') }}</div>
               <div v-if="isETD" style="margin-top: 16px">
                 <el-radio-group class="radiocheckbox" v-model="ETDRadio" @change="changeETDRadio">
-                  <el-radio value="1">Notify for all changes</el-radio>
+                  <el-radio value="1">{{ t('notificationRules.notifyAllChanges') }}</el-radio>
                   <el-radio value="2">
                     <div class="flex">
-                      Notify only when time difference
+                      {{ t('notificationRules.notifyOnlyWhenTimeDifference') }}
                       <span class="delayedIcon">≥</span>
                       <el-input
                       v-if="props.Title == 'Air Shipments'"
@@ -328,12 +330,12 @@ const clampedETAValue = computed({
                         <template #append>
                           <el-select
                             v-model="ETDSelect"
-                            placeholder="Select"
+                            :placeholder="t('destinationDelivery.select')"
                             class="arrivalselect"
                             @change="changedeparture('ETD')"
                           >
-                            <el-option label="Day(s)" value="Day(s)" />
-                            <el-option label="Hour(s)" value="Hour(s)" />
+                            <el-option :label="t('notificationRules.days')" value="Day(s)" />
+                            <el-option :label="t('notificationRules.hours')" value="Hour(s)" />
                           </el-select>
                         </template>
                       </el-input>
@@ -345,20 +347,20 @@ const clampedETAValue = computed({
                       >
                       </el-input>
                       <div
-                      v-if="props.Title != 'Air Shipments'" class="Days">Day(s)</div>
+                      v-if="props.Title != 'Air Shipments'" class="Days">{{ t('notificationRules.days') }}</div>
                     </div>
                   </el-radio>
                 </el-radio-group>
               </div>
             </el-checkbox>
             <el-checkbox @click="handleCheckboxClick($event)" class="delayedType" value="ETA">
-              <div class="titlecheckbox">ETA</div>
+              <div class="titlecheckbox">{{ t('common.eta') }}</div>
               <div v-if="isETA" style="margin-top: 16px">
                 <el-radio-group class="radiocheckbox" v-model="ETARadio" @change="changeETARadio">
-                  <el-radio value="1">Notify for all changes</el-radio>
+                  <el-radio value="1">{{ t('notificationRules.notifyAllChanges') }}</el-radio>
                   <el-radio value="2">
                     <div class="flex">
-                      Notify only when time difference
+                      {{ t('notificationRules.notifyOnlyWhenTimeDifference') }}
                       <span class="delayedIcon">≥</span>
                       <el-input
                       v-if="props.Title == 'Air Shipments'"
@@ -369,12 +371,12 @@ const clampedETAValue = computed({
                         <template #append>
                           <el-select
                             v-model="ETASelect"
-                            placeholder="Select"
+                            :placeholder="t('destinationDelivery.select')"
                             class="arrivalselect"
                             @change="changedeparture('ETA')"
                           >
-                            <el-option label="Day(s)" value="Day(s)" />
-                            <el-option label="Hour(s)" value="Hour(s)" />
+                            <el-option :label="t('notificationRules.days')" value="Day(s)" />
+                            <el-option :label="t('notificationRules.hours')" value="Hour(s)" />
                           </el-select>
                         </template>
                       </el-input>
@@ -386,7 +388,7 @@ const clampedETAValue = computed({
                       >
                       </el-input>
                       <div
-                      v-if="props.Title != 'Air Shipments'" class="Days">Day(s)</div>
+                      v-if="props.Title != 'Air Shipments'" class="Days">{{ t('notificationRules.days') }}</div>
                     </div>
                   </el-radio>
                 </el-radio-group>

+ 31 - 27
src/components/AddRules/src/components/NotiFrequency.vue

@@ -1,6 +1,10 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
 import { ref, watch } from 'vue'
 import moment from 'moment-timezone'
+const WEEK_DAYS = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
 
 const props = defineProps({
   FrequencyData: Object
@@ -14,31 +18,31 @@ const WeeklyTime = ref('')
 const WeeklyDay = ref('')
 const WeekDay = ref([
   {
-    label: 'Monday',
+    label: t('notificationRules.monday'),
     value: 'Monday'
   },
   {
-    label: 'Tuesday',
+    label: t('notificationRules.tuesday'),
     value: 'Tuesday'
   },
   {
-    label: 'Wednesday',
+    label: t('notificationRules.wednesday'),
     value: 'Wednesday'
   },
   {
-    label: 'Thursday',
+    label: t('notificationRules.thursday'),
     value: 'Thursday'
   },
   {
-    label: 'Friday',
+    label: t('notificationRules.friday'),
     value: 'Friday'
   },
   {
-    label: 'Saturday',
+    label: t('notificationRules.saturday'),
     value: 'Saturday'
   },
   {
-    label: 'Sunday',
+    label: t('notificationRules.sunday'),
     value: 'Sunday'
   }
 ])
@@ -214,19 +218,19 @@ const ChangeFrequency = (val: any) => {
       FrequencyList.value.push(str)
     }
     savesubscribeobj.frequency_type = 'Weekly'
-    if (WeeklyDay.value == 'Monday') {
+    if (WeeklyDay.value == WEEK_DAYS[0]) {
       savesubscribeobj.weekly_week = 1
-    } else if (WeeklyDay.value == 'Tuesday') {
+    } else if (WeeklyDay.value == WEEK_DAYS[1]) {
       savesubscribeobj.weekly_week = 2
-    } else if (WeeklyDay.value == 'Wednesday') {
+    } else if (WeeklyDay.value == WEEK_DAYS[2]) {
       savesubscribeobj.weekly_week = 3
-    } else if (WeeklyDay.value == 'Thursday') {
+    } else if (WeeklyDay.value == WEEK_DAYS[3]) {
       savesubscribeobj.weekly_week = 4
-    } else if (WeeklyDay.value == 'Friday') {
+    } else if (WeeklyDay.value == WEEK_DAYS[4]) {
       savesubscribeobj.weekly_week = 5
-    } else if (WeeklyDay.value == 'Saturday') {
+    } else if (WeeklyDay.value == WEEK_DAYS[5]) {
       savesubscribeobj.weekly_week = 6
-    } else if (WeeklyDay.value == 'Sunday') {
+    } else if (WeeklyDay.value == WEEK_DAYS[6]) {
       savesubscribeobj.weekly_week = 0
     }
     savesubscribeobj.weekly_time = WeeklyTime.value
@@ -274,13 +278,13 @@ defineExpose({
   <div style="margin-top: 11px">
     <el-radio-group v-model="radio" @change="ChangeFrequency">
       <el-radio :value="1" v-if="user_type != null && user_type != 'customer'"
-        >Instant notification for each update</el-radio
+        >{{ t('notificationRules.instantNotificationEachUpdate') }}</el-radio
       >
       <el-radio :value="2">
-        <div>Daily Summary</div>
+        <div>{{ t('notificationRules.dailySummary') }}</div>
         <div class="Daily" v-if="isDaily">
           <div class="Daily_left" style="margin-right: 8px">
-            Select Time
+            {{ t('notificationRules.selectTime') }}
             <div>
               <el-time-select
                 v-model="DailyTime"
@@ -289,16 +293,16 @@ defineExpose({
                 end="23:30"
                 prefix-icon=""
                 @change="changeTime('Daily')"
-                placeholder="Select Time"
+                :placeholder="t('notificationRules.selectTime')"
               ></el-time-select>
             </div>
           </div>
           <div class="Daily_left">
-            Select Time Zone
+            {{ t('notificationRules.selectTimeZone') }}
             <div>
               <el-select
                 v-model="TimeZoneDailySelect"
-                placeholder="Select Time Zone"
+                :placeholder="t('notificationRules.selectTimeZone')"
                 @change="changeTime('Daily')"
               >
                 <el-option
@@ -313,15 +317,15 @@ defineExpose({
         </div>
       </el-radio>
       <el-radio :value="3">
-        <div>Weekly Summary</div>
+        <div>{{ t('notificationRules.weeklySummary') }}</div>
         <div class="Daily" v-if="isWeekly">
           <div class="Weekly_left">
-            Select Day
+            {{ t('notificationRules.selectDay') }}
             <div>
               <el-select
                 v-model="WeeklyDay"
                 @change="changeTime('Weekly')"
-                placeholder="Select Day"
+                :placeholder="t('notificationRules.selectDay')"
               >
                 <el-option
                   v-for="item in WeekDay"
@@ -333,7 +337,7 @@ defineExpose({
             </div>
           </div>
           <div class="Weekly_left" style="margin: 0 8px">
-            Select Time
+            {{ t('notificationRules.selectTime') }}
             <div>
               <el-time-select
                 v-model="WeeklyTime"
@@ -342,16 +346,16 @@ defineExpose({
                 step="00:30"
                 end="23:30"
                 prefix-icon=""
-                placeholder="Select time"
+                :placeholder="t('notificationRules.selectTime')"
               ></el-time-select>
             </div>
           </div>
           <div class="Weekly_left">
-            Select Time Zone
+            {{ t('notificationRules.selectTimeZone') }}
             <div>
               <el-select
                 v-model="TimeZoneWeeklySelect"
-                placeholder="Select Time Zone"
+                :placeholder="t('notificationRules.selectTimeZone')"
                 @change="changeTime('Weekly')"
               >
                 <el-option

+ 12 - 8
src/components/AddRules/src/components/NotiMethods.vue

@@ -1,10 +1,14 @@
 <script setup lang="ts">
 import { ref, watch, computed } from 'vue'
+import { useI18n } from 'vue-i18n'
 import Light_methods from '../images/illustration_system massage@2x.png'
 import Dark_methods from '../images/illustration_system massage_darkmode@2x.png'
 import { useThemeStore } from '@/stores/modules/theme'
 
 const themeStore = useThemeStore()
+const { t } = useI18n()
+const METHOD_BY_EMAIL = 'By Email'
+const METHOD_BY_SYSTEM_MESSAGE = 'By System Message'
 
 const checkMethodList = ref()
 checkMethodList.value = []
@@ -30,13 +34,13 @@ const MethodsInit = () => {
   checkMethodList.value = []
   if (methods_data.value?.method_display != undefined) {
     if (methods_data.value?.method_display.indexOf('Email') != -1) {
-      checkMethodList.value.push('By Email')
+      checkMethodList.value.push(METHOD_BY_EMAIL)
       savesubscribeobj.method_by_email = true
     }
   }
   if (methods_data.value?.method_display != undefined) {
     if (methods_data.value?.method_display.indexOf('System Message') != -1) {
-      checkMethodList.value.push('By System Message')
+      checkMethodList.value.push(METHOD_BY_SYSTEM_MESSAGE)
       savesubscribeobj.method_by_message = true
     }
   }
@@ -45,12 +49,12 @@ const MethodsInit = () => {
 
 // 选中Method
 const changeMethod = (val: any) => {
-  if (val.indexOf('By Email') != -1) {
+  if (val.indexOf(METHOD_BY_EMAIL) != -1) {
     savesubscribeobj.method_by_email = true
   } else {
     savesubscribeobj.method_by_email = false
   }
-  if (val.indexOf('By System Message') != -1) {
+  if (val.indexOf(METHOD_BY_SYSTEM_MESSAGE) != -1) {
     savesubscribeobj.method_by_message = true
   } else {
     savesubscribeobj.method_by_message = false
@@ -77,12 +81,12 @@ defineExpose({
   <div style="margin-top: 11px">
     <div class="Method">
       <el-checkbox-group v-model="checkMethodList" @change="changeMethod">
-        <el-checkbox class="methodcheckbox" value="By Email">
-          <div>By Email</div>
+        <el-checkbox class="methodcheckbox" :value="METHOD_BY_EMAIL">
+          <div>{{ t('notificationRules.byEmail') }}</div>
           <div class="methos_image"><img src="../images/illustration_email@2x.png" /></div>
         </el-checkbox>
-        <el-checkbox class="methodcheckbox" value="By System Message">
-          <div>By System Message</div>
+        <el-checkbox class="methodcheckbox" :value="METHOD_BY_SYSTEM_MESSAGE">
+          <div>{{ t('notificationRules.bySystemMessage') }}</div>
           <div class="methos_image">
             <img :src="MethodsImg" />
           </div>

+ 15 - 12
src/components/AddRules/src/components/ShipmentRange.vue

@@ -1,5 +1,8 @@
 <script lang="ts" setup>
 import { ref, computed } from 'vue'
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
 
 const OceanActive = ref(['TransportMode', 'Time'])
 const TransportCheckedList = ref([])
@@ -10,15 +13,15 @@ interface OceanItem {
 const TransportList = ref<OceanItem[]>([])
 TransportList.value = [
   {
-    label: 'Ocean',
+    label: t('notificationRules.ocean'),
     value: 'Ocean'
   },
   {
-    label: 'Air',
+    label: t('notificationRules.air'),
     value: 'Air'
   },
   {
-    label: 'Road',
+    label: t('notificationRules.road'),
     value: 'Road'
   }
 ]
@@ -54,7 +57,7 @@ let Timestr: any = ''
 const emit = defineEmits(['ChangeCheckRules', 'ChangeCheckTimeRules'])
 const CheckChange = (val: any) => {
   if (val != '') {
-    Transportstr = 'Transport Mode: ' + val
+    Transportstr = `${t('common.transportMode')}: ${val}`
   } else {
     Transportstr = ''
   }
@@ -65,13 +68,13 @@ const CheckChange = (val: any) => {
 const changeTime = (val: any) => {
   if (val == 1) {
     if(clampedETDValue.value != '' && clampedETDValue.value != undefined) {
-      Timestr = 'ETD within ' + clampedETDValue.value + ' Day(s)'
+      Timestr = `ETD ${t('notificationRules.within')} ${clampedETDValue.value} ${t('notificationRules.days')}`
     } else{
       Timestr = ''
     }
   } else if (val == 2) {
     if(clampedETAValue.value != '' && clampedETAValue.value != undefined) {
-      Timestr = 'ETA within ' + clampedETAValue.value + ' Day(s)'
+      Timestr = `ETA ${t('notificationRules.within')} ${clampedETAValue.value} ${t('notificationRules.days')}`
     } else{
       Timestr = ''
     }
@@ -100,7 +103,7 @@ defineExpose({
     <el-collapse v-model="OceanActive">
       <el-collapse-item name="TransportMode">
         <template #title>
-          <div class="Rules_Title OceanTitle">Transport Mode</div>
+          <div class="Rules_Title OceanTitle">{{ t('common.transportMode') }}</div>
         </template>
         <div class="oceanCheckbox">
           <el-checkbox-group @change="CheckChange" v-model="TransportCheckedList">
@@ -115,32 +118,32 @@ defineExpose({
       </el-collapse-item>
       <el-collapse-item name="Time">
         <template #title>
-          <div class="Rules_Title OceanTitle">Time</div>
+          <div class="Rules_Title OceanTitle">{{ t('notificationRules.time') }}</div>
         </template>
         <div class="oceanCheckbox">
           <el-radio-group v-model="TimeChecked" @change="changeTime">
             <el-radio value="1">
               <div class="flex">
-                ETD within
+                ETD {{ t('notificationRules.within') }}
                 <el-input
                   @input="changeTime('1')"
                   v-model="clampedETDValue"
                   class="input-with-select"
                 >
                 </el-input>
-                <div class="Days">Day(s)</div>
+                <div class="Days">{{ t('notificationRules.days') }}</div>
               </div>
             </el-radio>
             <el-radio value="2">
               <div class="flex">
-                ETA within
+                ETA {{ t('notificationRules.within') }}
                 <el-input
                   @input="changeTime('2')"
                   v-model="clampedETAValue"
                   class="input-with-select"
                 >
                 </el-input>
-                <div class="Days">Day(s)</div>
+                <div class="Days">{{ t('notificationRules.days') }}</div>
               </div>
             </el-radio>
           </el-radio-group>

+ 7 - 5
src/components/AutoComplete/src/AutoComplete.vue

@@ -1,6 +1,8 @@
 <script setup lang="ts" name="SelTable">
 import _ from 'lodash'
 import { reactive, ref, onMounted, watch } from 'vue'
+import { useI18n } from 'vue-i18n'
+const { t } = useI18n()
 
 const emit = defineEmits(['check', 'input'])
 const props = defineProps({
@@ -147,7 +149,7 @@ const handleCurrentChange = (val: number) => {}
       <template #reference>
         <el-input
           v-model="searchVal"
-          placeholder="Please input country/city/uncode"
+          :placeholder="t('common.inputCountryCityUncode')"
           @input="handleSearch"
           @click="handleSearch"
         >
@@ -164,12 +166,12 @@ const handleCurrentChange = (val: number) => {}
         @row-click="handleRowClick"
         header-row-class-name="cus-header"
       >
-        <el-table-column width="120" property="Country" label="Country" />
-        <el-table-column width="120" property="City" label="City" />
-        <el-table-column min-width="120" property="Uncode" label="Uncode" />
+        <el-table-column width="120" property="Country" :label="t('common.country')" />
+        <el-table-column width="120" property="City" :label="t('common.city')" />
+        <el-table-column min-width="120" property="Uncode" :label="t('common.uncode')" />
       </el-table>
       <div class="pagination">
-        <span>Total {{ state.total }}</span>
+        <span>{{ t('common.total') }} {{ state.total }}</span>
         <el-pagination
           v-model:currentPage="state.currentPage"
           v-model:page-size="state.pageSize"

+ 4 - 1
src/components/AutoSelect/src/AutoSelect.vue

@@ -1,4 +1,7 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
 import { useFiltersStore } from '@/stores/modules/filtersList'
 import { cloneDeep, debounce } from 'lodash'
 import { useRoute } from 'vue-router'
@@ -75,7 +78,7 @@ const remoteMethod = (query: string) => {
       .catch((err) => {
         options.value = []
         if (!axios.isCancel(err) && !newController.signal.aborted) {
-          ElMessage.error('Failed to load options')
+          ElMessage.error(t('common.failedToLoadOptions'))
         }
       })
       .finally(() => {

+ 5 - 3
src/components/ContainerStatus/src/ContainerStatus.vue

@@ -4,9 +4,11 @@ import { useThemeStore } from '@/stores/modules/theme'
 import lightPng from './image/no_data.png'
 import darkPng from './image/no_data_dark.png'
 import { useUserStore } from '@/stores/modules/user'
+import { useI18n } from 'vue-i18n'
 
 const userStore = useUserStore()
 const themeStore = useThemeStore()
+const { t } = useI18n()
 
 const emptyImg = computed(() => {
   return themeStore.theme === 'dark' ? darkPng : lightPng
@@ -49,7 +51,7 @@ watch(
       >
         <template #title>
           <div class="title">
-            Container <span>{{ containers.label }}</span>
+            {{ t('containerStatus.container') }} <span>{{ containers.label }}</span>
           </div>
         </template>
         <div class="step-item" v-for="(item, index) in containers.content" :key="item.title">
@@ -69,10 +71,10 @@ watch(
     </el-collapse>
     <div v-else class="empty-content" style="">
       <img :src="emptyImg" :class="{ 'is-dark': themeStore.theme === 'dark' }" alt="empty" />
-      <div class="empty-text" style="">No data</div>
+      <div class="empty-text" style="">{{ t('common.noData') }}</div>
     </div>
     <div class="footer" v-if="props.website && userStore.userInfo?.user_type === 'employee'">
-      Tracking on carrier website:
+      {{ t('containerStatus.trackingOnCarrierWebsite') }}
       <a :href="props.website" target="_blank" class="link">{{ props.website }}</a>
     </div>
   </div>

+ 70 - 68
src/components/CreateAddRules/src/CreateAddRules.vue

@@ -1,5 +1,6 @@
 <script lang="ts" setup>
 import { ref, watch, onMounted } from 'vue'
+import { useI18n } from 'vue-i18n'
 import RulesShipments from './components/RulesShipments.vue'
 import AddedrluesTag from './components/AddedrluesTag.vue'
 import DelayedType from './components/DelayedType.vue'
@@ -10,6 +11,7 @@ import NotiMethods from './components/NotiMethods.vue'
 import submitsucessful from './images/icon_success_big@2x.png'
 import { useRouter } from 'vue-router'
 import moment from 'moment-timezone'
+const { t } = useI18n()
 
 const router = useRouter()
 interface CheckboxItem {
@@ -633,19 +635,19 @@ const Savesubscribe = () => {
       createObj.Timestr == ''
     ) {
       if (createObj.Transportstr == '') {
-        missingmessage.value += 'Transport Mode, '
+        missingmessage.value += t('common.transportMode') + ', '
       }
       if (createObj.Timestr == '') {
-        missingmessage.value += 'Time, '
+        missingmessage.value += t('notificationRules.time') + ', '
       }
       if (OceanCheckList.value.length == 0 && AirCheckList.value.length == 0) {
-        missingmessage.value += 'Select Milestone, '
+        missingmessage.value += t('notificationRules.selectMilestone') + ', '
       }
       if (MilFrequencyList.value.length == 0) {
-        missingmessage.value += 'Notification Frequency, '
+        missingmessage.value += t('notificationRules.notificationFrequency') + ', '
       }
       if (MilMethodsList.value.length == 0) {
-        missingmessage.value += 'Notification Method, '
+        missingmessage.value += t('notificationRules.notificationMethod') + ', '
       }
       missingmessage.value = missingmessage.value.substring(0, missingmessage.value.length - 2)
       UnableSaveVisible.value = true
@@ -677,19 +679,19 @@ const Savesubscribe = () => {
       createObj.Timestr == ''
     ) {
       if (createObj.Transportstr == '') {
-        missingmessage.value += 'Transport Mode, '
+        missingmessage.value += t('common.transportMode') + ', '
       }
       if (createObj.Timestr == '') {
-        missingmessage.value += 'Time, '
+        missingmessage.value += t('notificationRules.time') + ', '
       }
       if (ContainerOceanList.value.length == 0) {
-        missingmessage.value += 'Container Status, '
+        missingmessage.value += t('notificationRules.containerStatus') + ', '
       }
       if (ConFrequencyList.value.length == 0) {
-        missingmessage.value += 'Notification Frequency, '
+        missingmessage.value += t('notificationRules.notificationFrequency') + ', '
       }
       if (ConMethodsList.value.length == 0) {
-        missingmessage.value += 'Notification Method, '
+        missingmessage.value += t('notificationRules.notificationMethod') + ', '
       }
       missingmessage.value = missingmessage.value.substring(0, missingmessage.value.length - 2)
       UnableSaveVisible.value = true
@@ -710,19 +712,19 @@ const Savesubscribe = () => {
       createObj.Timestr == ''
     ) {
       if (createObj.Transportstr == '') {
-        missingmessage.value += 'Transport Mode, '
+        missingmessage.value += t('common.transportMode') + ', '
       }
       if (createObj.Timestr == '') {
-        missingmessage.value += 'Time, '
+        missingmessage.value += t('notificationRules.time') + ', '
       }
       if (DelayedDeparturedList.value.length == 0 && DelayedAirdList.value.length == 0) {
-        missingmessage.value += 'Select Delayed Shipments, '
+        missingmessage.value += t('notificationRules.selectDelayedShipments') + ', '
       }
       if (DepFrequencyList.value.length == 0) {
-        missingmessage.value += 'Notification Frequency, '
+        missingmessage.value += t('notificationRules.notificationFrequency') + ', '
       }
       if (DepMethodsList.value.length == 0) {
-        missingmessage.value += 'Notification Method, '
+        missingmessage.value += t('notificationRules.notificationMethod') + ', '
       }
       missingmessage.value = missingmessage.value.substring(0, missingmessage.value.length - 2)
       UnableSaveVisible.value = true
@@ -753,19 +755,19 @@ const Savesubscribe = () => {
       createObj.Timestr == ''
     ) {
       if (createObj.Transportstr == '') {
-        missingmessage.value += 'Transport Mode, '
+        missingmessage.value += t('common.transportMode') + ', '
       }
       if (createObj.Timestr == '') {
-        missingmessage.value += 'Time, '
+        missingmessage.value += t('notificationRules.time') + ', '
       }
       if (ETDOceanList.value.length == 0 && ETDAirList.value.length == 0) {
-        missingmessage.value += 'Select Time Type, '
+        missingmessage.value += t('notificationRules.selectTimeType') + ', '
       }
       if (ETDFrequencyList.value.length == 0) {
-        missingmessage.value += 'Notification Frequency, '
+        missingmessage.value += t('notificationRules.notificationFrequency') + ', '
       }
       if (ETDMethodsList.value.length == 0) {
-        missingmessage.value += 'Notification Method, '
+        missingmessage.value += t('notificationRules.notificationMethod') + ', '
       }
       missingmessage.value = missingmessage.value.substring(0, missingmessage.value.length - 2)
       UnableSaveVisible.value = true
@@ -851,7 +853,7 @@ defineExpose({
                     ></use>
                   </svg>
                 </span>
-                <span class="stars_red">*</span>Shipment Range
+                <span class="stars_red">*</span>{{ t('notificationRules.shipmentRange') }}
               </div>
             </template>
             <div>
@@ -901,12 +903,12 @@ defineExpose({
                     ></use>
                   </svg>
                 </span>
-                <span class="stars_red">*</span>Select Milestone
+                <span class="stars_red">*</span>{{ t('notificationRules.selectMilestone') }}
               </div>
             </template>
             <div>
               <RulesShipments
-                Title="Ocean Shipments"
+                :Title="t('notificationRules.oceanShipments')"
                 ref="MilOceanref"
                 @ChangeCheckRules="ChangeCheckOceanRules"
                 :CheckboxList="MilestoneOceanListInit"
@@ -915,7 +917,7 @@ defineExpose({
             </div>
             <div>
               <RulesShipments
-                Title="Air Shipments"
+                :Title="t('notificationRules.airShipments')"
                 ref="MilAirref"
                 @ChangeCheckRules="ChangeCheckAirRules"
                 :CheckboxList="MilestoneAirListInit"
@@ -939,12 +941,12 @@ defineExpose({
                     ></use>
                   </svg>
                 </span>
-                <span class="stars_red">*</span>Select Container Status
+                <span class="stars_red">*</span>{{ t('notificationRules.selectContainerStatus') }}
               </div>
             </template>
             <div>
               <RulesShipments
-                Title="Ocean Shipments"
+                :Title="t('notificationRules.oceanShipments')"
                 ref="ContainerOcean"
                 @ChangeCheckRules="ChangeContainerRules"
                 :CheckboxList="ContainerOceanListInit"
@@ -968,12 +970,12 @@ defineExpose({
                     ></use>
                   </svg>
                 </span>
-                <span class="stars_red">*</span>Select Delayed Type
+                <span class="stars_red">*</span>{{ t('notificationRules.selectDelayedType') }}
               </div>
             </template>
             <div>
               <DelayedType
-                Title="Ocean Shipments"
+                :Title="t('notificationRules.oceanShipments')"
                 ref="OceanDelayed"
                 :DelayedData="DelayedDataInit"
                 @ChangeCheckRules="ChangeDeayedRules"
@@ -981,7 +983,7 @@ defineExpose({
             </div>
             <div>
               <DelayedType
-                Title="Air Shipments"
+                :Title="t('notificationRules.airShipments')"
                 ref="AirDelayed"
                 :DelayedData="DelayedDataInitAir"
                 @ChangeCheckRules="ChangeAirRules"
@@ -1004,12 +1006,12 @@ defineExpose({
                     ></use>
                   </svg>
                 </span>
-                <span class="stars_red">*</span>Select Time Type
+                <span class="stars_red">*</span>{{ t('notificationRules.selectTimeType') }}
               </div>
             </template>
             <div>
               <ETDShipments
-                Title="Ocean Shipments"
+                :Title="t('notificationRules.oceanShipments')"
                 ref="OceanETD"
                 :ETDData="OceanETDInit"
                 @ChangeCheckRules="ChangeETDOceanRules"
@@ -1017,7 +1019,7 @@ defineExpose({
             </div>
             <div>
               <ETDShipments
-                Title="Air Shipments"
+                :Title="t('notificationRules.airShipments')"
                 ref="AirETD"
                 :ETDData="AirETDInit"
                 @ChangeCheckRules="ChangeETDAirRules"
@@ -1040,7 +1042,7 @@ defineExpose({
                     ></use>
                   </svg>
                 </span>
-                <span class="stars_red">*</span>Notification Frequency
+                <span class="stars_red">*</span>{{ t('notificationRules.notificationFrequency') }}
               </div>
             </template>
             <NotiFrequency
@@ -1084,7 +1086,7 @@ defineExpose({
                     ></use>
                   </svg>
                 </span>
-                <span class="stars_red">*</span>Notification Method
+                <span class="stars_red">*</span>{{ t('notificationRules.notificationMethod') }}
               </div>
             </template>
             <NotiMethods
@@ -1112,122 +1114,122 @@ defineExpose({
       </div>
     </div>
     <div class="Rules_right">
-      <div class="right_Title">Added Rules</div>
+      <div class="right_Title">{{ t('notificationRules.addedRules') }}</div>
       <AddedrluesTag
         v-if="props.TitleType == 'Milestone'"
         :CheckedList="createListMilestone"
-        Title="Shipment Range"
+        :Title="t('notificationRules.shipmentRange')"
         @handleCloseRadio="handleCloseCreateRule"
       ></AddedrluesTag>
       <AddedrluesTag
         v-if="props.TitleType == 'Container'"
         :CheckedList="createListContainer"
-        Title="Shipment Range"
+        :Title="t('notificationRules.shipmentRange')"
         @handleCloseRadio="handleCloseCreateRule"
       ></AddedrluesTag>
       <AddedrluesTag
         v-if="props.TitleType == 'Departure'"
         :CheckedList="createListDeparture"
-        Title="Shipment Range"
+        :Title="t('notificationRules.shipmentRange')"
         @handleCloseRadio="handleCloseCreateRule"
       ></AddedrluesTag>
       <AddedrluesTag
         v-if="props.TitleType == 'ETDChange'"
         :CheckedList="createListETDChange"
-        Title="Shipment Range"
+        :Title="t('notificationRules.shipmentRange')"
         @handleCloseRadio="handleCloseCreateRule"
       ></AddedrluesTag>
       <AddedrluesTag
         :CheckedList="DelayedDeparturedList"
         v-if="props.TitleType == 'Departure'"
         @handleCloseRadio="handleCloseDelayed"
-        Title="Ocean Shipments"
+        :Title="t('notificationRules.oceanShipments')"
       ></AddedrluesTag>
       <AddedrluesTag
         v-if="props.TitleType == 'Milestone'"
         @handleCloseRadio="handleCloseMilestoneOcean"
         :CheckedList="OceanCheckList"
-        Title="Ocean Shipments"
+        :Title="t('notificationRules.oceanShipments')"
       ></AddedrluesTag>
       <AddedrluesTag
         v-if="props.TitleType == 'Container'"
         :CheckedList="ContainerOceanList"
         @handleCloseRadio="handleCloseContainer"
-        Title="Ocean Shipments"
+        :Title="t('notificationRules.oceanShipments')"
       ></AddedrluesTag>
       <AddedrluesTag
         :CheckedList="ETDOceanList"
         v-if="props.TitleType == 'ETDChange'"
-        Title="Ocean Shipments"
+        :Title="t('notificationRules.oceanShipments')"
         @handleCloseRadio="closeOceanETD"
       ></AddedrluesTag>
       <AddedrluesTag
         :CheckedList="ETDAirList"
         v-if="props.TitleType == 'ETDChange'"
         @handleCloseRadio="closeAirETD"
-        Title="Air Shipments"
+        :Title="t('notificationRules.airShipments')"
       ></AddedrluesTag>
       <AddedrluesTag
         :CheckedList="DelayedAirdList"
         v-if="props.TitleType == 'Departure'"
         @handleCloseRadio="handleCloseAirDelayed"
-        Title="Air Shipments"
+        :Title="t('notificationRules.airShipments')"
       ></AddedrluesTag>
       <AddedrluesTag
         v-if="props.TitleType == 'Milestone'"
         :CheckedList="AirCheckList"
         @handleCloseRadio="handleCloseMilestoneAir"
-        Title="Air Shipments"
+        :Title="t('notificationRules.airShipments')"
       ></AddedrluesTag>
       <AddedrluesTag
         v-if="props.TitleType == 'Milestone'"
         :CheckedList="MilFrequencyList"
-        Title="Notification Frequency"
+        :Title="t('notificationRules.notificationFrequency')"
         @handleCloseRadio="handleCloseRadio('Mil')"
       ></AddedrluesTag>
       <AddedrluesTag
         v-if="props.TitleType == 'Container'"
         :CheckedList="ConFrequencyList"
-        Title="Notification Frequency"
+        :Title="t('notificationRules.notificationFrequency')"
         @handleCloseRadio="handleCloseRadio('Con')"
       ></AddedrluesTag>
       <AddedrluesTag
         v-if="props.TitleType == 'Departure'"
         :CheckedList="DepFrequencyList"
-        Title="Notification Frequency"
+        :Title="t('notificationRules.notificationFrequency')"
         @handleCloseRadio="handleCloseRadio('Dep')"
       ></AddedrluesTag>
       <AddedrluesTag
         v-if="props.TitleType == 'ETDChange'"
         :CheckedList="ETDFrequencyList"
-        Title="Notification Frequency"
+        :Title="t('notificationRules.notificationFrequency')"
         @handleCloseRadio="handleCloseRadio('ETD')"
       ></AddedrluesTag>
       <AddedrluesTag
         v-if="props.TitleType == 'Milestone'"
         :CheckedList="MilMethodsList"
-        Title="Notification Method"
+        :Title="t('notificationRules.notificationMethod')"
       ></AddedrluesTag>
       <AddedrluesTag
         v-if="props.TitleType == 'Container'"
         :CheckedList="ConMethodsList"
-        Title="Notification Method"
+        :Title="t('notificationRules.notificationMethod')"
       ></AddedrluesTag>
       <AddedrluesTag
         v-if="props.TitleType == 'Departure'"
         :CheckedList="DepMethodsList"
-        Title="Notification Method"
+        :Title="t('notificationRules.notificationMethod')"
       ></AddedrluesTag>
       <AddedrluesTag
         v-if="props.TitleType == 'ETDChange'"
         :CheckedList="ETDMethodsList"
-        Title="Notification Method"
+        :Title="t('notificationRules.notificationMethod')"
       ></AddedrluesTag>
     </div>
   </div>
   <el-dialog v-model="UnableSaveVisible" width="480">
-    <div>{{ missingmessage }} missing.</div>
-    <div>Please complete all required fields.</div>
+    <div>{{ missingmessage }} {{ t('notificationRules.missing') }}.</div>
+    <div>{{ t('destinationDelivery.completeRequiredFields') }}</div>
     <template #footer>
       <div class="dialog-footer">
         <el-button
@@ -1235,44 +1237,44 @@ defineExpose({
           @click="UnableSaveVisible = false"
           style="width: 100px"
         >
-          OK
+          {{ t('common.ok') }}
         </el-button>
       </div>
     </template>
     <template #header>
       <div class="unable-save-header dialog-header">
         <span class="font_family icon-icon_fail_fill_b"></span>
-        Unable to Save
+        {{ t('destinationDelivery.unableToSave') }}
       </div>
     </template>
   </el-dialog>
   <!-- 保存成功 -->
   <el-dialog v-model="SaveedVisible" width="320" style="height: 212px">
     <div style="text-align: center"><el-image :src="submitsucessful" style="width: 64px" /></div>
-    <div style="text-align: center; margin-top: 20px">Saved successfully</div>
+    <div style="text-align: center; margin-top: 20px">{{ t('common.saveSuccess') }}</div>
   </el-dialog>
   <!-- 保存失败 -->
   <el-dialog v-model="SaveVisibleError" width="480">
-    <div>Duplicate Rule Error.</div>
-    <div>This rule exactly matches an existing rule.</div>
+    <div>{{ t('notificationRules.duplicateRuleError') }}</div>
+    <div>{{ t('notificationRules.duplicateRuleExactMatch') }}</div>
     <template #footer>
       <div class="dialog-footer">
         <el-button class="el-button--danger" @click="SaveVisibleError = false" style="width: 100px">
-          OK
+          {{ t('common.ok') }}
         </el-button>
       </div>
     </template>
     <template #header>
       <div class="unable-save-header dialog-header">
         <span class="font_family icon-icon_fail_fill_b"></span>
-        Unable to Save
+        {{ t('destinationDelivery.unableToSave') }}
       </div>
     </template>
   </el-dialog>
   <!-- 三项重合提示 -->
   <el-dialog v-model="SaveVisibleDetected" width="480">
-    <div>A similar configuration rule already exists.</div>
-    <div>Would you like to proceed with creating this rule?</div>
+    <div>{{ t('notificationRules.similarRuleExists') }}</div>
+    <div>{{ t('notificationRules.proceedCreateRule') }}</div>
     <template #footer>
       <div class="dialog-footer">
         <el-button
@@ -1280,21 +1282,21 @@ defineExpose({
           @click="SaveVisibleDetected = false"
           style="width: 100px"
         >
-          Cancel
+          {{ t('common.cancel') }}
         </el-button>
         <el-button
           class="el-button--warning"
           @click="HandelSaveVisibleDetected"
           style="width: 100px"
         >
-          Save
+          {{ t('common.save') }}
         </el-button>
       </div>
     </template>
     <template #header>
       <div class="warning-header dialog-header">
         <span class="font_family icon-icon_warning_fill_b"></span>
-        Similar Rule Detected
+        {{ t('notificationRules.similarRuleDetected') }}
       </div>
     </template>
   </el-dialog>

+ 14 - 12
src/components/CreateAddRules/src/components/DelayedType.vue

@@ -1,5 +1,7 @@
 <script lang="ts" setup>
 import { ref, computed, watch } from 'vue'
+import { useI18n } from 'vue-i18n'
+const { t } = useI18n()
 
 const props = defineProps({
   Title: String,
@@ -217,9 +219,9 @@ defineExpose({
         <div class="oceanCheckbox">
           <el-checkbox-group @change="CheckChange" v-model="OceanCheckedList">
             <el-checkbox @click="handleCheckboxClick($event)"  class="delayedType" value="Departure Delayed">
-              <div class="titlecheckbox">Departure Delayed (ATD-ETD)</div>
+              <div class="titlecheckbox">{{ t('notificationRules.departureDelayedAtdEtd') }}</div>
               <div v-if="isDeparture" class="flex" style="margin-top: 16px">
-                <span class="delayedTitle">Delayed Time</span>
+                <span class="delayedTitle">{{ t('notificationRules.delayedTime') }}</span>
                 <span class="delayedIcon">≥</span>
                 <el-input
                   v-model="clampedValue"
@@ -230,12 +232,12 @@ defineExpose({
                   <template #append>
                     <el-select
                       v-model="DepartureSelect"
-                      placeholder="Select"
+                      :placeholder="t('destinationDelivery.select')"
                       class="arrivalselect"
                       @change="changedeparture('Departure')"
                     >
-                      <el-option label="Day(s)" value="Day(s)" />
-                      <el-option label="Hour(s)" value="Hour(s)" />
+                      <el-option :label="t('notificationRules.days')" value="Day(s)" />
+                      <el-option :label="t('notificationRules.hours')" value="Hour(s)" />
                     </el-select>
                   </template>
                 </el-input>
@@ -247,13 +249,13 @@ defineExpose({
                 >
                 </el-input>
                 <div
-                v-if="props.Title != 'Air Shipments'" class="Days">Day(s)</div>
+                v-if="props.Title != 'Air Shipments'" class="Days">{{ t('notificationRules.days') }}</div>
               </div>
             </el-checkbox>
             <el-checkbox @click="handleCheckboxClick($event)"  class="delayedType" value="Arrival Delayed (ATA-ETA)">
-              <div class="titlecheckbox">Arrival Delayed (ATA-ETA)</div>
+              <div class="titlecheckbox">{{ t('notificationRules.arrivalDelayedAtaEta') }}</div>
               <div v-if="isArrival" class="flex" style="margin-top: 16px">
-                <span class="delayedTitle">Delayed Time</span>
+                <span class="delayedTitle">{{ t('notificationRules.delayedTime') }}</span>
                 <span class="delayedIcon">≥</span>
                 <el-input
                   v-model="clampedArrivalValue"
@@ -264,12 +266,12 @@ defineExpose({
                   <template #append>
                     <el-select
                       v-model="ArrivalSelect"
-                      placeholder="Select"
+                      :placeholder="t('destinationDelivery.select')"
                       class="arrivalselect"
                       @change="changedeparture('Arrival')"
                     >
-                      <el-option label="Day(s)" value="Day(s)" />
-                      <el-option  label="Hour(s)" value="Hour(s)" />
+                      <el-option :label="t('notificationRules.days')" value="Day(s)" />
+                      <el-option :label="t('notificationRules.hours')" value="Hour(s)" />
                     </el-select>
                   </template>
                 </el-input>
@@ -280,7 +282,7 @@ defineExpose({
                   class="input-with-select1"
                 >
                 </el-input>
-                <div v-if="props.Title != 'Air Shipments'" class="Days">Day(s)</div>
+                <div v-if="props.Title != 'Air Shipments'" class="Days">{{ t('notificationRules.days') }}</div>
               </div>
             </el-checkbox>
           </el-checkbox-group>

+ 16 - 14
src/components/CreateAddRules/src/components/ETDShipments.vue

@@ -1,5 +1,7 @@
 <script lang="ts" setup>
 import { ref, computed, watch } from 'vue'
+import { useI18n } from 'vue-i18n'
+const { t } = useI18n()
 
 const props = defineProps({
   Title: String,
@@ -297,13 +299,13 @@ const clampedETAValue = computed({
         <div class="oceanCheckbox">
           <el-checkbox-group @change="CheckChange" v-model="OceanCheckedList">
             <el-checkbox @click="handleCheckboxClick($event)" class="delayedType" value="ETD">
-              <div class="titlecheckbox">ETD</div>
+              <div class="titlecheckbox">{{ t('common.etd') }}</div>
               <div v-if="isETD" style="margin-top: 16px">
                 <el-radio-group class="radiocheckbox" v-model="ETDRadio" @change="changeETDRadio">
-                  <el-radio value="1">Notify for all changes</el-radio>
+                  <el-radio value="1">{{ t('notificationRules.notifyAllChanges') }}</el-radio>
                   <el-radio value="2">
                     <div class="flex">
-                      Notify only when time difference
+                      {{ t('notificationRules.notifyOnlyWhenTimeDifference') }}
                       <span class="delayedIcon">≥</span>
                       <el-input
                         v-if="props.Title == 'Air Shipments'"
@@ -314,12 +316,12 @@ const clampedETAValue = computed({
                         <template #append>
                           <el-select
                             v-model="ETDSelect"
-                            placeholder="Select"
+                            :placeholder="t('destinationDelivery.select')"
                             class="arrivalselect"
                             @change="changedeparture('ETD')"
                           >
-                            <el-option label="Day(s)" value="Day(s)" />
-                            <el-option label="Hour(s)" value="Hour(s)" />
+                            <el-option :label="t('notificationRules.days')" value="Day(s)" />
+                            <el-option :label="t('notificationRules.hours')" value="Hour(s)" />
                           </el-select>
                         </template>
                       </el-input>
@@ -331,20 +333,20 @@ const clampedETAValue = computed({
                       >
                       </el-input>
                       <div
-                      v-if="props.Title != 'Air Shipments'" class="Days">Day(s)</div>
+                      v-if="props.Title != 'Air Shipments'" class="Days">{{ t('notificationRules.days') }}</div>
                     </div>
                   </el-radio>
                 </el-radio-group>
               </div>
             </el-checkbox>
             <el-checkbox class="delayedType" @click="handleCheckboxClick($event)"  value="ETA">
-              <div class="titlecheckbox">ETA</div>
+              <div class="titlecheckbox">{{ t('common.eta') }}</div>
               <div v-if="isETA" style="margin-top: 16px">
                 <el-radio-group class="radiocheckbox" v-model="ETARadio" @change="changeETARadio">
-                  <el-radio value="1">Notify for all changes</el-radio>
+                  <el-radio value="1">{{ t('notificationRules.notifyAllChanges') }}</el-radio>
                   <el-radio value="2">
                     <div class="flex">
-                      Notify only when time difference
+                      {{ t('notificationRules.notifyOnlyWhenTimeDifference') }}
                       <span class="delayedIcon">≥</span>
                       <el-input
                       v-if="props.Title == 'Air Shipments'"
@@ -355,12 +357,12 @@ const clampedETAValue = computed({
                         <template #append>
                           <el-select
                             v-model="ETASelect"
-                            placeholder="Select"
+                            :placeholder="t('destinationDelivery.select')"
                             class="arrivalselect"
                             @change="changedeparture('ETA')"
                           >
-                            <el-option label="Day(s)" value="Day(s)" />
-                            <el-option label="Hour(s)" value="Hour(s)" />
+                            <el-option :label="t('notificationRules.days')" value="Day(s)" />
+                            <el-option :label="t('notificationRules.hours')" value="Hour(s)" />
                           </el-select>
                         </template>
                       </el-input>
@@ -372,7 +374,7 @@ const clampedETAValue = computed({
                       >
                       </el-input>
                       <div
-                      v-if="props.Title != 'Air Shipments'" class="Days">Day(s)</div>
+                      v-if="props.Title != 'Air Shipments'" class="Days">{{ t('notificationRules.days') }}</div>
                     </div>
                   </el-radio>
                 </el-radio-group>

+ 30 - 26
src/components/CreateAddRules/src/components/NotiFrequency.vue

@@ -1,7 +1,11 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
 import { ref, watch } from 'vue'
 import moment from 'moment-timezone'
 import { defaultTimeZone } from '@/utils/timezone'
+const WEEK_DAYS = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
 
 const props = defineProps({
   FrequencyData: Object
@@ -15,31 +19,31 @@ const WeeklyTime = ref('')
 const WeeklyDay = ref('')
 const WeekDay = ref([
   {
-    label: 'Monday',
+    label: t('notificationRules.monday'),
     value: 'Monday'
   },
   {
-    label: 'Tuesday',
+    label: t('notificationRules.tuesday'),
     value: 'Tuesday'
   },
   {
-    label: 'Wednesday',
+    label: t('notificationRules.wednesday'),
     value: 'Wednesday'
   },
   {
-    label: 'Thursday',
+    label: t('notificationRules.thursday'),
     value: 'Thursday'
   },
   {
-    label: 'Friday',
+    label: t('notificationRules.friday'),
     value: 'Friday'
   },
   {
-    label: 'Saturday',
+    label: t('notificationRules.saturday'),
     value: 'Saturday'
   },
   {
-    label: 'Sunday',
+    label: t('notificationRules.sunday'),
     value: 'Sunday'
   }
 ])
@@ -215,19 +219,19 @@ const ChangeFrequency = (val: any) => {
       FrequencyList.value.push(str)
     }
     savesubscribeobj.frequency_type = 'Weekly'
-    if (WeeklyDay.value == 'Monday') {
+    if (WeeklyDay.value == WEEK_DAYS[0]) {
       savesubscribeobj.weekly_week = 1
-    } else if (WeeklyDay.value == 'Tuesday') {
+    } else if (WeeklyDay.value == WEEK_DAYS[1]) {
       savesubscribeobj.weekly_week = 2
-    } else if (WeeklyDay.value == 'Wednesday') {
+    } else if (WeeklyDay.value == WEEK_DAYS[2]) {
       savesubscribeobj.weekly_week = 3
-    } else if (WeeklyDay.value == 'Thursday') {
+    } else if (WeeklyDay.value == WEEK_DAYS[3]) {
       savesubscribeobj.weekly_week = 4
-    } else if (WeeklyDay.value == 'Friday') {
+    } else if (WeeklyDay.value == WEEK_DAYS[4]) {
       savesubscribeobj.weekly_week = 5
-    } else if (WeeklyDay.value == 'Saturday') {
+    } else if (WeeklyDay.value == WEEK_DAYS[5]) {
       savesubscribeobj.weekly_week = 6
-    } else if (WeeklyDay.value == 'Sunday') {
+    } else if (WeeklyDay.value == WEEK_DAYS[6]) {
       savesubscribeobj.weekly_week = 0
     }
     savesubscribeobj.weekly_time = WeeklyTime.value
@@ -278,10 +282,10 @@ defineExpose({
         >Instant notification for each update</el-radio
       > -->
       <el-radio :value="2">
-        <div>Daily Summary</div>
+        <div>{{ t('notificationRules.dailySummary') }}</div>
         <div class="Daily" v-if="isDaily">
           <div class="Daily_left" style="margin-right: 8px">
-            Select Time
+            {{ t('notificationRules.selectTime') }}
             <div>
               <el-time-select
                 v-model="DailyTime"
@@ -290,16 +294,16 @@ defineExpose({
                 end="23:30"
                 prefix-icon=""
                 @change="changeTime('Daily')"
-                placeholder="Select Time"
+                :placeholder="t('notificationRules.selectTime')"
               ></el-time-select>
             </div>
           </div>
           <div class="Daily_left">
-            Select Time Zone
+            {{ t('notificationRules.selectTimeZone') }}
             <div>
               <el-select
                 v-model="TimeZoneDailySelect"
-                placeholder="Select Time Zone"
+                :placeholder="t('notificationRules.selectTimeZone')"
                 @change="changeTime('Daily')"
               >
                 <el-option
@@ -314,15 +318,15 @@ defineExpose({
         </div>
       </el-radio>
       <el-radio :value="3">
-        <div>Weekly Summary</div>
+        <div>{{ t('notificationRules.weeklySummary') }}</div>
         <div class="Daily" v-if="isWeekly">
           <div class="Weekly_left">
-            Select Day
+            {{ t('notificationRules.selectDay') }}
             <div>
               <el-select
                 v-model="WeeklyDay"
                 @change="changeTime('Weekly')"
-                placeholder="Select Day"
+                :placeholder="t('notificationRules.selectDay')"
               >
                 <el-option
                   v-for="item in WeekDay"
@@ -334,7 +338,7 @@ defineExpose({
             </div>
           </div>
           <div class="Weekly_left" style="margin: 0 8px">
-            Select Time
+            {{ t('notificationRules.selectTime') }}
             <div>
               <el-time-select
                 v-model="WeeklyTime"
@@ -343,16 +347,16 @@ defineExpose({
                 step="00:30"
                 end="23:30"
                 prefix-icon=""
-                placeholder="Select time"
+                :placeholder="t('notificationRules.selectTime')"
               ></el-time-select>
             </div>
           </div>
           <div class="Weekly_left">
-            Select Time Zone
+            {{ t('notificationRules.selectTimeZone') }}
             <div>
               <el-select
                 v-model="TimeZoneWeeklySelect"
-                placeholder="Select Time Zone"
+                :placeholder="t('notificationRules.selectTimeZone')"
                 @change="changeTime('Weekly')"
               >
                 <el-option

+ 13 - 9
src/components/CreateAddRules/src/components/NotiMethods.vue

@@ -1,10 +1,14 @@
 <script setup lang="ts">
-import { ref, watch } from 'vue'
+import { ref, watch, computed } from 'vue'
+import { useI18n } from 'vue-i18n'
 import Light_methods from '../images/illustration_system massage@2x.png'
 import Dark_methods from '../images/illustration_system massage_darkmode@2x.png'
 import { useThemeStore } from '@/stores/modules/theme'
 
 const themeStore = useThemeStore()
+const { t } = useI18n()
+const METHOD_BY_EMAIL = 'By Email'
+const METHOD_BY_SYSTEM_MESSAGE = 'By System Message'
 
 const checkMethodList = ref()
 checkMethodList.value = []
@@ -29,12 +33,12 @@ const emits = defineEmits(['ChangeMethodsAdd'])
 const MethodsInit = () => {
   if (methods_data.value?.method_display != undefined) {
     if (methods_data.value?.method_display.indexOf('Email') != -1) {
-      checkMethodList.value.push('By Email')
+      checkMethodList.value.push(METHOD_BY_EMAIL)
     }
   }
   if (methods_data.value?.method_display != undefined) {
     if (methods_data.value?.method_display.indexOf('System Message') != -1) {
-      checkMethodList.value.push('By System Message')
+      checkMethodList.value.push(METHOD_BY_SYSTEM_MESSAGE)
     }
   }
   changeMethod(checkMethodList.value)
@@ -42,12 +46,12 @@ const MethodsInit = () => {
 
 // 选中Method
 const changeMethod = (val: any) => {
-  if (val.indexOf('By Email') != -1) {
+  if (val.indexOf(METHOD_BY_EMAIL) != -1) {
     savesubscribeobj.method_by_email = true
   } else {
     savesubscribeobj.method_by_email = false
   }
-  if (val.indexOf('By System Message') != -1) {
+  if (val.indexOf(METHOD_BY_SYSTEM_MESSAGE) != -1) {
     savesubscribeobj.method_by_message = true
   } else {
     savesubscribeobj.method_by_message = false
@@ -65,12 +69,12 @@ const user_type = localStorage.getItem('user_type')
   <div style="margin-top: 11px">
     <div class="Method">
       <el-checkbox-group v-model="checkMethodList" @change="changeMethod">
-        <el-checkbox class="methodcheckbox" value="By Email">
-          <div>By Email</div>
+        <el-checkbox class="methodcheckbox" :value="METHOD_BY_EMAIL">
+          <div>{{ t('notificationRules.byEmail') }}</div>
           <div class="methos_image"><img src="../images/illustration_email@2x.png" /></div>
         </el-checkbox>
-        <el-checkbox class="methodcheckbox" value="By System Message">
-          <div>By System Message</div>
+        <el-checkbox class="methodcheckbox" :value="METHOD_BY_SYSTEM_MESSAGE">
+          <div>{{ t('notificationRules.bySystemMessage') }}</div>
           <div class="methos_image">
             <img :src="MethodsImg" />
           </div>

+ 72 - 64
src/components/CreateAddRules/src/components/ShipmentRange.vue

@@ -1,5 +1,13 @@
 <script lang="ts" setup>
 import { ref, computed, watch } from 'vue'
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
+const RANGE_NEXT_30_DAYS = 'Next 30 days'
+const RANGE_NEXT_60_DAYS = 'Next 60 days'
+const RANGE_PAST_10_TO_NEXT_60_DAYS = 'Past 10 days to next 60 days'
+const RANGE_PAST_30_DAYS = 'Past 30 days'
+const RANGE_CUSTOMIZE = 'Customize'
 
 const OceanActive = ref(['TransportMode', 'Time'])
 const TransportCheckedList = ref([])
@@ -14,15 +22,15 @@ const props = defineProps({
 const ShipmentRange_data = ref(props.ShipmentRangeData)
 TransportList.value = [
   {
-    label: 'Ocean',
+    label: t('notificationRules.ocean'),
     value: 'Ocean'
   },
   {
-    label: 'Air',
+    label: t('notificationRules.air'),
     value: 'Air'
   },
   {
-    label: 'Road',
+    label: t('notificationRules.road'),
     value: 'Road'
   }
 ]
@@ -131,7 +139,7 @@ let Timestr: any = ''
 const emit = defineEmits(['ChangeCheckRules', 'ChangeCheckTimeRules'])
 const CheckChange = (val: any) => {
   if (val != '') {
-    Transportstr = 'Transport Mode: ' + val
+    Transportstr = `${t('common.transportMode')}: ${val}`
   } else {
     Transportstr = ''
   }
@@ -154,25 +162,25 @@ const changeTime = (val: any) => {
       typeof clampedETDValueEnd.value == 'number'
     ) {
       if (clampedETDValueStart.value == 0 && clampedETDValueEnd.value == 30) {
-        defaultradio.value = 'Next 30 days'
+        defaultradio.value = RANGE_NEXT_30_DAYS
       } else if (clampedETDValueStart.value == 0 && clampedETDValueEnd.value == 60) {
-        defaultradio.value = 'Next 60 days'
+        defaultradio.value = RANGE_NEXT_60_DAYS
       } else if (clampedETDValueStart.value == 10 && clampedETDValueEnd.value == 60) {
-        defaultradio.value = 'Past 10 days to next 60 day'
+        defaultradio.value = RANGE_PAST_10_TO_NEXT_60_DAYS
       } else if (clampedETDValueStart.value == 30 && clampedETDValueEnd.value == 0) {
-        defaultradio.value = 'Past 30 days'
+        defaultradio.value = RANGE_PAST_30_DAYS
       } else {
-        defaultradio.value = 'Customize'
+        defaultradio.value = RANGE_CUSTOMIZE
       }
       if (clampedETDValueStart.value == 0 && clampedETDValueEnd.value == 0) {
         Timestr = ''
       } else {
         Timestr =
-          'ETD: minus ' +
+          'ETD: ' + t('notificationRules.minus') + ' ' +
           clampedETDValueStart.value +
-          ' Day(s) to Plus ' +
+          ' ' + t('notificationRules.days') + ' ' + t('report.to') + ' ' + t('notificationRules.plus') + ' ' +
           clampedETDValueEnd.value +
-          ' Day(s)'
+          ' ' + t('notificationRules.days')
       }
     } else {
       Timestr = ''
@@ -190,25 +198,25 @@ const changeTime = (val: any) => {
       typeof clampedETAValueEnd.value == 'number'
     ) {
       if (clampedETAValueStart.value == 0 && clampedETAValueEnd.value == 30) {
-        defaultradio2.value = 'Next 30 days'
+        defaultradio2.value = RANGE_NEXT_30_DAYS
       } else if (clampedETAValueStart.value == 0 && clampedETAValueEnd.value == 60) {
-        defaultradio2.value = 'Next 60 days'
+        defaultradio2.value = RANGE_NEXT_60_DAYS
       } else if (clampedETAValueStart.value == 10 && clampedETAValueEnd.value == 60) {
-        defaultradio2.value = 'Past 10 days to next 60 day'
+        defaultradio2.value = RANGE_PAST_10_TO_NEXT_60_DAYS
       } else if (clampedETAValueStart.value == 30 && clampedETAValueEnd.value == 0) {
-        defaultradio2.value = 'Past 30 days'
+        defaultradio2.value = RANGE_PAST_30_DAYS
       } else {
-        defaultradio2.value = 'Customize'
+        defaultradio2.value = RANGE_CUSTOMIZE
       }
       if (clampedETAValueStart.value == 0 && clampedETAValueEnd.value == 0) {
         Timestr = ''
       } else {
         Timestr =
-          'ETA: minus ' +
+          'ETA: ' + t('notificationRules.minus') + ' ' +
           clampedETAValueStart.value +
-          ' Day(s) to Plus ' +
+          ' ' + t('notificationRules.days') + ' ' + t('report.to') + ' ' + t('notificationRules.plus') + ' ' +
           clampedETAValueEnd.value +
-          ' Day(s)'
+          ' ' + t('notificationRules.days')
       }
     } else {
       Timestr = ''
@@ -221,16 +229,16 @@ const changeTime = (val: any) => {
 
 //切换默认值
 const changedefaultradio = (val: any) => {
-  if (val == 'Next 30 days') {
+  if (val == RANGE_NEXT_30_DAYS) {
     clampedETDValueStart.value = 0
     clampedETDValueEnd.value = 30
-  } else if (val == 'Next 60 days') {
+  } else if (val == RANGE_NEXT_60_DAYS) {
     clampedETDValueStart.value = 0
     clampedETDValueEnd.value = 60
-  } else if (val == 'Past 30 days') {
+  } else if (val == RANGE_PAST_30_DAYS) {
     clampedETDValueStart.value = 30
     clampedETDValueEnd.value = 0
-  } else if (val == 'Past 10 days to next 60 days') {
+  } else if (val == RANGE_PAST_10_TO_NEXT_60_DAYS) {
     clampedETDValueStart.value = 10
     clampedETDValueEnd.value = 60
   } else {
@@ -241,26 +249,26 @@ const changedefaultradio = (val: any) => {
     Timestr = ''
   } else {
     Timestr =
-      'ETD: minus ' +
+      'ETD: ' + t('notificationRules.minus') + ' ' +
       clampedETDValueStart.value +
-      ' Day(s) to Plus ' +
+      ' ' + t('notificationRules.days') + ' ' + t('report.to') + ' ' + t('notificationRules.plus') + ' ' +
       clampedETDValueEnd.value +
-      ' Day(s)'
+      ' ' + t('notificationRules.days')
   }
   emit('ChangeCheckTimeRules', Timestr, clampedETDValueStart.value, clampedETDValueEnd.value)
 }
 //切换默认值
 const changedefaultradioETA = (val: any) => {
-  if (val == 'Next 30 days') {
+  if (val == RANGE_NEXT_30_DAYS) {
     clampedETAValueStart.value = 0
     clampedETAValueEnd.value = 30
-  } else if (val == 'Next 60 days') {
+  } else if (val == RANGE_NEXT_60_DAYS) {
     clampedETAValueStart.value = 0
     clampedETAValueEnd.value = 60
-  } else if (val == 'Past 30 days') {
+  } else if (val == RANGE_PAST_30_DAYS) {
     clampedETAValueStart.value = 30
     clampedETAValueEnd.value = 0
-  } else if (val == 'Past 10 days to next 60 days') {
+  } else if (val == RANGE_PAST_10_TO_NEXT_60_DAYS) {
     clampedETAValueStart.value = 10
     clampedETAValueEnd.value = 60
   } else {
@@ -271,11 +279,11 @@ const changedefaultradioETA = (val: any) => {
     Timestr = ''
   } else {
     Timestr =
-      'ETA: minus ' +
+      'ETA: ' + t('notificationRules.minus') + ' ' +
       clampedETAValueStart.value +
-      ' Day(s) to Plus ' +
+      ' ' + t('notificationRules.days') + ' ' + t('report.to') + ' ' + t('notificationRules.plus') + ' ' +
       clampedETAValueEnd.value +
-      ' Day(s)'
+      ' ' + t('notificationRules.days')
   }
   emit('ChangeCheckTimeRules', Timestr, clampedETAValueStart.value, clampedETAValueEnd.value)
 }
@@ -306,7 +314,7 @@ defineExpose({
         <template #title>
           <div class="Rules_Title_flex">
             <span class="stars_red">*</span>
-            <div class="Rules_Title OceanTitle">Transport Mode</div>
+            <div class="Rules_Title OceanTitle">{{ t('common.transportMode') }}</div>
           </div>
         </template>
         <div class="oceanCheckbox">
@@ -324,94 +332,94 @@ defineExpose({
         <template #title>
           <div class="Rules_Title_flex">
             <span class="stars_red">*</span>
-            <div class="Rules_Title OceanTitle">Time</div>
+            <div class="Rules_Title OceanTitle">{{ t('notificationRules.time') }}</div>
           </div>
         </template>
         <div class="oceanCheckbox">
           <el-radio-group v-model="TimeChecked" @change="changeTime">
             <el-radio :value="1">
-              <div>ETD</div>
+              <div>{{ t('common.etd') }}</div>
               <div v-if="isETDVisible" class="oceanCheckbox2">
                 <el-radio-group v-model="defaultradio" @change="changedefaultradio">
-                  <el-radio-button label="Next 30 days" value="Next 30 days" />
-                  <el-radio-button label="Next 60 days" value="Next 60 days" />
+                  <el-radio-button :label="t('notificationRules.next30Days')" :value="RANGE_NEXT_30_DAYS" />
+                  <el-radio-button :label="t('notificationRules.next60Days')" :value="RANGE_NEXT_60_DAYS" />
                   <el-radio-button
-                    label="Past 10 days to next 60 days"
-                    value="Past 10 days to next 60 days"
+                    :label="t('notificationRules.past10DaysToNext60Days')"
+                    :value="RANGE_PAST_10_TO_NEXT_60_DAYS"
                   />
-                  <el-radio-button label="Past 30 days" value="Past 30 days" />
-                  <el-radio-button label="Customize" value="Customize" />
+                  <el-radio-button :label="t('notificationRules.past30Days')" :value="RANGE_PAST_30_DAYS" />
+                  <el-radio-button :label="t('notificationRules.customize')" :value="RANGE_CUSTOMIZE" />
                 </el-radio-group>
                 <div class="flex" style="align-items: end; margin: 0 8px 8px 0; flex-wrap: wrap">
                   <div class="date_flex">
-                    <div class="time_title">Start Date</div>
+                    <div class="time_title">{{ t('report.startDate') }}</div>
                     <div class="flex">
-                      <div class="currentTime">Current time minus</div>
+                      <div class="currentTime">{{ t('notificationRules.currentTimeMinus') }}</div>
                       <el-input
                         @input="changeTime('1')"
                         v-model="clampedETDValueStart"
                         class="input-with-select"
                       >
                       </el-input>
-                      <div class="Days">Day(s)</div>
+                      <div class="Days">{{ t('notificationRules.days') }}</div>
                     </div>
                   </div>
-                  <div class="To">To</div>
+                  <div class="To">{{ t('report.to') }}</div>
                   <div class="date_flex">
-                    <div class="time_title">End Date</div>
+                    <div class="time_title">{{ t('report.endDate') }}</div>
                     <div class="flex">
-                      <div class="currentTime">Current time plus</div>
+                      <div class="currentTime">{{ t('notificationRules.currentTimePlus') }}</div>
                       <el-input
                         @input="changeTime('1')"
                         v-model="clampedETDValueEnd"
                         class="input-with-select"
                       >
                       </el-input>
-                      <div class="Days">Day(s)</div>
+                      <div class="Days">{{ t('notificationRules.days') }}</div>
                     </div>
                   </div>
                 </div>
               </div>
             </el-radio>
             <el-radio :value="2">
-              <div>ETA</div>
+              <div>{{ t('common.eta') }}</div>
               <div v-if="isETAVisible" class="oceanCheckbox2">
                 <el-radio-group v-model="defaultradio2" @change="changedefaultradioETA">
-                  <el-radio-button label="Next 30 days" value="Next 30 days" />
-                  <el-radio-button label="Next 60 days" value="Next 60 days" />
+                  <el-radio-button :label="t('notificationRules.next30Days')" :value="RANGE_NEXT_30_DAYS" />
+                  <el-radio-button :label="t('notificationRules.next60Days')" :value="RANGE_NEXT_60_DAYS" />
                   <el-radio-button
-                    label="Past 10 days to next 60 days"
-                    value="Past 10 days to next 60 days"
+                    :label="t('notificationRules.past10DaysToNext60Days')"
+                    :value="RANGE_PAST_10_TO_NEXT_60_DAYS"
                   />
-                  <el-radio-button label="Past 30 days" value="Past 30 days" />
-                  <el-radio-button label="Customize" value="Customize" />
+                  <el-radio-button :label="t('notificationRules.past30Days')" :value="RANGE_PAST_30_DAYS" />
+                  <el-radio-button :label="t('notificationRules.customize')" :value="RANGE_CUSTOMIZE" />
                 </el-radio-group>
                 <div class="flex" style="align-items: end; margin: 0 8px 8px 0; flex-wrap: wrap">
                   <div class="date_flex">
-                    <div class="time_title">Start Date</div>
+                    <div class="time_title">{{ t('report.startDate') }}</div>
                     <div class="flex">
-                      <div class="currentTime">Current time minus</div>
+                      <div class="currentTime">{{ t('notificationRules.currentTimeMinus') }}</div>
                       <el-input
                         @input="changeTime('2')"
                         v-model="clampedETAValueStart"
                         class="input-with-select"
                       >
                       </el-input>
-                      <div class="Days">Day(s)</div>
+                      <div class="Days">{{ t('notificationRules.days') }}</div>
                     </div>
                   </div>
-                  <div class="To">To</div>
+                  <div class="To">{{ t('report.to') }}</div>
                   <div class="date_flex">
-                    <div class="time_title">End Date</div>
+                    <div class="time_title">{{ t('report.endDate') }}</div>
                     <div class="flex">
-                      <div class="currentTime">Current time plus</div>
+                      <div class="currentTime">{{ t('notificationRules.currentTimePlus') }}</div>
                       <el-input
                         @input="changeTime('2')"
                         v-model="clampedETAValueEnd"
                         class="input-with-select"
                       >
                       </el-input>
-                      <div class="Days">Day(s)</div>
+                      <div class="Days">{{ t('notificationRules.days') }}</div>
                     </div>
                   </div>
                 </div>

+ 14 - 18
src/components/CustomizeColumns/src/CustomizeColumns.vue

@@ -1,4 +1,7 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
 import { VueDraggable } from 'vue-draggable-plus'
 import { useRoute } from 'vue-router'
 
@@ -342,7 +345,7 @@ defineExpose({
     class="customize-columns"
     v-model="dialogVisible"
     :width="1000"
-    title="Customize Columns"
+    :title="t('customizeColumns.title')"
     @close="clearData"
   >
     <div class="search-header">
@@ -351,7 +354,7 @@ defineExpose({
           v-model="searchColumn"
           @change="scrollToItem"
           filterable
-          placeholder="Search columns you preffered"
+          :placeholder="t('customizeColumns.searchPlaceholder')"
         >
           <template #prefix>
             <span class="font_family icon-icon_search_b"></span>
@@ -413,8 +416,7 @@ defineExpose({
       </div>
       <div class="right-select-columns">
         <div class="title">
-          Selected columns on your
-          {{ route.path.includes('booking') ? 'booking' : 'shipment' }} list
+          {{ t('customizeColumns.selectedColumnsOnList', { type: route.path.includes('booking') ? t('customizeColumns.booking') : t('customizeColumns.shipment') }) }}
         </div>
         <VueDraggable
           v-vloading="loading"
@@ -464,17 +466,17 @@ defineExpose({
         type="default"
         style="height: 40px; padding: 8px 40px"
         @click="dialogVisible = false"
-        >Cancel</el-button
+        >{{ t('common.cancel') }}</el-button
       >
       <el-button type="default" style="height: 40px; padding: 8px 20px" @click="handleReset"
-        >Reset to default</el-button
+        >{{ t('customizeColumns.resetToDefault') }}</el-button
       >
       <el-button
         class="el-button--dark"
         style="height: 40px; padding: 8px 40px"
         @click="handleApply"
       >
-        Apply
+        {{ t('report.apply') }}
       </el-button>
     </template>
     <el-tour
@@ -488,10 +490,9 @@ defineExpose({
       <el-tour-step :show-close="false" :target="step1?.[0]">
         <template #default>
           <div class="description">
-            <span>Drag</span> items to the right group or click the "<span>Add</span>" icon to add
-            columns to the {{ route.path.includes('booking') ? 'booking' : 'shipment' }} list.
+            {{ t('customizeColumns.tourStep1', { type: route.path.includes('booking') ? t('customizeColumns.booking') : t('customizeColumns.shipment') }) }}
           </div>
-          <div class="got-it-text" @click="handleCloseTour('step1')">Got it</div>
+          <div class="got-it-text" @click="handleCloseTour('step1')">{{ t('customizeColumns.gotIt') }}</div>
         </template>
       </el-tour-step>
     </el-tour>
@@ -506,17 +507,12 @@ defineExpose({
       <el-tour-step :show-close="false" :target="step2?.[0]">
         <template #default>
           <div class="description">
-            <span>Drag</span> items to the left group or click the "<span>Remove</span>" icon to
-            delete columns from the
-            {{ route.path.includes('booking') ? 'booking' : 'shipment' }} list.
+            {{ t('customizeColumns.tourStep2Line1', { type: route.path.includes('booking') ? t('customizeColumns.booking') : t('customizeColumns.shipment') }) }}
           </div>
           <div class="description">
-            <span>Drag</span> items up or down to reorder the
-            {{ route.path.includes('booking') ? 'booking' : 'shipment' }} list, or use the "<span
-              >Move up</span
-            >" and "<span>Move down</span>" icons.
+            {{ t('customizeColumns.tourStep2Line2', { type: route.path.includes('booking') ? t('customizeColumns.booking') : t('customizeColumns.shipment') }) }}
           </div>
-          <div class="got-it-text" @click="handleCloseTour('step2')">Got it</div>
+          <div class="got-it-text" @click="handleCloseTour('step2')">{{ t('customizeColumns.gotIt') }}</div>
         </template>
       </el-tour-step>
     </el-tour>

+ 11 - 8
src/components/DateRange/src/DateRange.vue

@@ -1,4 +1,7 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
 import { ref, watch, onMounted, onBeforeMount } from 'vue'
 import IconDropDown from '@/components/IconDropDown'
 import VCalendarDate from './components/VCalendarDate.vue'
@@ -139,7 +142,7 @@ const closeRset = () => {
           @blur="popoverVisible = false"
           @click="popoverVisible = !popoverVisible"
         >
-          <div class="select_title">Date Range</div>
+          <div class="select_title">{{ t('common.dateRange') }}</div>
           <span class="iconfont_icon">
             <svg class="iconfont icon_dark" aria-hidden="true">
               <use xlink:href="#icon-icon_dropdown_b"></use>
@@ -148,7 +151,7 @@ const closeRset = () => {
         </div>
       </template>
       <div class="date_header">
-        <div class="title">Date Range</div>
+        <div class="title">{{ t('common.dateRange') }}</div>
         <div class="ETD">
           <VCalendarDate CalendarTitle="ETD" v-model:date="etdDateRange"></VCalendarDate>
         </div>
@@ -158,7 +161,7 @@ const closeRset = () => {
         <div class="addType" v-for="(item, index) in otherDateType" :key="item.title">
           <div>
             <div class="ETD_title Date_Title">
-              <div class="Date_type">Date Type</div>
+              <div class="Date_type">{{ t('common.dateType') }}</div>
               <span class="iconfont_icon" @click="deleteType(index)">
                 <svg class="iconfont icon_delete" aria-hidden="true">
                   <use xlink:href="#icon-icon_delete_b"></use>
@@ -167,7 +170,7 @@ const closeRset = () => {
             </div>
             <el-select
               :suffix-icon="IconDropDown"
-              placeholder="Please Select Date Type"
+              :placeholder="t('common.pleaseSelectDateType')"
               v-model="item.title"
             >
               <el-option
@@ -181,7 +184,7 @@ const closeRset = () => {
           </div>
           <div style="margin-top: 16px">
             <VCalendarDate
-              :CalendarTitle="item.label || 'Date Range'"
+              :CalendarTitle="item.label || t('common.dateRange')"
               CalendarWidth="352px"
               v-model:date="item.value"
               :isType="true"
@@ -189,12 +192,12 @@ const closeRset = () => {
           </div>
         </div>
         <div class="MoreType" @click="addType" v-if="otherDateType.length != allOtherType.length">
-          <el-button class="el-button--noborder moretype">+ More Date Type</el-button>
+          <el-button class="el-button--noborder moretype">+ {{ t('common.moreDateType') }}</el-button>
         </div>
         <div class="daterange_bottom">
-          <div><el-button type="default" @click="clearRest" class="Clear">Reset</el-button></div>
+          <div><el-button type="default" @click="clearRest" class="Clear">{{ t('common.reset') }}</el-button></div>
           <div>
-            <el-button class="search el-button--dark" @click="DateRangeSearch">Search</el-button>
+            <el-button class="search el-button--dark" @click="DateRangeSearch">{{ t('common.search') }}</el-button>
           </div>
         </div>
       </div>

+ 10 - 8
src/components/DateRange/src/components/CalendarDate.vue

@@ -2,8 +2,10 @@
 import dayjs, { Dayjs } from 'dayjs'
 import { ref, watch } from 'vue'
 import { useUserStore } from '@/stores/modules/user'
+import { useI18n } from 'vue-i18n'
 
 const userStore = useUserStore()
+const { t } = useI18n()
 const formatDate = userStore.dateFormat
 const valueFormatDate = 'MM/DD/YYYY'
 // type RangeValue = [Dayjs, Dayjs]
@@ -39,9 +41,9 @@ const props = defineProps({
 const ETDDate = ref([])
 const CreatePlaceholder = computed(() => {
   if (props.isETA) {
-    return ['Start ETA Time', 'End ETA Time']
+    return [t('dateRange.startEtaTime'), t('dateRange.endEtaTime')]
   } else {
-    return ['Start ATA Time', 'End ATA Time']
+    return [t('dateRange.startAtaTime'), t('dateRange.endAtaTime')]
   }
 })
 watch(
@@ -125,7 +127,7 @@ const handlePanelChange = (value: any, mode: any) => {
   <div>
     <div class="ETD_title" v-if="props.CalendarTitle">{{ props.CalendarTitle }}</div>
     <a-range-picker
-      separator="To"
+      :separator="t('report.to')"
       :showToday="false"
       :style="{
         width: props.CalendarWidth,
@@ -135,7 +137,7 @@ const handlePanelChange = (value: any, mode: any) => {
       :open="open"
       :disabled="Disabled"
       @change="changeRangeData"
-      :placeholder="isNeedFooter ? ['Start Time', 'End Time'] : CreatePlaceholder"
+      :placeholder="isNeedFooter ? [t('dateRange.startTime'), t('dateRange.endTime')] : CreatePlaceholder"
       :format="userStore.dateFormat"
       valueFormat="MM/DD/YYYY"
       @openChange="handleCalendarOpen(ETDDate)"
@@ -152,14 +154,14 @@ const handlePanelChange = (value: any, mode: any) => {
       <template #renderExtraFooter v-if="isShowExtra && isNeedFooter">
         <div class="calender_flex">
           <div class="footer_left">
-            <el-button class="el-button--noborder" @click="Earliest">Earliest Time</el-button>
+            <el-button class="el-button--noborder" @click="Earliest">{{ t('dateRange.earliestTime') }}</el-button>
             <el-button class="el-button--noborder" @click="ChangeToday('Earliest')"
-              >Today</el-button
+              >{{ t('dateRange.today') }}</el-button
             >
           </div>
           <div class="footer_left footer_right">
-            <el-button @click="Latest" class="el-button--noborder">Latest Time</el-button>
-            <el-button class="el-button--noborder" @click="ChangeToday('Latest')">Today</el-button>
+            <el-button @click="Latest" class="el-button--noborder">{{ t('dateRange.latestTime') }}</el-button>
+            <el-button class="el-button--noborder" @click="ChangeToday('Latest')">{{ t('dateRange.today') }}</el-button>
           </div>
         </div>
       </template>

+ 12 - 10
src/components/DateRange/src/components/QuickCalendarDate.vue

@@ -3,8 +3,10 @@ import dayjs from 'dayjs'
 import { ref, watch, computed } from 'vue'
 import { formatTimezone } from '@/utils/tools'
 import { useUserStore } from '@/stores/modules/user'
+import { useI18n } from 'vue-i18n'
 
 const userStore = useUserStore()
+const { t } = useI18n()
 const props = defineProps({
   CalendarWidth: {
     type: String,
@@ -95,28 +97,28 @@ const handlePanelChange = (value: any, mode: any) => {
 }
 //预设范围
 const rangePresets = ref([
-  { label: 'Current Month', value: [dayjs().startOf('month'), dayjs().endOf('month')] },
+  { label: t('dateRange.currentMonth'), value: [dayjs().startOf('month'), dayjs().endOf('month')] },
   {
-    label: 'Last Month',
+    label: t('dateRange.lastMonth'),
     value: [
       dayjs().startOf('month').subtract(1, 'month'),
       dayjs().endOf('month').subtract(1, 'month')
     ]
   },
-  { label: 'This Year', value: [dayjs().startOf('year'), dayjs().endOf('year')] }
+  { label: t('dateRange.thisYear'), value: [dayjs().startOf('year'), dayjs().endOf('year')] }
 ])
 const placeholder = computed(() => {
   if (props.isDisabled) {
-    return ['No Start Limit', 'No End Limit']
+    return [t('dateRange.noStartLimit'), t('dateRange.noEndLimit')]
   } else {
-    return ['Start Time', 'End Time']
+    return [t('dateRange.startTime'), t('dateRange.endTime')]
   }
 })
 </script>
 <template>
   <div>
     <a-range-picker
-      separator="To"
+      :separator="t('report.to')"
       :showToday="false"
       :allowClear="false"
       :style="{ width: props.CalendarWidth }"
@@ -142,14 +144,14 @@ const placeholder = computed(() => {
       <template #renderExtraFooter v-if="isShowExtra">
         <div class="calender_flex">
           <div class="footer_left">
-            <el-button class="el-button--noborder" @click="Earliest">Earliest Time</el-button>
+            <el-button class="el-button--noborder" @click="Earliest">{{ t('dateRange.earliestTime') }}</el-button>
             <el-button class="el-button--noborder" @click="ChangeToday('Earliest')"
-              >Today</el-button
+              >{{ t('dateRange.today') }}</el-button
             >
           </div>
           <div class="footer_left footer_right">
-            <el-button @click="Latest" class="el-button--noborder">Latest Time</el-button>
-            <el-button class="el-button--noborder" @click="ChangeToday('Latest')">Today</el-button>
+            <el-button @click="Latest" class="el-button--noborder">{{ t('dateRange.latestTime') }}</el-button>
+            <el-button class="el-button--noborder" @click="ChangeToday('Latest')">{{ t('dateRange.today') }}</el-button>
           </div>
         </div>
       </template>

+ 4 - 2
src/components/DateRange/src/components/QuickMonth.vue

@@ -1,6 +1,8 @@
 <script lang="ts" setup>
 import { ref, watch } from 'vue'
 import moment from 'moment'
+import { useI18n } from 'vue-i18n'
+const { t } = useI18n()
 
 const props = defineProps({
   MonthStartDate: {
@@ -64,7 +66,7 @@ const EndChange = (val: any) => {
       type="month"
       @change="StartChange"
       :clearable="false"
-      placeholder="Start month"
+      :placeholder="t('dateRange.startMonth')"
       placement="bottom"
     />
     <el-date-picker
@@ -74,7 +76,7 @@ const EndChange = (val: any) => {
       type="month"
       @change="EndChange"
       :clearable="false"
-      placeholder="End month"
+      :placeholder="t('dateRange.endMonth')"
       placement="bottom"
     />
   </div>

+ 10 - 8
src/components/DateRange/src/components/VCalendarDate.vue

@@ -2,8 +2,10 @@
 import dayjs, { Dayjs } from 'dayjs'
 import { ref, watch } from 'vue'
 import { useUserStore } from '@/stores/modules/user'
+import { useI18n } from 'vue-i18n'
 
 const userStore = useUserStore()
+const { t } = useI18n()
 const formatDate = userStore.dateFormat
 const valueFormatDate = 'MM/DD/YYYY'
 const props = defineProps({
@@ -37,9 +39,9 @@ const props = defineProps({
 const ETDDate = ref([])
 const CreatePlaceholder = computed(() => {
   if (props.isETA) {
-    return ['Start ETA Time', 'End ETA Time']
+    return [t('dateRange.startEtaTime'), t('dateRange.endEtaTime')]
   } else {
-    return ['Start ATA Time', 'End ATA Time']
+    return [t('dateRange.startAtaTime'), t('dateRange.endAtaTime')]
   }
 })
 
@@ -94,7 +96,7 @@ const handleCalendarOpen = (date: any) => {
   <div>
     <div class="ETD_title" v-if="props.CalendarTitle">{{ props.CalendarTitle }}</div>
     <a-range-picker
-      separator="To"
+      :separator="t('report.to')"
       :showToday="false"
       :style="{
         width: props.CalendarWidth,
@@ -103,7 +105,7 @@ const handleCalendarOpen = (date: any) => {
       :popupClassName="props.isShowPopupClass ? 'th-color' : ''"
       :open="open"
       @change="changeRangeData"
-      :placeholder="isNeedFooter ? ['Start Time', 'End Time'] : CreatePlaceholder"
+      :placeholder="isNeedFooter ? [t('dateRange.startTime'), t('dateRange.endTime')] : CreatePlaceholder"
       :format="formatDate"
       @openChange="handleCalendarOpen(ETDDate)"
       :valueFormat="formatDate"
@@ -119,14 +121,14 @@ const handleCalendarOpen = (date: any) => {
       <template #renderExtraFooter v-if="isShowExtra && isNeedFooter">
         <div class="calender_flex">
           <div class="footer_left">
-            <el-button class="el-button--noborder" @click="Earliest">Earliest Time</el-button>
+            <el-button class="el-button--noborder" @click="Earliest">{{ t('dateRange.earliestTime') }}</el-button>
             <el-button class="el-button--noborder" @click="ChangeToday('Earliest')"
-              >Today</el-button
+              >{{ t('dateRange.today') }}</el-button
             >
           </div>
           <div class="footer_left footer_right">
-            <el-button @click="Latest" class="el-button--noborder">Latest Time</el-button>
-            <el-button class="el-button--noborder" @click="ChangeToday('Latest')">Today</el-button>
+            <el-button @click="Latest" class="el-button--noborder">{{ t('dateRange.latestTime') }}</el-button>
+            <el-button class="el-button--noborder" @click="ChangeToday('Latest')">{{ t('dateRange.today') }}</el-button>
           </div>
         </div>
       </template>

+ 16 - 1
src/components/FliterTags/src/FilterTags.vue

@@ -1,5 +1,6 @@
 <script setup lang="ts">
 import { cloneDeep } from 'lodash'
+import { useI18n } from 'vue-i18n'
 
 interface ListItem {
   name: string
@@ -11,8 +12,22 @@ interface Props {
   tagsList: ListItem[]
 }
 const props = withDefaults(defineProps<Props>(), {})
+const { t } = useI18n()
 
 const emits = defineEmits(['tabChange'])
+const getTagLabel = (name: string) => {
+  const map = {
+    All: t('common.all'),
+    Created: t('filterTags.created'),
+    Confirmed: t('filterTags.confirmed'),
+    Cancelled: t('filterTags.cancelled'),
+    Departed: t('filterTags.departed'),
+    'Cargo Received': t('filterTags.cargoReceived'),
+    Arrived: t('filterTags.arrived'),
+    Completed: t('filterTags.completed')
+  }
+  return map[name] || name
+}
 
 const getCheckedTabs = (tagsList: ListItem[]) => {
   const checkedList = tagsList.filter((item) => item.checked)
@@ -73,7 +88,7 @@ const handleTagToggle = (index: any, checked: any) => {
         :class="[item.checked ? 'checked' : '']"
         @click="handleTagToggle(index, item.checked)"
       >
-        {{ item.name }}
+        {{ getTagLabel(item.name) }}
         <div
           :class="[
             'v-tag',

+ 31 - 14
src/components/MoreFilters/src/MoreFilters.vue

@@ -1,4 +1,7 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
 import AutoSelect from '@/components/AutoSelect'
 import { useFiltersStore } from '@/stores/modules/filtersList'
 import { useRoute } from 'vue-router'
@@ -33,6 +36,20 @@ const transportaionData = ref([
     placeholder: 'Please input Voyage or flight no.'
   }
 ])
+const getTransportationLabel = (title: string) => {
+  const map = {
+    Vessel: t('moreFilters.vessel'),
+    'Voyage/Flight': t('moreFilters.voyageFlight')
+  }
+  return map[title] || title
+}
+const getTransportationPlaceholder = (title: string) => {
+  const map = {
+    Vessel: t('moreFilters.inputVesselNameOrCode'),
+    'Voyage/Flight': t('moreFilters.inputVoyageOrFlightNo')
+  }
+  return map[title] || ''
+}
 
 const partiesViewRef = ref()
 const placesViewRef = ref()
@@ -218,15 +235,15 @@ const moreFiltersGuideImg = computed(() => {
           <use xlink:href="#icon-icon_filter_b"></use>
         </svg>
       </span>
-      <div class="Filters_title">More Filters</div>
+      <div class="Filters_title">{{ t('common.moreFilters') }}</div>
     </el-button>
-    <el-drawer @open="initData" size="400px" title="More Filters" v-model="drawer">
+    <el-drawer @open="initData" size="400px" :title="t('common.moreFilters')" v-model="drawer">
       <el-collapse v-model="collapseValue"
         ><!-- General -->
         <el-collapse-item class="collapse_item" name="General" v-if="searchMode === 'tracking'">
           <template #title>
             <span class="collapse-title"
-              >General
+              >{{ t('common.general') }}
               <el-badge
                 v-if="generalBadgeCount > 0 && !collapseValue?.includes('General')"
                 class="mark"
@@ -235,18 +252,18 @@ const moreFiltersGuideImg = computed(() => {
             /></span>
           </template>
           <div class="ETD">
-            <div class="ETD_title">Incoterms</div>
+            <div class="ETD_title">{{ t('common.incoterms') }}</div>
             <SelectValue
-              title="Incoterms"
+              :title="t('common.incoterms')"
               keyValue="incoterms"
               ref="incotermsRef"
               :transportListData="props.incotermsList"
             ></SelectValue>
           </div>
           <div class="ETA">
-            <div class="ETD_title">Service</div>
+            <div class="ETD_title">{{ t('common.service') }}</div>
             <SelectValue
-              title="Service"
+              :title="t('common.service')"
               keyValue="service"
               ref="serviceRef"
               :transportListData="props.serviceList"
@@ -257,7 +274,7 @@ const moreFiltersGuideImg = computed(() => {
         <el-collapse-item class="collapse_item" name="Parties">
           <template #title>
             <span class="collapse-title"
-              >Parties
+              >{{ t('common.parties') }}
               <el-badge
                 v-if="partiesBadgeCount && !collapseValue?.includes('Parties')"
                 :value="partiesBadgeCount"
@@ -271,7 +288,7 @@ const moreFiltersGuideImg = computed(() => {
         <el-collapse-item class="collapse_item" name="Places">
           <template #title
             ><span class="collapse-title"
-              >Places<el-badge
+              >{{ t('common.places') }}<el-badge
                 class="mark"
                 v-if="placesBadgeCount && !collapseValue?.includes('Places')"
                 :value="placesBadgeCount"
@@ -283,7 +300,7 @@ const moreFiltersGuideImg = computed(() => {
         <el-collapse-item class="collapse_item" name="Transportation">
           <template #title
             ><span class="collapse-title"
-              >Transportation
+              >{{ t('common.transportation') }}
               <el-badge
                 class="mark"
                 v-if="transportationBadgeCount && !collapseValue?.includes('Transportation')"
@@ -291,13 +308,13 @@ const moreFiltersGuideImg = computed(() => {
                 type="warning" /></span
           ></template>
           <div class="ETD" v-for="item in transportaionData" :key="item.title">
-            <div class="ETD_title">{{ item.title }}</div>
+            <div class="ETD_title">{{ getTransportationLabel(item.title) }}</div>
             <AutoSelect
               :type="item.type"
               :title="item.title"
               @changeAutoSelect="changeTransportationData"
               :data="item.value"
-              :placeholder="item.placeholder"
+              :placeholder="getTransportationPlaceholder(item.title)"
             >
             </AutoSelect>
           </div>
@@ -306,11 +323,11 @@ const moreFiltersGuideImg = computed(() => {
       <div class="more_bottom">
         <el-button class="reset" type="default" @click="clearrest">
           <span class="font_family icon-icon_reset_b" style="padding-right: 4px"></span>
-          Reset
+          {{ t('common.reset') }}
         </el-button>
         <el-button class="reset el-button--dark" style="margin-left: 8px" @click="handleSearch">
           <span class="font_family icon-icon_search_b" style="padding-right: 4px"></span>
-          Search
+          {{ t('common.search') }}
         </el-button>
       </div>
     </el-drawer>

+ 20 - 4
src/components/MoreFilters/src/components/PartiesView.vue

@@ -1,9 +1,11 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
 import { useFiltersStore } from '@/stores/modules/filtersList'
 import { useRoute } from 'vue-router'
 
 const route = useRoute()
 const searchMode = route.path.includes('booking') ? 'booking' : 'tracking'
+const { t } = useI18n()
 
 const filtersStore = useFiltersStore()
 const filtersList = computed(() => filtersStore.filtersList)
@@ -24,6 +26,20 @@ const pageData = ref([
     placeholder: 'Please input consignee name'
   }
 ])
+const getPartyLabel = (title: string) => {
+  const map = {
+    'Shipper Name': t('moreFilters.shipperName'),
+    'Consignee Name': t('moreFilters.consigneeName'),
+    'Origin Agent': t('moreFilters.originAgent'),
+    'Destination Agent': t('moreFilters.destinationAgent'),
+    Sales: t('moreFilters.sales'),
+    'Notify Party': t('moreFilters.notifyParty'),
+    'Bill to': t('moreFilters.billTo'),
+    'Destination Operator': t('moreFilters.destinationOperator'),
+    'Controlling Customer': t('moreFilters.controllingCustomer')
+  }
+  return map[title] || title
+}
 const changeAutoSelect = (data: any, title: string) => {
   const index = pageData.value.findIndex((item: any) => item.title === title)
   pageData.value[index].value = data
@@ -188,12 +204,12 @@ defineExpose({
 <template>
   <div>
     <div class="ETD" v-for="item in pageData" :key="item.title">
-      <div class="ETD_title">{{ item.title }}</div>
+      <div class="ETD_title">{{ getPartyLabel(item.title) }}</div>
       <AutoSelect
         :type="item.type"
         :title="item.title"
         :data="item.value"
-        :placeholder="item.placeholder"
+        :placeholder="t(item.title === 'Shipper Name' ? 'moreFilters.inputShipperName' : 'moreFilters.inputConsigneeName')"
         @changeAutoSelect="changeAutoSelect"
       >
       </AutoSelect>
@@ -205,11 +221,11 @@ defineExpose({
       @deleteItem="deleteItem"
       @changeTitle="changeTitle"
       @changeValue="changeValue"
-      placeholder="Please input party name"
+      :placeholder="t('moreFilters.inputPartyName')"
     >
     </SelectAutoSelect>
     <div class="MoreType" @click="addType" v-if="partyTypeOptions.length != moreList.length">
-      <el-button class="el-button--noborder moretype">+ More Party Type</el-button>
+      <el-button class="el-button--noborder moretype">+ {{ t('moreFilters.morePartyType') }}</el-button>
     </div>
   </div>
 </template>

+ 16 - 3
src/components/MoreFilters/src/components/PlacesView.vue

@@ -1,10 +1,12 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
 import { useFiltersStore } from '@/stores/modules/filtersList'
 import { cloneDeep } from 'lodash'
 import { useRoute } from 'vue-router'
 
 const route = useRoute()
 const searchMode = route.path.includes('booking') ? 'booking' : 'tracking'
+const { t } = useI18n()
 
 const filtersStore = useFiltersStore()
 const filtersList = computed(() => filtersStore.filtersList)
@@ -41,6 +43,17 @@ const placeTypeOptions = ref([
     type: 'uncode'
   }
 ])
+const getPlaceLabel = (title: string) => {
+  const map = {
+    Origin: t('moreFilters.origin'),
+    Destination: t('moreFilters.destination'),
+    'Place of delivery': t('moreFilters.placeOfDelivery'),
+    ' Place of Receipt': t('moreFilters.placeOfReceipt'),
+    'Port of Loading': t('moreFilters.portOfLoading'),
+    'Place of Discharge': t('moreFilters.placeOfDischarge')
+  }
+  return map[title] || title
+}
 if (searchMode === 'tracking') {
   placeTypeOptions.value.push({
     title: 'Place of Discharge',
@@ -179,7 +192,7 @@ defineExpose({
 
 <template>
   <div class="ETD" v-for="item in pageData">
-    <div class="ETD_title">{{ item.title }}</div>
+    <div class="ETD_title">{{ getPlaceLabel(item.title) }}</div>
     <SelectTable :title="item.title" :data="item.value" @input="changePageData" />
   </div>
   <SelectTableSelect
@@ -188,12 +201,12 @@ defineExpose({
     @changeData="changeData"
     @changeTitle="changeTitle"
     @deleteType="deleteType"
-    placeholder="Please input places name"
+    :placeholder="t('moreFilters.inputPlacesName')"
   >
   </SelectTableSelect>
   <!-- More Place Type -->
   <div class="MoreType" @click="addType()" v-if="moreList.length !== placeTypeOptions.length">
-    <el-button class="el-button--noborder moretype">+ More Place Type</el-button>
+      <el-button class="el-button--noborder moretype">+ {{ t('moreFilters.morePlaceType') }}</el-button>
   </div>
 </template>
 

+ 23 - 8
src/components/MoreFilters/src/components/SelectValue.vue

@@ -1,4 +1,7 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
 import type { DropdownInstance } from 'element-plus'
 import { cloneDeep } from 'lodash'
 import { useFiltersStore } from '@/stores/modules/filtersList'
@@ -33,6 +36,18 @@ watch(
 )
 
 const dropdown1 = ref<DropdownInstance>()
+const displayTitle = computed(() => {
+  const map = {
+    Incoterms: t('common.incoterms'),
+    Service: t('common.service'),
+    Vessel: t('moreFilters.vessel'),
+    'Voyage/Flight': t('moreFilters.voyageFlight')
+  }
+  return map[props.title] || props.title
+})
+const displaySelectData = computed(() => {
+  return selectData.value === 'All' ? t('common.all') : selectData.value
+})
 
 const handleCheckAll = (val: any) => {
   transportList.value.forEach((item: any) => {
@@ -153,12 +168,12 @@ defineExpose({
     >
       <div class="el-dropdown-link">
         <div v-if="props.title == 'Incoterms' && !selectData" class="select_title">
-          Please Select Date Range
+          {{ t('common.pleaseSelectDateRange') }}
         </div>
         <div v-else-if="props.title == 'Service' && !selectData" class="select_title">
-          Please Select Service
+          {{ t('common.pleaseSelectService') }}
         </div>
-        <div v-else class="select_title_2">{{ selectData }}</div>
+        <div v-else class="select_title_2">{{ displaySelectData }}</div>
         <span class="iconfont_icon">
           <svg class="iconfont icon_dark" aria-hidden="true">
             <use xlink:href="#icon-icon_dropdown_b"></use>
@@ -167,11 +182,11 @@ defineExpose({
       </div>
       <template #dropdown>
         <div class="dropdownwidth">
-          <div class="title" style="margin-bottom: 3px">{{ props.title }}</div>
+          <div class="title" style="margin-bottom: 3px">{{ displayTitle }}</div>
           <el-dropdown-menu>
             <el-dropdown-item>
               <el-checkbox v-model="checkAll" class="checkbox" @change="handleCheckAll">
-                <div class="checkbox_title">Select All</div>
+                <div class="checkbox_title">{{ t('common.selectAll') }}</div>
               </el-checkbox>
             </el-dropdown-item>
             <el-divider style="border-color: var(--input-border)"></el-divider>
@@ -195,14 +210,14 @@ defineExpose({
                   <use xlink:href="#icon-icon_dropdown__line_b"></use>
                 </svg>
               </span>
-              See All
+              {{ t('common.seeAll') }}
             </div>
             <div class="transport_bottom">
               <div>
-                <el-button class="clear" type="default" @click="clearList">Reset</el-button>
+                <el-button class="clear" type="default" @click="clearList">{{ t('common.reset') }}</el-button>
               </div>
               <div>
-                <el-button class="search el-button--dark" @click="TransportSearch">OK</el-button>
+                <el-button class="search el-button--dark" @click="TransportSearch">{{ t('common.ok') }}</el-button>
               </div>
             </div>
           </el-dropdown-menu>

+ 5 - 2
src/components/NotificationMessageCard/src/NotificationMessageCard.vue

@@ -1,4 +1,7 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
 import EventCard from './components/EventCard.vue'
 import PasswordCard from './components/PasswordCard.vue'
 import FeatureUpdateCard from './components/FeatureUpdateCard.vue'
@@ -280,9 +283,9 @@ defineExpose({
         (pageData?.[0]?.notificationType !== 'feature' && pageData?.length > 0) || props.isDrawer
       "
     >
-      <el-divider v-if="loading"> loading... </el-divider>
+      <el-divider v-if="loading"> {{ t('common.loading') }} </el-divider>
       <el-divider v-if="finished && pageData.length > 0">
-        Only display the message data within three months
+        {{ t('notificationMessage.onlyDisplayWithinThreeMonths') }}
       </el-divider>
     </div>
   </div>

+ 11 - 8
src/components/NotificationMessageCard/src/components/EventCard.vue

@@ -1,4 +1,7 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
 import { transportationMode } from '@/components/transportationMode'
 import { useRouter } from 'vue-router'
 import { getTimezone } from '@/utils/tools'
@@ -80,7 +83,7 @@ const jumpTracking = (data: EventCardPropsData) => {
         class="more-tips"
         v-if="(data.type === 'milestone' || data.type === 'container') && data.numericRecords"
       >
-        <span>Latest Status Updates ({{ data.numericRecords }}) </span>
+        <span>{{ t('notificationMessage.latestStatusUpdates', { count: data.numericRecords }) }} </span>
         <el-button
           @click="
             handleSeeAll(data, {
@@ -89,14 +92,14 @@ const jumpTracking = (data: EventCardPropsData) => {
           "
           class="see-all-icon el-button--text"
         >
-          See All
+          {{ t('common.seeAll') }}
           <span class="font_family icon-icon_next_b"></span>
         </el-button>
       </div>
       <div class="more-tips" v-if="data.info?.etdOrdeparturNum || data.info?.etaOrarrivalNum">
         <div>
           <span v-if="data.info?.etdOrdeparturNum"
-            >{{ data.type === 'delay' ? 'Departure Delay' : 'ETD Change' }} ({{
+            >{{ data.type === 'delay' ? t('notificationMessage.departureDelay') : t('notificationMessage.etdChange') }} ({{
               data.info?.etdOrdeparturNum
             }})</span
           >
@@ -104,7 +107,7 @@ const jumpTracking = (data: EventCardPropsData) => {
             &nbsp;&nbsp;|&nbsp;&nbsp;</span
           >
           <span v-if="data.info?.etaOrarrivalNum">
-            {{ data.type === 'delay' ? 'Arrival Delay' : 'ETA Change' }} ({{
+            {{ data.type === 'delay' ? t('notificationMessage.arrivalDelay') : t('notificationMessage.etaChange') }} ({{
               data.info?.etaOrarrivalNum
             }})
           </span>
@@ -118,7 +121,7 @@ const jumpTracking = (data: EventCardPropsData) => {
           "
           class="see-all-icon el-button--text"
         >
-          See All
+          {{ t('common.seeAll') }}
           <span class="font_family icon-icon_next_b"></span>
         </el-button>
       </div>
@@ -134,7 +137,7 @@ const jumpTracking = (data: EventCardPropsData) => {
         <!-- container类型显示图标 -->
         <div v-else>
           <span class="font_family icon-icon_container__filled_b"></span>
-          <span class="no">Container: {{ data.no }}</span>
+          <span class="no">{{ t('containerStatus.container') }}: {{ data.no }}</span>
         </div>
         <div class="tag" :class="{ delay: data.type === 'delay', change: data.type === 'change' }">
           <span class="dot"></span>
@@ -143,7 +146,7 @@ const jumpTracking = (data: EventCardPropsData) => {
       </div>
       <div class="route" v-if="data?.info?.route?.length > 0">
         <span class="font_family icon-icon_route_b"></span>
-        <span>Route:&nbsp;</span>
+        <span>{{ t('notificationMessage.route') }}:&nbsp;</span>
         <template v-for="(item, index) in data.info.route" :key="index">
           <span>{{ item }}</span
           ><span style="margin: 0 3px" v-if="index !== data.info.route.length - 1">→</span>
@@ -152,7 +155,7 @@ const jumpTracking = (data: EventCardPropsData) => {
       <!-- change多程情况中的Leg-->
       <div class="location" v-if="data?.info?.leg?.length > 0">
         <span class="font_family icon-icon_location_b"></span>
-        <span>Current Leg:&nbsp;</span>
+        <span>{{ t('notificationMessage.currentLeg') }}:&nbsp;</span>
         <template v-for="(item, index) in data.info.leg" :key="index">
           <span>{{ item }}</span
           ><span style="margin: 0 3px" v-if="index !== data.info.leg.length - 1">→</span>

+ 6 - 3
src/components/NotificationMessageCard/src/components/FeatureUpdateCard.vue

@@ -1,4 +1,7 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
 import { useRouter } from 'vue-router'
 
 const router = useRouter()
@@ -34,9 +37,9 @@ const handleViewMore = () => {
 
 const handleContent = (header) => {
   if (header === 'New Feature: AI Smart Assistant') {
-    return 'Smart Assistant is live! Ask in natural language, get real-time shipment data with multi-language support.'
-  }else {
-    return 'Smart Notification is here! Four key event alerts with customizable rules and multi-channel delivery.'
+    return t('notificationMessage.smartAssistantContent')
+  } else {
+    return t('notificationMessage.smartNotificationContent')
   }
 }
 </script>

+ 3 - 1
src/components/NotificationMessageCard/src/components/PasswordCard.vue

@@ -1,5 +1,7 @@
 <script setup lang="ts">
 import ChangePasswordDialog from '@/views/Layout/src/components/Header/components/ChangePasswordDialog.vue'
+import { useI18n } from 'vue-i18n'
+const { t } = useI18n()
 
 interface PasswordCardPropsData {
   title: string
@@ -35,7 +37,7 @@ const handleChangePassword = () => {
       <div class="change-btn" style="text-align: center">
         <el-button @click="handleChangePassword" class="el-button--main" style="height: 40px">
           <span class="font_family icon-icon_edit_b" style="margin-right: 4px"></span>
-          <span>Change Password</span></el-button
+          <span>{{ t('login.changePassword') }}</span></el-button
         >
       </div>
     </div>

+ 4 - 2
src/components/ScoringGrade/components/DialogColorful.vue

@@ -1,8 +1,10 @@
 <script setup lang="ts">
 import { ref } from 'vue'
 import { useThemeStore } from '@/stores/modules/theme'
+import { useI18n } from 'vue-i18n'
 import BubbleLight from '../image/bubble_corner_colorful.png'
 import BubbleDark from '../image/bubble_corner_colorful_darkmode.png'
+const { t } = useI18n()
 const props = defineProps({
   colorfulSrc: String,
   isshowexpression: Boolean,
@@ -33,11 +35,11 @@ const themeStore = useThemeStore()
           style="width: 554px"
           :rows="4"
           type="textarea"
-          placeholder="We look forward to hearing from you. Thank you for helping to build a better customer system."
+          :placeholder="t('scoringGrade.feedbackPlaceholder')"
         />
         <div class="button_submit">
           <el-button class="submit_button el-button--dark" @click="submitDetails(inputdetails)"
-            >Submit</el-button
+            >{{ t('common.submit') }}</el-button
           >
         </div>
       </div>

+ 21 - 4
src/components/ScoringGrade/components/DialogUe.vue

@@ -1,5 +1,6 @@
 <script setup lang="ts">
 import { ref } from 'vue'
+import { useI18n } from 'vue-i18n'
 import { useThemeStore } from '@/stores/modules/theme'
 import BubbleLight from '../image/bubble_corner.png'
 import BubbleDark from '../image/bubble_corner_darkmode.png'
@@ -13,6 +14,7 @@ const props = defineProps({
 })
 const checkboxGroup1 = ref([])
 const checkboxGroup2 = ref([])
+const { t } = useI18n()
 const evaluate = [
   'Functionality',
   'Data accuracy',
@@ -20,6 +22,21 @@ const evaluate = [
   'Ease of use',
   'Website performance'
 ]
+const getAspectLabel = (key: string) => {
+  const map = {
+    Functionality: t('scoringGrade.aspectFunctionality'),
+    'Data accuracy': t('scoringGrade.aspectDataAccuracy'),
+    'Look and feel': t('scoringGrade.aspectLookAndFeel'),
+    'Ease of use': t('scoringGrade.aspectEaseOfUse'),
+    'Website performance': t('scoringGrade.aspectWebsitePerformance'),
+    'Highly Dissatisfied': t('scoringGrade.highlyDissatisfied'),
+    Dissatisfied: t('scoringGrade.dissatisfied'),
+    Neutral: t('scoringGrade.neutral'),
+    Satisfied: t('scoringGrade.satisfied'),
+    'Highly Satisfied': t('scoringGrade.highlySatisfied')
+  }
+  return map[key] || key
+}
 const happyevaluate = [
   'Functionality',
   'Data accuracy',
@@ -94,7 +111,7 @@ const themeStore = useThemeStore()
             :key="item"
             :value="item"
           >
-            {{ item }}
+            {{ getAspectLabel(item) }}
           </el-checkbox-button>
         </el-checkbox-group>
       </div>
@@ -106,7 +123,7 @@ const themeStore = useThemeStore()
             :key="item"
             :value="item"
           >
-            {{ item }}
+            {{ getAspectLabel(item) }}
           </el-checkbox-button>
         </el-checkbox-group>
       </div>
@@ -115,12 +132,12 @@ const themeStore = useThemeStore()
           <div class="smile_title_left"></div>
           <div class="smile_title_right">
             <div class="smile_title" v-for="item in Aspects" :key="item">
-              {{ item }}
+              {{ getAspectLabel(item) }}
             </div>
           </div>
         </div>
         <div class="smile_content_flex" v-for="(item, index) in smileAspects" :key="index">
-          <div class="smile_title_left content_left">{{ item.title }}</div>
+          <div class="smile_title_left content_left">{{ getAspectLabel(item.title) }}</div>
           <div class="smile_title_right content_right">
             <el-radio-group v-model="item.radio" @change="changeSmileRadio(item.title, item.radio)">
               <el-radio

+ 23 - 21
src/components/ScoringGrade/src/ScoringGrade.vue

@@ -16,8 +16,10 @@ import normalPng from '../image/score_normal.png'
 import submitsucessful from '../image/submit_successful.png'
 import { useUserStore } from '@/stores/modules/user'
 import emitter from '@/utils/bus'
+import { useI18n } from 'vue-i18n'
 
 const userStore = useUserStore()
+const { t } = useI18n()
 
 // const isShow = ref(true)
 const visible = ref(false)
@@ -50,41 +52,41 @@ avater.value = [
     src: angryPng,
     src1: angryPng,
     itemsrc: angryPng2,
-    itemtext: 'We are so sorry for the inconveniences. We value your experience immensely. ',
+    itemtext: t('scoringGrade.apologyMessage'),
     expression: 'angry',
-    proposal: 'Could you please tell us which aspects of the system you are dissatisfied with?'
+    proposal: t('scoringGrade.dissatisfiedAspectQuestion')
   },
   {
     src: sadPng,
     src1: sadPng,
     itemsrc: sadPng2,
-    itemtext: 'We are so sorry for the inconveniences. We value your experience immensely. ',
+    itemtext: t('scoringGrade.apologyMessage'),
     expression: 'sad',
-    proposal: 'Could you please tell us which aspects of the system you are dissatisfied with?'
+    proposal: t('scoringGrade.dissatisfiedAspectQuestion')
   },
   {
     src: smilePng,
     src1: smilePng,
     itemsrc: smilePng2,
-    itemtext: 'Thank you for sharing your thoughts with us. We value your experience greatly.',
+    itemtext: t('scoringGrade.thanksForSharing'),
     expression: 'smile',
-    proposal: 'Could you share what aspects you liked and what could be improved ?'
+    proposal: t('scoringGrade.shareLikedAndImprovement')
   },
   {
     src: hhhPng,
     src1: hhhPng,
     itemsrc: hhhPng2,
-    itemtext: 'Thank you very much for giving us a satisfied rating on our service or system.',
+    itemtext: t('scoringGrade.thanksForSatisfiedRating'),
     expression: 'hhh',
-    proposal: 'We are curious to learn more about what specifically made you feel satisfied. '
+    proposal: t('scoringGrade.whatMadeYouSatisfied')
   },
   {
     src: happyPng,
     src1: happyPng,
     itemsrc: happyPng2,
-    itemtext: 'Thank you very much for giving us a satisfied rating on our service or system.',
+    itemtext: t('scoringGrade.thanksForSatisfiedRating'),
     expression: 'happy',
-    proposal: 'We are curious to learn more about what specifically made you feel satisfied. '
+    proposal: t('scoringGrade.whatMadeYouSatisfied')
   }
 ]
 const dialogVisible = ref(false)
@@ -105,35 +107,35 @@ const showScore = (item: any) => {
     isLoaded.value = true
     dialogcontent.value = item.itemtext
     if (item.expression == 'angry') {
-      SubmitText.value = 'Apologize once again for your experience.'
+      SubmitText.value = t('scoringGrade.apologizeAgain')
       checkexpression.value = 'angry'
       setTimeout(() => {
         isShowAngry.value = true
         angryproposal.value = item.proposal
       }, 1000)
     } else if (item.expression == 'sad') {
-      SubmitText.value = 'Apologize once again for your experience.'
+      SubmitText.value = t('scoringGrade.apologizeAgain')
       checkexpression.value = 'sad'
       setTimeout(() => {
         isShowAngry.value = true
         angryproposal.value = item.proposal
       }, 1000)
     } else if (item.expression == 'smile') {
-      SubmitText.value = 'We greatly appreciate your valuable time and feedback.'
+      SubmitText.value = t('scoringGrade.appreciateFeedback')
       checkexpression.value = 'smile'
       setTimeout(() => {
         isShowSmile.value = true
         smileproposal.value = item.proposal
       }, 1000)
     } else if (item.expression == 'hhh') {
-      SubmitText.value = 'Once again, thank you for your positive evaluation.'
+      SubmitText.value = t('scoringGrade.thanksPositiveEvaluation')
       checkexpression.value = 'hhh'
       setTimeout(() => {
         isShowHappy.value = true
         angryproposal.value = item.proposal
       }, 1000)
     } else if (item.expression == 'happy') {
-      SubmitText.value = 'Once again, thank you for your positive evaluation.'
+      SubmitText.value = t('scoringGrade.thanksPositiveEvaluation')
       checkexpression.value = 'happy'
       setTimeout(() => {
         isShowHappy.value = true
@@ -309,7 +311,7 @@ onMounted(() => {
           v-for="(item, index) in avater"
           :key="index"
           popper-class="avater_popver"
-          content="How satisfied are you with this system?"
+          :content="t('scoringGrade.satisfactionQuestion')"
         >
           <template #reference>
             <div class="score">
@@ -328,7 +330,7 @@ onMounted(() => {
     </el-popover>
     <el-dialog
       v-model="dialogVisible"
-      title="Hi!"
+      :title="t('scoringGrade.hi')"
       :destroy-on-close="true"
       @close="closeDialog"
       width="640"
@@ -339,7 +341,7 @@ onMounted(() => {
       :close-on-click-modal="false"
     >
       <div v-if="isshowDetails">
-        <DialogUe content="How satisfied are you with this system ?" dialogWidth="298px"></DialogUe>
+        <DialogUe :content="t('scoringGrade.satisfactionQuestionWithSpace')" dialogWidth="298px"></DialogUe>
         <DialogColorful
           :colorfulSrc="colorfulSrc"
           :isshowexpression="isshowexpression"
@@ -373,7 +375,7 @@ onMounted(() => {
         </transition>
         <transition name="fade" v-if="isLoaded_share">
           <DialogUe
-            content="Would you like to share more details with us?"
+            :content="t('scoringGrade.shareMoreDetails')"
             dialogWidth="334px"
           ></DialogUe>
         </transition>
@@ -388,7 +390,7 @@ onMounted(() => {
         class="result"
         v-if="isshowDetails_submit"
         icon="success"
-        title="Submit Successful"
+        :title="t('scoringGrade.submitSuccessful')"
       >
         <template #icon>
           <el-image :src="submitsucessful" />
@@ -396,7 +398,7 @@ onMounted(() => {
         <template #sub-title>
           <div class="sub_title_text">{{ SubmitText }}</div>
           <div class="sub_title_text">
-            We are committed to working hard to provide better services.
+            {{ t('scoringGrade.betterServiceCommitment') }}
           </div>
         </template>
       </el-result>

+ 3 - 1
src/components/SeeAllIcon/src/SeeAllIcon.vue

@@ -1,4 +1,6 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
+const { t } = useI18n()
 const isCollapse = defineModel<boolean>()
 const emits = defineEmits<{ collapse: [boolean] }>()
 const handleSeeAll = () => {
@@ -12,7 +14,7 @@ const handleSeeAll = () => {
 
 <template>
   <div class="see-all-icon" @click="handleSeeAll">
-    <span class="btn">See All</span>
+    <span class="btn">{{ t('common.seeAll') }}</span>
     <span
       class="icon font_family icon-icon_dropdown__line_b"
       :class="{ 'is-rotate': isCollapse }"

+ 9 - 7
src/components/SelectTable/src/SelectTable.vue

@@ -1,4 +1,5 @@
 <script setup lang="ts" name="SelTable">
+import { useI18n } from 'vue-i18n'
 import { ref, reactive, watch, onUnmounted, nextTick } from 'vue'
 import { ElMessage } from 'element-plus'
 import { formatNumber } from '@/utils/tools'
@@ -8,6 +9,7 @@ import { useRoute } from 'vue-router'
 import axios from 'axios'
 
 const filtersStore = useFiltersStore()
+const { t } = useI18n()
 const route = useRoute()
 const searchMode = route.path.includes('booking') ? 'booking' : 'tracking'
 
@@ -84,7 +86,7 @@ const fetchData = async (query: string, page: number, isPageChange = false) => {
     }
   } catch (err) {
     if (!axios.isCancel(err) && !searchAbortController?.signal.aborted) {
-      ElMessage.error('Failed to load data')
+      ElMessage.error(t('common.failedToLoadData'))
       state.tableData = []
       state.total = 0
     }
@@ -120,7 +122,7 @@ const handlePageChange = () => {
 const handleRowClick = (row: any) => {
   const keyVal = row[props.needKey]
   if (innerTags.value.includes(keyVal)) {
-    ElMessage.success('Cannot add duplicate cities.')
+    ElMessage.success(t('common.cannotAddDuplicateCities'))
     return
   }
 
@@ -230,7 +232,7 @@ onUnmounted(() => {
               <input
                 v-model="searchVal"
                 ref="tagInputRef"
-                :placeholder="innerTags.length ? '' : 'Please input country/city/uncode'"
+                :placeholder="innerTags.length ? '' : t('common.inputCountryCityUncode')"
                 class="el-input__inner"
                 :disabled="props.disabled"
                 type="text"
@@ -260,14 +262,14 @@ onUnmounted(() => {
         header-row-class-name="cus-header"
         style="width: 100%"
       >
-        <el-table-column property="country" label="Country" width="75" />
-        <el-table-column property="city" label="City" />
-        <el-table-column property="uncode" label="Uncode" width="80" />
+        <el-table-column property="country" :label="t('common.country')" width="75" />
+        <el-table-column property="city" :label="t('common.city')" />
+        <el-table-column property="uncode" :label="t('common.uncode')" width="80" />
       </el-table>
 
       <!-- 分页 -->
       <div class="pagination">
-        <span>Total {{ formatNumber(state.total) }}</span>
+        <span>{{ t('common.total') }} {{ formatNumber(state.total) }}</span>
         <el-pagination
           v-model:currentPage="state.currentPage"
           v-model:page-size="state.pageSize"

+ 7 - 4
src/components/SelectTableSelect/src/SelectTableSelect.vue

@@ -1,4 +1,7 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
 import { ref, watch } from 'vue'
 import IconDropDown from '@/components/IconDropDown'
 import SelectTable from '@/components/SelectTable'
@@ -67,7 +70,7 @@ const changePageData = (data: any, title: string) => {
   <div class="AddType" v-for="item in pageData" :key="item.id">
     <div>
       <div class="Date_Title">
-        <div class="ETD_title">Places Type</div>
+        <div class="ETD_title">{{ t('common.placesType') }}</div>
         <span class="iconfont_icon" @click="deleteType(item.id)">
           <svg class="iconfont icon_delete" aria-hidden="true">
             <use xlink:href="#icon-icon_delete_b"></use>
@@ -78,7 +81,7 @@ const changePageData = (data: any, title: string) => {
         :model-value="item.title"
         :suffix-icon="IconDropDown"
         @change="changeTitle($event, item)"
-        placeholder="Please Select Party Type"
+        :placeholder="t('common.pleaseSelectPartyType')"
       >
         <el-option
           v-for="type in getAvailableOptions(item.title)"
@@ -90,7 +93,7 @@ const changePageData = (data: any, title: string) => {
       </el-select>
     </div>
     <div style="margin-top: 16px">
-      <div class="ETD_title">Places Details</div>
+      <div class="ETD_title">{{ t('common.placesDetails') }}</div>
       <SelectTable
         ref="selectTableRef"
         :title="item.title"
@@ -101,7 +104,7 @@ const changePageData = (data: any, title: string) => {
       />
 
       <div class="error" v-if="item.title != '' && item.value.length === 0">
-        Please Input Places Details
+        {{ t('common.pleaseInputPlacesDetails') }}
       </div>
     </div>
   </div>

+ 3 - 1
src/components/ShipmentStatus/src/ShipmentStatus.vue

@@ -1,6 +1,8 @@
 <script setup lang="ts">
 import { useOverflow } from '@/hooks/useOverflow'
 import { formatTimezone } from '@/utils/tools'
+import { useI18n } from 'vue-i18n'
+const { t } = useI18n()
 
 const props = defineProps({
   data: Object
@@ -161,7 +163,7 @@ const { isOverflow: isPathOverflow } = useOverflow(pathRef, props)
               </div>
               <div class="bottom">
                 <span class="font_family icon-icon_container_b"></span>
-                <div style="margin-top: 1px; margin-left: 4px">Container:</div>
+                <div style="margin-top: 1px; margin-left: 4px">{{ t('containerStatus.container') }}:</div>
                 <div style="margin-left: 4px; margin-top: 3px; font-weight: 700">
                   {{ deliveredItem.container }}
                 </div>

+ 3 - 1
src/components/TableImgEmpty/TableImgEmpty.vue

@@ -1,10 +1,12 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
 import { computed } from 'vue'
 import lightPng from './image/empty-light.png'
 import darkPng from './image/empty-dark.png'
 import { useThemeStore } from '@/stores/modules/theme'
 
 const themeStore = useThemeStore()
+const { t } = useI18n()
 // 判断当前系统主题模式
 const emptyImg = computed(() => {
   return themeStore.theme === 'dark' ? darkPng : lightPng
@@ -18,7 +20,7 @@ const props = defineProps({
 <template>
   <div class="table-img-empty">
     <img :src="emptyImg" />
-    <div class="empty-text">No data</div>
+    <div class="empty-text">{{ t('common.noData') }}</div>
   </div>
 </template>
 

+ 11 - 8
src/components/TransportMode/src/TransportMode.vue

@@ -1,4 +1,7 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
 import type { DropdownInstance } from 'element-plus'
 import { cloneDeep } from 'lodash'
 import { useFiltersStore } from '@/stores/modules/filtersList'
@@ -95,7 +98,7 @@ const handleDropdownVisibleChange = (visible: boolean) => {
       @visible-change="handleDropdownVisibleChange"
     >
       <div class="el-dropdown-link">
-        <div class="select_title">Transport Mode</div>
+        <div class="select_title">{{ t('common.transportMode') }}</div>
         <span class="iconfont_icon">
           <svg class="iconfont icon_dark" aria-hidden="true">
             <use xlink:href="#icon-icon_dropdown_b"></use>
@@ -104,7 +107,7 @@ const handleDropdownVisibleChange = (visible: boolean) => {
       </div>
       <template #dropdown>
         <div class="dropdownwidth">
-          <div class="title">Transport Mode</div>
+          <div class="title">{{ t('common.transportMode') }}</div>
           <el-dropdown-menu>
             <el-dropdown-item>
               <el-checkbox v-model="checkAll" class="checkbox" @change="handleCheckAllChange">
@@ -114,7 +117,7 @@ const handleDropdownVisibleChange = (visible: boolean) => {
                       <use xlink:href="#icon-icon_all_b"></use>
                     </svg>
                   </span>
-                  Select All
+                  {{ t('common.selectAll') }}
                 </div>
                 <div class="checkbox_number">({{ totalNumber }})</div>
               </el-checkbox>
@@ -133,19 +136,19 @@ const handleDropdownVisibleChange = (visible: boolean) => {
                       <use :xlink:href="item.icon"></use>
                     </svg>
                   </span>
-                  {{ item.name }}
+                  {{ item.name === 'All' ? t('common.all') : item.name }}
                 </div>
                 <div class="checkbox_number">({{ item.number }})</div>
               </el-checkbox>
             </el-dropdown-item>
             <div class="transport_bottom">
               <div>
-                <el-button class="clear" type="default" @click="clearList">Reset</el-button>
+                <el-button class="clear" type="default" @click="clearList">{{ t('common.reset') }}</el-button>
               </div>
               <div>
-                <el-button class="search el-button--dark" @click="transportSearch"
-                  >Search</el-button
-                >
+                <el-button class="search el-button--dark" @click="transportSearch">
+                  {{ t('common.search') }}
+                </el-button>
               </div>
             </div>
           </el-dropdown-menu>

+ 4 - 2
src/components/VBox/src/VBox.vue

@@ -1,4 +1,6 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
+const { t } = useI18n()
 const props = withDefaults(
   defineProps<{
     id?: number
@@ -48,7 +50,7 @@ const vBoxPopoverRef = ref()
 <template>
   <div class="v-box">
     <div class="header">
-      <slot name="header">Title</slot>
+      <slot name="header">{{ t('common.title') }}</slot>
       <div class="option">
         <SeeAllIcon
           v-if="props.isSeeAll"
@@ -70,7 +72,7 @@ const vBoxPopoverRef = ref()
               :key="item.name"
             >
               <span class="font_family" :class="[`icon-${item.icon}`]"></span>
-              <span>{{ item.name }}</span>
+              <span>{{ t(`vbox.${item.id === 1 ? 'moveToTop' : item.id === 2 ? 'moveUp' : item.id === 3 ? 'moveDown' : 'moveToBottom'}`) }}</span>
             </div>
           </div>
           <template #reference v-if="props.isDraggable">

+ 3 - 1
src/components/VBox_Dashboard/src/VBox_Dashboard.vue

@@ -1,5 +1,7 @@
 <script setup lang="ts">
 import { ref } from 'vue'
+import { useI18n } from 'vue-i18n'
+const { t } = useI18n()
 const props = withDefaults(
   defineProps<{
     id?: number
@@ -44,7 +46,7 @@ const vBoxPopoverRef = ref()
 <template>
   <div class="v-box">
     <div class="header">
-      <slot name="header">Title</slot>
+      <slot name="header">{{ t('common.title') }}</slot>
       <div class="option" style="width: 48px; height: 48px">
         <el-button
           type="text"

+ 7 - 5
src/components/VBreadcrumb/src/VBreadcrumb.vue

@@ -2,10 +2,12 @@
 import { useRouter } from 'vue-router'
 import { useBreadCrumb } from '@/stores/modules/breadCrumb'
 import { useThemeStore } from '@/stores/modules/theme'
+import { useI18n } from 'vue-i18n'
 
 const router = useRouter()
 const breadCrumb = useBreadCrumb()
 const themeStore = useThemeStore()
+const { t } = useI18n()
 const CancelRulesVisible = ref(false)
 
 const handleGoBack = () => {
@@ -67,15 +69,15 @@ const jumpLinkMonitoring = () => {
   <div v-else></div>
   <!-- 取消保存 -->
   <el-dialog v-model="CancelRulesVisible" width="480">
-    <div style="font-weight: 400">You have unsaved changes.</div>
-    <div style="font-weight: 400">Are you sure you want to leave this page?</div>
+    <div style="font-weight: 400">{{ t('destinationDelivery.unsavedChanges') }}</div>
+    <div style="font-weight: 400">{{ t('destinationDelivery.confirmLeavePage') }}</div>
     <template #footer>
       <div class="dialog-footer">
         <el-button type="default" @click="CancelRulesVisible = false" style="width: 100px"
-          >Cancel</el-button
+          >{{ t('common.cancel') }}</el-button
         >
         <el-button class="el-button--warning" @click="jumpLinkMonitoring" style="width: 100px">
-          OK
+          {{ t('common.ok') }}
         </el-button>
       </div>
     </template>
@@ -86,7 +88,7 @@ const jumpLinkMonitoring = () => {
             <use xlink:href="#icon-icon_tipsfilled_b"></use>
           </svg>
         </span>
-        Unsaved Changes
+        {{ t('destinationDelivery.unsavedChangesTitle') }}
       </div>
     </template>
   </el-dialog>

+ 5 - 2
src/components/VDriverGuide/src/VDriverGuide.vue

@@ -1,10 +1,13 @@
-<script setup lang="ts"></script>
+<script setup lang="ts">
+import { useI18n } from 'vue-i18n'
+const { t } = useI18n()
+</script>
 
 <template>
   <div id="page-guide-btn-guide" class="driver-guide-btn">
     <img src="./img/guide-icon.png" alt="" />
     <span style="margin-top: 2px; font-size: 12px; font-style: italic; color: var(--color-theme)">
-      Page Guide
+      {{ t('common.pageGuide') }}
     </span>
   </div>
 </template>

+ 5 - 3
src/components/VEmpty/src/VEmpty.vue

@@ -2,8 +2,10 @@
 import lightPng from './images/default_image.png'
 import darkPng from './images/default_dark_image.png'
 import { useThemeStore } from '@/stores/modules/theme'
+import { useI18n } from 'vue-i18n'
 
 const themeStore = useThemeStore()
+const { t } = useI18n()
 // 判断当前系统主题模式
 const emptyImg = computed(() => {
   return themeStore.theme === 'dark' ? darkPng : lightPng
@@ -16,11 +18,11 @@ const emptyImg = computed(() => {
       <img :src="emptyImg" alt="" />
     </div>
     <p class="title">
-      <slot name="title">No Results Found</slot>
+      <slot name="title">{{ t('common.noResultsFound') }}</slot>
     </p>
     <slot name="result">
-      <p class="light">We didn't find any search results,</p>
-      <p class="light">please try to adjust your search keywords.</p>
+      <p class="light">{{ t('common.emptyResultLine1') }}</p>
+      <p class="light">{{ t('common.emptyResultLine2') }}</p>
     </slot>
     <div class="suggestion">
       <slot name="suggestion"></slot>

+ 3 - 1
src/components/VLoading/src/VLoading.vue

@@ -1,4 +1,5 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
 /**
  *
  * @param {string} target - 挂载的目标元素
@@ -15,6 +16,7 @@ const props = withDefaults(defineProps<internalProps>(), {
   target: 'body',
   isLoadingBackground: false
 })
+const { t } = useI18n()
 </script>
 <template>
   <Teleport :to="props.target">
@@ -26,7 +28,7 @@ const props = withDefaults(defineProps<internalProps>(), {
       <div class="v-loading-spinner">
         <div>
           <img class="circular" src="./images/icon_loading.png" alt="" />
-          <p class="loading-text">Loading...</p>
+          <p class="loading-text">{{ t('common.loading') }}</p>
         </div>
       </div>
     </div>

+ 8 - 6
src/components/VSliderVerification/src/VSliderVerification.vue

@@ -1,4 +1,5 @@
 <script lang="ts" setup>
+import { useI18n } from 'vue-i18n'
 //滑动图片验证码部分
 import Img01 from './image/verification-img-1.png'
 import Img02 from './image/verification-img-2.png'
@@ -8,6 +9,7 @@ import { ref } from 'vue'
 //引入'vue3-puzzle-vcode'插件
 import Vcode from './components/SliderVerification.vue'
 // import Vcode from 'vue3-puzzle-vcode'
+const { t } = useI18n()
 
 const openDialog = () => {
   isShow.value = true
@@ -42,8 +44,8 @@ const addTipsNode = () => {
       <span class="font_family icon-icon_reject_b close-icon" style="margin-right: -20px;">
       </span>
     </div>
-    <p>Please drag the slider below to complete the</p>
-    <p>verification to ensure normal access</p>
+    <p>${t('common.sliderVerifyTipLine1')}</p>
+    <p>${t('common.sliderVerifyTipLine2')}</p>
   `
   const parentNode = document.querySelector('.vue-auth-box_')
   if (parentNode.firstChild) {
@@ -115,7 +117,7 @@ const onSuccess = () => {
     const observer = new MutationObserver((mutationsList) => {
       mutationsList.forEach((mutation) => {
         if (mutation.type === 'childList') {
-          if ((mutation.target as any).innerHTML === 'Verification successful') {
+          if ((mutation.target as any).innerHTML === t('common.verificationSuccessful')) {
             updateSliderBackground('success')
             addSliderBtnNode('success')
             setTimeout(() => {
@@ -159,10 +161,10 @@ defineExpose({
       @fail="fail"
       :canvasWidth="320"
       :canvasHeight="180"
-      sliderText="Swipe to verify"
+      :sliderText="t('common.swipeToVerify')"
       :sliderSize="38"
-      successText="Verification successful"
-      failText="Verification failed"
+      :successText="t('common.verificationSuccessful')"
+      :failText="t('common.verificationFailed')"
       :range="5"
       :imgs="[Img01, Img02, Img03, Img04]"
     ></Vcode>

+ 7 - 4
src/components/selectAutoSelect/src/selectAutoSelect.vue

@@ -1,4 +1,7 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
 import { ref, watch, onUnmounted } from 'vue'
 import IconDropDown from '@/components/IconDropDown'
 import { cloneDeep, debounce } from 'lodash'
@@ -173,7 +176,7 @@ onUnmounted(() => {
   <div class="addType" v-for="item in pageData" :key="item.id">
     <div>
       <div class="Date_Title">
-        <div class="ETD_title">Party Type</div>
+        <div class="ETD_title">{{ t('common.partyType') }}</div>
         <span class="iconfont_icon" @click="deleteItem(item.title)">
           <svg class="iconfont icon_delete" aria-hidden="true">
             <use xlink:href="#icon-icon_delete_b"></use>
@@ -184,7 +187,7 @@ onUnmounted(() => {
         :model-value="item.title"
         :suffix-icon="IconDropDown"
         @change="changeTitle($event, item)"
-        placeholder="Please Select Party Type"
+        :placeholder="t('common.pleaseSelectPartyType')"
       >
         <el-option
           v-for="type in getAvailableOptions(item.title)"
@@ -195,7 +198,7 @@ onUnmounted(() => {
       </el-select>
     </div>
     <div style="margin-top: 16px">
-      <div class="ETD_title">Party Details</div>
+      <div class="ETD_title">{{ t('common.partyDetails') }}</div>
       <el-select
         :model-value="item.value"
         multiple
@@ -227,7 +230,7 @@ onUnmounted(() => {
         </el-option>
       </el-select>
       <div class="error" v-if="item.value.length === 0 && item.title">
-        Please Input Party Details
+        {{ t('common.pleaseInputPartyDetails') }}
       </div>
     </div>
   </div>

+ 3 - 0
src/directive/VLoading.ts

@@ -1,5 +1,6 @@
 import { createApp, h } from 'vue'
 import Loading from '../components/VLoading'
+import i18n from '../locales'
 
 export const VLoading = {
   mounted(el: any, binding: any) {
@@ -30,6 +31,7 @@ export const VLoading = {
         return h(Loading, { loading: binding.value, ...options })
       }
     })
+    app.use(i18n)
 
     const newDiv = document.createElement('div')
     el.appendChild(newDiv) // 将 Loading 组件的根元素添加到目标元素中
@@ -62,6 +64,7 @@ export const VLoading = {
           return h(Loading, { loading: binding.value, target: `#${target}` })
         }
       })
+      app.use(i18n)
 
       const newDiv = document.createElement('div')
       el.appendChild(newDiv)

+ 908 - 2
src/locales/en.json

@@ -16,6 +16,912 @@
     "newPassword": "New Password",
     "confirmNewPassword": "Confirm New Password",
     "passwordMismatch": "Passwords do not match",
-    "activationEmailSent": "Activation email has been sent to your email"
+    "activationEmailSent": "Activation email has been sent to your email",
+    "welcomeTitle": "Welcome to KLN Portal",
+    "welcomeSubtitle": "Login to your account",
+    "accountNotExist": "This account does not exist.",
+    "accountNotExistShort": "This account does not exist",
+    "loginBtn": "Login",
+    "resetPasswordTitle": "Reset Password",
+    "resetPasswordSubtitle": "We'll send your password to your email address.",
+    "setYourPassword": "Set Your Password",
+    "helloCreateNewPassword": "Hello {name}, please create a new password for your account.",
+    "userName": "User Name",
+    "emailAddress": "Email Address",
+    "sendResetLink": "Send Reset Link",
+    "backToLogin": "Back to login",
+    "usernamePlaceholder": "Please input user name",
+    "passwordPlaceholder": "Please input password",
+    "emailPlaceholder": "Please input your email address",
+    "oldPasswordPlaceholder": "Please input Old Password",
+    "newPasswordComplexityHint": "New password must contain both letter and numeral",
+    "confirmPasswordPlaceholder": "Please Confirm Password",
+    "rememberPassword": "Remember Password",
+    "incorrectPassword": "Incorrect password. Please try again.",
+    "incorrectEmail": "Incorrect email. Please try again.",
+    "passwordSecurityAlert": "Please change your password for security.",
+    "changePassword": "Change Password",
+    "passwordLength12to20": "Password length between 12 - 20",
+    "passwordMustContainUppercase": "Password must contain uppercase letters",
+    "passwordMustContainLowercase": "Password must contain lowercase letters",
+    "passwordMustContainNumber": "Password must contain numbers",
+    "setPasswordAndActivateAccount": "Set Password & Activate Account",
+    "firstLoginChangePassword": "First login, please change your password.",
+    "updateYourPassword": "Update your password",
+    "firstLoginPasswordUpdateTip": "To make your account secure, please create a new password to replace the temporary password you were given in the email.",
+    "passwordResetSuccessfully": "Password Reset Successfully",
+    "passwordResetSuccessfullyLine1": "Your password has been successfully reset.",
+    "passwordResetSuccessfullyLine2": "You can now log in using your email and your new password.",
+    "accountActivatedSuccessfully": "Account Activated Successfully",
+    "accountActivatedSuccessfullyLine1": "Your account has been successfully activated.",
+    "accountActivatedSuccessfullyLine2": "You can now log in using your email and the password you just created.",
+    "goToLogin": "Go to Login",
+    "pleaseLoginFirst": "Please login first"
+  },
+  "common": {
+    "search": "Search",
+    "searchUserNamePlaceholder": "Search user name",
+    "searchQuestionPlaceholder": "Search Question ID、User",
+    "searchChatPlaceholder": "Search Question ID、User",
+    "customerSearchPlaceholder": "Search by Customer code, Customer name",
+    "customerType": "Customer Type",
+    "controllingCustomer": "Controlling Customer",
+    "billTo": "Bill to",
+    "notifyParty": "Notify Party",
+    "cancel": "Cancel",
+    "save": "Save",
+    "reset": "Reset",
+    "ok": "OK",
+    "change": "Change",
+    "all": "All",
+    "update": "Update",
+    "prompt": "Prompt",
+    "forgetPassword": "Forget Password",
+    "tooManyTries": "Oops, too many tries!",
+    "passwordAttemptsExceeded": "You have exceeded the maximum number of password attempts.",
+    "tryAgainInFiveMinutesOrReset": "Please try again in 5 minutes or click {action} to reset.",
+    "logout": "Logout",
+    "clearFilters": "Clear Filters",
+    "downloadFile": "Download File",
+    "selectedColumns": "Selected columns",
+    "downloadWithAllColumns": "Download with all columns",
+    "saveSuccess": "Saved successfully",
+    "requestFailedRetry": "The request failed. Please try again later",
+    "errorPleaseTryAgainLater": "Error! Please try again later.",
+    "copySuccess": "Copy success",
+    "invalidToken": "Invalid token",
+    "details": "Details",
+    "description": "Description",
+    "userType": "User Type",
+    "questionType": "Question Type",
+    "answerType": "Answer Type",
+    "answerSatisfaction": "Answer Satisfaction",
+    "page": "Page",
+    "chatLog": "Chat Log",
+    "customer": "Customer",
+    "employee": "Employee",
+    "predefinedQuestion": "Predefined Question",
+    "freeText": "Free Text",
+    "suspend": "Suspend",
+    "timeout": "Timeout",
+    "predefinedTemplate": "Predefined Template",
+    "aiAnswer": "AI Answer",
+    "null": "Null",
+    "good": "Good",
+    "notGood": "Not Good",
+    "responseDuration": "Response Duration",
+    "schedule": "Schedule",
+    "failedToLoadOptions": "Failed to load options",
+    "stations": "Stations",
+    "bookingWindow": "Booking Window",
+    "operation": "Operation",
+    "configureRegionsAndTimeSlots": "Configure available destination delivery regions and time slots.",
+    "deleteRule": "Delete Rule",
+    "confirmDeleteRule": "Are you sure to delete this notification event?",
+    "failedToLoadData": "Failed to load data",
+    "cannotAddDuplicateCities": "Cannot add duplicate cities.",
+    "inputCountryCityUncode": "Please input country/city/uncode",
+    "country": "Country",
+    "city": "City",
+    "uncode": "Uncode",
+    "etd": "ETD",
+    "eta": "ETA",
+    "total": "Total",
+    "noData": "No data",
+    "loading": "Loading...",
+    "input": "Input",
+    "sliderVerifyTipLine1": "Please drag the slider below to complete the",
+    "sliderVerifyTipLine2": "verification to ensure normal access",
+    "swipeToVerify": "Swipe to verify",
+    "verificationSuccessful": "Verification successful",
+    "verificationFailed": "Verification failed",
+    "transportMode": "Transport Mode",
+    "regionalSolutions": "REGIONAL SOLUTIONS",
+    "selectAll": "Select All",
+    "select": "Select",
+    "dateRange": "Date Range",
+    "dateType": "Date Type",
+    "pleaseSelectDateType": "Please Select Date Type",
+    "moreDateType": "More Date Type",
+    "moreFilters": "More Filters",
+    "general": "General",
+    "incoterms": "Incoterms",
+    "service": "Service",
+    "parties": "Parties",
+    "places": "Places",
+    "transportation": "Transportation",
+    "pleaseSelectDateRange": "Please Select Date Range",
+    "pleaseSelectService": "Please Select Service",
+    "seeAll": "See All",
+    "partyType": "Party Type",
+    "partyDetails": "Party Details",
+    "pleaseSelectPartyType": "Please Select Party Type",
+    "pleaseInputPartyDetails": "Please Input Party Details",
+    "placesType": "Places Type",
+    "placesDetails": "Places Details",
+    "pleaseInputPlacesDetails": "Please Input Places Details",
+    "title": "Title",
+    "noResultsFound": "No Results Found",
+    "emptyResultLine1": "We didn't find any search results,",
+    "emptyResultLine2": "please try to adjust your search keywords.",
+    "pageGuide": "Page Guide"
+  },
+  "tracking": {
+    "title": "Tracking",
+    "searchPlaceholder": "Enter Booking/HBL/PO/Container/Carrier Booking No. ",
+    "referenceTip": "We support the following references number to find bookings: Booking No./HAWB No./MAWB No./PO No./Carrier Booking No./Contract No./File No./Quote No.",
+    "selectRecordForAttachmentDownload": "Please select at least one record to download attachments",
+    "download": "Download",
+    "downloadShipmentDetails": "Download Shipment Details",
+    "downloadAttachments": "Download Attachments",
+    "referenceTipForTracking": "We support the following references number to find tracking:",
+    "subscribe": "Subscribe",
+    "vgm": "VGM",
+    "publicSearchPlaceholder": "Enter Booking/HBL/MBL/PO/Container No.",
+    "publicReferenceTipTitle": "We support the following references number to find shipment:",
+    "publicReferenceTipList": "· Tracking No./Booking No./HAWB No./MAWB No./PO No./Quote No./Invoice No./Container No.",
+    "multipleResultsTitle": "Sorry,Multiple results",
+    "multipleResultsLine1": "To correctly display the details page,",
+    "multipleResultsLine2": "please search using the HBL No. Thank you.",
+    "selectDataOnShipmentList": "Select data on your shipment list",
+    "downloadWithSelectedColumns": "Download with selected columns",
+    "refNoDialogTitle": "Ref No.",
+    "selectReferenceTypePlaceholder": "Select Reference Type",
+    "enterRefNoPlaceholder": "Enter Ref No.",
+    "uploadFiles": "Upload Files",
+    "upload": "Upload",
+    "fileType": "File Type",
+    "supportedFormats": "Supported formats: .pdf, .xlsx, .docx",
+    "maximumSize": "Maximum Size: 5MB;",
+    "maximumNumber": "Maximum Number: 5 files",
+    "selectFileTypeAndUpload": "Please select file type and upload file",
+    "uploadSuccessfully": "Upload successfully",
+    "uploadFailed": "Upload failed",
+    "allowedFileTypes": "The file types allowed for upload are: PDF, XLSX and DOCX.",
+    "fileSizeLimit": "File size must not exceed 5MB!",
+    "selectAtLeastOneFileToDownload": "Please select at least one file to download.",
+    "attachmentSummary": "Attachment Summary",
+    "downloadSelected": "Download Selected ({count})",
+    "noFile": "No file",
+    "attachmentNumber": "Attachment {no}",
+    "defaultSettings": "Default Settings",
+    "settingsSavedSuccessfully": "Settings saved successfully",
+    "submitter": "Submitter",
+    "signature": "Signature",
+    "authorizedEmail": "Authorized Email",
+    "authorizedTel": "Authorized Tel",
+    "addVgm": "Add VGM",
+    "generalInformation": "General Information",
+    "detailInformation": "Detail Information",
+    "isSend": "Is Send",
+    "defaultSetting": "Default Setting",
+    "hblNo": "HBL No.",
+    "carrierBookingNo": "Carrier Booking No.",
+    "vessel": "Vessel",
+    "voyage": "Voyage",
+    "etd": "ETD",
+    "eta": "ETA",
+    "lastUpdatedUser": "Last Updated User",
+    "lastUpdatedTime": "Last Updated Time",
+    "noAccess": "No access",
+    "drawerTitleBoth": "AMS/ISF",
+    "drawerTitleAms": "AMS",
+    "drawerTitleIsf": "ISF",
+    "amsM1Log": "AMS-M1 Log",
+    "isfLog": "ISF Log",
+    "copiedToClipboard": "Copied to clipboard",
+    "failedToCopy": "Failed to copy",
+    "tips": "Copy the link to share this shipment with anyone.",
+    "copyLink": "Copy Link",
+    "pleaseEnter": "Please enter...",
+    "pleaseSelect": "Please select...",
+    "pickDate": "Pick a Date",
+    "pleaseLoginFirst": "Please login first",
+    "origin": "Origin",
+    "destination": "Destination",
+    "etdAtd": "ETD/ATD",
+    "etaAta": "ETA/ATA",
+    "basicInformation": "Basic Information",
+    "businessPartners": "Business Partners",
+    "containers": "Containers",
+    "milestones": "Milestones",
+    "routes": "Routes",
+    "attachment": "Attachment",
+    "tdShare": "Share",
+    "tdHouseNo": "House No.",
+    "tdAmsIsf": "AMS/ISF",
+    "tdAddReference": "Add Reference",
+    "tdReferenceType": "Reference Type",
+    "tdRefNo": "Ref No.",
+    "tdAction": "Action",
+    "ptMawbMblNo": "MAWB/MBL No.",
+    "ptHawbHblNo": "HAWB/HBL No.",
+    "ptBookingNo": "Booking No.",
+    "ptPoNo": "PO No.",
+    "ptVesselAirline": "Vessel / Airline",
+    "ptVoyageFlight": "Voyage / Flight",
+    "ptOriginAgent": "Origin Agent",
+    "ptDestinationAgent": "Destination Agent",
+    "ptQuantityUnit": "Quantity / Unit",
+    "ptGWeight": "G. Weight",
+    "ptChWeight": "Ch. Weight",
+    "ptVolume": "Volume",
+    "ptMarks": "Marks",
+    "ptDescription": "Description"
+  },
+  "systemMessage": {
+    "title": "System Message",
+    "eventNotifications": "Event Notifications",
+    "allNotifications": "All Notifications",
+    "unread": "Unread",
+    "read": "Read",
+    "featureUpdate": "Feature Update",
+    "milestoneUpdate": "Milestone Update",
+    "containerStatusUpdate": "Container Status Update",
+    "departureArrivalDelay": "Departure/Arrival Delay",
+    "etdEtaChange": "ETD/ETA Change"
+  },
+  "layout": {
+    "logoutConfirm": "Are you sure you want to logout?",
+    "changePasswordTitle": "Change Password",
+    "passwordUpdatedSuccessfully": "Password updated successfully",
+    "passwordLength12to20": "Password length between 12 - 20",
+    "passwordMustContainUppercase": "Password must contain uppercase letters",
+    "passwordMustContainLowercase": "Password must contain lowercase letters",
+    "passwordMustContainNumber": "Password must contain numbers",
+    "downloadKLNPortalTitle": "Download KLN Portal",
+    "iphoneAppStore": "iPhone App Store",
+    "androidPlayStore": "Android-Play Store",
+    "androidChinaUser": "Android-China User",
+    "viewAsCustomer": "View as Customer",
+    "selectCustomer": "Select customer",
+    "noData": "no data",
+    "passwordExpiredUnavailable": "This account's password has expired and is currently unavailable. Please select a different customer account to continue.",
+    "accountNotActivated": "This account has not been activated yet. Please select a different customer account to continue.",
+    "accountDisabled": "This account has been disabled and is no longer accessible. Please select a different customer account to continue.",
+    "operationFailedRetry": "Operation failed, please try again later",
+    "customizeColumns": "Customize Columns",
+    "openDetailedPage": "Open Detailed Page",
+    "closeDetailedPage": "Close Detailed Page",
+    "sendEmail": "Send Email",
+    "publicTracking": "Public Tracking",
+    "addReference": "Add Reference",
+    "uploadFile": "Upload File",
+    "amsIsf": "AMS/ISF",
+    "changePassword": "Change Password"
+  },
+  "report": {
+    "title": "Report",
+    "searchPlaceholder": "Search Report Name",
+    "excelFile": "Excel(.xlsx)",
+    "csvFile": "CSV(.csv)",
+    "downloadReport": "Download Report",
+    "manageFields": "Manage Fields",
+    "filters": "Filters",
+    "pleaseEnter": "Please enter...",
+    "pleaseSelect": "Please select...",
+    "to": "To",
+    "startDate": "Start date",
+    "endDate": "End date",
+    "manageReportFields": "Manage Report Fields",
+    "showAll": "Show All",
+    "hideAll": "Hide All",
+    "of": "of",
+    "fieldsVisible": "Fields Visible",
+    "systemName": "System Name",
+    "displayNameInReport": "Display Name in Report",
+    "apply": "Apply",
+    "selectScheduleRuleValidityPeriod": "Please select the Schedule Rule Validity Period",
+    "permanentValid": "Permanent Valid",
+    "activeContinuouslyTip": "Active continuously once enabled, until manually disabled or deleted.",
+    "customPeriod": "Custom Period",
+    "onlyExecuteInSpecifiedPeriodTip": "Only automatically execute during specified time period.",
+    "effectiveStartDate": "Effective Start Date",
+    "effectiveEndDate": "Effective End Date",
+    "pickDate": "Pick a Date"
+  },
+  "templateManagement": {
+    "title": "Report Template Management",
+    "createNew": "Create New Report Template",
+    "searchReportNamePlaceholder": "Search report name",
+    "isActivePlaceholder": "Is Active",
+    "applicationScopePlaceholder": "Application Scope",
+    "partyIdsPlaceholder": "Party IDs",
+    "active": "Active",
+    "inactive": "Inactive",
+    "allUsers": "All Users",
+    "specificUsers": "Specific Users",
+    "selectPartyIdsMulti": "Select Party IDs (Multi-select allowed)",
+    "selectGroupNameMulti": "Select Group Name (Multi-select allowed)",
+    "addEditFieldTitle": "Add/Edit Field",
+    "searchFieldPlaceholder": "Search field",
+    "reportFieldsConfigurationNewFieldNameRequired": "Please enter the new field name.",
+    "reportFieldsConfigurationFixedValueRequired": "Please enter the fixed value.",
+    "reportFieldsConfigurationReportLevelRequired": "Please select the report level.",
+    "createReportTemplateReportNameRequired": "Please enter the Report Name.",
+    "createReportTemplateReportLevelRequired": "Please enter the Report Level",
+    "createReportTemplateReportDescriptionRequired": "Please enter the Report Description.",
+    "createReportTemplateSavedSuccessfully": "Report Template saved successfully!",
+    "createReportTemplateAddNewFieldTitle": "Add New Field",
+    "createReportTemplateNewFieldNameLabel": "New Field Name",
+    "createReportTemplateReportNameLabel": "Report Name",
+    "createReportTemplateReportLevelLabel": "Report Level",
+    "createReportTemplateReportDescriptionLabel": "Report Description",
+    "createReportTemplateBlankText": "Blank",
+    "createReportTemplateFixedValueText": "Fixed Value",
+    "createReportTemplateFieldValueLabel": "Field Value",
+    "createReportTemplateFixedValueLabel": "Fixed Value",
+    "createReportTemplateMappingTitle": "Mapping (Field: Final Destination)",
+    "createReportTemplateSystemValueLabel": "System Value",
+    "createReportTemplateOutputValueLabel": "Output Value",
+    "createReportTemplateAddMapping": "Add Mapping",
+    "createReportTemplateFilter": "Filter",
+    "createReportTemplateSort": "Sort",
+    "createReportTemplateCancel": "Cancel",
+    "createReportTemplateApply": "Apply"
+  },
+  "reportSchedule": {
+    "scheduleConfiguration": "Schedule Configuration - {name}",
+    "scheduleRuleValidityPeriod": "Schedule Rule Validity Period",
+    "reportDataTimeRange": "Report Data Time Range",
+    "reportDeliveryFrequencyEmail": "Report Delivery Frequency & Email Configuration",
+    "selectReportDeliveryFrequencyEmailConfig": "Please select the Report Delivery Frequency & Email Configuration",
+    "emailRecipientsPlaceholder": "Enter email address separated by commas",
+    "saveSuccess": "Save Success",
+    "selectReportDataTimeRange": "Please select the Report Data Time Range",
+    "dataTimeReferenceFieldTitle": "Data Time Reference Field Selection",
+    "dataRangeMethodTitle": "Data Range Configuration Method",
+    "dynamicRollingRange": "Dynamic Rolling Range",
+    "fixedRange": "Fixed Range",
+    "startDate": "Start Date",
+    "endDate": "End Date",
+    "past": "Past",
+    "future": "Future",
+    "dynamicRollingTooltipLine1": "Configuration: Past X days to Future Y days, always dynamically calculated based on current date when generating reports.",
+    "dynamicRollingTooltipLine2": "Usage: For example, setting 5 days to 3 days means the data range differs each time it's automatically generated. Used for short-cycle operational monitoring.",
+    "fixedRangeTooltipLine1": "Configuration: Specific start and end dates, always query this range when automatically generating reports.",
+    "fixedRangeTooltipLine2": "Example: Start date [2025-01-01], End date [2025-12-31]",
+    "thisMonth": "This Month",
+    "thisQuarter": "This Quarter",
+    "lastQuarter": "Last Quarter",
+    "lastYear": "Last Year"
+  },
+  "personalProfile": {
+    "basicInformation": "Basic Information",
+    "firstName": "First Name",
+    "lastName": "Last Name",
+    "email": "Email",
+    "password": "Password",
+    "passwordExpirePrefix": "Your password will be expire in",
+    "passwordExpireDays": "{n} day(s)",
+    "maskCustomerInformation": "Mask Customer Information",
+    "yes": "Yes",
+    "no": "No",
+    "saveSuccessfully": "Save successfully",
+    "saveFailed": "Save failed",
+    "personalPreferences": "Personal Preferences",
+    "dateAndTime": "Date & Time",
+    "numbersFormatNav": "Numbers Format",
+    "dateFormat": "Date Format",
+    "numbersFormat": "Numbers Format",
+    "numbersUsUk": "1,234.56 (US/UK)",
+    "numbersEuropean": "1.234,56 (European)"
+  },
+  "aiApiLog": {
+    "title": "AI API Log",
+    "searchPlaceholder": "Search Request ID, Question ID",
+    "aiModel": "AI Model",
+    "responseDuration": "Response Duration",
+    "selected": "Selected",
+    "requestContent": "Request Content",
+    "responseContent": "Response Content",
+    "selectDataOnLogList": "Select data on your Operation Log list:",
+    "downloadWithSelectedColumns": "Download with selected columns",
+    "deepseek": "Deepseek",
+    "claude": "Claude",
+    "greaterOrEqual": ">=",
+    "equal": "=",
+    "lessOrEqual": "<=",
+    "download": "Download"
+  },
+  "notificationRules": {
+    "shipmentRange": "Shipment Range",
+    "selectMilestone": "Select Milestone",
+    "selectContainerStatus": "Select Container Status",
+    "selectDelayedType": "Select Delayed Type",
+    "selectDelayedShipments": "Select Delayed Shipments",
+    "selectTimeType": "Select Time Type",
+    "notificationFrequency": "Notification Frequency",
+    "notificationMethod": "Notification Method",
+    "addedRules": "Added Rules",
+    "oceanShipments": "Ocean Shipments",
+    "airShipments": "Air Shipments",
+    "containerStatus": "Container Status",
+    "time": "Time",
+    "missing": "missing",
+    "duplicateRuleError": "Duplicate Rule Error.",
+    "duplicateRuleExactMatch": "This rule exactly matches an existing rule.",
+    "similarRuleDetected": "Similar Rule Detected",
+    "similarRuleExists": "A similar configuration rule already exists.",
+    "proceedCreateRule": "Would you like to proceed with creating this rule?",
+    "byEmail": "By Email",
+    "bySystemMessage": "By System Message",
+    "instantNotificationEachUpdate": "Instant notification for each update",
+    "dailySummary": "Daily Summary",
+    "weeklySummary": "Weekly Summary",
+    "selectTime": "Select Time",
+    "selectTimeZone": "Select Time Zone",
+    "selectDay": "Select Day",
+    "within": "within",
+    "days": "Day(s)",
+    "ocean": "Ocean",
+    "air": "Air",
+    "road": "Road",
+    "next30Days": "Next 30 days",
+    "next60Days": "Next 60 days",
+    "past10DaysToNext60Days": "Past 10 days to next 60 days",
+    "past30Days": "Past 30 days",
+    "customize": "Customize",
+    "minus": "minus",
+    "plus": "plus",
+    "currentTimeMinus": "Current time minus",
+    "currentTimePlus": "Current time plus",
+    "departureDelayedAtdEtd": "Departure Delayed (ATD-ETD)",
+    "arrivalDelayedAtaEta": "Arrival Delayed (ATA-ETA)",
+    "delayedTime": "Delayed Time",
+    "hours": "Hour(s)",
+    "notifyAllChanges": "Notify for all changes",
+    "notifyOnlyWhenTimeDifference": "Notify only when time difference",
+    "monday": "Monday",
+    "tuesday": "Tuesday",
+    "wednesday": "Wednesday",
+    "thursday": "Thursday",
+    "friday": "Friday",
+    "saturday": "Saturday",
+    "sunday": "Sunday"
+  },
+  "customizeColumns": {
+    "title": "Customize Columns",
+    "searchPlaceholder": "Search columns you preferred",
+    "selectedColumnsOnList": "Selected columns on your {type} list",
+    "booking": "booking",
+    "shipment": "shipment",
+    "resetToDefault": "Reset to default",
+    "tourStep1": "Drag items to the right group or click the Add icon to add columns to the {type} list.",
+    "tourStep2Line1": "Drag items to the left group or click the Remove icon to delete columns from the {type} list.",
+    "tourStep2Line2": "Drag items up or down to reorder the {type} list, or use the Move up and Move down icons.",
+    "gotIt": "Got it"
+  },
+  "aiRobot": {
+    "greeting": "Hi! I'm your Freight Assistant, always on call",
+    "frequentlyAskedQuestions": "Frequently Asked Questions",
+    "continueConversation": "Continue the conversation",
+    "disclaimerTitle": "Disclaimer",
+    "sidebarWindow": "Sidebar Window",
+    "maximizedWindow": "Maximized Window",
+    "collapsedToWidget": "Collapsed to Widget",
+    "answerInProgress": "Answer in progress, please wait.",
+    "cancelAnswer": "Cancel Answer",
+    "typeYourQuestionHere": "Type your question here...",
+    "contentGeneratedByAI": "Content is generated by Al, please check carefully!",
+    "importantNoticeAI": "Important Notice: AI-Generated Content",
+    "aiGeneratedResponses": "AI-Generated Responses:",
+    "dataPrivacySecurity": "Data Privacy & Security:",
+    "informationAccuracy": "Information Accuracy:",
+    "serviceLimitations": "Service Limitations:",
+    "aiAcknowledgement": "By using this AI assistant, you acknowledge these limitations and agree to use the information provided accordingly.",
+    "sidebarWindowTip": "Sidebar Window",
+    "maximizedWindowTip": "Maximized Window",
+    "collapsedToWidgetTip": "Collapsed to Widget"
+  },
+  "containerStatus": {
+    "container": "Container",
+    "trackingOnCarrierWebsite": "Tracking on carrier website:"
+  },
+  "notificationMessage": {
+    "latestStatusUpdates": "Latest Status Updates ({count})",
+    "departureDelay": "Departure Delay",
+    "etdChange": "ETD Change",
+    "arrivalDelay": "Arrival Delay",
+    "etaChange": "ETA Change",
+    "route": "Route",
+    "currentLeg": "Current Leg",
+    "onlyDisplayWithinThreeMonths": "Only display the message data within three months",
+    "smartAssistantContent": "Smart Assistant is live! Ask in natural language, get real-time shipment data with multi-language support.",
+    "smartNotificationContent": "Smart Notification is here! Four key event alerts with customizable rules and multi-channel delivery."
+  },
+  "vbox": {
+    "moveToTop": "Move to Top",
+    "moveUp": "Move Up",
+    "moveDown": "Move Down",
+    "moveToBottom": "Move to Bottom"
+  },
+  "moreFilters": {
+    "vessel": "Vessel",
+    "voyageFlight": "Voyage/Flight",
+    "inputVesselNameOrCode": "Please input vessel name or code",
+    "inputVoyageOrFlightNo": "Please input Voyage or flight no.",
+    "origin": "Origin",
+    "destination": "Destination",
+    "placeOfDelivery": "Place of delivery",
+    "placeOfReceipt": "Place of Receipt",
+    "portOfLoading": "Port of Loading",
+    "placeOfDischarge": "Place of Discharge",
+    "inputPlacesName": "Please input places name",
+    "morePlaceType": "More Place Type",
+    "shipperName": "Shipper Name",
+    "consigneeName": "Consignee Name",
+    "originAgent": "Origin Agent",
+    "destinationAgent": "Destination Agent",
+    "sales": "Sales",
+    "notifyParty": "Notify Party",
+    "billTo": "Bill to",
+    "destinationOperator": "Destination Operator",
+    "controllingCustomer": "Controlling Customer",
+    "inputShipperName": "Please input shipper name",
+    "inputConsigneeName": "Please input consignee name",
+    "inputPartyName": "Please input party name",
+    "morePartyType": "More Party Type"
+  },
+  "scoringGrade": {
+    "feedbackPlaceholder": "We look forward to hearing from you. Thank you for helping to build a better customer system.",
+    "shareYourFeedback": "Share Your Feedback",
+    "apologyMessage": "We are so sorry for the inconveniences. We value your experience immensely.",
+    "dissatisfiedAspectQuestion": "Could you please tell us which aspects of the system you are dissatisfied with?",
+    "thanksForSharing": "Thank you for sharing your thoughts with us. We value your experience greatly.",
+    "shareLikedAndImprovement": "Could you share what aspects you liked and what could be improved ?",
+    "thanksForSatisfiedRating": "Thank you very much for giving us a satisfied rating on our service or system.",
+    "whatMadeYouSatisfied": "We are curious to learn more about what specifically made you feel satisfied.",
+    "apologizeAgain": "Apologize once again for your experience.",
+    "appreciateFeedback": "We greatly appreciate your valuable time and feedback.",
+    "thanksPositiveEvaluation": "Once again, thank you for your positive evaluation.",
+    "satisfactionQuestion": "How satisfied are you with this system?",
+    "satisfactionQuestionWithSpace": "How satisfied are you with this system ?",
+    "shareMoreDetails": "Would you like to share more details with us?",
+    "hi": "Hi!",
+    "submitSuccessful": "Submit Successful",
+    "betterServiceCommitment": "We are committed to working hard to provide better services.",
+    "aspectFunctionality": "Functionality",
+    "aspectDataAccuracy": "Data accuracy",
+    "aspectLookAndFeel": "Look and feel",
+    "aspectEaseOfUse": "Ease of use",
+    "aspectWebsitePerformance": "Website performance",
+    "highlyDissatisfied": "Highly Dissatisfied",
+    "dissatisfied": "Dissatisfied",
+    "neutral": "Neutral",
+    "satisfied": "Satisfied",
+    "highlySatisfied": "Highly Satisfied"
+  },
+  "filterTags": {
+    "created": "Created",
+    "modified": "Modified",
+    "pending": "Pending",
+    "confirmed": "Confirmed",
+    "cancelled": "Cancelled",
+    "departed": "Departed",
+    "cargoReceived": "Cargo Received",
+    "arrived": "Arrived",
+    "completed": "Completed"
+  },
+  "dateRange": {
+    "startEtaTime": "Start ETA Time",
+    "endEtaTime": "End ETA Time",
+    "startAtaTime": "Start ATA Time",
+    "endAtaTime": "End ATA Time",
+    "startTime": "Start Time",
+    "endTime": "End Time",
+    "earliestTime": "Earliest Time",
+    "latestTime": "Latest Time",
+    "today": "Today",
+    "current": "Current",
+    "currentMonth": "Current Month",
+    "lastMonth": "Last Month",
+    "thisYear": "This Year",
+    "noStartLimit": "No Start Limit",
+    "noEndLimit": "No End Limit",
+    "startMonth": "Start month",
+    "endMonth": "End month"
+  },
+  "systemSettings": {
+    "title": "System Settings",
+    "profile": "Profile",
+    "personalProfileTab": "Personal Profile",
+    "subscribeNotificationsTab": "Subscribe Notifications",
+    "monitoringSettingsTab": "Monitoring Settings",
+    "notificationEventsForSubscribedShipments": "Notification Events for Subscribed Shipments",
+    "edit": "Edit",
+    "add": "Add",
+    "subscribedShipments": "Subscribed Shipments",
+    "addRule": "Add Rule"
+  },
+ 
+  "dashboard": {
+    "title": "Dashboard",
+    "rememberSaveLayout": "Please remember to click the save button in order to keep the new dashboard layout and widgets.",
+    "save": "Save",
+    "saveFilters": "Save Filters",
+    "saveLayout": "Save Layout",
+    "filters": "Filters",
+    "transportMode": "Transport Mode",
+    "dateRange": "Date Range",
+    "fixed12Months": "The time range is fixed at 12 months.",
+    "salesConfigWarning": "*To ensure the accuracy of the data display, this report needs to be configured and displayed after communicating clearly with Sales.",
+    "kpiReportTip": "KPI Report: Day difference between actual and estimate.",
+    "pendingReportTip": "Pending Report: Showing shipments which are soon to depart/arrive.",
+    "etdToEtaTip": "ETD to ETA (Days): Distribution of Transit Time (ETA-ETD) for All Shipments in Last 12 Months.",
+    "containerCountTip": "Container Count: Total Container Volume by Month (Last 12 Months)",
+    "top10Tip": "Top 10 Origin & Destination: Last 12 Months Shipment Volume Rankings: Top 10 Origin Cities and Top 10 Destination Cities",
+    "co2eTip": "CO2e Emission by Origin or Destination: Last 12 Months CO2e Emission Rankings: Top 10 Origin Cities and Top 10 Destination Cities",
+    "recentStatusTip": "Recent Status: Active shipment list with ETD within the past three months and the next month.",
+    "revenueSpentTip": "Revenue Spent: Based on the billto object, display the corresponding revenue data.",
+    "revenueSpentTitle": "Revenue Spent",
+    "departure": "Departure",
+    "arrival": "Arrival",
+    "satisfactionQuestionWithSpace": "Satisfaction Question ",
+    "shareYourFeedback": "Share your feedback",
+    "feedbackPlaceholder": "Please enter your feedback.",
+    "submitSuccessful": "Submit successfully",
+    "scoringSorryText": "We are so sorry for the inconveniences. We value your experience immensely.",
+    "scoringDissatisfiedTip": "Could you please tell us which aspects of the system you are dissatisfied with?",
+    "scoringNeutralText": "Thank you for sharing your thoughts with us. We value your experience greatly.",
+    "scoringNeutralProposal": "Could you share what aspects you liked and what could be improved?",
+    "scoringSatisfiedText": "Thank you very much for giving us a satisfied rating on our service or system.",
+    "scoringSatisfiedProposal": "We are curious to learn more about what specifically made you feel satisfied.",
+    "highlyDissatisfied": "Highly dissatisfied",
+    "dissatisfied": "Dissatisfied",
+    "neutral": "Neutral",
+    "satisfied": "Satisfied",
+    "highlySatisfied": "Highly satisfied",
+    "satisfactionDetailsQuestion": "Would you like to share more details with us?",
+    "previous": "Previous",
+    "submit": "Submit",
+    "submitAndCommitment": "We are committed to working hard to provide better services.",
+    "apologizeAgain": "Apologize once again for your experience.",
+    "appreciateFeedback": "We greatly appreciate your valuable time and feedback.",
+    "thankYouPositive": "Once again, thank you for your positive evaluation."
+  },
+  "booking": {
+    "title": "Booking",
+    "sendEmail": "Send Email",
+    "search": "Search",
+    "selectDataOnBookingList": "Select data on your booking list",
+    "download": "Download",
+    "downloadWithSelectedColumns": "Download with selected columns",
+    "emailSentSuccessfully": "Email sent successfully",
+    "enterRefNoPlaceholder": "Enter Ref No.",
+    "failedToSendEmail": "Failed to send email",
+    "pleaseEnterEmailContent": "Please enter the email content",
+    "refNoDialogTitle": "Ref No.",
+    "searchPlaceholder": "Enter Booking/HBL/PO/Container/Carrier Booking No. ",
+    "selectReferenceTypePlaceholder": "Select Reference Type",
+    "selected": "Selected",
+    "total": "Total",
+    "transportMode": "Transport Mode",
+    "shipmentStatus": "Shipment Status",
+    "etd": "ETD",
+    "textSearch": "text search",
+    "clearFilters": "Clear Filters",
+    "bookingNo": "Booking No.",
+    "hawbHblNo": "HAWB/HBL No.",
+    "carrierBookingNo": "Carrier Booking No.",
+    "poNo": "PO No.",
+    "refNo": "Ref No.",
+    "vesselAirline": "Vessel / Airline",
+    "voyageFlight": "Voyage / Flight",
+    "incoterm": "Incoterm",
+    "serviceType": "Service Type",
+    "shipper": "Shipper",
+    "consignee": "Consignee",
+    "originAgent": "Origin Agent",
+    "destinationAgent": "Destination Agent",
+    "quantityUnit": "Quantity / Unit",
+    "gWeight": "G. Weight",
+    "chWeight": "Ch. Weight",
+    "volume": "Volume",
+    "marks": "Marks",
+    "description": "Description",
+    "packing": "Packing",
+    "marksAndDescription": "Marks and Description",
+    "bookingListEmptyTip": "We support the following references number to find booking:",
+    "bookingListEmptyTipDetail": "· Booking No./HAWB No./MAWB No./PO No./Carrier Booking No./Contract No./File No./Quote No.",
+    "noResultsFound": "No Results Found",
+    "basicInformation": "Basic Information",
+    "businessPartners": "Business Partners",
+    "containers": "Containers",
+    "origin": "Origin",
+    "destination": "Destination",
+    "etdAtd": "ETD/ATD",
+    "etaAta": "ETA/ATA",
+    "guideSearchAreaTitle": "Frequently Used Search Criteria Display Area",
+    "guideSearchAreaStatus": "Key Booking Status",
+    "guideSearchAreaReferences": "Reference Numbers",
+    "guideSearchAreaTransportMode": "Transport Mode",
+    "guideSearchAreaDateRange": "Date Type & Range",
+    "guideSelectedCriteria": "Selected query criteria display area",
+    "guideClearCriteria": "You can quickly clear selected conditions at this position",
+    "guideMoreFilters": "Click \"More Filters\" to see more search options.",
+    "guideOpenDetail": "Click on the Booking No. or double-click anywhere on a single booking data to enter the detailed page.",
+    "guideDownloadTipLine1": "View the number of shipments selected for download",
+    "guideDownloadTipLine2": "Two download list templates are available",
+    "guideCustomizeColumns": "Drag to right to add columns",
+    "guideCustomizeColumnsLeft": "Drag to left to unselect columns",
+    "guideCustomizeColumnsReorder": "Drag up and down to reorder or select/unselect",
+    "guidePageGuide": "After closing, you can still click the \"Page Guide\" button to view the page guide of the current page.",
+    "guideDashboardIntro": "The Dashboard integrates different types of reports. You can freely select the reports you need.",
+    "guideDashboardFilter": "Each report comes with its own filter options. Simply make your selection and click \"Search\" to refresh the data.",
+    "guideDashboardTooltip": "Hover the icon next to the report name to view the data explanation for each report.",
+    "guideDashboardDrag": "Drag the icon to rearrange the report cards on the dashboard.",
+    "guideDashboardSaveLayout": "Click \"Save Layout\" to preserve your customized report arrangement.",
+    "openInNewTab": "Open in New Tab",
+    "customizeColumnsTip": "Drag item over to this selection or click \"add\" icon to show the column on your booking list",
+    "tdReferenceType": "Reference Type",
+    "tdAction": "Action"
+  },
+  "destinationDelivery": {
+    "title": "Destination Delivery",
+    "configurations": "Configurations",
+    "calendarView": "Calendar View",
+    "listView": "List View",
+    "createNewBooking": "Create New Booking",
+    "truck": "Truck",
+    "rail": "Rail",
+    "totalBookings": "Total Bookings",
+    "pendingApproval": "Pending Approval",
+    "approved": "Approved",
+    "rejected": "Rejected",
+    "cancelled": "Cancelled",
+    "bookingList": "Booking List",
+    "selectDataOnOperationLogList": "Select data on your Operation Log list:",
+    "downloadWithSelectedColumns": "Download with selected columns",
+    "addNewDeliveryAddressTitle": "Add New Delivery Address",
+    "addressLine1Placeholder": "Line 1",
+    "addressLine2Placeholder": "Line 2",
+    "addressLine3Placeholder": "Line 3",
+    "addressLine4Placeholder": "Line 4",
+    "selectCountry": "Select Country",
+    "selectCity": "Select City",
+    "enterPostalCode": "Enter postal code",
+    "contactNamePlaceholder": "Name",
+    "contactMobilePlaceholder": "Mobile Numer",
+    "deliveryDate": "Delivery Date",
+    "deliveryMode": "Delivery Mode",
+    "creationDate": "Creation Date",
+    "searchQuestionPlaceholder": "Search Question Booking No, HBOL No, MBL No, Container No, Consignee",
+    "communication": "Communication",
+    "communicateWithUs": "Communicate with us",
+    "separatedBySemicolon": "Separated by ;",
+    "sendEmail": "Send Email",
+    "pleaseEnterEmailContent": "Please enter the email content",
+    "emailSentSuccessfully": "Email sent successfully",
+    "failedToSendEmail": "Failed to send email",
+    "emptyBookingTitle": "You haven't created any destination delivery bookings yet.",
+    "emptyBookingLine1": "Book truck or rail delivery for your shipments to save time and",
+    "emptyBookingLine2": "ensure smooth last-mile delivery.",
+    "modifyBooking": "Modify Booking",
+    "selected": "Selected",
+    "submit": "Submit",
+    "selectShipments": "Select Shipments",
+    "selectSameConsigneeTip": "Please select items with the same consignee.",
+    "selectSameConsigneeWarning": "Please select same consignee with same delivery information",
+    "searchBookingPlaceholder": "Enter Booking/HBL/PO/Carrier Booking No.",
+    "shipper": "Shipper",
+    "consignee": "Consignee",
+    "requiredFieldsNotEntered": "Required fields not entered.",
+    "outsideRecommendedPeriodForFollowing": "This date for following shipments is outside recommended period.",
+    "outsideRecommendedPeriod": "This date is outside our recommended period.",
+    "eta": "ETA",
+    "ata": "ATA",
+    "recommendedDeliveryDate": "Recommended Delivery Date",
+    "inputVesselName": "Input Vessel Name",
+    "vesselName": "Vessel Name",
+    "deliveryInformation": "Delivery Information",
+    "deliveryAddress": "Delivery Address",
+    "addressBook": "Address Book",
+    "addNewAddress": "Add New Address",
+    "modeType": "Mode Type",
+    "select": "Select",
+    "preferredDeliveryDate": "Preferred Delivery Date",
+    "pleaseSelectDate": "Please Select Date",
+    "deliveryTime": "Delivery Time",
+    "pleaseSelectTime": "Please Select Time",
+    "deliveryReference": "Delivery Reference",
+    "deliveryReferenceTooltip": "Reference to be quoted on arrival at the Warehouse/DC",
+    "specialRequirements": "Special Requirements",
+    "tailLiftRequired": "Tail Lift Required",
+    "sideLoading": "Side Loading",
+    "forkliftRequired": "Forklift Required",
+    "specialEquipment": "Special Equipment",
+    "additionalRequirementsPlaceholder": "Enter any additional requirements or notes...",
+    "modificationReason": "Modification Reason",
+    "createNewRule": "Create New Rule",
+    "completeRequiredFields": "Please complete all required fields.",
+    "unableToSave": "Unable to Save",
+    "setting": "Setting",
+    "selectStationEnableBooking": "Select Station (Enable Booking)",
+    "setBookingWindow": "Set Booking Window",
+    "klnPic": "KLN PIC",
+    "selectEmployeeAccount": "Select Employee Account",
+    "modifyRule": "Modify Rule",
+    "unsavedChanges": "There are unsaved changes.",
+    "confirmLeavePage": "Are you sure you want to leave this page?",
+    "unsavedChangesTitle": "Unsaved Changes",
+    "specificRule": "Specific Rule",
+    "singleDimension": "Single Dimension",
+    "defaultRule": "Default Rule",
+    "noSpecificRecommendedTimeForChoosingDeliveryDate": "No Specific recommended time for choosing delivery date",
+    "recommendDeliveryDate": "Recommend Delivery Date (ETA/ATA)",
+    "etdAtdRuleWindow": "ETD/ATD: {before} days before to {after} days after",
+    "etaAtaRuleWindow": "ETA/ATA: {before} days before to {after} days after",
+    "ok": "OK",
+    "savingSuccess": "Saving successful",
+    "air": "Air",
+    "sea": "Sea",
+    "priority": "Priority",
+    "ruleType": "Rule Type",
+    "port": "Port",
+    "carrier": "Carrier",
+    "fromEtaAtaDays": "From (ETA/ATA + Days)",
+    "toEtaAtaDays": "To (ETA/ATA + Days)",
+    "add": "Add",
+    "configureRegionsAndTimeSlots": "Configure available destination delivery regions and time slots.",
+    "addRule": "Add Rule",
+    "deleteRule": "Delete Rule",
+    "confirmDeleteRule": "Are you sure to delete this notification event?",
+    "configuration": "Configuration",
+    "addedRules": "Added Rules",
+    "failedToLoadOptions": "Failed to load options",
+    "bookings": "Bookings",
+    "accountPasswordExpiredUnavailable": "This account's password has expired and is currently unavailable. Please select a different customer account to continue.",
+    "noTimeRestrictionsForBooking": "No Specific time restrictions for creating booking",
+    "bookingWindowEtdAtd": "Booking Window (ETD/ATD)",
+    "bookingWindowEtaAta": "Booking Window (ETA/ATA)",
+    "revenueSpentTitle": "Revenue Spent",
+    "daysBeforeTo": "days before to",
+    "daysAfter": "days after",
+    "stationList": "Station List",
+    "noData": "No Data",
+    "recommendedDeliveryShipmentsFor": "Recommended Delivery Shipments for {date}",
+    "freeStoragePeriodEnds": "Free Storage Period Ends",
+    "total": "Total",
+    "shipments": "shipments",
+    "totalCartons": "Total Cartons",
+    "packingList": "Packing List",
+    "bookingDetail": "Booking Detail",
+    "confirmRejectBooking": "Are you sure you want to Reject Booking ",
+    "confirmApproveBooking": "Are you sure you want to Approve Booking ",
+    "confirmCancelBooking": "Are you sure you want to Cancel Booking ",
+    "approve": "Approve",
+    "reject": "Reject",
+    "rejectRemarkRequired": "A remark must be filled in for the rejection operation.",
+    "actionSuccessfully": "successfully",
+    "actionFailedTryAgain": "failed, Please try again.",
+    "inputRemarks": "Input remarks",
+    "booking": "Booking",
+    "serviceNotAvailableTitle": "Destination Delivery Service Not Available",
+    "serviceNotAvailableText": "Destination delivery service is not yet available for your shipment destinations. To request this service, please contact the destination office first.",
+    "noEligibleShipmentsTitle": "No Eligible Shipments for Booking",
+    "noEligibleShipmentsText": "Your shipments are currently outside the booking window. Eligible shipments will appear here when booking window opens.",
+    "additionalStorageFeesMayApply": "Additional storage fees may apply.",
+    "additionalFeesNotice": "Additional Fees Notice"
   }
-}
+}

+ 5 - 3
src/locales/index.ts

@@ -1,13 +1,15 @@
 // lang -> index.ts
 import { createI18n } from 'vue-i18n'
 import ch from './zh-tw.json'
+import zhHans from './zh-cn.json'
 import en from './en.json'
 import zhCN from 'vxe-pc-ui/lib/language/zh-CN'
 import enUS from 'vxe-pc-ui/lib/language/en-US'
 
 /* 这里必须是messages名称 */
 const messages: any = {
-  zh_TW: { ...zhCN, ...ch },
+  zh_CN: { ...zhCN, ...zhHans, ...en },
+  zh_TW: { ...zhCN, ...ch, ...en },
   en_US: { ...enUS, ...en }
 }
 messages.en_US.vxe.table.seqTitle = 'seq'
@@ -15,8 +17,8 @@ messages.en_US.vxe.table.seqTitle = 'seq'
 const i18n = createI18n({
   legacy: false, // 使用 Composition API 模式,则需要将其设置为false
   globalInjection: true, //全局生效$t
-  locale: 'en_US', // 默认使用的语言
-  // fallbackLocale: 'zh_TW', // 回退语言
+  locale: 'zh_CN', // 默认使用简体中文
+  fallbackLocale: 'en_US', // 简体未覆盖时回退英文,避免落回繁体
   messages // 使用数据源
 })
 

+ 21 - 0
src/locales/zh-cn.json

@@ -0,0 +1,21 @@
+{
+  "login": {
+    "username": "用戶名",
+    "password": "密碼",
+    "login": "登錄",
+    "forgotPassword": "忘記密碼?",
+    "setPassword": "設置密碼",
+    "activateAccount": "激活賬戶",
+    "noAccount": "沒有賬戶?",
+    "registerNow": "立即註冊",
+    "captchaPlaceholder": "請輸入驗證碼",
+    "sendCaptcha": "發送驗證碼",
+    "resendCaptcha": "重新發送驗證碼",
+    "captchaSent": "驗證碼已發送至您的郵箱",
+    "resetPassword": "重置密碼",
+    "newPassword": "新密碼",
+    "confirmNewPassword": "確認新密碼",
+    "passwordMismatch": "密碼不匹配",
+    "activationEmailSent": "激活郵件已發送至您的郵箱"
+  }
+}

+ 2 - 0
src/main.ts

@@ -24,6 +24,7 @@ import { createPinia } from 'pinia'
 
 import App from './App.vue'
 import router from './router'
+import i18n from './locales'
 import { useThemeStore } from '@/stores/modules/theme'
 import piniaPluginPersistedstate from 'pinia-plugin-persistedstate';
 
@@ -48,6 +49,7 @@ app.use(createPinia())
 const pinia = createPinia()
 pinia.use(piniaPluginPersistedstate)
 app.use(pinia)
+app.use(i18n)
 app.use(VXETable)
 app.use(VxeUI)
 app.use(router)

+ 13 - 10
src/views/AIApiLog/src/AIApiLog.vue

@@ -2,6 +2,9 @@
 import { useCalculatingHeight } from '@/hooks/calculatingHeight'
 import TableView from './components/TableView'
 import dayjs from 'dayjs'
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
 
 const filterRef: Ref<HTMLElement | null> = ref(null)
 const containerHeight = useCalculatingHeight(document.documentElement, 290, [filterRef])
@@ -16,26 +19,26 @@ const searchData = ref({
 
 const aiModelList = [
   {
-    label: 'Deepseek',
+    label: t('aiApiLog.deepseek'),
     value: 'Deepseek'
   },
   {
-    label: 'Claude',
+    label: t('aiApiLog.claude'),
     value: 'Claude'
   }
 ]
 
 const comparatorList = [
   {
-    label: '>=',
+    label: t('aiApiLog.greaterOrEqual'),
     value: 'thanOrEqual'
   },
   {
-    label: '=',
+    label: t('aiApiLog.equal'),
     value: 'equal'
   },
   {
-    label: '<=',
+    label: t('aiApiLog.lessOrEqual'),
     value: 'lessOrEqual'
   }
 ]
@@ -58,12 +61,12 @@ const DateChange = (date: any) => {
 </script>
 <template>
   <div class="dashboard">
-    <div class="Title">AI API Log</div>
+    <div class="Title">{{ t('aiApiLog.title') }}</div>
     <div class="display">
       <div class="heaer_top">
         <div class="input-tips_filter">
           <el-input
-            placeholder="Search Request ID、Question ID"
+            :placeholder="t('aiApiLog.searchPlaceholder')"
             v-model="searchData.text_search"
             class="log_input"
           >
@@ -81,7 +84,7 @@ const DateChange = (date: any) => {
           <CalendarDate @DateChange="DateChange"></CalendarDate>
         </div>
         <div class="tips_filter">
-          <el-select v-model="searchData.ai_model" clearable placeholder="AI Model">
+          <el-select v-model="searchData.ai_model" clearable :placeholder="t('aiApiLog.aiModel')">
             <el-option
               v-for="item in aiModelList"
               :key="item.value"
@@ -91,7 +94,7 @@ const DateChange = (date: any) => {
           </el-select>
         </div>
         <div class="comparator-tips_filter">
-          <span>Response Duration</span>
+          <span>{{ t('aiApiLog.responseDuration') }}</span>
           <el-select
             placeholder=""
             clearable
@@ -115,7 +118,7 @@ const DateChange = (date: any) => {
           ></el-input-number>
         </div>
 
-        <el-button class="el-button--dark" @click="Search">Search</el-button>
+        <el-button class="el-button--dark" @click="Search">{{ t('common.search') }}</el-button>
       </div>
     </div>
     <TableView :height="containerHeight" ref="tableRef"></TableView>

+ 6 - 3
src/views/AIApiLog/src/components/LogDialog.vue

@@ -1,6 +1,9 @@
 <script setup lang="ts">
 import VueJsonPretty from 'vue-json-pretty'
 import 'vue-json-pretty/lib/styles.css'
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
 
 const dialogVisible = ref(false)
 
@@ -35,19 +38,19 @@ defineExpose({
     v-model="dialogVisible"
     class="ai-api-log-dialog"
     @closed="clearData"
-    title="AI API Log"
+    :title="t('aiApiLog.title')"
     width="1000"
     top="10vh"
   >
     <div class="request-section">
-      <div class="title">Request Content</div>
+      <div class="title">{{ t('aiApiLog.requestContent') }}</div>
       <div class="content" ref="requestContentRef">
         {{ requestContent }}
       </div>
     </div>
     <el-divider style="margin: 16px 0" />
     <div class="response-section">
-      <div class="title">Response Content</div>
+      <div class="title">{{ t('aiApiLog.responseContent') }}</div>
       <div class="content" :style="{ height: responseHeight + 'px' }">
         <vue-json-pretty :data="responseContent" :deep="4" />
       </div>

+ 8 - 5
src/views/AIApiLog/src/components/TableView/src/TableView.vue

@@ -1,4 +1,7 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
 import { ref, nextTick, onMounted } from 'vue'
 import { type VxeGridInstance, type VxeGridProps } from 'vxe-table'
 import DownloadDialog from './components/DownloadDialog.vue'
@@ -368,22 +371,22 @@ defineExpose({
     style="padding: 0px 20px"
     class="table-box"
     v-loading.fullscreen.lock="exportLoading"
-    element-loading-text="Loading..."
+    :element-loading-text="t('common.loading')"
     element-loading-custom-class="element-loading"
     element-loading-background="rgb(43, 47, 54, 0.7)"
   >
     <div
       class="table-tools"
       v-loading.fullscreen.lock="logLoading"
-      element-loading-text="Loading..."
+      :element-loading-text="t('common.loading')"
       element-loading-custom-class="element-loading"
       element-loading-background="rgb(43, 47, 54, 0.7)"
     >
-      <div class="left-total-records">{{ selectedNumber }} Selected</div>
+      <div class="left-total-records">{{ selectedNumber }} {{ t('aiApiLog.selected') }}</div>
       <div class="right-tools-btn">
         <el-button class="el-button--main el-button--pain-theme" @click="handleDownload">
           <span style="margin-right: 8px" class="font_family icon-icon_download_b"></span>
-          Download
+          {{ t('tracking.download') }}
         </el-button>
       </div>
     </div>
@@ -408,7 +411,7 @@ defineExpose({
     </vxe-grid>
     <vxe-grid :height="10" ref="allTableRef" class="all-table" v-bind="allTable"> </vxe-grid>
     <div class="bottom-pagination">
-      <div class="left-total-records">Total {{ formatNumber(pageInfo.total) }}</div>
+      <div class="left-total-records">{{ t('common.total') }} {{ formatNumber(pageInfo.total) }}</div>
       <div class="right-pagination">
         <el-pagination
           v-model:current-page="pageInfo.pageNo"

+ 10 - 7
src/views/AIApiLog/src/components/TableView/src/components/DownloadDialog.vue

@@ -1,4 +1,7 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
 const props = withDefaults(
   defineProps<{
     isHideSelectColumn: boolean
@@ -38,11 +41,11 @@ defineExpose({
 
 <template>
   <div>
-    <el-dialog @close="clearData" v-model="dialogVisible" title="Download File" width="540">
+    <el-dialog @close="clearData" v-model="dialogVisible" :title="t('common.downloadFile')" width="540">
       <div class="download-dialog">
         <div class="select-data">
           <div style="display: inline-block">
-            Select data on your Opeartion Log list:<span style="color: var(--color-theme)">{{
+            {{ t('aiApiLog.selectDataOnLogList') }}<span style="color: var(--color-theme)">{{
               selectedDataNumber
             }}</span>
           </div>
@@ -50,7 +53,7 @@ defineExpose({
         <div class="download-filter" v-if="!props.isHideSelectColumn">
           <el-radio-group v-model="downloadFilter">
             <el-radio :value="1"
-              >Download with selected columns
+              >{{ t('aiApiLog.downloadWithSelectedColumns') }}
               <span class="column-number">{{ columns.length }}</span>
               <SeeAllIcon v-model="isShowSelectColumn" />
             </el-radio>
@@ -59,23 +62,23 @@ defineExpose({
               class="select-columns"
               :class="{ show: isShowSelectColumn }"
             >
-              <div class="title">Selected columns</div>
+              <div class="title">{{ t('common.selectedColumns') }}</div>
               <div class="content">
                 <div class="column-item" v-for="item in columns" :key="item">{{ item }}</div>
               </div>
             </div>
-            <el-radio :value="2">Download with all columns</el-radio>
+            <el-radio :value="2">{{ t('common.downloadWithAllColumns') }}</el-radio>
           </el-radio-group>
         </div>
       </div>
       <template #footer>
         <div class="dialog-footer">
           <el-button class="cancel-btn" type="default" @click="dialogVisible = false"
-            >Cancel</el-button
+            >{{ t('common.cancel') }}</el-button
           >
           <el-button class="download-btn el-button--dark" @click="handleDownload"
             ><span style="margin-right: 8px" class="font_family icon-icon_download_b"></span>
-            Download</el-button
+            {{ t('tracking.download') }}</el-button
           >
         </div>
       </template>

+ 17 - 14
src/views/AIRobotChat/src/AIRobotChat.vue

@@ -1,4 +1,7 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
 import AutoResizeTextarea from './components/AutoResizeTextarea.vue'
 import LoadingDots from './components/LoadingDots.vue'
 import AIQuestions from './components/AIQuestions.vue'
@@ -65,7 +68,7 @@ const messages = ref<MessageItem[]>([
   {
     type: 'robot',
     isShowFeedback: false,
-    content: 'You can click on Frequently Asked Questions above or type your own question'
+    content: t('aiRobot.continueConversation')
   }
 ])
 messages.value = JSON.parse(sessionStorage.getItem('AIChat')) || messages.value
@@ -86,7 +89,7 @@ watch(
 const queryTime = ref(-1)
 // 当前用户问题回复进度
 const progressStatus = {
-  init: 'You can click on Frequently Asked Questions above or type your own question',
+  init: t('aiRobot.continueConversation'),
   '0': 'Thinking about your question...',
   '15': 'Searching for relevant data, please wait...',
   '30': 'This query is complex and may take more time',
@@ -322,7 +325,7 @@ const clearData = () => {
     {
       type: 'robot',
       isShowFeedback: false,
-      content: 'You can click on Frequently Asked Questions above or type your own question'
+      content: t('aiRobot.continueConversation')
     }
   ]
   progressInterval.value && clearInterval(progressInterval.value)
@@ -350,9 +353,9 @@ defineExpose({
   <div class="ai-robot" :style="{ width: modalSize === 'large' ? '1000px' : '484px' }">
     <div class="top-section" :class="[isShowHeaderShadow ? 'box-shadow' : '']">
       <div class="header">
-        <span class="welcome">Hi! I'm your Freight Assistant</span>
+        <span class="welcome">{{ t('aiRobot.greeting') }}</span>
         <div class="option-icon">
-          <el-tooltip v-if="modalSize === 'large'" trigger="hover" content="Sidebar Window">
+          <el-tooltip v-if="modalSize === 'large'" trigger="hover" :content="t('aiRobot.sidebarWindow')">
             <el-button
               style="width: 24px; height: 24px"
               class="el-button--text"
@@ -361,7 +364,7 @@ defineExpose({
             ></el-button>
           </el-tooltip>
 
-          <el-tooltip v-else-if="modalSize !== 'large'" trigger="hover" content="Maximized Window">
+          <el-tooltip v-else-if="modalSize !== 'large'" trigger="hover" :content="t('aiRobot.maximizedWindow')">
             <el-button
               style="width: 24px; height: 24px"
               class="el-button--text"
@@ -370,7 +373,7 @@ defineExpose({
             ></el-button>
           </el-tooltip>
 
-          <el-tooltip trigger="hover" content="Collapsed to Widget">
+          <el-tooltip trigger="hover" :content="t('aiRobot.collapsedToWidget')">
             <el-button
               style="width: 24px; height: 24px"
               class="el-button--text"
@@ -388,7 +391,7 @@ defineExpose({
         <div class="warning-bg">
           <span class="warning-icon font_family icon-icon_warning_fill_b"></span>
         </div>
-        <span>Answer in progress, please wait.</span>
+        <span>{{ t('aiRobot.answerInProgress') }}</span>
       </div>
     </div>
     <div class="chat-messages" ref="messagesRef" @scroll="handleScroll">
@@ -465,7 +468,7 @@ defineExpose({
         <!-- 暂停回答 icon -->
         <el-tooltip
           v-if="index === messages.length - 1 && queryTime > 29 && queryTime < 120"
-          content="Cancel Answer"
+          :content="t('aiRobot.cancelAnswer')"
           placement="bottom-start"
           effect="dark"
           ><div class="pause-btn" @click="handlePause">
@@ -487,7 +490,7 @@ defineExpose({
         <AutoResizeTextarea
           style="flex: 1"
           v-model="userQuestion"
-          :placeholder="'Type your question here...'"
+          :placeholder="t('aiRobot.typeYourQuestionHere')"
           @focus="isFooterInputFocus = true"
           @blur="isFooterInputFocus = false"
         />
@@ -500,14 +503,14 @@ defineExpose({
         </div>
       </div>
       <div class="liability-exemption">
-        <span>Content is generated by Al, please check carefully!</span>
-        <span class="liability-exemption-btn" @click="handleLiabilityExeDialog">Disclaimer</span>
+        <span>{{ t('aiRobot.contentGeneratedByAI') }}</span>
+        <span class="liability-exemption-btn" @click="handleLiabilityExeDialog">{{ t('aiRobot.disclaimerTitle') }}</span>
       </div>
     </div>
     <el-dialog
       class="liability-exemption-dialog"
       v-model="liabilityExeDialog"
-      title="Disclaimer"
+      :title="t('aiRobot.disclaimerTitle')"
       width="800"
       top="20vh"
     >
@@ -515,7 +518,7 @@ defineExpose({
         class="title"
         style="margin-bottom: 8px; font-size: 16px; font-weight: 700; line-height: 24px"
       >
-        Important Notice: AI-Generated Content
+        {{ t('aiRobot.importantNoticeAI') }}
       </div>
       <p>
         This chat assistant is powered by artificial intelligence (AI) and is designed to help you

+ 4 - 1
src/views/AIRobotChat/src/components/AIQuestions.vue

@@ -1,4 +1,7 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
 import { ref, onMounted } from 'vue'
 import emitter from '@/utils/bus'
 
@@ -118,7 +121,7 @@ const clickQuestion = (question) => {
             src="../image/icon_ai_robot48_b@2x.png"
             width="48px"
           />
-          <div class="dialogue_title_left_text">Frequently Asked Questions</div>
+          <div class="dialogue_title_left_text">{{ t('aiRobot.frequentlyAskedQuestions') }}</div>
         </div>
       </div>
       <el-carousel

+ 13 - 10
src/views/Booking/src/BookingView.vue

@@ -4,12 +4,15 @@ import TransportMode from '@/components/TransportMode'
 import BookingTable from './components/BookingTable'
 import DateRange from '@/components/DateRange'
 import MoreFilters from '@/components/MoreFilters'
-import { ref, reactive } from 'vue'
+import { ref } from 'vue'
+import { useI18n } from 'vue-i18n'
 import { useCalculatingHeight } from '@/hooks/calculatingHeight'
 import { useUserStore } from '@/stores/modules/user'
 import dayjs from 'dayjs'
 import { useFiltersStore } from '@/stores/modules/filtersList'
 
+const { t } = useI18n()
+
 const userStore = useUserStore()
 const formatDate = userStore.dateFormat
 const filtersStore = useFiltersStore()
@@ -53,13 +56,13 @@ const tableLoadingTableData = ref(false)
 const initPage = () => {
   if (!filtersList.value || (filtersList.value && filtersList.value.length == 0)) {
     filtersStore.updateFilter({
-      title: 'Transport Mode',
+      title: t('booking.transportMode'),
       value: ['All'],
       keyType: 'array',
       key: 'transport_mode'
     })
     filtersStore.updateFilter({
-      title: 'ETD',
+      title: t('booking.etd'),
       value: [
         dayjs().subtract(2, 'month').startOf('month').format(formatDate),
         dayjs().add(1, 'month').format(formatDate)
@@ -68,7 +71,7 @@ const initPage = () => {
       key: ['f_etd_start', 'f_etd_end']
     })
     filtersStore.updateFilter({
-      title: 'Shipment Status',
+      title: t('booking.shipmentStatus'),
       value: ['All'],
       keyType: 'array',
       key: 'filterTag'
@@ -118,7 +121,7 @@ const getBookingData = () => {
           .filter((item) => item.checked)
           .map((item) => item.name)
         filtersStore.updateFilter({
-          title: 'Shipment Status',
+          title: t('booking.shipmentStatus'),
           value: checkedTabNames,
           keyType: 'array',
           key: 'filterTag'
@@ -189,7 +192,7 @@ const handleSearch = () => {
 
 <template>
   <div class="Title">
-    <span>Booking</span>
+    <span>{{ t('booking.title') }}</span>
     <VDriverGuide @click="handleGuide"></VDriverGuide>
   </div>
   <BookingGuide ref="bookingGuideRef"></BookingGuide>
@@ -200,7 +203,7 @@ const handleSearch = () => {
         <div class="heaer_top">
           <div class="search">
             <el-input
-              placeholder="Enter Booking/HBL/PO/Carrier Booking No. "
+              :placeholder="t('booking.searchPlaceholder')"
               v-model="textSearch"
               class="log_input"
               @keyup.enter="SearchInput"
@@ -218,7 +221,7 @@ const handleSearch = () => {
                   :offset="6"
                   popper-class="ShowAlerIcon"
                   effect="dark"
-                  content="We support the following references number to find bookings:· Booking No./HAWB No./MAWB No./PO No./Carrier Booking No./Contract No./File No./Quote No."
+                  :content="t('booking.bookingListEmptyTipLine1') + ' ' + t('booking.bookingListEmptyTipLine2')"
                   placement="bottom"
                 >
                   <span class="iconfont_icon iconfont_icon_tip">
@@ -246,7 +249,7 @@ const handleSearch = () => {
         :isShowMoreFiltersGuidePhoto="guideStore.booking.isShowMoreFiltersGuidePhoto"
       ></MoreFilters>
       <el-button class="el-button--dark" style="margin-left: 8px" @click="SearchInput"
-        >Search</el-button
+        >{{ t('booking.search') }}</el-button
       >
     </div>
     <!-- 筛选项 -->
@@ -272,7 +275,7 @@ const handleSearch = () => {
         </el-tooltip>
         <div v-else>{{ tag.value }}</div>
       </el-tag>
-      <div class="text_button" @click="clearfilters">Clear Filters</div>
+      <div class="text_button" @click="clearfilters">{{ t('common.clearFilters') }}</div>
     </div>
   </div>
   <BookingTable

+ 12 - 10
src/views/Booking/src/components/BookingDetail/src/BookingDetail.vue

@@ -9,15 +9,17 @@ import { useRoute } from 'vue-router'
 import { useOverflow } from '@/hooks/useOverflow'
 import { formatTimezone } from '@/utils/tools'
 import { useThemeStore } from '@/stores/modules/theme'
+import { useI18n } from 'vue-i18n'
 
+const { t } = useI18n()
 const route = useRoute()
 
 const themeStore = useThemeStore()
 
 // 可拖拽模块的列表
 const boxList = ref([
-  { id: 1, name: 'Basic Information' },
-  { id: 2, name: 'Containers' }
+  { id: 1, name: t('booking.basicInformation') },
+  { id: 2, name: t('booking.containers') }
 ])
 
 const infoContentRef = ref()
@@ -104,7 +106,7 @@ const { isOverflow: isDestinationOverflow } = useOverflow(destinationRef, allDat
           :class="[`icon-${transportationMode?.[allData?.transportInfo?.mode]}`]"
           style="font-size: 64px"
         ></span>
-        <div class="no">Booking No. {{ allData?.transportInfo?.['bookingNo.'] }}</div>
+        <div class="no">{{ t('booking.bookingNo') }} {{ allData?.transportInfo?.['bookingNo.'] }}</div>
         <VTag large :type="allData?.transportInfo?.status">{{
           allData?.transportInfo?.status
         }}</VTag>
@@ -112,7 +114,7 @@ const { isOverflow: isDestinationOverflow } = useOverflow(destinationRef, allDat
       <div class="detail-info">
         <div class="item transport-way">
           <div class="origin">
-            <div class="title">Origin</div>
+            <div class="title">{{ t('booking.origin') }}</div>
             <div class="content">
               <el-tooltip v-if="isOriginOverflow" placement="top">
                 <template #content>{{ allData?.transportInfo?.origin || '--' }}</template>
@@ -130,7 +132,7 @@ const { isOverflow: isDestinationOverflow } = useOverflow(destinationRef, allDat
             </div>
           </div>
           <div class="destination">
-            <div class="title">Destination</div>
+            <div class="title">{{ t('booking.destination') }}</div>
             <div class="content">
               <el-tooltip v-if="isDestinationOverflow" placement="top">
                 <template #content>{{ allData?.transportInfo?.destination || '--' }}</template>
@@ -145,7 +147,7 @@ const { isOverflow: isDestinationOverflow } = useOverflow(destinationRef, allDat
           </div>
         </div>
         <div class="item">
-          <div class="title">ETD/ATD</div>
+          <div class="title">{{ t('booking.etdAtd') }}</div>
           <div class="content">
             <span
               >{{
@@ -159,7 +161,7 @@ const { isOverflow: isDestinationOverflow } = useOverflow(destinationRef, allDat
           </div>
         </div>
         <div class="item">
-          <div class="title">ETA/ATA</div>
+          <div class="title">{{ t('booking.etaAta') }}</div>
           <div class="content">
             <span
               >{{
@@ -196,13 +198,13 @@ const { isOverflow: isDestinationOverflow } = useOverflow(destinationRef, allDat
               <template #header>
                 <div style="display: flex; border-radius: 12px 12px 0 0">
                   <div class="basic-information-header">
-                    <span>Basic Information</span>
+                    <span>{{ t('booking.basicInformation') }}</span>
                     <SeeAllIcon
                       v-model="isBusinessPartnersCollapse"
                       @collapse="handleBusinessPartnersCollapse"
                     ></SeeAllIcon>
                   </div>
-                  <div style="padding-left: 16px">Business Partners</div>
+                  <div style="padding-left: 16px">{{ t('booking.businessPartners') }}</div>
                 </div>
               </template>
               <template #content>
@@ -214,7 +216,7 @@ const { isOverflow: isDestinationOverflow } = useOverflow(destinationRef, allDat
           <!-- Containers -->
           <div v-if="item.id === 2 && allData?.transportInfo?.mode !== 'Air Freight'">
             <VBox :id="item.id" :isSeeAll="false" @draggable="handleDraggable">
-              <template #header>Containers</template>
+              <template #header>{{ t('booking.containers') }}</template>
               <template #content>
                 <ContainersView :data="allData"></ContainersView>
               </template>

+ 21 - 8
src/views/Booking/src/components/BookingDetail/src/components/AddReferenceDialog.vue

@@ -1,5 +1,7 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
 import { type VxeGridInstance, type VxeGridProps } from 'vxe-table'
+const { t } = useI18n()
 const dialogVisible = ref(false)
 
 const openDialog = () => {
@@ -24,20 +26,20 @@ const tableData = ref<VxeGridProps<any>>({
   minHeight: 100,
   columns: [
     {
-      title: 'Reference Type',
+      title: t('booking.tdReferenceType'),
       field: 'referenceType',
       width: 250,
       editRender: { enabled: true },
       slots: { edit: 'refType' }
     },
     {
-      title: 'Ref No.',
+      title: t('booking.refNo'),
       field: 'refNo',
       editRender: { enabled: true },
       slots: { edit: 'refNo' }
     },
     {
-      title: 'Action',
+      title: t('booking.tdAction'),
       width: 90,
       fixed: 'left',
       slots: { default: 'action' }
@@ -98,11 +100,16 @@ defineExpose({
 </script>
 
 <template>
-  <el-dialog v-model="dialogVisible" :width="800" title="Ref No." @closed="clearData">
+  <el-dialog
+    v-model="dialogVisible"
+    :width="800"
+    :title="t('booking.refNoDialogTitle')"
+    @closed="clearData"
+  >
     <div>
       <el-button style="margin-bottom: 8px" class="el-button--text" @click="addNewReference">
         <span class="font_family icon-icon_add_b"></span>
-        Add Reference
+        {{ t('tracking.tdAddReference') }}
       </el-button>
       <vxe-grid ref="tableRef" v-bind="tableData">
         <!-- action操作栏的插槽 -->
@@ -123,7 +130,10 @@ defineExpose({
         </template>
         <!-- Reference Type的编辑插槽 -->
         <template #refType="{ row, column }">
-          <el-select v-model="row[column.field]" placeholder="Select Reference Type">
+          <el-select
+            v-model="row[column.field]"
+            :placeholder="t('booking.selectReferenceTypePlaceholder')"
+          >
             <el-option
               v-for="num in 15"
               :key="num"
@@ -134,10 +144,13 @@ defineExpose({
         </template>
         <!-- Ref No.的编辑插槽 -->
         <template #refNo="{ row, column }">
-          <el-input v-model="row[column.field]" placeholder="Enter Ref No." />
+          <el-input
+            v-model="row[column.field]"
+            :placeholder="t('booking.enterRefNoPlaceholder')"
+          />
         </template>
         <template>
-          <div>No data</div>
+          <div>{{ t('common.noData') }}</div>
         </template>
       </vxe-grid>
     </div>

+ 36 - 33
src/views/Booking/src/components/BookingDetail/src/components/BasicInformation.vue

@@ -1,9 +1,12 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
 import { useRouter } from 'vue-router'
 import XEClipboard from 'xe-clipboard'
 import AddReferenceDialog from './AddReferenceDialog.vue'
 import { formatNumber } from '@/utils/tools'
 
+const { t } = useI18n()
+
 const router = useRouter()
 
 const props = defineProps({
@@ -13,42 +16,42 @@ const allData: any = ref({
   basicInformation: {
     top: [
       {
-        label: 'Booking No.',
+        label: t('booking.bookingNo'),
         content: ''
       },
       {
-        label: 'HAWB/HBL No.',
+        label: t('booking.hawbHblNo'),
         content: '',
         type: 'link'
       },
       {
-        label: 'Carrier Booking No.',
+        label: t('booking.carrierBookingNo'),
         content: ''
       },
       {
-        label: 'PO No.',
+        label: t('booking.poNo'),
         content: ''
       },
       {
-        label: 'Ref No.',
+        label: t('booking.refNo'),
         content: []
       }
     ],
     bottom: [
       {
-        label: 'Vessel / Airline',
+        label: t('booking.vesselAirline'),
         content: ''
       },
       {
-        label: 'Voyage / Flight',
+        label: t('booking.voyageFlight'),
         content: ''
       },
       {
-        label: 'Incoterm',
+        label: t('booking.incoterm'),
         content: ''
       },
       {
-        label: 'Service Type',
+        label: t('booking.serviceType'),
         content: ''
       }
     ]
@@ -126,38 +129,38 @@ const convertData = (data: any) => {
           content: data.basicInfo['bookingNo.'] || '--'
         },
         {
-          label: 'HAWB/HBL No.',
+          label: t('booking.hawbHblNo'),
           content: data.basicInfo['HAWB/HBOL'] || '--',
           type: 'link'
         },
         {
-          label: 'Carrier Booking No.',
+          label: t('booking.carrierBookingNo'),
           content: data.basicInfo['Carrier_Booking_No'] || '--'
         },
         {
-          label: 'PO No.',
+          label: t('booking.poNo'),
           content: data.basicInfo['PO_NO'] || '--'
         },
         {
-          label: 'Ref No.',
+          label: t('booking.refNo'),
           content: data.ref_no
         }
       ],
       bottom: [
         {
-          label: 'Vessel / Airline',
+          label: t('booking.vesselAirline'),
           content: data.basicInfo['Vessel/Airline'] || '--'
         },
         {
-          label: 'Voyage / Flight',
+          label: t('booking.voyageFlight'),
           content: data.basicInfo['Voyage/Filght'] || '--'
         },
         {
-          label: 'Incoterm',
+          label: t('booking.incoterm'),
           content: data.basicInfo['Incoterm'] || '--'
         },
         {
-          label: 'Service Type',
+          label: t('booking.serviceType'),
           content: data.basicInfo['Service_Type'] || '--'
         }
       ],
@@ -165,25 +168,25 @@ const convertData = (data: any) => {
     },
     businessPartners: [
       {
-        title: 'Shipper',
+        title: t('booking.shipper'),
         company: data.businessPartners.shipper.company || '--',
         address: data.businessPartners.shipper.address || '--',
         phone: data.businessPartners.shipper.phone || '--'
       },
       {
-        title: 'Consignee',
+        title: t('booking.consignee'),
         company: data.businessPartners.consignee.company || '--',
         address: data.businessPartners.consignee.address || '--',
         phone: data.businessPartners.consignee.phone || '--'
       },
       {
-        title: 'Origin Agent',
+        title: t('booking.originAgent'),
         company: data.businessPartners.origin.company || '--',
         address: data.businessPartners.origin.address || '--',
         phone: data.businessPartners.origin.phone || '--'
       },
       {
-        title: 'Destination Agent',
+        title: t('booking.destinationAgent'),
         company: data.businessPartners.destination.company || '--',
         address: data.businessPartners.destination.address || '--',
         phone: data.businessPartners.destination.phone || '--'
@@ -191,29 +194,29 @@ const convertData = (data: any) => {
     ],
     packing: [
       {
-        label: 'Quantity / Unit',
+        label: t('booking.quantityUnit'),
         content: data.packing['Quantity/Unit'] || '--'
       },
       {
-        label: 'G. Weight',
+        label: t('booking.gWeight'),
         content: substringFromStart(data.packing['G. Weight'], -4) + ' KGS'
       },
       {
-        label: 'Ch. Weight',
+        label: t('booking.chWeight'),
         content: substringFromStart(data.packing['Ch. Weight'], -4) + ' KGS'
       },
       {
-        label: 'Volume',
+        label: t('booking.volume'),
         content: substringFromStart(data.packing['Volume'], -4) + ' CBM'
       }
     ],
     marksAndDescription: [
       {
-        label: 'Marks',
+        label: t('booking.marks'),
         content: data.marksAndDescription.marsk || '--'
       },
       {
-        label: 'Description',
+        label: t('booking.description'),
         content: data.marksAndDescription.description || '--'
       }
     ]
@@ -289,7 +292,7 @@ const handleCopy = (data: any) => {
   }) // 用空格替换换行符
   const copyText = sanitizedData.join('\n') // 拼接为单行文本
   if (XEClipboard.copy(copyText)) {
-    ElMessage.success('Copy success')
+    ElMessage.success(t('common.copySuccess'))
   }
 }
 
@@ -353,7 +356,7 @@ defineExpose({
             </div>
           </template>
           <template v-else>
-            <div class="content">--</div>
+            <div class="content">{{ t('common.noData') }}</div>
           </template>
         </template>
       </div>
@@ -396,7 +399,7 @@ defineExpose({
       </div>
 
       <div class="packing">
-        <div class="header">Packing</div>
+        <div class="header">{{ t('booking.packing') }}</div>
         <div class="content">
           <div class="data-item" v-for="item in allData.packing" :key="item.label">
             <div class="title">{{ item.label }}</div>
@@ -406,7 +409,7 @@ defineExpose({
       </div>
       <div class="marks-and-description">
         <div class="header">
-          <span>Marks and Description</span>
+          <span>{{ t('booking.marksAndDescription') }}</span>
         </div>
         <div class="data-info">
           <div class="data-item" v-for="item in allData.marksAndDescription" :key="item.label">
@@ -439,7 +442,7 @@ defineExpose({
                 popper-class="marks-popover description-see-all"
                 placement="top-end"
                 :width="700"
-                title="Description"
+                :title="t('common.description')"
                 trigger="click"
               >
                 <div class="description-info">
@@ -447,7 +450,7 @@ defineExpose({
                 </div>
                 <template #reference>
                   <el-button class="description-see-all el-button--text">
-                    <span>See All</span>
+                    <span>{{ t('common.seeAll') }}</span>
                   </el-button>
                 </template>
               </el-popover>

+ 4 - 1
src/views/Booking/src/components/BookingDetail/src/components/ContainersView.vue

@@ -3,6 +3,9 @@ import { type VxeGridInstance, type VxeGridProps } from 'vxe-table'
 // import { autoWidth } from '@/utils/table'
 import { useRowClickStyle } from '@/hooks/rowClickStyle'
 import { formatTimezone, formatNumber } from '@/utils/tools'
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
 
 const props = defineProps({
   data: Object
@@ -81,7 +84,7 @@ useRowClickStyle(tableRef)
   <div class="containers">
     <vxe-grid ref="tableRef" v-bind="tableData">
       <template #empty>
-        <div class="empty">No data</div>
+        <div class="empty">{{ t('common.noData') }}</div>
       </template>
     </vxe-grid>
   </div>

+ 10 - 7
src/views/Booking/src/components/BookingDetail/src/components/EmailView.vue

@@ -1,4 +1,7 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
 import '@wangeditor/editor/dist/css/style.css'
 import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
 import { i18nChangeLanguage, DomEditor } from '@wangeditor/editor'
@@ -138,7 +141,7 @@ const sendEmail = () => {
   const html = editorRef.value.getHtml()
   const text = editorRef.value.getText()
   if (!text) {
-    ElMessage.warning('Please enter the email content')
+    ElMessage.warning(t('booking.pleaseEnterEmailContent'))
     return
   }
   $api
@@ -152,12 +155,12 @@ const sendEmail = () => {
     })
     .then((res: any) => {
       if (res.code === 200) {
-        ElMessage.success('Email sent successfully')
+        ElMessage.success(t('booking.emailSentSuccessfully'))
         emailRecords.value = res.data.emailRecords
       }
     })
     .catch(() => {
-      ElMessage.error('Failed to send email')
+      ElMessage.error(t('booking.failedToSendEmail'))
     })
 }
 </script>
@@ -166,7 +169,7 @@ const sendEmail = () => {
   <div class="email-view">
     <div class="email-path">
       <span class="font_family icon-icon_email_b" style="font-size: 18px"></span>
-      <span class="label">Communicate with us:&nbsp;</span>
+      <span class="label">{{ t('destinationDelivery.communicateWithUs') }}:&nbsp;</span>
       <span class="content">{{ emailData.email }}</span>
     </div>
     <div class="separated-by">
@@ -184,7 +187,7 @@ const sendEmail = () => {
             <el-tooltip
               class="box-item"
               effect="dark"
-              content="Separated by;"
+              :content="t('destinationDelivery.separatedBySemicolon')"
               placement="top-start"
               :offset="-8"
             >
@@ -214,8 +217,8 @@ const sendEmail = () => {
         @click="sendEmail"
         class="el-button--dark"
         style="float: right; margin: 8px 0 14px 0; height: 40px"
-        ><span class="font_family icon-icon_submit_b" style="margin-right: 4px"></span> Send
-        Email</el-button
+        ><span class="font_family icon-icon_submit_b" style="margin-right: 4px"></span>
+        {{ t('booking.sendEmail') }}</el-button
       >
     </div>
     <div class="show-records">

+ 18 - 17
src/views/Booking/src/components/BookingGuide.vue

@@ -2,6 +2,7 @@
 import { useDriver } from '@/utils/driverGuide'
 import { useGuideStore } from '@/stores/modules/guide'
 import { useThemeStore } from '@/stores/modules/theme'
+import { useI18n } from 'vue-i18n'
 
 import customizeColumnsImgLight from '../image/customize-columns.png'
 import customizeColumnsImgDark from '../image/dark-customize-columns.png'
@@ -9,6 +10,8 @@ import customizeColumnsImgDark from '../image/dark-customize-columns.png'
 import downloadFileImgLight from '@/views/Tracking/src/image/download-guide.png'
 import downloadFileImgDark from '@/views/Tracking/src/image/dark-download-guide.png'
 
+const { t } = useI18n()
+
 const themeStore = useThemeStore()
 
 const customizeColumnsImg = computed(() => {
@@ -26,12 +29,12 @@ const steps: any = [
     element: '#booking-filters-container-guide',
     popover: {
       title: '',
-      description: `Frequently Used Search Criteria Display Area:
+      description: `${t('booking.guideSearchAreaTitle')}:
         <ul>
-          <li>Key Booking Status</li>
-          <li>Reference Numbers</li>
-          <li>Transport Mode</li>
-          <li>Date Type & Range</li>
+          <li>${t('booking.guideSearchAreaStatus')}</li>
+          <li>${t('booking.guideSearchAreaReferences')}</li>
+          <li>${t('booking.guideSearchAreaTransportMode')}</li>
+          <li>${t('booking.guideSearchAreaDateRange')}</li>
         </ul>`,
       side: 'bottom'
     }
@@ -42,8 +45,8 @@ const steps: any = [
       title: '',
       description: `
         <ul>
-          <li>Selected query criteria display area</li>
-          <li>You can quickly clear selected conditions at this position</li>
+          <li>${t('booking.guideSelectedCriteria')}</li>
+          <li>${t('booking.guideClearCriteria')}</li>
         </ul>
       `,
       side: 'bottom'
@@ -53,7 +56,7 @@ const steps: any = [
     element: '#more-filters-guide',
     popover: {
       title: '',
-      description: 'Click "More Filters" to see more search options.',
+      description: t('booking.guideMoreFilters'),
       side: 'left'
     }
   },
@@ -61,8 +64,7 @@ const steps: any = [
     element: '.booking-no-header',
     popover: {
       title: '',
-      description:
-        'Click on the Booking No. or double-click anywhere on a single booking data to enter the detailed page.',
+      description: t('booking.guideOpenDetail'),
       side: 'bottom'
     }
   },
@@ -72,8 +74,8 @@ const steps: any = [
       title: '',
       description: `
         <ul>
-          <li>View the number of shipments selected for download</li>
-          <li>Two download list templates are available</li>
+          <li>${t('booking.guideDownloadTipLine1')}</li>
+          <li>${t('booking.guideDownloadTipLine2')}</li>
         </ul>
       `,
       side: 'bottom'
@@ -85,9 +87,9 @@ const steps: any = [
       title: '',
       description: `
         <ul>
-          <li>Drag to right to add columns</li>
-          <li>Drag to left to unselect columns</li>
-          <li>Drag up and down to reorder or select/unselect</li>
+          <li>${t('booking.guideCustomizeColumns')}</li>
+          <li>${t('booking.guideCustomizeColumnsLeft')}</li>
+          <li>${t('booking.guideCustomizeColumnsReorder')}</li>
         </ul>
       `,
       side: 'bottom'
@@ -97,8 +99,7 @@ const steps: any = [
     element: '#page-guide-btn-guide',
     popover: {
       title: '',
-      description:
-        'After closing, you can still click the "Page Guide" button to view the page guide of the current page.',
+      description: t('booking.guidePageGuide'),
       side: 'bottom'
     }
   }

+ 12 - 10
src/views/Booking/src/components/BookingTable/src/BookingTable.vue

@@ -1,4 +1,7 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
 import { type VxeGridInstance, type VxeGridProps } from 'vxe-table'
 import DownloadDialog from './components/DownloadDialog.vue'
 import { autoWidth } from '@/utils/table'
@@ -220,7 +223,7 @@ const bookingTable = ref<VxeGridProps<any>>({
         [
           {
             code: 'newTab',
-            name: 'Open in New Tab',
+            name: t('booking.openInNewTab'),
             prefixConfig: { icon: 'icon-icon_jumplink_b1 font_family' }
           }
         ]
@@ -406,7 +409,7 @@ const handleCustomizeColumns = () => {
   CustomizeColumnsRef.value.openDialog(
     params,
     -220,
-    'Drag item over to this selection or click "add" icon to show the column on your booking list'
+    t('booking.customizeColumnsTip')
   )
 }
 // 定制表格
@@ -488,12 +491,12 @@ defineExpose({
     style="padding: 0px 24px"
     class="booking-table"
     v-loading.fullscreen.lock="exportLoading"
-    element-loading-text="Loading..."
+    element-loading-text="{{ t('common.loading') }}"
     element-loading-custom-class="element-loading"
     element-loading-background="rgb(43, 47, 54, 0.7)"
   >
     <div class="table-tools">
-      <div class="left-total-records">{{ selectedNumber }} Selected</div>
+      <div class="left-total-records">{{ selectedNumber }} {{ t('booking.selected') }}</div>
       <div class="right-tools-btn">
         <el-button
           :class="{ 'el-button--pain-theme': themeStore.theme === 'dark' }"
@@ -505,11 +508,11 @@ defineExpose({
           }"
         >
           <span style="margin-right: 7px" class="font_family icon-icon_download_b"></span>
-          Download
+          {{ t('booking.download') }}
         </el-button>
         <el-button style="padding: 0 17px 0 9px" type="default" @click="handleCustomizeColumns">
           <span style="margin-right: 6px" class="font_family icon-icon_column_b"></span>
-          Customize Columns
+          {{ t('customizeColumns.title') }}
         </el-button>
       </div>
     </div>
@@ -531,11 +534,10 @@ defineExpose({
         <VEmpty>
           <template #suggestion>
             <p style="color: var(--color-neutral-3)">
-              We support the following references number to find booking:
+              {{ t('booking.bookingListEmptyTip') }}
             </p>
             <p style="color: var(--color-neutral-3)">
-              · Booking No./HAWB No./MAWB No./PO No./Carrier Booking No./Contract No./File No./Quote
-              No.
+              {{ t('booking.bookingListEmptyTipDetail') }}
             </p>
           </template>
         </VEmpty>
@@ -566,7 +568,7 @@ defineExpose({
     </vxe-grid>
     <vxe-grid :height="10" ref="allTableRef" class="all-table" v-bind="allTable"> </vxe-grid>
     <div class="bottom-pagination">
-      <div class="left-total-records">Total {{ formatNumber(pageInfo.total) }}</div>
+      <div class="left-total-records">{{ t('common.total') }} {{ formatNumber(pageInfo.total) }}</div>
       <div class="right-pagination">
         <el-pagination
           v-model:current-page="pageInfo.pageNo"

+ 10 - 7
src/views/Booking/src/components/BookingTable/src/components/DownloadDialog.vue

@@ -1,4 +1,7 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
 import { useFiltersStore } from '@/stores/modules/filtersList'
 
 const filtersStore = useFiltersStore()
@@ -36,11 +39,11 @@ defineExpose({
 
 <template>
   <div>
-    <el-dialog @close="clearData" v-model="dialogVisible" title="Download File" width="540">
+    <el-dialog @close="clearData" v-model="dialogVisible" :title="t('common.downloadFile')" width="540">
       <div class="download-dialog">
         <div class="select-data">
           <div style="display: inline-block">
-            Select data on your booking list:<span style="color: var(--color-theme)">{{
+            {{ t('booking.selectDataOnBookingList') }}:<span style="color: var(--color-theme)">{{
               selectedDataNumber
             }}</span>
           </div>
@@ -63,7 +66,7 @@ defineExpose({
         <div class="download-filter">
           <el-radio-group v-model="downloadFilter">
             <el-radio :value="1"
-              >Download with selected columns
+              >{{ t('booking.downloadWithSelectedColumns') }}
               <span class="column-number">{{ columns.length }}</span>
               <SeeAllIcon v-model="isShowSelectColumn" />
             </el-radio>
@@ -72,23 +75,23 @@ defineExpose({
               class="select-columns"
               :class="{ show: isShowSelectColumn }"
             >
-              <div class="title">Selected columns</div>
+              <div class="title">{{ t('common.selectedColumns') }}</div>
               <div class="content">
                 <div class="column-item" v-for="item in columns" :key="item">{{ item }}</div>
               </div>
             </div>
-            <el-radio :value="2">Download with all columns</el-radio>
+            <el-radio :value="2">{{ t('common.downloadWithAllColumns') }}</el-radio>
           </el-radio-group>
         </div>
       </div>
       <template #footer>
         <div class="dialog-footer">
           <el-button class="cancel-btn" type="default" @click="dialogVisible = false"
-            >Cancel</el-button
+            >{{ t('common.cancel') }}</el-button
           >
           <el-button class="download-btn el-button--dark" @click="handleDownload"
             ><span style="margin-right: 8px" class="font_family icon-icon_download_b"></span>
-            Download</el-button
+            {{ t('booking.download') }}</el-button
           >
         </div>
       </template>

+ 34 - 19
src/views/ChatLog/src/ChatLog.vue

@@ -2,6 +2,9 @@
 import { useCalculatingHeight } from '@/hooks/calculatingHeight'
 import TableView from './components/TableView'
 import dayjs from 'dayjs'
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
 
 const filterRef: Ref<HTMLElement | null> = ref(null)
 const containerHeight = useCalculatingHeight(document.documentElement, 290, [filterRef])
@@ -19,53 +22,53 @@ const searchData = ref({
 
 const userTypeList = [
   {
-    label: 'Customer',
+    label: t('common.customer'),
     value: 'customer'
   },
   {
-    label: 'Employee',
+    label: t('common.employee'),
     value: 'employee'
   }
 ]
 const questionTypeList = [
   {
-    label: 'Predefined Question',
+    label: t('common.predefinedQuestion'),
     value: 'Predefined Question'
   },
   {
-    label: 'Free Text',
+    label: t('common.freeText'),
     value: 'Free Text'
   }
 ]
 const AnswerTypeList = [
   {
-    label: 'Suspend',
+    label: t('common.suspend'),
     value: 'Suspend'
   },
   {
-    label: 'Timeout',
+    label: t('common.timeout'),
     value: 'Timeout'
   },
   {
-    label: 'Predefined Template',
+    label: t('common.predefinedTemplate'),
     value: 'Predefined Template'
   },
   {
-    label: 'AI Answer',
+    label: t('common.aiAnswer'),
     value: 'AI Answer'
   }
 ]
 const answerSatisfactionList = [
   {
-    label: 'Null',
+    label: t('common.null'),
     value: 'Null'
   },
   {
-    label: 'Good',
+    label: t('common.good'),
     value: 'Good'
   },
   {
-    label: 'Not Good',
+    label: t('common.notGood'),
     value: 'Not Good'
   }
 ]
@@ -102,12 +105,12 @@ const DateChange = (date: any) => {
 </script>
 <template>
   <div class="dashboard">
-    <div class="Title">Chat Log</div>
+    <div class="Title">{{ t('common.chatLog') }}</div>
     <div class="display">
       <div class="heaer_top">
         <div class="input-tips_filter">
           <el-input
-            placeholder="Search Question ID、User"
+            :placeholder="t('common.searchChatPlaceholder')"
             v-model="searchData.text_search"
             clearable
             class="log_input"
@@ -126,7 +129,11 @@ const DateChange = (date: any) => {
           <CalendarDate @DateChange="DateChange"></CalendarDate>
         </div>
         <div class="tips_filter">
-          <el-select v-model="searchData.user_type" clearable placeholder="User Type">
+          <el-select
+            v-model="searchData.user_type"
+            clearable
+            :placeholder="t('common.userType')"
+          >
             <el-option
               v-for="item in userTypeList"
               :key="item.value"
@@ -136,7 +143,11 @@ const DateChange = (date: any) => {
           </el-select>
         </div>
         <div class="tips_filter">
-          <el-select clearable v-model="searchData.question_type" placeholder="Question Type">
+          <el-select
+            clearable
+            v-model="searchData.question_type"
+            :placeholder="t('common.questionType')"
+          >
             <el-option
               v-for="item in questionTypeList"
               :key="item.value"
@@ -147,7 +158,11 @@ const DateChange = (date: any) => {
           </el-select>
         </div>
         <div class="tips_filter">
-          <el-select clearable v-model="searchData.answer_type" placeholder="Answer Type">
+          <el-select
+            clearable
+            v-model="searchData.answer_type"
+            :placeholder="t('common.answerType')"
+          >
             <el-option
               v-for="item in AnswerTypeList"
               :key="item.value"
@@ -161,7 +176,7 @@ const DateChange = (date: any) => {
           <el-select
             clearable
             v-model="searchData.answer_satisfication"
-            placeholder="Answer Satisfaction"
+            :placeholder="t('common.answerSatisfaction')"
           >
             <el-option
               v-for="item in answerSatisfactionList"
@@ -173,7 +188,7 @@ const DateChange = (date: any) => {
           </el-select>
         </div>
         <div class="response_duration_type-tips_filter">
-          <span>Response Duration</span>
+          <span>{{ t('common.responseDuration') }}</span>
           <el-select
             clearable
             v-model="searchData.response_duration_type"
@@ -196,7 +211,7 @@ const DateChange = (date: any) => {
             style="width: 60px; height: 34px"
           ></el-input-number>
         </div>
-        <el-button class="el-button--dark" @click="Search">Search</el-button>
+        <el-button class="el-button--dark" @click="Search">{{ t('common.search') }}</el-button>
       </div>
     </div>
     <TableView :height="containerHeight" :searchData="searchData" ref="tableRef"></TableView>

+ 8 - 5
src/views/ChatLog/src/components/TableView/src/TableView.vue

@@ -1,4 +1,7 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
 import { ref, nextTick, onMounted } from 'vue'
 import { type VxeGridInstance, type VxeGridProps } from 'vxe-table'
 import DownloadDialog from './components/DownloadDialog.vue'
@@ -68,7 +71,7 @@ const getTableColumns = async () => {
       tableData.value.columns = [
         { type: 'checkbox', width: 50, fixed: 'left' },
         ...handleColumns(res.data.OperationTableColumns),
-        { title: 'Action', width: 106, fixed: 'left', slots: { default: 'action' } }
+        { title: t('common.operation'), width: 106, fixed: 'left', slots: { default: 'action' } }
       ]
       tableOriginColumnsField.value = res.data.OperationTableColumns
     }
@@ -380,11 +383,11 @@ defineExpose({
       element-loading-custom-class="element-loading"
       element-loading-background="rgb(43, 47, 54, 0.7)"
     >
-      <div class="left-total-records">{{ selectedNumber }} Selected</div>
+      <div class="left-total-records">{{ selectedNumber }} {{ t('common.selected') }}</div>
       <div class="right-tools-btn">
         <el-button class="el-button--main el-button--pain-theme" @click="handleDownload">
           <span style="margin-right: 8px" class="font_family icon-icon_download_b"></span>
-          Download
+          {{ t('booking.download') }}
         </el-button>
       </div>
     </div>
@@ -413,13 +416,13 @@ defineExpose({
             style="margin-right: 2px; font-size: 15px"
             class="font_family icon-icon_ai_api_log_b"
           ></span
-          >AI API Log
+          >{{ t('aiApiLog.title') }}
         </el-button>
       </template>
     </vxe-grid>
     <vxe-grid :height="10" ref="allTableRef" class="all-table" v-bind="allTable"> </vxe-grid>
     <div class="bottom-pagination">
-      <div class="left-total-records">Total {{ formatNumber(pageInfo.total) }}</div>
+      <div class="left-total-records">{{ t('common.total') }} {{ formatNumber(pageInfo.total) }}</div>
       <div class="right-pagination">
         <el-pagination
           v-model:current-page="pageInfo.pageNo"

+ 10 - 7
src/views/ChatLog/src/components/TableView/src/components/DownloadDialog.vue

@@ -1,4 +1,7 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
 const props = withDefaults(
   defineProps<{
     isHideSelectColumn: boolean
@@ -38,11 +41,11 @@ defineExpose({
 
 <template>
   <div>
-    <el-dialog @close="clearData" v-model="dialogVisible" title="Download File" width="540">
+    <el-dialog @close="clearData" v-model="dialogVisible" :title="t('common.downloadFile')" width="540">
       <div class="download-dialog">
         <div class="select-data">
           <div style="display: inline-block">
-            Select data on your Opeartion Log list:<span style="color: var(--color-theme)">{{
+            {{ t('aiApiLog.selectDataOnLogList') }}<span style="color: var(--color-theme)">{{
               selectedDataNumber
             }}</span>
           </div>
@@ -50,7 +53,7 @@ defineExpose({
         <div class="download-filter" v-if="!props.isHideSelectColumn">
           <el-radio-group v-model="downloadFilter">
             <el-radio :value="1"
-              >Download with selected columns
+              >{{ t('destinationDelivery.downloadWithSelectedColumns') }}
               <span class="column-number">{{ columns.length }}</span>
               <SeeAllIcon v-model="isShowSelectColumn" />
             </el-radio>
@@ -59,23 +62,23 @@ defineExpose({
               class="select-columns"
               :class="{ show: isShowSelectColumn }"
             >
-              <div class="title">Selected columns</div>
+              <div class="title">{{ t('common.selectedColumns') }}</div>
               <div class="content">
                 <div class="column-item" v-for="item in columns" :key="item">{{ item }}</div>
               </div>
             </div>
-            <el-radio :value="2">Download with all columns</el-radio>
+            <el-radio :value="2">{{ t('common.downloadWithAllColumns') }}</el-radio>
           </el-radio-group>
         </div>
       </div>
       <template #footer>
         <div class="dialog-footer">
           <el-button class="cancel-btn" type="default" @click="dialogVisible = false"
-            >Cancel</el-button
+            >{{ t('common.cancel') }}</el-button
           >
           <el-button class="download-btn el-button--dark" @click="handleDownload"
             ><span style="margin-right: 8px" class="font_family icon-icon_download_b"></span>
-            Download</el-button
+            {{ t('tracking.download') }}</el-button
           >
         </div>
       </template>

+ 21 - 21
src/views/Dashboard/src/DashboardView.vue

@@ -16,6 +16,9 @@ import { formatNumber } from '@/utils/tools'
 import { useFiltersStore, FiltersType } from '@/stores/modules/filtersList'
 import dayjs from 'dayjs'
 import { useUserStore } from '@/stores/modules/user'
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
 
 const userStore = useUserStore()
 const formatDate = userStore.dateFormat
@@ -686,7 +689,6 @@ const transformDateRange = (
 const ClickParams = (val: string) => {
   const configFactory = CLICK_CONFIG_MAP[val]
   if (!configFactory) {
-    console.warn(`No config found for click value: ${val}`)
     return
   }
 
@@ -708,7 +710,7 @@ const ClickParams = (val: string) => {
 
   // 2. 设置运输方式
   filtersStore.updateFilter({
-    title: 'Transport Mode',
+    title: t('dashboard.transportMode'),
     key: 'transport_mode',
     value: config.transportMode,
     keyType: 'array'
@@ -831,7 +833,7 @@ const handleGuide = () => {
     <!-- Title -->
     <div class="Title">
       <div>
-        <span>Dashboard</span>
+        <span>{{ t('dashboard.title') }}</span>
         <VDriverGuide style="margin-top: -1px" @click="handleGuide"></VDriverGuide>
       </div>
 
@@ -844,12 +846,12 @@ const handleGuide = () => {
                   <use xlink:href="#icon-icon_view__management_b"></use>
                 </svg>
               </span>
-              View Management
+              {{ t('dashboard.viewManagement') }}
             </el-button>
           </template>
 
           <div class="Management">
-            <div class="title">View Management</div>
+            <div class="title">{{ t('dashboard.viewManagement') }}</div>
             <div class="management-content">
               <div class="management-item" v-for="(item, index) in Management" :key="index">
                 <div class="management_flex">
@@ -868,8 +870,7 @@ const handleGuide = () => {
                   class="content_text_warining"
                   v-if="item.isRevenueDisplay != undefined && item.isRevenueDisplay == false"
                 >
-                  *To ensure the accuracy of the data display, this report needs to be configured
-                  and displayed after communicating clearly with Sales.
+                  {{ t('dashboard.salesConfigWarning') }}
                 </div>
               </div>
             </div>
@@ -881,8 +882,7 @@ const handleGuide = () => {
                 </svg>
               </span>
               <div class="tips_text">
-                Please remember to click the save button in order to keep the new dashboard layout
-                and widgets.
+                {{ t('dashboard.rememberSaveLayout') }}
               </div>
             </div>
           </div>
@@ -922,7 +922,7 @@ const handleGuide = () => {
                   <use xlink:href="#icon-icon_save_b"></use>
                 </svg>
               </span>
-              Save
+              {{ t('dashboard.save') }}
               <span class="iconfont_icon">
                 <svg class="iconfont" aria-hidden="true">
                   <use xlink:href="#icon-icon_dropdown_b"></use>
@@ -936,7 +936,7 @@ const handleGuide = () => {
                 <use xlink:href="#icon-icon_save_b"></use>
               </svg>
             </span>
-            <div>Save Filters</div>
+            <div>{{ t('dashboard.saveFilters') }}</div>
           </div>
           <div class="Save_filters" @click="SaveLayout">
             <span class="iconfont_icon iconfont_icon_save">
@@ -944,7 +944,7 @@ const handleGuide = () => {
                 <use xlink:href="#icon-icon_save_b"></use>
               </svg>
             </span>
-            <div>Save Layout</div>
+            <div>{{ t('dashboard.saveLayout') }}</div>
           </div>
         </el-popover>
 
@@ -1018,7 +1018,7 @@ const handleGuide = () => {
                     <VTipTooltip
                       :img="kpiChartTip"
                       :width="410"
-                      :label="'KPI Report:Day difference between actual and estimate.'"
+                      :label="t('dashboard.kpiReportTip')"
                       placement="bottom-start"
                     ></VTipTooltip>
                   </div>
@@ -1065,7 +1065,7 @@ const handleGuide = () => {
                       :img="pendingChartTip"
                       :width="420"
                       :placement="'bottom-start'"
-                      :label="'Pending Report:Showing shipments which are soon to depart/arrive.'"
+                      :label="t('dashboard.pendingReportTip')"
                     ></VTipTooltip>
                   </div>
                   <DashFilters
@@ -1114,7 +1114,7 @@ const handleGuide = () => {
                       :img="etdToEtaChartsTip"
                       :width="430"
                       :placement="'bottom-start'"
-                      :label="'ETD to ETA (Days):Distribution of Transit Time (ETA-ETD) for All Shipments in Last 12 Months.'"
+                      :label="t('dashboard.etdToEtaTip')"
                     ></VTipTooltip>
                   </div>
                   <DashFilters
@@ -1149,7 +1149,7 @@ const handleGuide = () => {
                     <VTipTooltip
                       :img="containerChartTip"
                       :placement="'bottom-start'"
-                      :label="'Container Count:Total Container Volume by Month (Last 12 Months)'"
+                      :label="t('dashboard.containerCountTip')"
                     ></VTipTooltip>
                   </div>
                   <DashFilters
@@ -1193,7 +1193,7 @@ const handleGuide = () => {
                     <VTipTooltip
                       style="margin-left: 4px"
                       :img="top10ChartTip"
-                      :label="'Top 10 Origin & Destination: Last 12 Months Shipment Volume Rankings: Top 10 Origin Cities and Top 10 Destination Cities'"
+                      :label="t('dashboard.top10Tip')"
                       :width="700"
                     ></VTipTooltip>
                   </div>
@@ -1254,7 +1254,7 @@ const handleGuide = () => {
                     {{ item.title }}
                     <VTipTooltip
                       :img="co2eChartTip"
-                      :label="'CO2e Emission by Origin or Destination: Last 12 Months CO2e Emission Rankings: Top 10 Origin Cities and Top 10 Destination Cities'"
+                      :label="t('dashboard.co2eTip')"
                       :width="700"
                       :placement="'bottom-start'"
                     ></VTipTooltip>
@@ -1330,7 +1330,7 @@ const handleGuide = () => {
                     {{ item.title }}
                     <VTipTooltip
                       :img="recentStatusChartTip"
-                      :label="'Recent Status: Active shipment list with ETD within the past three months and the next month.'"
+                      :label="t('dashboard.recentStatusTip')"
                       :width="700"
                       :placement="'bottom-start'"
                     ></VTipTooltip>
@@ -1367,10 +1367,10 @@ const handleGuide = () => {
               <template #header>
                 <div class="Title_flex">
                   <div>
-                    Revenue Spent
+                    {{ t('dashboard.revenueSpentTitle') }}
                     <VTipTooltip
                       :img="revenueSpentChartTip"
-                      :label="'Revenue Spent: Based on the billto object, display the corresponding revenue data. '"
+                      :label="t('dashboard.revenueSpentTip')"
                       :placement="'bottom-start'"
                       :width="700"
                     ></VTipTooltip>

+ 12 - 9
src/views/Dashboard/src/components/CustomerFilter.vue

@@ -1,4 +1,7 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
 import { cloneDeep, debounce } from 'lodash'
 import axios from 'axios'
 
@@ -73,7 +76,7 @@ const remoteMethod = (query: string) => {
     .catch((err) => {
       options.value = []
       if (!axios.isCancel(err) && !newController.signal.aborted) {
-        ElMessage.error('Failed to load options')
+        ElMessage.error(t('common.failedToLoadOptions'))
       }
     })
     .finally(() => {
@@ -93,23 +96,23 @@ onUnmounted(() => {
 
 const typeOptions = ref([
   {
-    label: 'Shipper',
+    label: t('common.shipper'),
     value: 'shipper_id'
   },
   {
-    label: 'Consignee',
+    label: t('common.consignee'),
     value: 'consignee_id'
   },
   {
-    label: 'Controlling Customer',
+    label: t('common.controllingCustomer'),
     value: 'customer_code'
   },
   {
-    label: 'Bill to',
+    label: t('common.billTo'),
     value: 'billto_id'
   },
   {
-    label: 'Notify Party',
+    label: t('common.notifyParty'),
     value: 'notify_party_id'
   }
 ])
@@ -126,7 +129,7 @@ const handleSearch = () => {
       multiple
       filterable
       reserve-keyword
-      placeholder="Search by Customer code, Customer name"
+      :placeholder="t('common.customerSearchPlaceholder')"
       :loading="loading"
       style="width: 400px"
       collapse-tags
@@ -155,7 +158,7 @@ const handleSearch = () => {
       </el-option>
     </el-select>
     <el-select
-      placeholder="Customer Type"
+      :placeholder="t('common.customerType')"
       multiple
       :max-collapse-tags="1"
       collapse-tags
@@ -180,7 +183,7 @@ const handleSearch = () => {
         </div>
       </el-option>
     </el-select>
-    <el-button class="el-button--default" @click="handleSearch">Search</el-button>
+    <el-button class="el-button--default" @click="handleSearch">{{ t('common.search') }}</el-button>
   </div>
 </template>
 

+ 11 - 8
src/views/Dashboard/src/components/DashFiters.vue

@@ -1,4 +1,7 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
 import { ref, watch, onMounted, computed } from 'vue'
 import dayjs from 'dayjs'
 import { formatTimezone } from '@/utils/tools'
@@ -198,7 +201,7 @@ const guideStore = useGuideStore()
               <use xlink:href="#icon-icon_filter_b"></use>
             </svg>
           </span>
-          Filters
+          {{ t('dashboard.filters') }}
           <span class="iconfont_icon">
             <svg class="iconfont icon_dark" aria-hidden="true">
               <use xlink:href="#icon-icon_dropdown_b"></use>
@@ -206,7 +209,7 @@ const guideStore = useGuideStore()
           </span>
         </el-button>
       </template>
-      <div class="Dash_title">Transport Mode</div>
+      <div class="Dash_title">{{ t('dashboard.transportMode') }}</div>
       <div class="filter_filter_one">
         <el-checkbox-group
           @change="changeCheckboxGroup1"
@@ -225,9 +228,9 @@ const guideStore = useGuideStore()
         </el-checkbox-group>
       </div>
       <div class="Dash_title">
-        <div>Date Range</div>
+        <div>{{ t('dashboard.dateRange') }}</div>
         <div v-if="props.isContainer && !props.isETDToETA" class="dash_tips">
-          The time range is fixed at 12 months.
+          {{ t('dashboard.fixed12Months') }}
         </div>
       </div>
       <div class="dash_flex">
@@ -255,7 +258,7 @@ const guideStore = useGuideStore()
               type="month"
               @change="StartChange"
               :clearable="false"
-              placeholder="Start month"
+              :placeholder="t('dateRange.startMonth')"
               placement="bottom"
             />
             <el-date-picker
@@ -269,7 +272,7 @@ const guideStore = useGuideStore()
               type="month"
               @change="EndChange"
               :clearable="false"
-              placeholder="End month"
+              :placeholder="t('dateRange.endMonth')"
               placement="bottom"
             />
           </div>
@@ -282,9 +285,9 @@ const guideStore = useGuideStore()
         </div>
       </div>
       <div class="daterange_bottom">
-        <div><el-button type="default" @click="clearrest" class="Clear">Reset</el-button></div>
+        <div><el-button type="default" @click="clearrest" class="Clear">{{ t('common.reset') }}</el-button></div>
         <div>
-          <el-button class="search el-button--dark" @click="DateRangeSearch">Search</el-button>
+          <el-button class="search el-button--dark" @click="DateRangeSearch">{{ t('common.search') }}</el-button>
         </div>
       </div>
     </el-popover>

+ 9 - 6
src/views/Dashboard/src/components/DashboardGuide.vue

@@ -1,6 +1,9 @@
 <script setup lang="ts">
 import { useDriver } from '@/utils/driverGuide'
 import { useGuideStore } from '@/stores/modules/guide'
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
 
 const guideStore = useGuideStore()
 
@@ -11,7 +14,7 @@ const steps: any = [
     popover: {
       title: '',
       description:
-        'The Dashboard integrates different types of reports. You can freely select the reports you need. ',
+        t('dashboard.guideDashboardIntro'),
       side: 'left'
     }
   },
@@ -20,7 +23,7 @@ const steps: any = [
     popover: {
       title: '',
       description:
-        'Each report comes with its own filter options. Simply make your selection and click "Search" to refresh the data. ',
+        t('dashboard.guideDashboardFilter'),
       side: 'bottom'
     }
   },
@@ -29,7 +32,7 @@ const steps: any = [
     popover: {
       title: '',
       description:
-        'Hover the icon next to the report name to view the data explanation for each report.',
+        t('dashboard.guideDashboardTooltip'),
       side: 'bottom'
     }
   },
@@ -38,7 +41,7 @@ const steps: any = [
     popover: {
       title: '',
       description:
-        'Drag and drop reports using the drag handle (eight-dot icon) to reposition them in any direction. ',
+        t('dashboard.guideDashboardDrag'),
       side: 'bottom'
     }
   },
@@ -46,7 +49,7 @@ const steps: any = [
     element: '#save-config-guide',
     popover: {
       title: '',
-      description: 'Click "Save Layout" to preserve your customized report arrangement.',
+      description: t('dashboard.guideDashboardSaveLayout'),
       side: 'bottom'
     }
   },
@@ -55,7 +58,7 @@ const steps: any = [
     popover: {
       title: '',
       description:
-        'After closing, you can still click the "Page Guide" button to view the page guide of the current page.',
+        t('dashboard.guidePageGuide'),
       side: 'bottom'
     }
   }

+ 9 - 6
src/views/Dashboard/src/components/RecentStatus.vue

@@ -1,7 +1,10 @@
 <script lang="ts" setup>
 import { useRouter } from 'vue-router'
+import { useI18n } from 'vue-i18n'
 import { formatTimezoneByUTCorGMT, formatTimezone } from '@/utils/tools'
 
+const { t } = useI18n()
+
 const router = useRouter()
 interface RecentItem {
   Title: string
@@ -78,7 +81,7 @@ const SubscribeShipments = (val: any) => {
         <div class="recent-title" @click="RouteToDetail(item)">{{ item.Title }}</div>
         <div class="recent-name">{{ item.name }}</div>
         <div class="recent-booking">
-          Booking No.:
+          {{ t('dashboard.bookingNo') }}
           <span>{{ item.bookingNumber }}</span>
         </div>
       </div>
@@ -98,7 +101,7 @@ const SubscribeShipments = (val: any) => {
               <use xlink:href="#icon-icon_unmark_b"></use>
             </svg>
           </span>
-          <span class="Subscribe">Subscribe</span>
+          <span class="Subscribe">{{ t('tracking.subscribe') }}</span>
         </el-button>
       </div>
     </div>
@@ -106,10 +109,10 @@ const SubscribeShipments = (val: any) => {
       <!-- 左 -->
       <div class="recent-content-left">
         <div class="left_title">
-          Shipper: <span class="left_text">{{ item.shipperName }}</span>
+          {{ t('common.shipper') }}: <span class="left_text">{{ item.shipperName }}</span>
         </div>
         <div class="left_title">
-          Consignee: <span class="left_text">{{ item.consigneeName }}</span>
+          {{ t('common.consignee') }}: <span class="left_text">{{ item.consigneeName }}</span>
         </div>
       </div>
       <!-- 中 -->
@@ -123,7 +126,7 @@ const SubscribeShipments = (val: any) => {
             </span>
             {{ item.startStation }}
           </div>
-          <div class="startStation_time">ETD: {{ formatTimezone(item.ETD) }}</div>
+          <div class="startStation_time">{{ t('tracking.etd') }}: {{ formatTimezone(item.ETD) }}</div>
         </div>
         <div class="StationIcon">
           <div>
@@ -144,7 +147,7 @@ const SubscribeShipments = (val: any) => {
             </span>
             {{ item.endStation }}
           </div>
-          <div class="startStation_time">ETA: {{ formatTimezone(item.ETA) }}</div>
+          <div class="startStation_time">{{ t('tracking.eta') }}: {{ formatTimezone(item.ETA) }}</div>
         </div>
       </div>
       <!-- 右 -->

+ 3 - 1
src/views/Dashboard/src/components/RevenueChart.vue

@@ -5,7 +5,9 @@ import { useThemeStore } from '@/stores/modules/theme'
 import { onMounted, ref, reactive, watch, computed } from 'vue'
 import * as XLSX from 'xlsx'
 import { formatNumber } from '@/utils/tools'
-import { on } from 'events'
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
 
 const themeStore = useThemeStore()
 const props = defineProps({

+ 49 - 41
src/views/Dashboard/src/components/ScoringSystem.vue

@@ -1,5 +1,6 @@
 <script setup lang="ts">
 import { ref } from 'vue'
+import { useI18n } from 'vue-i18n'
 import angryPng from '../image/score_angry.png'
 import angryPng2 from '../image/angry_2.png'
 import sadPng from '../image/score_sad.png'
@@ -14,6 +15,7 @@ import submitsucessful from '../image/submit_successful.png'
 import { useUserStore } from '@/stores/modules/user'
 
 const userStore = useUserStore()
+const { t } = useI18n()
 
 const dialogVisible = ref(false)
 const innerVisible = ref(false)
@@ -43,75 +45,75 @@ avater.value = [
     src: angryPng,
     src1: angryPng,
     itemsrc: angryPng2,
-    itemtext: 'We are so sorry for the inconveniences. We value your experience immensely. ',
+    itemtext: t('dashboard.scoringSorryText'),
     expression: 'angry',
-    proposal: 'Could you please tell us which aspects of the system you are dissatisfied with?',
-    itemtitle: 'Highly dissatisfied',
+    proposal: t('dashboard.scoringDissatisfiedTip'),
+    itemtitle: t('dashboard.highlyDissatisfied'),
     changecolortext: false
   },
   {
     src: sadPng,
     src1: sadPng,
     itemsrc: sadPng2,
-    itemtext: 'We are so sorry for the inconveniences. We value your experience immensely. ',
+    itemtext: t('dashboard.scoringSorryText'),
     expression: 'sad',
-    proposal: 'Could you please tell us which aspects of the system you are dissatisfied with?',
-    itemtitle: 'Dissatisfied',
+    proposal: t('dashboard.scoringDissatisfiedTip'),
+    itemtitle: t('dashboard.dissatisfied'),
     changecolortext: false
   },
   {
     src: smilePng,
     src1: smilePng,
     itemsrc: smilePng2,
-    itemtext: 'Thank you for sharing your thoughts with us. We value your experience greatly.',
+    itemtext: t('dashboard.scoringNeutralText'),
     expression: 'smile',
-    proposal: 'Could you share what aspects you liked and what could be improved ?',
-    itemtitle: 'Neutral',
+    proposal: t('dashboard.scoringNeutralProposal'),
+    itemtitle: t('dashboard.neutral'),
     changecolortext: false
   },
   {
     src: hhhPng,
     src1: hhhPng,
     itemsrc: hhhPng2,
-    itemtext: 'Thank you very much for giving us a satisfied rating on our service or system.',
+    itemtext: t('dashboard.scoringSatisfiedText'),
     expression: 'hhh',
-    proposal: 'We are curious to learn more about what specifically made you feel satisfied. ',
-    itemtitle: 'Satisfied',
+    proposal: t('dashboard.scoringSatisfiedProposal'),
+    itemtitle: t('dashboard.satisfied'),
     changecolortext: false
   },
   {
     src: happyPng,
     src1: happyPng,
     itemsrc: happyPng2,
-    itemtext: 'Thank you very much for giving us a satisfied rating on our service or system.',
+    itemtext: t('dashboard.scoringSatisfiedText'),
     expression: 'happy',
-    proposal: 'We are curious to learn more about what specifically made you feel satisfied. ',
-    itemtitle: 'Highly satisfied',
+    proposal: t('dashboard.scoringSatisfiedProposal'),
+    itemtitle: t('dashboard.highlySatisfied'),
     changecolortext: false
   }
 ]
 const checkboxGroup1 = ref([])
 const checkboxGroup2 = ref([])
 const evaluate = [
-  'Functionality',
-  'Data accuracy',
-  'Look and feel',
-  'Ease of use',
-  'Website performance'
+  t('dashboard.functionality'),
+  t('dashboard.dataAccuracy'),
+  t('dashboard.lookAndFeel'),
+  t('dashboard.easeOfUse'),
+  t('dashboard.websitePerformance')
 ]
 const happyevaluate = [
-  'Functionality',
-  'Data accuracy',
-  'Look and feel',
-  'Ease of use',
-  'Website performance'
+  t('dashboard.functionality'),
+  t('dashboard.dataAccuracy'),
+  t('dashboard.lookAndFeel'),
+  t('dashboard.easeOfUse'),
+  t('dashboard.websitePerformance')
 ]
 const Aspects = ref([
-  'Highly Dissatisfied',
-  'Dissatisfied',
-  'Neutral',
-  'Satisfied',
-  'Highly Satisfied'
+  t('dashboard.highlyDissatisfied'),
+  t('dashboard.dissatisfied'),
+  t('dashboard.neutral'),
+  t('dashboard.satisfied'),
+  t('dashboard.highlySatisfied')
 ])
 interface smileAspectsItem {
   title: string
@@ -121,8 +123,14 @@ interface smileAspectsItem {
 const smileAspects = ref<smileAspectsItem[]>([])
 smileAspects.value = [
   {
-    title: 'Functionality',
-    proposal: ['Highly Dissatisfied', 'Dissatisfied', 'Neutral', 'Satisfied', 'Highly Satisfied'],
+    title: t('dashboard.functionality'),
+    proposal: [
+      t('dashboard.highlyDissatisfied'),
+      t('dashboard.dissatisfied'),
+      t('dashboard.neutral'),
+      t('dashboard.satisfied'),
+      t('dashboard.highlySatisfied')
+    ],
     radio: ''
   },
   {
@@ -297,8 +305,8 @@ const submitDetails = (val: any) => {
   <div class="scoring_dashboard" v-if="isShowScoring">
     <div class="scoring_left">
       <img class="scoring_img" src="../image/dashboard_scoring.png" />
-      <div>How satisfied are you with this system ?</div>
-      <div class="das_share" @click="dialogVisible = true">Share Your Feedback</div>
+      <div>{{ t('scoringGrade.satisfactionQuestionWithSpace') }}</div>
+      <div class="das_share" @click="dialogVisible = true">{{ t('scoringGrade.shareYourFeedback') }}</div>
       <el-dialog
         v-model="dialogVisible"
         :destroy-on-close="true"
@@ -308,7 +316,7 @@ const submitDetails = (val: any) => {
         custom-class="my-dialog"
         :close-on-click-modal="false"
       >
-        <div class="das_title">How satisfied are you with this system ?</div>
+        <div class="das_title">{{ t('scoringGrade.satisfactionQuestionWithSpace') }}</div>
         <div class="das_flex">
           <div class="das_score" v-for="(item, index) in avater" :key="index">
             <el-avatar
@@ -393,7 +401,7 @@ const submitDetails = (val: any) => {
             </el-checkbox-group>
           </div>
           <div class="das_in_ques" style="margin: 0">
-            Would you like to share more details with us?
+            {{ t('dashboard.satisfactionDetailsQuestion') }}
           </div>
           <div>
             <el-input
@@ -402,7 +410,7 @@ const submitDetails = (val: any) => {
               style="width: 592px"
               :rows="4"
               type="textarea"
-              placeholder="We look forward to hearing from you. Thank you for helping to build a better customer system."
+              :placeholder="t('scoringGrade.feedbackPlaceholder')"
             />
           </div>
           <div class="buttom">
@@ -412,14 +420,14 @@ const submitDetails = (val: any) => {
                   <use xlink:href="#icon-icon_back_b"></use>
                 </svg>
               </span>
-              Previous
+              {{ t('dashboard.previous') }}
             </div>
             <div class="button_submit">
               <el-button
                 :disabled="buttonDisabled"
                 class="submit_button el-button--dark"
                 @click="submitDetails(inputdetails)"
-                >Submit</el-button
+                >{{ t('dashboard.submit') }}</el-button
               >
             </div>
           </div>
@@ -428,7 +436,7 @@ const submitDetails = (val: any) => {
           class="result"
           v-if="isshowDetails_submit"
           icon="success"
-          title="Submit Successful"
+          :title="t('scoringGrade.submitSuccessful')"
         >
           <template #icon>
             <el-image :src="submitsucessful" />
@@ -436,7 +444,7 @@ const submitDetails = (val: any) => {
           <template #sub-title>
             <div class="sub_title_text">{{ SubmitText }}</div>
             <div class="sub_title_text">
-              We are committed to working hard to provide better services.
+              {{ t('dashboard.submitAndCommitment') }}
             </div>
           </template>
         </el-result>

+ 7 - 5
src/views/DestinationDelivery/src/DestinationDelivery.vue

@@ -1,9 +1,11 @@
 <script lang="ts" setup>
+import { useI18n } from 'vue-i18n'
 import ListView from './components/ListView.vue'
 import CalendarView from './components/CalendarView.vue'
 import { useRouter } from 'vue-router'
 import { useUserStore } from '@/stores/modules/user'
 
+const { t } = useI18n()
 const router = useRouter()
 const userStore = useUserStore()
 
@@ -34,14 +36,14 @@ const changePageType = () => {
 }
 const pageType = ref('Calendar View')
 const directionOptions = [
-  { label: 'Calendar View', value: 'Calendar View', icon: 'icon_ratesheet_b' },
-  { label: 'List View', value: 'List View', icon: 'icon_date_b' }
+  { label: t('destinationDelivery.calendarView'), value: 'Calendar View', icon: 'icon_ratesheet_b' },
+  { label: t('destinationDelivery.listView'), value: 'List View', icon: 'icon_date_b' }
 ]
 </script>
 <template>
   <div class="destination-delivery">
     <div class="header">
-      <span>Destination Delivery</span>
+      <span>{{ t('destinationDelivery.title') }}</span>
       <div class="operator">
         <el-button
           style="height: 40px"
@@ -50,7 +52,7 @@ const directionOptions = [
           v-if="userStore.userInfo.user_type === 'employee'"
         >
           <span style="margin-right: 4px" class="font_family icon-icon_configurations_b"></span>
-          <span style="font-weight: 400">Configurations</span></el-button
+          <span style="font-weight: 400">{{ t('destinationDelivery.configurations') }}</span></el-button
         >
         <el-button
           style="height: 38px"
@@ -59,7 +61,7 @@ const directionOptions = [
           v-if="userStore.userInfo.user_type !== 'employee'"
         >
           <span style="margin-right: 4px" class="font_family icon-icon_add_b"></span>
-          <span style="font-weight: 400">Create New Booking</span>
+          <span style="font-weight: 400">{{ t('destinationDelivery.createNewBooking') }}</span>
         </el-button>
       </div>
     </div>

+ 11 - 6
src/views/DestinationDelivery/src/components/CalendarTagDetailDialog.vue

@@ -1,4 +1,7 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
 import { autoWidth } from '@/utils/table'
 import { type VxeGridInstance, type VxeGridProps } from 'vxe-table'
 import { useRowClickStyle } from '@/hooks/rowClickStyle'
@@ -98,7 +101,9 @@ const openDialog = (type, date, data) => {
   pageData.value = data
   tagType.value = type
 
-  title.value = `Recommended Delivery Shipments for ${dayjs(date).format('YYYY-MM-DD')}`
+  title.value = t('destinationDelivery.recommendedDeliveryShipmentsFor', {
+    date: dayjs(date).format('YYYY-MM-DD')
+  })
   tableData.value.data = data['shipmentDetail']
   nextTick(() => {
     tableRef.value &&
@@ -179,18 +184,18 @@ defineExpose({
               class="font_family icon-icon_delay_b1"
               style="margin-right: 3px; font-size: 13px"
             ></span>
-            <span class="type">{{ pageData?.endingNumber }} Free Storage Period Ends</span>
+            <span class="type">{{ pageData?.endingNumber }} {{ t('destinationDelivery.freeStoragePeriodEnds') }}</span>
           </div>
         </div>
       </template>
       <span style="display: inline-block; color: var(--color-neutral-2)"
-        >Total:
+        >{{ t('destinationDelivery.total') }}:
         {{ pageData?.shipmentNumber || 0 }}
-        shipments</span
+        {{ t('destinationDelivery.shipments') }}</span
       >
       <span style="color: var(--color-neutral-2)"> | </span>
       <span style="color: var(--color-neutral-2)"
-        >Total Cartons: {{ pageData?.shipmentCtns || 0 }} ctns</span
+        >{{ t('destinationDelivery.totalCartons') }}: {{ pageData?.shipmentCtns || 0 }} ctns</span
       >
       <vxe-grid style="margin-top: 8px" ref="tableRef" v-bind="tableData" :row-style="rowStyle">
         <template #download="{ row, column }">
@@ -203,7 +208,7 @@ defineExpose({
           </div>
         </template>
         <template #empty>
-          <div class="empty">No data</div>
+          <div class="empty">{{ t('destinationDelivery.noData') }}</div>
         </template>
       </vxe-grid>
     </el-dialog>

+ 22 - 22
src/views/DestinationDelivery/src/components/CalendarView.vue

@@ -1,4 +1,7 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
 import { ref, computed, onMounted } from 'vue'
 import type { Dayjs } from 'dayjs'
 import dayjs from 'dayjs'
@@ -57,7 +60,7 @@ const remoteMethod = (query: string) => {
     .catch((err) => {
       consignessList.value = []
       if (!axios.isCancel(err) && !newController.signal.aborted) {
-        ElMessage.error('Failed to load options')
+        ElMessage.error(t('destinationDelivery.failedToLoadOptions'))
       }
     })
     .finally(() => {
@@ -134,13 +137,10 @@ const jumpListPage = (date) => {
 }
 
 const test = () => {
-  ElMessageBox.alert(
-    "This account's password has expired and is currently unavailable. Please select a different customer account to continue.",
-    {
-      confirmButtonText: 'OK',
-      confirmButtonClass: 'el-button--dark'
-    }
-  )
+  ElMessageBox.alert(t('destinationDelivery.accountPasswordExpiredUnavailable'), {
+    confirmButtonText: t('common.ok'),
+    confirmButtonClass: 'el-button--dark'
+  })
 }
 </script>
 
@@ -193,18 +193,18 @@ const test = () => {
             <a-select-option v-for="m in 12" :key="m" :value="m">
               {{
                 [
-                  'Jan',
-                  'Feb',
-                  'Mar',
-                  'Apr',
-                  'May',
-                  'Jun',
-                  'Jul',
-                  'Aug',
-                  'Sep',
-                  'Oct',
-                  'Nov',
-                  'Dec'
+                  t('common.jan'),
+                  t('common.feb'),
+                  t('common.mar'),
+                  t('common.apr'),
+                  t('common.may'),
+                  t('common.jun'),
+                  t('common.jul'),
+                  t('common.aug'),
+                  t('common.sep'),
+                  t('common.oct'),
+                  t('common.nov'),
+                  t('common.dec')
                 ][m - 1]
               }}
             </a-select-option>
@@ -220,7 +220,7 @@ const test = () => {
             filterable
             reserve-keyword
             clearable
-            placeholder="Consignee"
+            :placeholder="t('destinationDelivery.consignee')"
             :loading="consigneeLoading"
             style="width: 240px"
             popper-class="part-id-select-popper"
@@ -284,7 +284,7 @@ const test = () => {
               <!-- <div class="label">Destination Booking</div> -->
               <div class="tag-style">
                 <span class="font_family icon-icon_booking_order_b" style="font-size: 12px"></span>
-                <span class="type">{{ getDataByDate(current, 'bookingNumber') }} Bookings</span>
+                <span class="type">{{ getDataByDate(current, 'bookingNumber') }} {{ t('destinationDelivery.bookings') }}</span>
                 <div class="grid-lines"></div>
                 <span class="ctns-tag">{{ getDataByDate(current, 'bookingCtns') }} ctns</span>
               </div>

+ 8 - 6
src/views/DestinationDelivery/src/components/ConfiguRations/src/ConfiguRations.vue

@@ -1,29 +1,31 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
 import ConfigurationsTable from './components/ConfigurationsTable.vue'
 import { useCalculatingHeight } from '@/hooks/calculatingHeight'
 import { useRouter } from 'vue-router'
 
 const filterRef: Ref<HTMLElement | null> = ref(null)
 const router = useRouter()
+const { t } = useI18n()
 
 const AddRulesTableColumns = ref([
   {
     field: 'country',
-    title: 'Country',
+    title: t('common.country'),
     type: 'link',
     width: '10%',
     formatter: ''
   },
   {
     field: 'station',
-    title: 'Stations',
+    title: t('destinationDelivery.stations'),
     type: 'normal',
     width: '20%',
     formatter: ''
   },
   {
     field: 'booking_window_desc',
-    title: 'Booking Window',
+    title: t('destinationDelivery.bookingWindow'),
     type: 'normal',
     formatter: ''
   }
@@ -46,16 +48,16 @@ const ToCreateRule = () => {
 
 <template>
   <div>
-    <div class="Title">Configuration</div>
+    <div class="Title">{{ t('destinationDelivery.configuration') }}</div>
     <div class="AddRules">
       <div class="monitoring_flex">
-        <div class="subscribedTitle">Added Rules</div>
+        <div class="subscribedTitle">{{ t('destinationDelivery.addedRules') }}</div>
         <el-button
           class="el-button--main"
           style="height: 40px"
           v-if="tabledatalength != 0 && tabledatalength != null"
           @click="ToCreateRule"
-          >+ Add Rule</el-button
+          >+ {{ t('destinationDelivery.addRule') }}</el-button
         >
       </div>
       <ConfigurationsTable

+ 9 - 9
src/views/DestinationDelivery/src/components/ConfiguRations/src/components/ConfigurationsTable.vue

@@ -1,4 +1,5 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
 import { type VxeGridInstance, type VxeGridProps } from 'vxe-table'
 import { useRowClickStyle } from '@/hooks/rowClickStyle'
 import { formatNumber } from '@/utils/tools'
@@ -7,6 +8,7 @@ import { useRouter } from 'vue-router'
 import DefaultConfiguration from '../images/default_configuration@2x.png'
 
 const router = useRouter()
+const { t } = useI18n()
 
 interface ColumnConfig {
   field: string
@@ -64,7 +66,7 @@ const handleColumns = (columns: any) => {
 const getTableColumns = async () => {
   tableData.value.columns = handleColumns(columnstest.value)
   tableData.value.columns?.push({
-    title: 'Operation',
+    title: t('common.operation'),
     fixed: 'left',
     width: 100,
     slots: { default: 'action' }
@@ -145,14 +147,12 @@ onMounted(() => {
       <template #empty>
         <div>
           <img :src="DefaultConfiguration" width="100" />
-          <div class="empty-text">
-            Configure available destination delivery regions and time slots.
-          </div>
+          <div class="empty-text">{{ t('destinationDelivery.configureRegionsAndTimeSlots') }}</div>
           <el-button
             class="el-button--main"
             style="width: 117px; height: 40px"
             @click="clickAddNewRule"
-            >+ Add Rule</el-button
+            >+ {{ t('destinationDelivery.addRule') }}</el-button
           >
         </div>
       </template>
@@ -169,12 +169,12 @@ onMounted(() => {
         <el-popover trigger="click" :visible="row.visible" placement="left" :width="480">
           <div class="delete_title">
             <span class="font_family icon_alert icon-icon_tipsfilled_b"></span>
-            Delete Rule
+            {{ t('destinationDelivery.deleteRule') }}
           </div>
-          <p class="delete_content">Are you sure to delete this notification event?</p>
+          <p class="delete_content">{{ t('destinationDelivery.confirmDeleteRule') }}</p>
           <div style="text-align: right; margin: 0; padding: 8px">
             <el-button style="width: 100px" class="el-button--default" @click="row.visible = false"
-              >cancel</el-button
+              >{{ t('common.cancel') }}</el-button
             >
             <el-button style="width: 100px" type="warning" @click="deleteMoniTable(row)">
               OK
@@ -190,7 +190,7 @@ onMounted(() => {
     </vxe-grid>
   </div>
   <div class="pagination">
-    <span>Total {{ formatNumber(pageInfo.total) }}</span>
+    <span>{{ t('destinationDelivery.total') }} {{ formatNumber(pageInfo.total) }}</span>
     <el-pagination
       v-model:current-page="pageInfo.pageNo"
       v-model:page-size="pageInfo.pageSize"

+ 27 - 29
src/views/DestinationDelivery/src/components/ConfiguRations/src/components/CreateNewRule.vue

@@ -1,4 +1,5 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
 import SelectStation from './SelectStation.vue'
 import SetBookingWindow from './SetBookingWindow.vue'
 import RecommendDate from './RecommendDate.vue'
@@ -6,6 +7,7 @@ import { useRouter } from 'vue-router'
 import submitsucessful from '../images/icon_success_big@2x.png'
 
 const router = useRouter()
+const { t } = useI18n()
 const { currentRoute } = router
 const { value } = currentRoute
 const { query } = value
@@ -208,23 +210,19 @@ const handleSubmitRule = () => {
   let mergeData = []
   if (windowRadio.value == 1) {
     bookingWindow.value = 'No_Restrictions'
-    bookingdetail.value = 'No Specific time restrictions for creating booking'
+    bookingdetail.value = t('destinationDelivery.noTimeRestrictionsForBooking')
   } else if (windowRadio.value == 2) {
     bookingWindow.value = 'Restrictions_ETD_ATD'
-    bookingdetail.value =
-      'ETD/ATD: ' +
-      windowBeforeDays.value +
-      ' days before to ' +
-      windowAfterDays.value +
-      ' days after'
+    bookingdetail.value = t('destinationDelivery.etdAtdRuleWindow', {
+      before: windowBeforeDays.value,
+      after: windowAfterDays.value
+    })
   } else if (windowRadio.value == 3) {
     bookingWindow.value = 'Restrictions_ETA_ATA'
-    bookingdetail.value =
-      'ETA/ATA: ' +
-      windowBeforeDays.value +
-      ' days before to ' +
-      windowAfterDays.value +
-      ' days after'
+    bookingdetail.value = t('destinationDelivery.etaAtaRuleWindow', {
+      before: windowBeforeDays.value,
+      after: windowAfterDays.value
+    })
   }
   // if (recommendRadio.value == 1) {
   //   recommendDelivery.value = 'No_Recommended'
@@ -302,8 +300,8 @@ onMounted(() => {
 <template>
   <div>
     <div class="Title">
-      <div v-if="a != undefined">Modify Rule</div>
-      <div v-else>Create New Rule</div>
+      <div v-if="a != undefined">{{ t('destinationDelivery.modifyRule') }}</div>
+      <div v-else>{{ t('destinationDelivery.createNewRule') }}</div>
       <div class="operator">
         <el-button
           @click="CancelRulesVisible = true"
@@ -311,7 +309,7 @@ onMounted(() => {
           type="default"
         >
           <span style="margin-right: 4px" class="font_family icon-icon_return_b"></span>
-          <span style="font-weight: 400">Cancel</span></el-button
+          <span style="font-weight: 400">{{ t('common.cancel') }}</span></el-button
         >
         <el-button
           style="height: 40px; width: 120px"
@@ -328,16 +326,16 @@ onMounted(() => {
             "
             class="font_family icon-icon_submit_b"
           ></span>
-          <span style="font-weight: 400">Save</span>
+          <span style="font-weight: 400">{{ t('common.save') }}</span>
         </el-button>
         <!-- 取消保存 -->
         <el-dialog v-model="CancelRulesVisible" width="480">
-          <div style="font-weight: 400">You have unsaved changes.</div>
-          <div style="font-weight: 400">Are you sure you want to leave this page?</div>
+          <div style="font-weight: 400">{{ t('destinationDelivery.unsavedChanges') }}</div>
+          <div style="font-weight: 400">{{ t('destinationDelivery.confirmLeavePage') }}</div>
           <template #footer>
             <div class="dialog-footer">
               <el-button type="default" @click="CancelRulesVisible = false" style="width: 100px"
-                >Cancel</el-button
+                >{{ t('common.cancel') }}</el-button
               >
               <el-button class="el-button--warning" @click="router.back()" style="width: 100px">
                 OK
@@ -347,14 +345,14 @@ onMounted(() => {
           <template #header>
             <div class="dialog-header warning-header">
               <span class="font_family icon-icon_tipsfilled_b"></span>
-              Unsaved Changes
+              {{ t('destinationDelivery.unsavedChangesTitle') }}
             </div>
           </template>
         </el-dialog>
         <!-- 保存失败 -->
         <el-dialog v-model="UnableSaveVisible" width="480">
           <div>{{ missingmessage }}</div>
-          <div>Please complete all required fields.</div>
+          <div>{{ t('destinationDelivery.completeRequiredFields') }}</div>
           <template #footer>
             <div class="dialog-footer">
               <el-button
@@ -369,7 +367,7 @@ onMounted(() => {
           <template #header>
             <div class="unable-save-header dialog-header">
               <span class="font_family icon-icon_fail_fill_b"></span>
-              Unable to Save
+              {{ t('destinationDelivery.unableToSave') }}
             </div>
           </template>
         </el-dialog>
@@ -378,12 +376,12 @@ onMounted(() => {
           <div style="text-align: center">
             <el-image :src="submitsucessful" style="width: 64px" />
           </div>
-          <div style="text-align: center; margin-top: 20px">Saved successfully</div>
+          <div style="text-align: center; margin-top: 20px">{{ t('common.saveSuccess') }}</div>
         </el-dialog>
       </div>
     </div>
     <div class="setting-content">
-      <div class="setting-top-title">Setting</div>
+      <div class="setting-top-title">{{ t('destinationDelivery.setting') }}</div>
       <el-collapse v-model="activeRules" @change="IsFirstActive = !IsFirstActive">
         <el-collapse-item name="SelectStation">
           <template #title>
@@ -395,7 +393,7 @@ onMounted(() => {
                   ></use>
                 </svg>
               </span>
-              <span class="stars_red">*</span>Select Station (Enable Booking)
+              <span class="stars_red">*</span>{{ t('destinationDelivery.selectStationEnableBooking') }}
             </div>
           </template>
           <div>
@@ -420,7 +418,7 @@ onMounted(() => {
                   ></use>
                 </svg>
               </span>
-              <span class="stars_red">*</span>Set Booking Window
+              <span class="stars_red">*</span>{{ t('destinationDelivery.setBookingWindow') }}
             </div>
           </template>
           <div>
@@ -464,7 +462,7 @@ onMounted(() => {
                   ></use>
                 </svg>
               </span>
-              <span class="stars_red">*</span>KLN PIC
+              <span class="stars_red">*</span>{{ t('destinationDelivery.klnPic') }}
             </div>
           </template>
           <div>
@@ -473,7 +471,7 @@ onMounted(() => {
               filterable
               remote
               multiple
-              placeholder="Select Employee Account"
+              :placeholder="t('destinationDelivery.selectEmployeeAccount')"
               :remote-method="querySearchAsync"
               :loading="loading"
               style="width: 400px; margin-bottom: 5px"

+ 29 - 26
src/views/DestinationDelivery/src/components/ConfiguRations/src/components/RecommendDate.vue

@@ -1,4 +1,7 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
 import SelectValue from './SelectValue.vue'
 import { ref } from 'vue'
 import { cloneDeep } from 'lodash'
@@ -45,11 +48,11 @@ const isAir = ref(false)
 const isSea = ref(false)
 const RecommendCheckedList = ref<string[]>([])
 // 选项配置
-const AirRuleTypeoptions = ref<RuleOption[]>([{ label: 'Specific Rule', value: 'Specific Rule' }])
+const AirRuleTypeoptions = ref<RuleOption[]>([{ label: t('destinationDelivery.specificRule'), value: 'Specific Rule' }])
 
 const RuleTypeoptions = ref<RuleOption[]>([
-  { label: 'Specific Rule', value: 'Specific Rule' },
-  { label: 'Single Dimension', value: 'Single Dimension' }
+  { label: t('destinationDelivery.specificRule'), value: 'Specific Rule' },
+  { label: t('destinationDelivery.singleDimension'), value: 'Single Dimension' }
 ])
 
 // 规则数据
@@ -410,15 +413,15 @@ const changeRuleType = (val: string, index: number, list: RuleItem[]) => {
 <template>
   <div>
     <el-radio-group v-model="Recommendradio" @change="ChangeFrequency">
-      <el-radio :value="1">No Specific recommended time for choosing delivery date</el-radio>
+      <el-radio :value="1">{{ t('destinationDelivery.noSpecificRecommendedTimeForChoosingDeliveryDate') }}</el-radio>
       <el-radio :value="2">
-        <div>Recommend Delivery Date (ETA/ATA)</div>
+        <div>{{ t('destinationDelivery.recommendDeliveryDate') }}</div>
         <div v-if="isRecommendETA" class="oceanCheckbox">
           <el-checkbox-group v-model="RecommendCheckedList" @change="CheckChange">
             <!-- Air 部分 -->
             <el-checkbox class="delayedType" value="Air" @click="handleCheckboxClick">
               <div class="titlecheckbox clickable-area">
-                <div>Air</div>
+                <div>{{ t('destinationDelivery.air') }}</div>
                 <span
                   class="icon_grey font_family"
                   :class="isAir ? 'icon-icon_dropdown_b' : 'icon-icon_up_b'"
@@ -426,9 +429,9 @@ const changeRuleType = (val: string, index: number, list: RuleItem[]) => {
               </div>
               <div v-if="isAir" class="radiocheckbox" style="margin-top: 16px; padding-left: 8px">
                 <div class="AirCoulumn">
-                  <div class="AicoulumnTitile" style="width: 10%">priority</div>
-                  <div class="AicoulumnTitile" style="width: 20%">Rule Type</div>
-                  <div class="AicoulumnTitile" style="width: 40%">Air Port</div>
+                  <div class="AicoulumnTitile" style="width: 10%">{{ t('destinationDelivery.priority') }}</div>
+                  <div class="AicoulumnTitile" style="width: 20%">{{ t('destinationDelivery.ruleType') }}</div>
+                  <div class="AicoulumnTitile" style="width: 40%">{{ t('destinationDelivery.port') }}</div>
                   <div
                     style="
                       display: flex;
@@ -437,15 +440,15 @@ const changeRuleType = (val: string, index: number, list: RuleItem[]) => {
                       width: 20%;
                     "
                   >
-                    <div class="AicoulumnTitile2">Recommended Delivery Date</div>
+                    <div class="AicoulumnTitile2">{{ t('destinationDelivery.recommendedDeliveryDate') }}</div>
                     <div style="display: flex; height: 24px; align-items: center">
                       <div
                         class="datetitle"
                         style="border-right: 1px solid var(--color-system-border)"
                       >
-                        From (ETA/ATA + Days)
+                        {{ t('destinationDelivery.fromEtaAtaDays') }}
                       </div>
-                      <div class="datetitle">To (ETA/ATA + Days)</div>
+                      <div class="datetitle">{{ t('destinationDelivery.toEtaAtaDays') }}</div>
                     </div>
                   </div>
                   <div
@@ -453,7 +456,7 @@ const changeRuleType = (val: string, index: number, list: RuleItem[]) => {
                     style="width: 10%"
                     @click="AddRuleItem(AirContentList, 'Air')"
                   >
-                    + Add
+                    + {{ t('destinationDelivery.add') }}
                   </div>
                 </div>
                 <div class="AirContent" v-for="(item, index) in AirContentList" :key="index">
@@ -491,7 +494,7 @@ const changeRuleType = (val: string, index: number, list: RuleItem[]) => {
                         (val) =>
                           handleInput(val, index, 'recommended_delivery_from', AirContentList)
                       "
-                      placeholder="Input"
+                      :placeholder="t('common.input')"
                       v-model="item.recommended_delivery_from"
                     />
                   </div>
@@ -500,7 +503,7 @@ const changeRuleType = (val: string, index: number, list: RuleItem[]) => {
                       @input="
                         (val) => handleInput(val, index, 'recommended_delivery_to', AirContentList)
                       "
-                      placeholder="Input"
+                      :placeholder="t('common.input')"
                       v-model="item.recommended_delivery_to"
                     />
                   </div>
@@ -520,7 +523,7 @@ const changeRuleType = (val: string, index: number, list: RuleItem[]) => {
             <!-- Sea 部分 -->
             <el-checkbox class="delayedType" value="Sea" @click="handleCheckboxClick">
               <div class="titlecheckbox clickable-area">
-                <div>Sea</div>
+                <div>{{ t('destinationDelivery.sea') }}</div>
                 <span
                   class="icon_grey font_family"
                   :class="isSea ? 'icon-icon_dropdown_b' : 'icon-icon_up_b'"
@@ -528,10 +531,10 @@ const changeRuleType = (val: string, index: number, list: RuleItem[]) => {
               </div>
               <div v-if="isSea" style="margin-top: 16px; padding-left: 8px">
                 <div class="AirCoulumn">
-                  <div class="AicoulumnTitile" style="width: 10%">priority</div>
-                  <div class="AicoulumnTitile" style="width: 14%">Rule Type</div>
-                  <div class="AicoulumnTitile" style="width: 23%">Port</div>
-                  <div class="AicoulumnTitile" style="width: 23%">Carrier</div>
+                  <div class="AicoulumnTitile" style="width: 10%">{{ t('destinationDelivery.priority') }}</div>
+                  <div class="AicoulumnTitile" style="width: 14%">{{ t('destinationDelivery.ruleType') }}</div>
+                  <div class="AicoulumnTitile" style="width: 23%">{{ t('destinationDelivery.port') }}</div>
+                  <div class="AicoulumnTitile" style="width: 23%">{{ t('destinationDelivery.carrier') }}</div>
                   <div
                     style="
                       display: flex;
@@ -540,15 +543,15 @@ const changeRuleType = (val: string, index: number, list: RuleItem[]) => {
                       width: 20%;
                     "
                   >
-                    <div class="AicoulumnTitile2">Recommended Delivery Date</div>
+                    <div class="AicoulumnTitile2">{{ t('destinationDelivery.recommendedDeliveryDate') }}</div>
                     <div style="display: flex; height: 24px; align-items: center">
                       <div
                         class="datetitle"
                         style="border-right: 1px solid var(--color-system-border)"
                       >
-                        From (ETA/ATA + Days)
+                        {{ t('destinationDelivery.fromEtaAtaDays') }}
                       </div>
-                      <div class="datetitle">To (ETA/ATA + Days)</div>
+                      <div class="datetitle">{{ t('destinationDelivery.toEtaAtaDays') }}</div>
                     </div>
                   </div>
                   <div
@@ -556,7 +559,7 @@ const changeRuleType = (val: string, index: number, list: RuleItem[]) => {
                     style="width: 10%"
                     @click="AddRuleItem(SeaContentList, 'Sea')"
                   >
-                    + Add
+                    + {{ t('destinationDelivery.add') }}
                   </div>
                 </div>
                 <div class="AirContent" v-for="(item, index) in SeaContentList" :key="index">
@@ -606,7 +609,7 @@ const changeRuleType = (val: string, index: number, list: RuleItem[]) => {
                         (val) =>
                           handleInput(val, index, 'recommended_delivery_from', SeaContentList)
                       "
-                      placeholder="Input"
+                      :placeholder="t('common.input')"
                       v-model="item.recommended_delivery_from"
                     />
                   </div>
@@ -615,7 +618,7 @@ const changeRuleType = (val: string, index: number, list: RuleItem[]) => {
                       @input="
                         (val) => handleInput(val, index, 'recommended_delivery_to', SeaContentList)
                       "
-                      placeholder="Input"
+                      :placeholder="t('common.input')"
                       v-model="item.recommended_delivery_to"
                     />
                   </div>

+ 6 - 3
src/views/DestinationDelivery/src/components/ConfiguRations/src/components/SelectStation.vue

@@ -1,4 +1,7 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
 
 interface CountryItem {
   value: string
@@ -71,7 +74,7 @@ const querySearchAsync = (queryString: string, cb: (arg: any) => void) => {
     if(results.length == 0) {
       cb([{ 
         isNoData: true, 
-        value: 'No Data' 
+        value: t('destinationDelivery.noData') 
       }]);
     } else {
       cb(results)
@@ -116,7 +119,7 @@ onMounted(() => {
     <el-autocomplete
       v-model="SelectCountry"
       style="width: 400px;margin-bottom: 5px;"
-      placeholder="Select Country"
+      :placeholder="t('destinationDelivery.selectCountry')"
       :popper-class="popperClass"
       :fetch-suggestions="querySearchAsync"
       @select="handleSelect"
@@ -132,7 +135,7 @@ onMounted(() => {
       </template>
     </el-autocomplete>
     <div class="station-list">
-      <div class="station-list-title">Station List</div>
+      <div class="station-list-title">{{ t('destinationDelivery.stationList') }}</div>
       <div class="oceanCheckbox">
         <el-checkbox-group v-model="CheckedList" :class="{isEmpty :  CheckboxList.length === 0}" @change="handlechangestation">
           <el-checkbox

+ 4 - 1
src/views/DestinationDelivery/src/components/ConfiguRations/src/components/SelectValue.vue

@@ -1,5 +1,8 @@
 <script setup lang="ts">
 import { ref, watch } from 'vue'
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
 
 interface PortOption {
   value: string
@@ -185,7 +188,7 @@ const hiddenTagsCount = computed(() => {
       filterable
       remote
       :disabled="typeisDisabled  == '*Default Rule'"
-      placeholder="Select"
+      :placeholder="t('destinationDelivery.select')"
       :remote-method="remoteMethod"
       popper-class="custom-sheader-select"
       @change="handelchangeSelect"

+ 39 - 37
src/views/DestinationDelivery/src/components/ConfiguRations/src/components/SetBookingWindow.vue

@@ -1,4 +1,6 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
+const { t } = useI18n()
 const isBookingETD = ref(false)
 const isBookingETA = ref(false)
 const beforeETDdays = ref('')
@@ -16,17 +18,17 @@ const props = defineProps({
 const windowradio = ref()
 const setbookingdata = ref(props.setbookingdata)
 const initBookingWindowData = () => {
-  if(setbookingdata.value) {
+  if (setbookingdata.value) {
     windowradio.value = setbookingdata.value.windowradio
-  if(windowradio.value == 2) {
-    isBookingETD.value = true
-    beforeETDdays.value = setbookingdata.value.windowBeforeDays
-    afterETDdays.value = setbookingdata.value.windowAfterDays
-  } else if (windowradio.value == 3) {
-    isBookingETA.value = true
-    beforeETAdays.value = setbookingdata.value.windowBeforeDays
-    afterETAdays.value = setbookingdata.value.windowAfterDays
-  }
+    if (windowradio.value == 2) {
+      isBookingETD.value = true
+      beforeETDdays.value = setbookingdata.value.windowBeforeDays
+      afterETDdays.value = setbookingdata.value.windowAfterDays
+    } else if (windowradio.value == 3) {
+      isBookingETA.value = true
+      beforeETAdays.value = setbookingdata.value.windowBeforeDays
+      afterETAdays.value = setbookingdata.value.windowAfterDays
+    }
   }
 }
 
@@ -71,63 +73,63 @@ const ChangeFrequency = (val: any) => {
 // 处理输入值,确保只能是正整数
 const validatePositiveInteger = (value: string) => {
   // 移除非数字字符
-  let numStr = value.replace(/[^\d]/g, '');
+  let numStr = value.replace(/[^\d]/g, '')
   // 如果为空字符串,直接返回
-  if (numStr === '') return '';
+  if (numStr === '') return ''
   // 转换为整数
-  let num = parseInt(numStr, 10);
+  let num = parseInt(numStr, 10)
   // 处理NaN情况,确保最小值为1
   if (isNaN(num) || num < 1) {
-    return '1';
+    return '1'
   }
   // 返回数字字符串(去掉前导零)
-  return num.toString();
-};
+  return num.toString()
+}
 
 // 处理输入变化
 const handleInput = (val: string, target: 'ETD_BEFORE' | 'ETD_AFTER' | 'ETA_BEFORE' | 'ETA_AFTER') => {
-  const validatedValue = validatePositiveInteger(val);
+  const validatedValue = validatePositiveInteger(val)
   switch (target) {
     case 'ETD_BEFORE':
-      beforeETDdays.value = validatedValue;
-      ChangeFrequency(2);
-      break;
+      beforeETDdays.value = validatedValue
+      ChangeFrequency(2)
+      break
     case 'ETD_AFTER':
-      afterETDdays.value = validatedValue;
-      ChangeFrequency(2);
-      break;
+      afterETDdays.value = validatedValue
+      ChangeFrequency(2)
+      break
     case 'ETA_BEFORE':
-      beforeETAdays.value = validatedValue;
-      ChangeFrequency(3);
-      break;
+      beforeETAdays.value = validatedValue
+      ChangeFrequency(3)
+      break
     case 'ETA_AFTER':
-      afterETAdays.value = validatedValue;
-      ChangeFrequency(3);
-      break;
+      afterETAdays.value = validatedValue
+      ChangeFrequency(3)
+      break
   }
-};
+}
 
 </script>
 <template>
   <div>
     <el-radio-group v-model="windowradio" @change="ChangeFrequency">
-      <el-radio :value="1">No Specific time restrictions for creating booking</el-radio>
+      <el-radio :value="1">{{ t('destinationDelivery.noTimeRestrictionsForBooking') }}</el-radio>
       <el-radio :value="2">
-        <div>Booking Window (ETD/ATD)</div>
+        <div>{{ t('destinationDelivery.bookingWindowEtdAtd') }}</div>
         <div class="ETDWindow" v-if="isBookingETD">
           <el-input style="width: 80px;height: 32px;" v-model="beforeETDdays" @input="val => handleInput(val, 'ETD_BEFORE')"></el-input>
-          days before to 
+          {{ t('destinationDelivery.daysBeforeTo') }}
           <el-input style="width: 80px;height: 32px;" v-model="afterETDdays" @input="val => handleInput(val, 'ETD_AFTER')"></el-input>
-          days after
+          {{ t('destinationDelivery.daysAfter') }}
         </div>
       </el-radio>
       <el-radio :value="3">
-        <div>Booking Window (ETA/ATA)</div>
+        <div>{{ t('destinationDelivery.bookingWindowEtaAta') }}</div>
         <div class="ETDWindow" v-if="isBookingETA">
           <el-input style="width: 80px;height: 32px;" v-model="beforeETAdays" @input="val => handleInput(val, 'ETA_BEFORE')"></el-input>
-          days before to 
+          {{ t('destinationDelivery.daysBeforeTo') }}
           <el-input style="width: 80px;height: 32px;" v-model="afterETAdays" @input="val => handleInput(val, 'ETA_AFTER')"></el-input>
-          days after
+          {{ t('destinationDelivery.daysAfter') }}
         </div>
       </el-radio>
     </el-radio-group>

+ 77 - 72
src/views/DestinationDelivery/src/components/CreateNewBooking/src/CreateNewbooking.vue

@@ -1,4 +1,7 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
 import CalendarDate from '@/components/DateRange/src/components/CalendarDate.vue'
 import NewbookingTable from './components/NewbookingTable.vue'
 import AddNewAddress from './images/default_add_address@2x.png'
@@ -361,7 +364,7 @@ const SaveNewAddress = () => {
     currentEditAddress.value = null
   } else {
     ElMessage({
-      message: 'Required fields not entered.',
+      message: t('destinationDelivery.requiredFieldsNotEntered'),
       type: 'warning'
     })
   }
@@ -581,18 +584,18 @@ const changeDatePicker = (val: any) => {
   if (specialDates.value.length != 0) {
     if (isConsistent.value) {
       isRecommendDate.value = true
-      recommendateWarning.value = 'This date for following shipments is outside recommended period.'
+      recommendateWarning.value = t('destinationDelivery.outsideRecommendedPeriodForFollowing')
       isConsistent.value = false
     } else {
       if (!specialDates.value.includes(val)) {
         isRecommendDate.value = true
-        recommendateWarning.value = 'This date is outside our recommended period.'
+        recommendateWarning.value = t('destinationDelivery.outsideRecommendedPeriod')
         isConsistent.value = false
       }
     }
   } else {
     isRecommendDate.value = true
-    recommendateWarning.value = 'This date for following shipments is outside recommended period.'
+    recommendateWarning.value = t('destinationDelivery.outsideRecommendedPeriodForFollowing')
     isConsistent.value = false
   }
 }
@@ -720,11 +723,11 @@ onMounted(() => {
 <template>
   <div>
     <div class="Title">
-      <div v-if="a == undefined">Create New Booking</div>
-      <div v-else>Modify Booking</div>
+      <div v-if="a == undefined">{{ t('destinationDelivery.createNewBooking') }}</div>
+      <div v-else>{{ t('destinationDelivery.modifyBooking') }}</div>
       <div class="flex">
         <div class="select-info">
-          <span style="color: var(--color-neutral-2)">Selected: </span>
+          <span style="color: var(--color-neutral-2)">{{ t('destinationDelivery.selected') }}: </span>
           <span
             >{{ bookingTableRef?.getTableCheckedRows().length }} Shipments|{{
               ctnsCount
@@ -732,14 +735,14 @@ onMounted(() => {
             ctns</span
           >
         </div>
-        <el-button @click="CancelRulesVisible = true" class="el-button--default create-button"
-          ><span class="font_family icon-icon_return_b"></span> Cancel</el-button
-        >
+        <el-button @click="CancelRulesVisible = true" class="el-button--default create-button">
+          <span class="font_family icon-icon_return_b"></span> {{ t('common.cancel') }}
+        </el-button>
         <el-button
           :disabled="isNotSubmit"
           @click="SubmitBooking"
           class="el-button--main create-button"
-          ><span class="font_family icon-icon_submit_b"></span> Submit</el-button
+          ><span class="font_family icon-icon_submit_b"></span> {{ t('destinationDelivery.submit') }}</el-button>
         >
       </div>
     </div>
@@ -753,19 +756,19 @@ onMounted(() => {
     <!-- Select Shipments -->
     <div class="Delivery" style="font-weight: bold">
       <span class="serial-number">1</span>
-      <span class="stars_red">*</span>Select Shipments<span class="title_warning"
-        >*Please select items with the same consignee.</span
+      <span class="stars_red">*</span>{{ t('destinationDelivery.selectShipments') }}<span class="title_warning"
+        >*{{ t('destinationDelivery.selectSameConsigneeTip') }}</span
       >
     </div>
     <div class="select_shipments">
       <div v-if="isNotSameConfiguration" class="alertIndormation">
-        Please select same consignee with same delivery information
+        {{ t('destinationDelivery.selectSameConsigneeWarning') }}
       </div>
 
       <div class="shipments_search" v-if="a == undefined">
         <div class="top-filter-search">
           <el-input
-            placeholder="Enter Booking/HBL/PO/Carrier Booking No. "
+            :placeholder="t('destinationDelivery.searchBookingPlaceholder')"
             v-model="CreateNewBOokingSearch"
             class="log_input"
           >
@@ -776,26 +779,26 @@ onMounted(() => {
           <div class="input-with-label">
             <!-- <AutoSelect ASPlaceholder="Input Vessel Name" :ASValue="VesselName" @changeFocus="changeFocus"></AutoSelect> -->
             <el-input
-              placeholder="Shipper"
+              :placeholder="t('destinationDelivery.shipper')"
               v-model="ShipperValue"
               @focus="changeFocustest('Shipper', true)"
               @blur="changeFocustest('Shipper', false)"
               class="log_input"
             >
             </el-input>
-            <span v-if="showLabelShipper" class="border-label">Shipper</span>
+            <span v-if="showLabelShipper" class="border-label">{{ t('destinationDelivery.shipper') }}</span>
           </div>
           <div class="input-with-label">
             <!-- <AutoSelect ASPlaceholder="Input Vessel Name" :ASValue="VesselName" @changeFocus="changeFocus"></AutoSelect> -->
             <el-input
-              placeholder="Consignee"
+              :placeholder="t('destinationDelivery.consignee')"
               v-model="ConsigneeValue"
               @focus="changeFocustest('Consignee', true)"
               @blur="changeFocustest('Consignee', false)"
               class="log_input"
             >
             </el-input>
-            <span v-if="showLabelConsignee" class="border-label">Consignee</span>
+            <span v-if="showLabelConsignee" class="border-label">{{ t('destinationDelivery.consignee') }}</span>
           </div>
 
           <div class="input-with-label">
@@ -806,7 +809,7 @@ onMounted(() => {
               @DateRangeChange="DateRangeChangeETA"
               @DateChangeFocus="DateChangeFocusETA"
             ></CalendarDate>
-            <span v-if="showETAlabel" class="border-label">ETA</span>
+            <span v-if="showETAlabel" class="border-label">{{ t('destinationDelivery.eta') }}</span>
           </div>
           <div class="input-with-label">
             <CalendarDate
@@ -815,7 +818,7 @@ onMounted(() => {
               @DateRangeChange="DateRangeChangeATA"
               @DateChangeFocus="DateChangeFocusATA"
             ></CalendarDate>
-            <span v-if="showataLabel" class="border-label">ATA</span>
+            <span v-if="showataLabel" class="border-label">{{ t('destinationDelivery.ata') }}</span>
           </div>
 
           <div class="input-with-label">
@@ -826,20 +829,20 @@ onMounted(() => {
               @focus="isFocused.deliveryDate = true"
               @blur="isFocused.deliveryDate = false"
               :format="userStore.dateFormat"
-              placeholder="Recommended Delivery Date"
+              :placeholder="t('destinationDelivery.recommendedDeliveryDate')"
               valueFormat="MM/DD/YYYY"
               style="width: 100%; height: 40px"
             >
             </a-date-picker>
             <span v-if="isFocused.deliveryDate" class="border-label"
-              >Recommended Delivery Date</span
+              >{{ t('destinationDelivery.recommendedDeliveryDate') }}</span
             >
           </div>
 
           <div class="input-with-label">
             <!-- <AutoSelect ASPlaceholder="Input Vessel Name" :ASValue="VesselName" @changeFocus="changeFocus"></AutoSelect> -->
             <el-input
-              placeholder="Input Vessel Name"
+              :placeholder="t('destinationDelivery.inputVesselName')"
               v-model="VesselNametest"
               @focus="changeFocustest('Vessel', true)"
               @blur="changeFocustest('Vessel', false)"
@@ -847,14 +850,14 @@ onMounted(() => {
               style="width: 100%"
             >
             </el-input>
-            <span v-if="showLabelVessel" class="border-label">Vessel Name</span>
+            <span v-if="showLabelVessel" class="border-label">{{ t('destinationDelivery.vesselName') }}</span>
           </div>
           <div class="right-btn">
             <el-button
               style="width: 108px"
               class="el-button--dark create-button"
               @click="SearchShipment"
-              >Search</el-button
+              >{{ t('common.search') }}</el-button
             >
           </div>
         </div>
@@ -867,16 +870,16 @@ onMounted(() => {
     <!-- Delivery Information -->
     <div class="Delivery">
       <span class="serial-number">2</span>
-      <span>Delivery Information</span>
+      <span>{{ t('destinationDelivery.deliveryInformation') }}</span>
     </div>
     <div class="delivery_address">
       <div class="deliverty_flex">
-        <div><span class="stars_red">*</span>Delivery Address</div>
+        <div><span class="stars_red">*</span>{{ t('destinationDelivery.deliveryAddress') }}</div>
         <el-button
           :disabled="isNotClickAddress"
           @click="handleClickAddress"
           class="el-button--noborder--configuration"
-          ><span class="font_family icon-icon_location_b"></span> Address Book</el-button
+          ><span class="font_family icon-icon_location_b"></span> {{ t('destinationDelivery.addressBook') }}</el-button
         >
       </div>
       <div class="empty_address" v-if="isselectedAddress == null">
@@ -933,10 +936,10 @@ onMounted(() => {
       </div>
       <div class="delivery_type">
         <div>
-          <div class="delivery_type_title"><span class="stars_red">*</span>Mode Type</div>
+          <div class="delivery_type_title"><span class="stars_red">*</span>{{ t('destinationDelivery.modeType') }}</div>
           <el-select
             v-model="modetypeValue"
-            placeholder="Select"
+            :placeholder="t('destinationDelivery.select')"
             :disabled="isNotClickAddress"
             style="width: 240px"
           >
@@ -950,7 +953,7 @@ onMounted(() => {
         </div>
         <div style="margin: 0 12px">
           <div class="delivery_type_title">
-            <span class="stars_red">*</span>Preferred Delivery Date
+            <span class="stars_red">*</span>{{ t('destinationDelivery.preferredDeliveryDate') }}
           </div>
           <a-date-picker
             v-model:value="DateValue"
@@ -960,13 +963,13 @@ onMounted(() => {
             style="width: 240px"
             :format="userStore.dateFormat"
             valueFormat="YYYY.MM.DD"
-            placeholder="Please Select Date"
+            :placeholder="t('destinationDelivery.pleaseSelectDate')"
             class="delivery-date-picker"
           >
             <template #renderExtraFooter>
               <div class="recommended">
                 <div class="recommend_color"></div>
-                Recommended delivery date
+                {{ t('destinationDelivery.recommendedDeliveryDate') }}
               </div>
             </template>
             <template #dateRender="{ current }">
@@ -977,7 +980,7 @@ onMounted(() => {
           </a-date-picker>
         </div>
         <div>
-          <div class="delivery_type_title"><span class="stars_red">*</span>Delivery Time</div>
+          <div class="delivery_type_title"><span class="stars_red">*</span>{{ t('destinationDelivery.deliveryTime') }}</div>
           <el-time-select
             v-model="DeliveryTime"
             :disabled="isNotClickAddress"
@@ -986,64 +989,64 @@ onMounted(() => {
             end="23:30"
             style="width: 240px"
             prefix-icon=""
-            placeholder="Please Select Time"
+            :placeholder="t('destinationDelivery.pleaseSelectTime')"
           ></el-time-select>
         </div>
         <div style="margin-left: 12px">
-          <div class="delivery_type_title">Delivery Reference</div>
+          <div class="delivery_type_title">{{ t('destinationDelivery.deliveryReference') }}</div>
           <el-tooltip
             class="item"
             effect="dark"
-            content="Reference to be quoted on arrival at the Warehouse/DC"
+            :content="t('destinationDelivery.deliveryReferenceTooltip')"
             placement="bottom"
           >
             <el-input v-model="DeliveryReference" class="delivery_reference"></el-input>
           </el-tooltip>
         </div>
       </div>
-      <div class="delivery_type_title">Special Requirements</div>
+      <div class="delivery_type_title">{{ t('destinationDelivery.specialRequirements') }}</div>
       <div class="flex" style="margin-top: 8px">
         <el-button
           :disabled="isNotClickAddress"
           class="el-button--grey"
-          @click="handleclickbutton('Tail Lift Required')"
-          >Tail Lift Required</el-button
+          @click="handleclickbutton(t('destinationDelivery.tailLiftRequired'))"
+          >{{ t('destinationDelivery.tailLiftRequired') }}</el-button
         >
         <el-button
           :disabled="isNotClickAddress"
           class="el-button--grey"
-          @click="handleclickbutton('Side Loading')"
-          >Side Loading</el-button
+          @click="handleclickbutton(t('destinationDelivery.sideLoading'))"
+          >{{ t('destinationDelivery.sideLoading') }}</el-button
         >
         <el-button
           :disabled="isNotClickAddress"
           class="el-button--grey"
-          @click="handleclickbutton('Forklift Required')"
-          >Forklift Required</el-button
+          @click="handleclickbutton(t('destinationDelivery.forkliftRequired'))"
+          >{{ t('destinationDelivery.forkliftRequired') }}</el-button
         >
         <el-button
           :disabled="isNotClickAddress"
           class="el-button--grey"
-          @click="handleclickbutton('Special Equipment')"
-          >Special Equipment</el-button
+          @click="handleclickbutton(t('destinationDelivery.specialEquipment'))"
+          >{{ t('destinationDelivery.specialEquipment') }}</el-button
         >
       </div>
       <el-input
         :disabled="isNotClickAddress"
         v-model="Requirements"
-        placeholder="Eenter any additional requirements or notes..."
+        :placeholder="t('destinationDelivery.additionalRequirementsPlaceholder')"
         type="textarea"
         style="margin-top: 8px"
         :rows="4"
       ></el-input>
       <div v-if="a != undefined" class="delivery_type_title" style="margin-top: 16px">
-        <span class="stars_red">*</span>Modification Reason
+        <span class="stars_red">*</span>{{ t('destinationDelivery.modificationReason') }}
       </div>
       <el-input
         v-if="a != undefined"
         :disabled="isNotClickAddress"
         v-model="Modification"
-        placeholder="Eenter any additional requirements or notes..."
+        :placeholder="t('destinationDelivery.additionalRequirementsPlaceholder')"
         type="textarea"
         style="margin-top: 8px"
         :rows="4"
@@ -1064,7 +1067,7 @@ onMounted(() => {
             class="el-button--main"
             @click="AddNewAddressDelivery"
           >
-            + Add New Address</el-button
+            + {{ t('destinationDelivery.addNewAddress') }}</el-button
           >
         </div>
         <el-radio-group v-model="Addressradio" style="max-height: 50vh; overflow: auto">
@@ -1140,7 +1143,7 @@ onMounted(() => {
     <el-dialog
       v-model="AddNewAddressVisible"
       width="640"
-      title="Add New Delivery Address"
+    :title="t('destinationDelivery.addNewDeliveryAddressTitle')"
       :show-close="false"
     >
       <div class="diaolog_add_title"><span class="stars_red">*</span>Address</div>
@@ -1154,7 +1157,7 @@ onMounted(() => {
         <el-input
           @input="handleAddressLine1"
           class="inputmargin2"
-          placeholder="Line 1"
+            :placeholder="t('destinationDelivery.addressLine1Placeholder')"
           v-model="AddressLine1"
         >
           <template #suffix>
@@ -1174,7 +1177,7 @@ onMounted(() => {
         <el-input
           @input="handleAddressLine2"
           class="inputmargin2"
-          placeholder="Line 2"
+            :placeholder="t('destinationDelivery.addressLine2Placeholder')"
           v-model="AddressLine2"
         >
           <template #suffix>
@@ -1194,7 +1197,7 @@ onMounted(() => {
         <el-input
           @input="handleAddressLine3"
           class="inputmargin2"
-          placeholder="Line 3"
+            :placeholder="t('destinationDelivery.addressLine3Placeholder')"
           v-model="AddressLine3"
         >
           <template #suffix>
@@ -1215,7 +1218,7 @@ onMounted(() => {
           style="margin-bottom: 16px"
           @input="handleAddressLine4"
           class="inputmargin2"
-          placeholder="Line 4"
+            :placeholder="t('destinationDelivery.addressLine4Placeholder')"
           v-model="AddressLine4"
         >
           <template #suffix>
@@ -1232,7 +1235,7 @@ onMounted(() => {
             v-model="CountryCode"
             filterable
             remote
-            placeholder="Select Country"
+            :placeholder="t('destinationDelivery.selectCountry')"
             :remote-method="querySearchCountry"
             :loading="Countryloading"
             class="inputmargin"
@@ -1251,7 +1254,7 @@ onMounted(() => {
             v-model="CityCode"
             filterable
             remote
-            placeholder="Select City"
+            :placeholder="t('destinationDelivery.selectCity')"
             :remote-method="querySearchCity"
             :loading="cityloading"
             class="inputmargin"
@@ -1268,7 +1271,7 @@ onMounted(() => {
           <div class="diaolog_add_title"><span class="stars_red">*</span>Postal Code</div>
           <el-input
             class="inputmargin"
-            placeholder="Enter postal code"
+            :placeholder="t('destinationDelivery.enterPostalCode')"
             v-model="PostalCode"
           ></el-input>
         </div>
@@ -1277,13 +1280,17 @@ onMounted(() => {
       <div class="flex">
         <div style="margin-right: 9px; width: 50%">
           <div class="diaolog_add_title"><span class="stars_red">*</span>Contact Person</div>
-          <el-input style="margin-top: 4px" placeholder="Name" v-model="ContactPerson"></el-input>
+          <el-input
+            style="margin-top: 4px"
+            :placeholder="t('destinationDelivery.contactNamePlaceholder')"
+            v-model="ContactPerson"
+          ></el-input>
         </div>
         <div style="width: 50%">
           <div class="diaolog_add_title"><span class="stars_red">*</span>Contact Number</div>
           <el-input
             style="margin-top: 4px"
-            placeholder="Mobile Numer"
+            :placeholder="t('destinationDelivery.contactMobilePlaceholder')"
             v-model="ContactNumber"
           ></el-input>
         </div>
@@ -1309,10 +1316,9 @@ onMounted(() => {
     >
       <div class="flex_center">
         <img :src="NotAvailable" width="100" />
-        <div class="alert_title">Destination Delivery Service Not Available</div>
+        <div class="alert_title">{{ t('destinationDelivery.serviceNotAvailableTitle') }}</div>
         <div class="alert_text">
-          Destination delivery service is not yet available for your shipment destinations. To
-          request this service, please contact the destination office first.
+          {{ t('destinationDelivery.serviceNotAvailableText') }}
         </div>
       </div>
       <template #footer>
@@ -1336,10 +1342,9 @@ onMounted(() => {
     >
       <div class="flex_center">
         <img :src="NotShipment" width="100" />
-        <div class="alert_title">No Eligible Shipments for Booking</div>
+        <div class="alert_title">{{ t('destinationDelivery.noEligibleShipmentsTitle') }}</div>
         <div class="alert_text">
-          Your shipments are currently outside the booking window. Eligible shipments will appear
-          here when booking window opens.
+          {{ t('destinationDelivery.noEligibleShipmentsText') }}
         </div>
       </div>
       <template #footer>
@@ -1358,7 +1363,7 @@ onMounted(() => {
     <!-- 保存失败 -->
     <el-dialog v-model="UnableSaveVisible" width="480">
       <div>{{ missingmessage }}</div>
-      <div>Please complete all required fields.</div>
+      <div>{{ t('destinationDelivery.completeRequiredFields') }}</div>
       <template #footer>
         <div class="dialog-footer">
           <el-button
@@ -1373,7 +1378,7 @@ onMounted(() => {
       <template #header>
         <div class="unable-save-header dialog-header">
           <span class="font_family icon-icon_fail_fill_b"></span>
-          Unable to Save
+          {{ t('destinationDelivery.unableToSave') }}
         </div>
       </template>
     </el-dialog>
@@ -1394,14 +1399,14 @@ onMounted(() => {
       <template #header>
         <div class="warning-header dialog-header">
           <span class="font_family icon-icon_fail_fill_b"></span>
-          Unsaved Changes
+          {{ t('destinationDelivery.unsavedChangesTitle') }}
         </div>
       </template>
     </el-dialog>
     <!-- 當選擇非建議日期内的date時給出的提示 -->
     <el-dialog v-model="isRecommendDate" width="480">
       <div>{{ recommendateWarning }}</div>
-      <div style="margin-top: 4px">Additional storage fees may apply.</div>
+      <div style="margin-top: 4px">{{ t('destinationDelivery.additionalStorageFeesMayApply') }}</div>
       <div style="margin-top: 8px">
         <div class="Notice" v-for="(item, index) in RecommendateList" :key="index">
           HOBL: {{ item.Hbol }}
@@ -1421,7 +1426,7 @@ onMounted(() => {
       <template #header>
         <div class="warning-header dialog-header">
           <span class="font_family icon-icon_warning_fill_b"></span>
-          Additional Fees Notice
+          {{ t('destinationDelivery.additionalFeesNotice') }}
         </div>
       </template>
     </el-dialog>

+ 16 - 13
src/views/DestinationDelivery/src/components/ListView.vue

@@ -1,4 +1,7 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
 import { useCalculatingHeight } from '@/hooks/calculatingHeight'
 import TableView from './TableView'
 import dayjs from 'dayjs'
@@ -22,42 +25,42 @@ const isEmployeeRole = computed(() => {
 
 const modeList = [
   {
-    label: 'Truck',
+    label: t('destinationDelivery.truck'),
     value: 'Truck'
   },
   {
-    label: 'Rail',
+    label: t('destinationDelivery.rail'),
     value: 'Rail'
   }
 ]
 
 const numberCards = ref([
   {
-    label: 'Total Bookings',
+    label: t('destinationDelivery.totalBookings'),
     key: 'ALL',
     value: 0,
     color: '#2b2f36'
   },
   {
-    label: 'Pending Approval',
+    label: t('destinationDelivery.pendingApproval'),
     value: 0,
     color: '#edb82f',
     icon: 'icon_time_b'
   },
   {
-    label: 'Approved',
+    label: t('destinationDelivery.approved'),
     value: 0,
     color: '#00a870',
     icon: 'icon_confirm_b'
   },
   {
-    label: 'Rejected',
+    label: t('destinationDelivery.rejected'),
     value: 0,
     color: '#c9353f',
     icon: 'icon_reject_b'
   },
   {
-    label: 'Cancelled',
+    label: t('destinationDelivery.cancelled'),
     value: 0,
     color: '#243041',
     icon: 'icon_cancelled_b'
@@ -110,12 +113,12 @@ defineExpose({
   <div class="display">
     <div class="header_top">
       <div class="date-tips_filter filter-item">
-        <span class="label">Delivery Date:</span>
+        <span class="label">{{ t('destinationDelivery.deliveryDate') }}:</span>
         <DeliveryDate @DateChange="deliveryDataChange" :Date="DateStart" />
       </div>
 
       <div class="tips_filter filter-item">
-        <span class="label">Delivery Mode:</span>
+        <span class="label">{{ t('destinationDelivery.deliveryMode') }}:</span>
         <el-select v-model="queryData.delivery_mode" placeholder="" clearable>
           <el-option
             v-for="item in modeList"
@@ -126,12 +129,12 @@ defineExpose({
         </el-select>
       </div>
       <div class="date-tips_filter filter-item">
-        <span class="label">Creation Date</span>
+        <span class="label">{{ t('destinationDelivery.creationDate') }}</span>
         <CalendarDate :isShowPopupClass="true" @DateChange="DateChange"></CalendarDate>
       </div>
       <div class="input-tips_filter filter-item">
         <el-input
-          placeholder="Search Question Booking No、HBOL No、MBL No、Container No、Consignee"
+          :placeholder="t('destinationDelivery.searchQuestionPlaceholder')"
           v-model="queryData.text_search"
           class="log_input"
         >
@@ -144,7 +147,7 @@ defineExpose({
           </template>
         </el-input>
       </div>
-      <el-button class="el-button--dark" @click="handleSearch">Search</el-button>
+      <el-button class="el-button--dark" @click="handleSearch">{{ t('common.search') }}</el-button>
     </div>
     <div class="number-cards">
       <div
@@ -162,7 +165,7 @@ defineExpose({
         <div
           class="card-value"
           :style="{
-            color: item.label === 'Cancelled' ? `var(--color-card-number-cancelled)` : item.color
+            color: item.label === t('destinationDelivery.cancelled') ? `var(--color-card-number-cancelled)` : item.color
           }"
         >
           {{ item.value }}

+ 8 - 5
src/views/DestinationDelivery/src/components/TableView/src/TableView.vue

@@ -1,4 +1,7 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
 import { ref, nextTick, onMounted } from 'vue'
 import { type VxeGridInstance, type VxeGridProps } from 'vxe-table'
 import { autoWidth } from '@/utils/table'
@@ -352,7 +355,7 @@ defineExpose({
     element-loading-background="rgb(43, 47, 54, 0.7)"
   >
     <div class="table-tools">
-      <div class="left-total-records">Booking List</div>
+      <div class="left-total-records">{{ t('destinationDelivery.bookingList') }}</div>
     </div>
     <vxe-grid
       ref="tableRef"
@@ -365,9 +368,9 @@ defineExpose({
       <template #empty v-if="!tableLoadingTableData && tableData.data.length === 0">
         <div class="empty-box">
           <img class="empty-img" src="./img/table-empty-img.png" alt="" />
-          <p>You haven't created any destination delivery bookings yet.</p>
-          <p>Book truck or rail delivery for your shipments to save time and</p>
-          <p>ensure smooth last-mile delivery.</p>
+          <p>{{ t('destinationDelivery.emptyBookingTitle') }}</p>
+          <p>{{ t('destinationDelivery.emptyBookingLine1') }}</p>
+          <p>{{ t('destinationDelivery.emptyBookingLine2') }}</p>
           <el-button
             style="height: 40px"
             v-if="isEmployeeRole === false"
@@ -375,7 +378,7 @@ defineExpose({
             @click="handleCreate"
           >
             <span style="margin-right: 4px" class="font_family icon-icon_add_b"></span>
-            <span style="font-weight: 400">Create New Booking</span>
+            <span style="font-weight: 400">{{ t('destinationDelivery.createNewBooking') }}</span>
           </el-button>
         </div>
       </template>

+ 12 - 10
src/views/DestinationDelivery/src/components/TableView/src/components/BookingDetailDialog.vue

@@ -1,9 +1,11 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
 import DetailStep from './DetailStep.vue'
 import ShipmentInforTable from './ShipmentInforTable.vue'
 import OperationLogProcess from './OperationLogProcess.vue'
 import { formatTimezone } from '@/utils/tools'
 
+const { t } = useI18n()
 const visible = ref(false)
 const loading = ref(false)
 
@@ -35,21 +37,21 @@ const stepList: any = ref([])
 
 const handleStepData = (status, data) => {
   stepList.value.push({
-    label: 'Created',
+    label: t('common.created'),
     date: data.created_time || '--',
     icon: 'icon_submit_b'
   })
   if (status === 'Modified') {
     stepList.value.push({
-      label: 'Modified',
+      label: t('common.modified'),
       date: data.update_time || '--',
       icon: 'icon_edit_b'
     })
   }
   if (status === 'Pending Approval' || status === 'Modified') {
     stepList.value.push({
-      label: 'Pending',
-      date: 'Current',
+      label: t('common.pending'),
+      date: t('common.current'),
       icon: 'icon_time_b',
       status: 'current'
     })
@@ -97,7 +99,7 @@ defineExpose({
 
 <template>
   <el-dialog
-    title="Booking Detail"
+    :title="t('destinationDelivery.bookingDetail')"
     class="booking-detail-dialog"
     v-model="visible"
     :close-on-click-modal="false"
@@ -117,28 +119,28 @@ defineExpose({
       </div>
       <ShipmentInforTable :data="shipmentInfoTableData" />
       <div class="delivery-information">
-        <div class="label">Delivery Information</div>
+        <div class="label">{{ t('destinationDelivery.deliveryInformation') }}</div>
         <div class="delivery-info">
           <div class="delivery-item inline-flex" style="width: 200px">
-            <span class="item-label">Mode Type</span>
+            <span class="item-label">{{ t('destinationDelivery.modeType') }}</span>
             <span class="item-value">{{ rowData?.delivery_mode || '--' }}</span>
           </div>
           <div class="delivery-item inline-flex">
-            <span class="item-label">Delivery Date</span>
+            <span class="item-label">{{ t('destinationDelivery.deliveryDate') }}</span>
             <span class="item-value">
               <span class="font_family icon-icon_date_b" style="margin-right: 4px"></span>
               <span style="margin-top: 1px">{{ formatTimezone(rowData?.delivery_date) }}</span>
             </span>
           </div>
           <div class="delivery-item">
-            <span class="item-label">Delivery Address</span>
+            <span class="item-label">{{ t('destinationDelivery.deliveryAddress') }}</span>
             <span class="item-value">
               <span class="font_family icon-icon_location_b" style="margin-right: 2px"></span>
               <span style="margin-top: 1px">{{ rowData?.delivery_address || '--' }}</span>
             </span>
           </div>
           <div class="delivery-item">
-            <span class="item-label">Special Requirements</span>
+            <span class="item-label">{{ t('destinationDelivery.specialRequirements') }}</span>
             <span class="item-value">
               <span class="font_family icon-icon_paragraph_b" style="margin-right: 2px"></span>
               <span style="margin-top: 1px">{{ rowData?.special_requirements || '--' }}</span>

+ 12 - 9
src/views/DestinationDelivery/src/components/TableView/src/components/DownloadDialog.vue

@@ -1,4 +1,7 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
 const dialogVisible = ref(false)
 
 const openDialog = (selectedColumns: string[], slectedDataNumber: number) => {
@@ -32,11 +35,11 @@ defineExpose({
 
 <template>
   <div>
-    <el-dialog @close="clearData" v-model="dialogVisible" title="Download File" width="540">
+    <el-dialog @close="clearData" v-model="dialogVisible" :title="t('common.downloadFile')" width="540">
       <div class="download-dialog">
         <div class="select-data">
           <div style="display: inline-block">
-            Select data on your Opeartion Log list:<span style="color: var(--color-theme)">{{
+            {{ t('destinationDelivery.selectDataOnOperationLogList') }}<span style="color: var(--color-theme)">{{
               selectedDataNumber
             }}</span>
           </div>
@@ -44,7 +47,7 @@ defineExpose({
         <div class="download-filter">
           <el-radio-group v-model="downloadFilter">
             <el-radio :value="1"
-              >Download with selected columns
+              >{{ t('destinationDelivery.downloadWithSelectedColumns') }}
               <span class="column-number">{{ columns.length }}</span>
               <SeeAllIcon v-model="isShowSelectColumn" />
             </el-radio>
@@ -53,23 +56,23 @@ defineExpose({
               class="select-columns"
               :class="{ show: isShowSelectColumn }"
             >
-              <div class="title">Selected columns</div>
+              <div class="title">{{ t('common.selectedColumns') }}</div>
               <div class="content">
                 <div class="column-item" v-for="item in columns" :key="item">{{ item }}</div>
               </div>
             </div>
-            <el-radio :value="2">Download with all columns</el-radio>
+            <el-radio :value="2">{{ t('common.downloadWithAllColumns') }}</el-radio>
           </el-radio-group>
         </div>
       </div>
       <template #footer>
         <div class="dialog-footer">
-          <el-button class="cancel-btn" type="default" @click="dialogVisible = false"
-            >Cancel</el-button
-          >
+          <el-button class="cancel-btn" type="default" @click="dialogVisible = false">
+            {{ t('common.cancel') }}
+          </el-button>
           <el-button class="download-btn el-button--dark" @click="handleDownload"
             ><span style="margin-right: 8px" class="font_family icon-icon_download_b"></span>
-            Download</el-button
+            {{ t('tracking.download') }}</el-button
           >
         </div>
       </template>

+ 11 - 8
src/views/DestinationDelivery/src/components/TableView/src/components/EmailDialog.vue

@@ -1,4 +1,7 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
 import '@wangeditor/editor/dist/css/style.css'
 import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
 import { i18nChangeLanguage, DomEditor } from '@wangeditor/editor'
@@ -152,7 +155,7 @@ const sendEmail = () => {
   const html = editorRef.value.getHtml()
   const text = editorRef.value.getText()
   if (!text) {
-    ElMessage.warning('Please enter the email content')
+    ElMessage.warning(t('destinationDelivery.pleaseEnterEmailContent'))
     return
   }
   $api
@@ -166,12 +169,12 @@ const sendEmail = () => {
     })
     .then((res: any) => {
       if (res.code === 200) {
-        ElMessage.success('Email sent successfully')
+        ElMessage.success(t('destinationDelivery.emailSentSuccessfully'))
         emailRecords.value = res.data.emailRecords
       }
     })
     .catch(() => {
-      ElMessage.error('Failed to send email')
+      ElMessage.error(t('destinationDelivery.failedToSendEmail'))
     })
 }
 
@@ -191,7 +194,7 @@ defineExpose({
 
 <template>
   <el-dialog
-    title="Communication"
+    :title="t('destinationDelivery.communication')"
     class="delivery-email-dialog"
     v-model="visible"
     :close-on-click-modal="false"
@@ -203,7 +206,7 @@ defineExpose({
     <div class="email-view">
       <div class="email-path">
         <span class="font_family icon-icon_email_b" style="font-size: 18px"></span>
-        <span class="label">Communicate with us:&nbsp;</span>
+        <span class="label">{{ t('destinationDelivery.communicateWithUs') }}:&nbsp;</span>
         <span class="content">{{ emailData.email }}</span>
       </div>
       <div class="separated-by">
@@ -221,7 +224,7 @@ defineExpose({
               <el-tooltip
                 class="box-item"
                 effect="dark"
-                content="Separated by;"
+                :content="t('destinationDelivery.separatedBySemicolon')"
                 placement="top-start"
                 :offset="-8"
               >
@@ -251,8 +254,8 @@ defineExpose({
           @click="sendEmail"
           class="el-button--dark"
           style="float: right; margin: 8px 0 14px 0; height: 40px"
-          ><span class="font_family icon-icon_submit_b" style="margin-right: 4px"></span> Send
-          Email</el-button
+          ><span class="font_family icon-icon_submit_b" style="margin-right: 4px"></span>
+          {{ t('destinationDelivery.sendEmail') }}</el-button
         >
       </div>
     </div>

+ 24 - 13
src/views/DestinationDelivery/src/components/TableView/src/components/TipsDialog.vue

@@ -1,19 +1,22 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
 const dialogType = ref()
 const bookingNo = ref()
 const dialogVisible = ref(false)
 const serialNo = ref()
 const tipList = ref({
-  reject: 'Are you sure you want to Reject Booking ',
-  approve: 'Are you sure you want to Approve Booking ',
-  cancel: 'Are you sure you want to Cancel Booking '
+  reject: t('destinationDelivery.confirmRejectBooking'),
+  approve: t('destinationDelivery.confirmApproveBooking'),
+  cancel: t('destinationDelivery.confirmCancelBooking')
 })
 const notes = ref()
 
 const typeList = {
-  approve: 'Approve',
-  reject: 'Reject',
-  cancel: 'Cancel'
+  approve: t('destinationDelivery.approve'),
+  reject: t('destinationDelivery.reject'),
+  cancel: t('common.cancel')
 }
 
 const openDialog = (type: string, row: any) => {
@@ -26,7 +29,7 @@ const emit = defineEmits(['stateChange'])
 
 const handleSubmit = () => {
   if (dialogType.value === 'reject' && !notes.value) {
-    ElMessage.warning('A remark must be filled in for the rejection operation.')
+    ElMessage.warning(t('destinationDelivery.rejectRemarkRequired'))
     return
   }
   $api
@@ -38,14 +41,20 @@ const handleSubmit = () => {
     .then((res) => {
       if (res.code === 200) {
         dialogVisible.value = false
-        ElMessage.success(`${typeList[dialogType.value]} successfully`)
+        ElMessage.success(
+          `${typeList[dialogType.value]} ${t('destinationDelivery.actionSuccessfully')}`
+        )
         emit('stateChange')
       } else {
-        ElMessage.error(`${typeList[dialogType.value]} failed, Please try again.`)
+        ElMessage.error(
+          `${typeList[dialogType.value]} ${t('destinationDelivery.actionFailedTryAgain')}`
+        )
       }
     })
     .catch(() => {
-      ElMessage.error(`${typeList[dialogType.value]} failed, Please try again.`)
+      ElMessage.error(
+        `${typeList[dialogType.value]} ${t('destinationDelivery.actionFailedTryAgain')}`
+      )
     })
 }
 const clearData = () => {
@@ -81,10 +90,12 @@ defineExpose({
       v-model="notes"
       class="input-textarea"
       style="height: 120px"
-      placeholder="Input remarks"
+      :placeholder="t('destinationDelivery.inputRemarks')"
     ></el-input>
     <template #footer>
-      <el-button class="cancel-btn" type="default" @click="dialogVisible = false">Cancel</el-button>
+      <el-button class="cancel-btn" type="default" @click="dialogVisible = false">{{
+        t('common.cancel')
+      }}</el-button>
       <el-button
         class="submit-btn"
         :class="{
@@ -93,7 +104,7 @@ defineExpose({
           'el-button--warning': dialogType === 'cancel'
         }"
         @click="handleSubmit"
-        >{{ typeList[dialogType] }} Booking</el-button
+        >{{ typeList[dialogType] }} {{ t('destinationDelivery.booking') }}</el-button
       >
     </template>
   </el-dialog>

+ 8 - 5
src/views/Layout/src/components/Header/HeaderView.vue

@@ -1,4 +1,7 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
 import DownloadKLNPortal from './components/DownloadKLNPortal.vue'
 import ChangePasswordDialog from './components/ChangePasswordDialog.vue'
 import LogoutDialog from './components/LogoutDialog.vue'
@@ -111,8 +114,8 @@ const handleUserManual = () => {
       .getUserGuide()
       .then(async (res) => {
         if (res.status !== 200) {
-          ElMessageBox.alert('The request failed. Please try again later', 'Prompt', {
-            confirmButtonText: 'OK',
+          ElMessageBox.alert(t('common.requestFailedRetry'), t('common.prompt'), {
+            confirmButtonText: t('common.ok'),
             confirmButtonClass: 'el-button--dark'
           })
           return
@@ -138,8 +141,8 @@ const handleUserManual = () => {
         userManualLoading.value = false
       })
   } catch (error) {
-    ElMessageBox.alert('The request failed. Please try again later', 'Prompt', {
-      confirmButtonText: 'OK',
+    ElMessageBox.alert(t('common.requestFailedRetry'), t('common.prompt'), {
+      confirmButtonText: t('common.ok'),
       confirmButtonClass: 'el-button--dark'
     })
   }
@@ -228,7 +231,7 @@ const handleDemoVideo = () => {
         v-model="searchValue"
         size="large"
         @keyup.enter="handleSearch"
-        placeholder="Enter Booking/HBL/PO/Container No."
+        :placeholder="t('tracking.searchPlaceholder')"
       >
         <template #prefix>
           <span style="margin-top: -1px" class="font_family icon-icon_search_b"></span>

+ 20 - 17
src/views/Layout/src/components/Header/components/ChangePasswordDialog.vue

@@ -1,4 +1,7 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
 import { useUserStore } from '@/stores/modules/user'
 import dayjs from 'dayjs'
 
@@ -29,22 +32,22 @@ const handleUpdate = () => {
   }
   if (pwdData.value.newPassword.length < 12 || pwdData.value.newPassword.length > 20) {
     loginError.value.newPassword = true
-    newPwdErrTips.value = 'Password length between 12 - 20'
+    newPwdErrTips.value = t('layout.passwordLength12to20')
     return
   }
   if (!/[A-Z]/.test(pwdData.value.newPassword)) {
     loginError.value.newPassword = true
-    newPwdErrTips.value = 'Password must contain uppercase letters'
+    newPwdErrTips.value = t('layout.passwordMustContainUppercase')
     return
   }
   if (!/[a-z]/.test(pwdData.value.newPassword)) {
     loginError.value.newPassword = true
-    newPwdErrTips.value = 'Password must contain lowercase letters'
+    newPwdErrTips.value = t('layout.passwordMustContainLowercase')
     return
   }
   if (!/[0-9]/.test(pwdData.value.newPassword)) {
     loginError.value.newPassword = true
-    newPwdErrTips.value = 'Password must contain numbers'
+    newPwdErrTips.value = t('layout.passwordMustContainNumber')
     return
   }
   $api
@@ -54,7 +57,7 @@ const handleUpdate = () => {
     })
     .then((res) => {
       if (res.code === 200) {
-        ElMessage.success('Password updated successfully')
+        ElMessage.success(t('layout.passwordUpdatedSuccessfully'))
         userStore.setUserInfo({
           ...userStore.userInfo,
           last_pwd_change: dayjs().format('YYYY-MM-DD HH:mm:ss')
@@ -126,7 +129,7 @@ defineExpose({
     @close="clearData"
     top="20vh"
     :width="400"
-    title="Change Password"
+    :title="t('layout.changePasswordTitle')"
   >
     <div>
       <div class="form-item">
@@ -136,14 +139,14 @@ defineExpose({
           type="password"
           show-password
           :class="{ 'is-error': loginError.oldPassword }"
-          placeholder="Please input Old Password"
+          :placeholder="t('login.oldPasswordPlaceholder')"
           @focus="hiddenError('oldPassword')"
         >
           <template #prefix>
             <span class="font_family icon-icon_password_b"></span>
           </template>
         </el-input>
-        <div class="error" v-if="loginError.oldPassword">Incorrect password. Please try again.</div>
+        <div class="error" v-if="loginError.oldPassword">{{ t('login.incorrectPassword') }}</div>
       </div>
       <div class="form-item">
         <span class="label">New Password</span>
@@ -152,7 +155,7 @@ defineExpose({
           type="password"
           show-password
           :class="{ 'is-error': loginError.newPassword }"
-          placeholder="New password must contain both letter and numeral"
+          :placeholder="t('login.newPasswordComplexityHint')"
           @focus="hiddenError('newPassword')"
           @input="checkPassword"
         >
@@ -168,19 +171,19 @@ defineExpose({
               class="font_family icon-icon_confirm_b"
               :class="{ active: hasUppercase }"
             ></span>
-            <span>Password must contain uppercase letters</span>
+            <span>{{ t('layout.passwordMustContainUppercase') }}</span>
           </div>
           <div class="tip-item">
             <span class="font_family icon-icon_confirm_b" :class="{ active: hasLowercase }"></span>
-            <span>Password must contain lowercase letters</span>
+            <span>{{ t('layout.passwordMustContainLowercase') }}</span>
           </div>
           <div class="tip-item">
             <span class="font_family icon-icon_confirm_b" :class="{ active: hasNumber }"></span>
-            <span>Password must contain numbers</span>
+            <span>{{ t('layout.passwordMustContainNumber') }}</span>
           </div>
           <div class="tip-item">
             <span class="font_family icon-icon_confirm_b" :class="{ active: isValidLength }"></span>
-            <span>Password length between12 - 20 </span>
+            <span>{{ t('layout.passwordLength12to20') }}</span>
           </div>
         </div>
       </div>
@@ -191,7 +194,7 @@ defineExpose({
           type="password"
           show-password
           :class="{ 'is-error': loginError.confirmPassword }"
-          placeholder="Please Confirm Password"
+          :placeholder="t('login.confirmPasswordPlaceholder')"
           @focus="hiddenError('confirmPassword')"
           @blur="confirmPwd"
         >
@@ -200,16 +203,16 @@ defineExpose({
           </template>
         </el-input>
         <div class="error" v-if="loginError.confirmPassword">
-          The password does not match. Please try again.
+          {{ t('layout.passwordMismatch') }}
         </div>
       </div>
     </div>
     <template #footer>
       <el-button class="el-button--default" style="height: 40px" @click="dialogVisible = false"
-        >Cancel</el-button
+        >{{ t('common.cancel') }}</el-button
       >
       <el-button @click="handleUpdate" class="el-button--dark" style="height: 40px"
-        >Update</el-button
+        >{{ t('common.update') }}</el-button
       >
     </template>
   </el-dialog>

+ 8 - 4
src/views/Layout/src/components/Header/components/DownloadKLNPortal.vue

@@ -1,4 +1,8 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
+
 const dialogVisible = ref(false)
 
 const openDialog = () => {
@@ -14,21 +18,21 @@ defineExpose({
     top="30vh"
     v-model="dialogVisible"
     :width="640"
-    title="Download KLN Portal"
+    :title="t('layout.downloadKLNPortalTitle')"
     class="download-portal"
   >
     <div class="download-app-list">
       <div class="download-item">
         <img src="../images/kios.png" alt="" />
-        <span>iPhone App Store</span>
+        <span>{{ t('layout.iphoneAppStore') }}</span>
       </div>
       <div class="download-item">
         <img src="../images/kandroid.png" alt="" />
-        <span>Andriod-Play Store</span>
+        <span>{{ t('layout.androidPlayStore') }}</span>
       </div>
       <div class="download-item">
         <img src="../images/kdownload.png" alt="" />
-        <span>Android-China User</span>
+        <span>{{ t('layout.androidChinaUser') }}</span>
       </div>
     </div>
   </el-dialog>

+ 14 - 11
src/views/Layout/src/components/Header/components/KAMMapping.vue

@@ -1,4 +1,7 @@
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
 import { useUserStore } from '@/stores/modules/user'
 
 const userStore = useUserStore()
@@ -71,31 +74,31 @@ const processCustomerSwitch = async (targetUname: string, isMappingMode: boolean
       window.location.reload()
     } else if (res.code === 4001) {
       ElMessageBox.alert(
-        "This account's password has expired and is currently unavailable. Please select a different customer account to continue.",
+        t('layout.passwordExpiredUnavailable'),
         {
-          confirmButtonText: 'OK',
+          confirmButtonText: t('common.ok'),
           confirmButtonClass: 'el-button--dark'
         }
       )
     } else if (res.code === 4002) {
       ElMessageBox.alert(
-        'This account has not been activated yet. Please select a different customer account to continue.',
+        t('layout.accountNotActivated'),
         {
-          confirmButtonText: 'OK',
+          confirmButtonText: t('common.ok'),
           confirmButtonClass: 'el-button--dark'
         }
       )
     } else if (res.code === 4003) {
       ElMessageBox.alert(
-        'This account has been disabled and is no longer accessible. Please select a different customer account to continue.',
+        t('layout.accountDisabled'),
         {
-          confirmButtonText: 'OK',
+          confirmButtonText: t('common.ok'),
           confirmButtonClass: 'el-button--dark'
         }
       )
     }
   } catch (err) {
-    ElMessage.error('操作失败,请稍后重试') // 给用户一个友好的提示
+    ElMessage.error(t('layout.operationFailedRetry')) // 给用户一个友好的提示
   } finally {
     loadingVisible.value = false
   }
@@ -147,7 +150,7 @@ watch(
     :style="{ 'justify-content': !isShowMapping ? 'center' : 'space-between' }"
   >
     <span style="margin-right: 5px; color: var(--color-neutral-3)" v-if="!isShowMapping"
-      >View as Customer</span
+      >{{ t('layout.viewAsCustomer') }}</span
     >
 
     <el-popover
@@ -165,7 +168,7 @@ watch(
         <div class="select-customer">
           <span
             :style="{ color: customer ? 'var(--color-neutral-1)' : 'var(--color-neutral-3)' }"
-            >{{ customer || 'Select customer' }}</span
+            >{{ customer || t('layout.selectCustomer') }}</span
           >
           <span class="font_family icon-icon_dropdown_b"></span></div
       ></template>
@@ -174,7 +177,7 @@ watch(
           v-model="customerSearchQuery"
           @input="filterCustomer"
           class="customer-input"
-          placeholder="Search customer"
+          :placeholder="t('common.search')"
         >
           <template #suffix>
             <span class="font_family icon-icon_search_b"></span>
@@ -189,7 +192,7 @@ watch(
           >
             {{ cust.kam_customers_name }}
           </div>
-          <div class="empty-content" v-if="showCustomerList.length === 0">no data</div>
+          <div class="empty-content" v-if="showCustomerList.length === 0">{{ t('layout.noData') }}</div>
         </div>
       </div>
     </el-popover>

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor