瀏覽代碼

feat: 完善多语言

Jack Zhou 1 周之前
父節點
當前提交
7aca8e2c6e
共有 20 個文件被更改,包括 246 次插入69 次删除
  1. 8 11
      src/App.vue
  2. 22 3
      src/api/module/multilingual.ts
  3. 41 13
      src/components/CustomizeColumns/src/CustomizeColumns.vue
  4. 43 11
      src/locales/en.json
  5. 83 0
      src/locales/index.ts
  6. 0 3
      src/views/Booking/src/components/BookingTable/src/BookingTable.vue
  7. 5 2
      src/views/DestinationDelivery/src/components/CalendarTagDetailDialog.vue
  8. 1 1
      src/views/DestinationDelivery/src/components/ConfiguRations/src/components/ConfigurationsTable.vue
  9. 1 1
      src/views/DestinationDelivery/src/components/CreateNewBooking/src/components/NewbookingTable.vue
  10. 1 1
      src/views/DestinationDelivery/src/components/TableView/src/TableView.vue
  11. 18 1
      src/views/Layout/src/components/Header/HeaderView.vue
  12. 11 11
      src/views/Layout/src/components/Menu/MenuView.vue
  13. 2 1
      src/views/Report/src/components/ReportDetail/src/ReportDetail.vue
  14. 1 1
      src/views/Report/src/components/ReportDetail/src/components/FieldsTable.vue
  15. 1 1
      src/views/Report/src/components/ReportSchedule/src/components/FieldsTable.vue
  16. 1 1
      src/views/Tracking/src/components/PublicTracking/src/components/MilestonesTable.vue
  17. 3 3
      src/views/Tracking/src/components/TrackingDetail/src/components/AttachmentView.vue
  18. 1 1
      src/views/Tracking/src/components/TrackingDetail/src/components/MilestonesTable.vue
  19. 2 2
      src/views/Tracking/src/components/TrackingTable/src/TrackingTable.vue
  20. 1 1
      src/views/Tracking/src/components/TrackingTable/src/components/VGMView.vue

+ 8 - 11
src/App.vue

@@ -4,6 +4,7 @@ import { useLangStore } from '@/stores/modules/lang'
 import { useI18n } from 'vue-i18n'
 import { useRoute } from 'vue-router'
 import VideoView from '@/views/Video/src/VideoView.vue'
+import { resolveLocaleByLangLabel, switchAppLocale } from '@/locales'
 
 const route = useRoute()
 
@@ -16,19 +17,15 @@ const langValue = ref()
 const { locale } = useI18n() // 解构出 locale
 
 langValue.value = localStorage.getItem('lang') || 'English'
-const languageChangetest = (label: string) => {
-  if (label === 'Chinese') {
-    // current.$i18n.locale = 'zh_TW'
-    locale.value = 'en_US' // 修改 locale 的值
-    langStore.setLang('English')
-  } else {
-    // current.$i18n.locale = 'en_US'
-    locale.value = 'en_US'
-    langStore.setLang('English')
-  }
+const languageChangetest = async (label: string) => {
+  const targetLocale = resolveLocaleByLangLabel(label)
+  await switchAppLocale(targetLocale)
+  locale.value = targetLocale
 }
 
-languageChangetest(langValue.value)
+onMounted(() => {
+  languageChangetest(langValue.value)
+})
 </script>
 
 <template>

+ 22 - 3
src/api/module/multilingual.ts

@@ -3,6 +3,15 @@ import HttpAxios from '@/utils/axios'
 const base = import.meta.env.VITE_API_HOST
 const baseUrl = `${base}/main_new_version.php`
 
+type MultilingualLangKey =
+  | 'english'
+  | 'simplifiedChinese'
+  | 'traditionalChinese'
+  | 'french'
+  | 'spanish'
+  | 'portuguese'
+  | string
+
 /**
  * save Multilingual Config
  */
@@ -51,15 +60,25 @@ export const getMultilingualPageInfo = (params: any, config: any) => {
 /**
  * get Multilingual json file
  */
-export const getMultilingualJsonFile = (params: any, config: any) => {
+export const getMultilingualJsonFile = (
+  params: {
+    langkey?: MultilingualLangKey
+    langKey?: MultilingualLangKey
+    [key: string]: any
+  },
+  config: any
+) => {
+  const { langKey, langkey, ...restParams } = params || {}
+  const targetLangKey = langkey || langKey
+
   return HttpAxios.post(
     `${baseUrl}`,
     {
       action: 'multilingual',
       operate: 'load_all_pages_by_lang',
-      ...params
+      ...restParams,
+      langkey: targetLangKey
     },
     config
   )
-
 }

+ 41 - 13
src/components/CustomizeColumns/src/CustomizeColumns.vue

@@ -362,7 +362,7 @@ defineExpose({
           <el-option
             v-for="item in searchOptions"
             :key="item.field"
-            :label="item.label"
+            :label="t(item.cleaned_field_name)"
             :value="item.field"
           />
         </el-select>
@@ -379,7 +379,7 @@ defineExpose({
             <el-tab-pane
               v-for="groupItem in groupColumns"
               :key="groupItem.name"
-              :label="groupItem.name"
+              :label="t(groupItem.cleaned_field_name)"
               :name="groupItem.name"
             >
               <VueDraggable
@@ -400,7 +400,7 @@ defineExpose({
                     @mouseleave="hoverAllIcon = ''"
                   >
                     <span class="font_family icon-icon_dragsort__b draggable-icon"></span>
-                    <span class="title">{{ item.label }}</span>
+                    <span class="title">{{ t(item.cleaned_field_name) }}</span>
                     <span
                       ref="step1"
                       v-if="hoverAllIcon === item.field || (index === 0 && isShowStep1)"
@@ -416,7 +416,13 @@ defineExpose({
       </div>
       <div class="right-select-columns">
         <div class="title">
-          {{ t('customizeColumns.selectedColumnsOnList', { type: route.path.includes('booking') ? t('customizeColumns.booking') : t('customizeColumns.shipment') }) }}
+          {{
+            t('customizeColumns.selectedColumnsOnList', {
+              type: route.path.includes('booking')
+                ? t('customizeColumns.booking')
+                : t('customizeColumns.shipment')
+            })
+          }}
         </div>
         <VueDraggable
           v-vloading="loading"
@@ -439,7 +445,7 @@ defineExpose({
                 class="font_family icon-icon_dragsort__b draggable-icon"
                 style="font-size: 16px"
               ></span>
-              <span class="title">{{ item.label }}</span>
+              <span class="title">{{ t(item.cleaned_field_name) }}</span>
               <span
                 v-if="hoverSelectIcon === item.field || (index === 0 && isShowStep2)"
                 class="font_family icon-icon_moveup_b move-icon"
@@ -468,9 +474,9 @@ defineExpose({
         @click="dialogVisible = false"
         >{{ t('common.cancel') }}</el-button
       >
-      <el-button type="default" style="height: 40px; padding: 8px 20px" @click="handleReset"
-        >{{ t('customizeColumns.resetToDefault') }}</el-button
-      >
+      <el-button type="default" style="height: 40px; padding: 8px 20px" @click="handleReset">{{
+        t('customizeColumns.resetToDefault')
+      }}</el-button>
       <el-button
         class="el-button--dark"
         style="height: 40px; padding: 8px 40px"
@@ -490,9 +496,17 @@ defineExpose({
       <el-tour-step :show-close="false" :target="step1?.[0]">
         <template #default>
           <div class="description">
-            {{ t('customizeColumns.tourStep1', { type: route.path.includes('booking') ? t('customizeColumns.booking') : t('customizeColumns.shipment') }) }}
+            {{
+              t('customizeColumns.tourStep1', {
+                type: route.path.includes('booking')
+                  ? t('customizeColumns.booking')
+                  : t('customizeColumns.shipment')
+              })
+            }}
+          </div>
+          <div class="got-it-text" @click="handleCloseTour('step1')">
+            {{ t('customizeColumns.gotIt') }}
           </div>
-          <div class="got-it-text" @click="handleCloseTour('step1')">{{ t('customizeColumns.gotIt') }}</div>
         </template>
       </el-tour-step>
     </el-tour>
@@ -507,12 +521,26 @@ defineExpose({
       <el-tour-step :show-close="false" :target="step2?.[0]">
         <template #default>
           <div class="description">
-            {{ t('customizeColumns.tourStep2Line1', { type: route.path.includes('booking') ? t('customizeColumns.booking') : t('customizeColumns.shipment') }) }}
+            {{
+              t('customizeColumns.tourStep2Line1', {
+                type: route.path.includes('booking')
+                  ? t('customizeColumns.booking')
+                  : t('customizeColumns.shipment')
+              })
+            }}
           </div>
           <div class="description">
-            {{ t('customizeColumns.tourStep2Line2', { type: route.path.includes('booking') ? t('customizeColumns.booking') : t('customizeColumns.shipment') }) }}
+            {{
+              t('customizeColumns.tourStep2Line2', {
+                type: route.path.includes('booking')
+                  ? t('customizeColumns.booking')
+                  : t('customizeColumns.shipment')
+              })
+            }}
+          </div>
+          <div class="got-it-text" @click="handleCloseTour('step2')">
+            {{ t('customizeColumns.gotIt') }}
           </div>
-          <div class="got-it-text" @click="handleCloseTour('step2')">{{ t('customizeColumns.gotIt') }}</div>
         </template>
       </el-tour-step>
     </el-tour>

+ 43 - 11
src/locales/en.json

@@ -204,15 +204,12 @@
     "pleaseEnterEmailContent": "Please enter the email content",
     "emailSentSuccessfully": "Email sent successfully",
     "failedToSendEmail": "Failed to send email",
-
     "hblNo": "HBL No.",
     "carrierBookingNo": "Carrier Booking No.",
     "vessel": "Vessel",
     "voyage": "Voyage",
     "lastUpdatedUser": "Last Updated User",
     "lastUpdatedTime": "Last Updated Time",
-
-
     "textSearch": "text search",
     "created": "Created",
     "cargoReceived": "Cargo Received",
@@ -310,11 +307,6 @@
     "tdAmsIsf": "AMS/ISF",
     "tdAddReference": "Add Reference",
 
-
-
-
-
-
     "packages": "Packages",
     "quantity": "Quantity",
     "cbm": "CBM",
@@ -373,7 +365,30 @@
     "shipment_type": "Shipment Type",
     "port_of_transhipment": "Port of Transhipment",
     "container_size": "Container Size",
-    "last_mile_delivery_date": "Last Mile Delivery Date"
+    "last_mile_delivery_date": "Last Mile Delivery Date",
+
+
+
+    "date_time": "Date Time",
+    "code": "Code",
+    "name": "Name",
+    "sn": "SN",
+    "vgm_weight": "VGM Weight",
+    "vgm_unit": "VGM Unit",
+    "vgm_time": "VGM Time",
+    "vgm_method": "VGM Method",
+    "file_type": "File Type",
+    "file": "File",
+    "locations": "Locations",
+    "remarks": "Remarks",
+    "all": "All",
+    "reference_no": "Reference No",
+    "general": "General",
+    "time": "Time",
+    "places": "Places",
+    "transportation": "Transportation",
+    "others": "Others"
+
   },
   "systemMessage": {
     "title": "System Message",
@@ -710,7 +725,13 @@
     "last_mile_delivery_time": "Last Mile Delivery Time",
     "last_mile_delivery_city": "Last Mile Delivery City",
     "last_mile_delivery_address": "Last Mile Delivery Address",
-    "carrier_teu": "CARRIER TEU"
+    "carrier_teu": "CARRIER TEU",
+
+
+
+
+
+    "invoice_no": "Invoice No."
   },
  
   "notificationRules": {
@@ -1116,7 +1137,18 @@
     "cbm": "CBM",
     "sch_b": "SCH_B",
     "unit": "Unit",
-    "po": "PO#"
+    "po": "PO#",
+
+
+
+    "all": "All",
+    "reference_no": "Reference No",
+    "general": "General",
+    "parties": "Parties",
+    "time": "Time",
+    "places": "Places",
+    "transportation": "Transportation",
+    "others": "Others"
   },
   "destinationDelivery": {
     "title": "Destination Delivery",

+ 83 - 0
src/locales/index.ts

@@ -22,4 +22,87 @@ const i18n = createI18n({
   messages // 使用数据源
 })
 
+const localeToApiLangKey: Record<string, string> = {
+  en_US: 'english',
+  zh_CN: 'simplifiedChinese',
+  zh_TW: 'traditionalChinese',
+  fr_FR: 'french',
+  es_ES: 'spanish',
+  pt_BR: 'portuguese'
+}
+
+const langLabelToLocale: Record<string, string> = {
+  English: 'en_US',
+  Chinese: 'zh_TW',
+  'Simplified Chinese': 'zh_CN',
+  'Traditional Chinese': 'zh_TW',
+  French: 'fr_FR',
+  Spanish: 'es_ES',
+  Portuguese: 'pt_BR'
+}
+
+const loadedLocaleSet = new Set<string>()
+
+const normalizeApiMessages = (rawData: any): Record<string, any> => {
+  if (!rawData) return {}
+
+  // shape 1: { login: { username: '...' } }
+  if (!Array.isArray(rawData) && typeof rawData === 'object') {
+    const objectValues = Object.values(rawData)
+    if (objectValues.length > 0 && typeof objectValues[0] === 'object' && !Array.isArray(objectValues[0])) {
+      return rawData as Record<string, any>
+    }
+  }
+
+  // shape 2: [{ page: 'login', data: [{ key: 'username', english: '...', french: '...' }] }]
+  if (Array.isArray(rawData)) {
+    const result: Record<string, any> = {}
+    rawData.forEach((pageItem: any) => {
+      if (!pageItem?.page || !Array.isArray(pageItem?.data)) return
+      result[pageItem.page] = {}
+      pageItem.data.forEach((row: any) => {
+        if (!row?.key) return
+        // 优先拿后端已经按语言过滤后的通用 value,否则回退 english
+        result[pageItem.page][row.key] = row.value ?? row.english ?? row.originEnglish ?? ''
+      })
+    })
+    return result
+  }
+
+  return {}
+}
+
+export const resolveLocaleByLangLabel = (langLabel?: string) => {
+  if (!langLabel) return 'en_US'
+  return langLabelToLocale[langLabel] || 'en_US'
+}
+
+export const loadLocaleMessages = async (locale: string) => {
+  if (loadedLocaleSet.has(locale)) return
+
+  const langkey = localeToApiLangKey[locale]
+  if (!langkey) return
+
+  try {
+    // Lazy import avoids circular-init issues while still using API module method.
+    const { getMultilingualJsonFile } = await import('@/api/module/multilingual')
+    const res = await getMultilingualJsonFile({ langkey }, {})
+    const normalized = normalizeApiMessages(res?.data || res)
+    i18n.global.setLocaleMessage(locale, {
+      ...en,
+      ...normalized,
+      ...enUS
+    })
+    loadedLocaleSet.add(locale)
+  } catch (error) {
+    // keep fallback English messages when remote file fails
+    console.error('loadLocaleMessages failed:', error)
+  }
+}
+
+export const switchAppLocale = async (locale: string) => {
+  await loadLocaleMessages(locale)
+  i18n.global.locale.value = locale
+}
+
 export default i18n

+ 0 - 3
src/views/Booking/src/components/BookingTable/src/BookingTable.vue

@@ -263,16 +263,13 @@ const bookingTable = ref<VxeGridProps<any>>({
               return Number(aValue) - Number(bValue)
             }
           }
-
           const result = compareResult(aValue, bValue)
           if (result !== 0) {
             return order === 'asc' ? result : -result
           }
         }
-
         return 0 // 如果所有字段都相等
       })
-
       return sortedData
     }
   },

+ 5 - 2
src/views/DestinationDelivery/src/components/CalendarTagDetailDialog.vue

@@ -58,7 +58,7 @@ onMounted(() => {
 const handleColumns = (columns: any) => {
   const newColumns = columns.map((item: any) => {
     let curColumn: any = {
-      title: item.title,
+      title: t(item.cleaned_field_name),
       field: item.field,
       width: '150px'
     }
@@ -184,7 +184,10 @@ defineExpose({
               class="font_family icon-icon_delay_b1"
               style="margin-right: 3px; font-size: 13px"
             ></span>
-            <span class="type">{{ pageData?.endingNumber }} {{ t('destinationDelivery.freeStoragePeriodEnds') }}</span>
+            <span class="type"
+              >{{ pageData?.endingNumber }}
+              {{ t('destinationDelivery.freeStoragePeriodEnds') }}</span
+            >
           </div>
         </div>
       </template>

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

@@ -47,7 +47,7 @@ const pageInfo = ref({ pageNo: 1, pageSize: 15, total: 0 })
 const handleColumns = (columns: any) => {
   const newColumns = columns.map((item: any) => {
     let curColumn: any = {
-      title: item.title,
+      title: t(item.cleaned_field_name),
       field: item.field,
       width: item.width
     }

+ 1 - 1
src/views/DestinationDelivery/src/components/CreateNewBooking/src/components/NewbookingTable.vue

@@ -41,7 +41,7 @@ const tableRef = ref<VxeGridInstance | null>(null)
 const handleColumns = (columns: any) => {
   const newColumns = columns.map((item: any) => {
     let curColumn: any = {
-      title: item.title,
+      title: t(item.cleaned_field_name),
       field: item.field,
       width: '150px'
     }

+ 1 - 1
src/views/DestinationDelivery/src/components/TableView/src/TableView.vue

@@ -32,7 +32,7 @@ const tableOriginColumnsField = ref()
 const handleColumns = (columns: any, status?: string) => {
   const newColumns = columns.map((item: any) => {
     let curColumn: any = {
-      title: item.title,
+      title: t(item.cleaned_field_name),
       field: item.field,
       sortable: true,
       minWidth: 120

+ 18 - 1
src/views/Layout/src/components/Header/HeaderView.vue

@@ -15,6 +15,7 @@ import NotificationDrawer from './components/NotificationDrawer.vue'
 import TrainingCard from './components/TrainingCard.vue'
 import KAMMapping from './components/KAMMapping.vue'
 import emitter from '@/utils/bus'
+import { resolveLocaleByLangLabel, switchAppLocale } from '@/locales'
 
 const notificationMsgStore = useNotificationMessage()
 import { useGuideStore } from '@/stores/modules/guide'
@@ -208,6 +209,16 @@ const handleDemoVideo = () => {
   const { href } = router.resolve({ name: 'Demo Video' })
   window.open(href, '_blank')
 }
+
+const langValue = ref()
+const { locale } = useI18n() // 解构出 locale
+
+langValue.value = localStorage.getItem('lang') || 'English'
+const languageChangetest = async (label: string) => {
+  const targetLocale = resolveLocaleByLangLabel(label)
+  await switchAppLocale(targetLocale)
+  locale.value = targetLocale
+}
 </script>
 
 <template>
@@ -326,7 +337,13 @@ const handleDemoVideo = () => {
           </el-tooltip>
         </template>
       </el-popover>
-
+      <el-button
+        style="height: 40px; width: 40px; margin-left: 0"
+        class="el-button--text"
+        @click="languageChangetest('English')"
+      >
+        <span class="font_family icon-icon_language_b" style="font-size: 16px"></span>
+      </el-button>
       <el-popover
         placement="bottom-end"
         :width="256"

+ 11 - 11
src/views/Layout/src/components/Menu/MenuView.vue

@@ -41,12 +41,12 @@ const getMenuList = () => {
       children: [
         {
           index: '2-1',
-          label: 'bookingManagement',
+          label: 'booking_management',
           path: '/booking'
         },
         {
           index: '2-2',
-          label: 'destinationDelivery',
+          label: 'destination_delivery',
           path: '/destination-delivery'
         }
       ]
@@ -65,48 +65,48 @@ const getMenuList = () => {
     },
     {
       index: '5',
-      label: 'systemMessage',
+      label: 'system_management',
       icon: 'icon_system__management_fill_b',
       type: 'list',
       children: [
         {
           index: '5-7',
-          label: 'templateManagement',
+          label: 'template_management',
           path: '/template-management'
         },
         {
           index: '5-1',
-          label: 'systemMessage',
+          label: 'system_message',
           path: '/system-message'
         },
         {
           index: '5-2',
-          label: 'systemSettings',
+          label: 'system_settings',
           path: '/system-settings'
         },
         {
           index: '5-3',
-          label: 'multilingualConfig',
+          label: 'multilingual_config',
           path: '/multilingual-config'
         },
         {
           index: '5-4',
-          label: 'chatLog',
+          label: 'chat_log',
           path: '/chat-log'
         },
         {
           index: '5-5',
-          label: 'aiApiLog',
+          label: 'ai_api_log',
           path: '/ai-api-log'
         },
         {
           index: '5-6',
-          label: 'operationLog',
+          label: 'operation_log',
           path: '/operation-log'
         },
         {
           index: '5-7',
-          label: 'promptConfiguration',
+          label: 'prompt_configuration',
           path: '/prompt-configuration'
         }
       ]

+ 2 - 1
src/views/Report/src/components/ReportDetail/src/ReportDetail.vue

@@ -59,6 +59,7 @@ const handleFilterData = (filterData, name) => {
       ]
     }
     return {
+      cleaned_field_name: item.cleaned_field_name,
       label: item.label,
       field: item.field,
       type: item.data_type,
@@ -147,7 +148,7 @@ const applyNewColumn = () => {
       </div>
       <div class="filter-search" ref="filterRef">
         <div class="filters-input" v-for="item in filterList" :key="item.label">
-          <div class="filters-input-title">{{ item.label }}</div>
+          <div class="filters-input-title">{{ t(item.cleaned_field_name) }}</div>
           <el-input
             v-if="item.type === 'string'"
             :placeholder="t('report.pleaseEnter')"

+ 1 - 1
src/views/Report/src/components/ReportDetail/src/components/FieldsTable.vue

@@ -77,7 +77,7 @@ const reportName = ref('')
 const handleColumns = (columns: any) => {
   const newColumns = columns.map((item: any) => {
     let curColumn: any = {
-      title: item.title,
+      title: t(item.cleaned_field_name),
       field: item.field,
       width: item.width
     }

+ 1 - 1
src/views/Report/src/components/ReportSchedule/src/components/FieldsTable.vue

@@ -61,7 +61,7 @@ const pageInfo = ref({ pageNo: 1, pageSize: 50, total: 0 })
 const handleColumns = (columns: any) => {
   const newColumns = columns.map((item: any) => {
     let curColumn: any = {
-      title: item.title,
+      title: t(item.cleaned_field_name),
       field: item.field,
       width: item.width
     }

+ 1 - 1
src/views/Tracking/src/components/PublicTracking/src/components/MilestonesTable.vue

@@ -40,7 +40,7 @@ const tableData = ref<VxeGridProps<any>>({
 const handleColumns = (columns: any) => {
   const newColumns = columns.map((item: any) => {
     let curColumn: any = {
-      title: item.title,
+      title: t(item.cleaned_field_name),
       field: item.field,
       minWidth: 30
     }

+ 3 - 3
src/views/Tracking/src/components/TrackingDetail/src/components/AttachmentView.vue

@@ -38,7 +38,7 @@ const tableRef = ref<VxeGridInstance | null>(null)
 const handleColumns = (columns: any) => {
   const newColumns = columns.map((item: any) => {
     let curColumn: any = {
-      title: item.title,
+      title: t(item.cleaned_field_name),
       field: item.field,
       minWidth: 30
     }
@@ -117,7 +117,7 @@ const updateData = () => {
   <div class="attachment">
     <el-button @click="openUploadFilesDialog" class="el-button--text title">
       <span class="font_family icon-icon_upload_b"></span>
-          <span>{{ t('tracking.uploadFiles') }}</span>
+      <span>{{ t('tracking.uploadFiles') }}</span>
     </el-button>
     <vxe-grid class="radius-bottom" ref="tableRef" v-bind="tableData">
       <template #action="{ row }">
@@ -137,7 +137,7 @@ const updateData = () => {
         </div>
       </template>
       <template #empty>
-          <div class="empty">{{ t('common.noData') }}</div>
+        <div class="empty">{{ t('common.noData') }}</div>
       </template>
     </vxe-grid>
     <UploadFilesDialog

+ 1 - 1
src/views/Tracking/src/components/TrackingDetail/src/components/MilestonesTable.vue

@@ -40,7 +40,7 @@ const tableRef = ref<VxeGridInstance | null>(null)
 const handleColumns = (columns: any) => {
   const newColumns = columns.map((item: any) => {
     let curColumn: any = {
-      title: item.title,
+      title: t(item.cleaned_field_name),
       field: item.field
     }
 

+ 2 - 2
src/views/Tracking/src/components/TrackingTable/src/TrackingTable.vue

@@ -586,7 +586,7 @@ defineExpose({
     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-popover
           trigger="click"
@@ -628,7 +628,7 @@ defineExpose({
 
         <el-button style="padding-left: 10px" 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>

+ 1 - 1
src/views/Tracking/src/components/TrackingTable/src/components/VGMView.vue

@@ -93,7 +93,7 @@ const tableData = ref<VxeGridProps<any>>({
 const handleColumns = (columns: any) => {
   const newColumns = columns.map((item: any) => {
     let curColumn: any = {
-      title: item.title,
+      title: t(item.cleaned_field_name),
       field: item.field,
       minWidth: 30
     }