815 lines
85 KiB
JavaScript
815 lines
85 KiB
JavaScript
(() => {
|
||
"use strict";
|
||
|
||
const STORAGE_KEY = "arr:locale:v1";
|
||
const COOKIE_KEY = "arr_locale";
|
||
const DEFAULT_LOCALE = "zh-CN";
|
||
const LOCALES = {
|
||
"zh-CN": { label: "中文", intl: "zh-CN-u-ca-gregory-nu-latn" },
|
||
en: { label: "English", intl: "en-GB-u-ca-gregory-nu-latn" },
|
||
th: { label: "ไทย", intl: "th-TH-u-ca-gregory-nu-latn" },
|
||
};
|
||
|
||
// The first value is the current product copy. Keeping the source copy here
|
||
// gives the legacy static markup a safe Chinese fallback while all new
|
||
// dynamic strings use the semantic key.
|
||
const CATALOG = {
|
||
"common.language": ["语言", "Language", "ภาษา"],
|
||
"common.skip_main": ["跳到主要内容", "Skip to main content", "ข้ามไปยังเนื้อหาหลัก"],
|
||
"common.skip_login": ["跳到登录表单", "Skip to login form", "ข้ามไปยังแบบฟอร์มเข้าสู่ระบบ"],
|
||
"common.cancel": ["取消", "Cancel", "ยกเลิก"],
|
||
"common.close": ["关闭", "Close", "ปิด"],
|
||
"common.confirm": ["确认", "Confirm", "ยืนยัน"],
|
||
"common.save": ["保存", "Save", "บันทึก"],
|
||
"common.delete": ["删除", "Delete", "ลบ"],
|
||
"common.select": ["选择", "Select", "เลือก"],
|
||
"common.view": ["查看", "View", "ดู"],
|
||
"common.download": ["下载", "Download", "ดาวน์โหลด"],
|
||
"common.refresh": ["刷新", "Refresh", "รีเฟรช"],
|
||
"common.previous": ["上一页", "Previous", "ก่อนหน้า"],
|
||
"common.next": ["下一页", "Next", "ถัดไป"],
|
||
"common.status": ["状态", "Status", "สถานะ"],
|
||
"common.company": ["公司", "Company", "บริษัท"],
|
||
"common.room_type": ["房型", "Room type", "ประเภทห้อง"],
|
||
"common.quantity": ["数量", "Quantity", "จำนวน"],
|
||
"common.operation": ["操作", "Actions", "การดำเนินการ"],
|
||
"common.total": ["合计", "Total", "รวม"],
|
||
"common.details": ["详情", "Details", "รายละเอียด"],
|
||
"common.file": ["文件", "File", "ไฟล์"],
|
||
"common.submitted_at": ["提交时间", "Submitted", "เวลาส่ง"],
|
||
"common.submitted_at_bangkok": ["提交时间(曼谷)", "Submitted (Bangkok)", "เวลาส่ง (กรุงเทพฯ)"],
|
||
"common.generated_at": ["生成时间", "Generated", "เวลาสร้าง"],
|
||
"common.none": ["暂无", "None", "ไม่มีข้อมูล"],
|
||
"common.unknown": ["未知", "Unknown", "ไม่ทราบ"],
|
||
"common.not_selected": ["未选择", "Not selected", "ยังไม่ได้เลือก"],
|
||
"common.pending": ["待生成", "Pending", "รอสร้าง"],
|
||
"common.no_result": ["无结果", "No result", "ไม่มีผลลัพธ์"],
|
||
"common.not_recorded": ["未记录", "Not recorded", "ไม่ได้บันทึก"],
|
||
"common.not_available": ["未完成", "Not completed", "ยังไม่เสร็จ"],
|
||
"common.year_month_unselected": ["未选择", "Not selected", "ยังไม่ได้เลือก"],
|
||
"common.loading": ["正在读取…", "Loading…", "กำลังโหลด…"],
|
||
"common.reading": ["正在读取", "Reading", "กำลังอ่าน"],
|
||
"common.retrying": ["重试中", "Retrying", "กำลังลองอีกครั้ง"],
|
||
"common.choose_file": ["选择文件", "Choose file", "เลือกไฟล์"],
|
||
"common.download_short": ["下载", "Download", "ดาวน์โหลด"],
|
||
"common.select_month": ["月份", "Month", "เดือน"],
|
||
"history.business_month": ["业务月份", "Business month", "เดือนธุรกิจ"],
|
||
"history.view_month": ["查看月份", "View month", "เดือนที่ดู"],
|
||
"history.generation_month": ["生成月份", "Generation month", "เดือนที่สร้าง"],
|
||
"history.generation_month_title": ["选择 C/O 所属的生成月份", "Choose the generation month for C/O", "เลือกเดือนที่สร้างสำหรับ C/O"],
|
||
"history.previous_month": ["上一个月", "Previous month", "เดือนก่อนหน้า"],
|
||
"history.next_month": ["下一个月", "Next month", "เดือนถัดไป"],
|
||
"history.current_month": ["本月", "This month", "เดือนนี้"],
|
||
"common.locale_zh": ["中文", "Chinese", "ภาษาจีน"],
|
||
"nav.primary": ["主要模块", "Main modules", "โมดูลหลัก"],
|
||
"nav.daily": ["日报处理", "Daily processing", "ประมวลผลรายวัน"],
|
||
"nav.monthly": ["月报", "Monthly report", "รายงานประจำเดือน"],
|
||
"nav.bi": ["渠道BI", "Channel BI", "Channel BI"],
|
||
"nav.company_detail": ["公司渠道明细", "Company channel detail", "รายละเอียดช่องทางบริษัท"],
|
||
"brand.home": ["ARR Report 首页", "ARR Report home", "หน้าหลัก ARR Report"],
|
||
"status.connecting_service": ["正在连接数据服务", "Connecting to data service", "กำลังเชื่อมต่อบริการข้อมูล"],
|
||
"status.service_ready": ["服务正常", "Service ready", "บริการพร้อมใช้งาน"],
|
||
"status.service_disconnected": ["数据服务未连接", "Data service unavailable", "บริการข้อมูลไม่พร้อมใช้งาน"],
|
||
"status.connecting": ["正在连接", "Connecting", "กำลังเชื่อมต่อ"],
|
||
"status.received": ["已接收", "Received", "รับแล้ว"],
|
||
"status.uploaded": ["已上传", "Uploaded", "อัปโหลดแล้ว"],
|
||
"status.waiting_processing": ["等待处理", "Waiting for processing", "รอประมวลผล"],
|
||
"status.processing": ["处理中", "Processing", "กำลังประมวลผล"],
|
||
"status.waiting_validation": ["等待验收", "Waiting for validation", "รอตรวจสอบผล"],
|
||
"status.validating": ["正在验收", "Validating", "กำลังตรวจสอบผล"],
|
||
"status.completed": ["已完成", "Completed", "เสร็จแล้ว"],
|
||
"status.validation_failed": ["验收失败", "Validation failed", "ตรวจสอบผลไม่ผ่าน"],
|
||
"status.failed": ["处理失败", "Processing failed", "ประมวลผลไม่สำเร็จ"],
|
||
"status.cancelled": ["已取消", "Cancelled", "ยกเลิกแล้ว"],
|
||
"status.generating": ["生成中", "Generating", "กำลังสร้าง"],
|
||
"status.validated": ["已验收", "Validated", "ตรวจสอบแล้ว"],
|
||
"status.current_version": ["当前版本", "Current version", "เวอร์ชันปัจจุบัน"],
|
||
"status.history_version": ["历史版本", "Historical version", "เวอร์ชันประวัติ"],
|
||
"status.generation_failed": ["生成失败", "Generation failed", "สร้างไม่สำเร็จ"],
|
||
"auth.logout": ["退出登录", "Log out", "ออกจากระบบ"],
|
||
"auth.logout_short": ["退出", "Log out", "ออก"],
|
||
"auth.logout_failed": ["退出登录失败,请重试", "Could not log out. Please try again.", "ออกจากระบบไม่สำเร็จ โปรดลองอีกครั้ง"],
|
||
"auth.session_expired": ["登录状态已失效", "Your session has expired.", "เซสชันหมดอายุแล้ว"],
|
||
"auth.session_init_failed": ["页面会话初始化失败", "Could not initialize the page session.", "เริ่มเซสชันของหน้าไม่สำเร็จ"],
|
||
"auth.username": ["username", "username", "ชื่อผู้ใช้"],
|
||
"auth.password": ["password", "password", "รหัสผ่าน"],
|
||
"auth.show": ["显示", "Show", "แสดง"],
|
||
"auth.hide": ["隐藏", "Hide", "ซ่อน"],
|
||
"auth.show_password": ["显示密码", "Show password", "แสดงรหัสผ่าน"],
|
||
"auth.hide_password": ["隐藏密码", "Hide password", "ซ่อนรหัสผ่าน"],
|
||
"auth.submit": ["登录 ARR Report", "Log in to ARR Report", "เข้าสู่ระบบ ARR Report"],
|
||
"auth.validating": ["正在验证…", "Verifying…", "กำลังตรวจสอบ…"],
|
||
"auth.unavailable": ["登录暂时不可用,请稍后重试", "Login is temporarily unavailable. Please try again later.", "การเข้าสู่ระบบไม่พร้อมใช้งานชั่วคราว โปรดลองอีกครั้งภายหลัง"],
|
||
"auth.required_username": ["请输入用户名", "Enter your username.", "กรุณากรอกชื่อผู้ใช้"],
|
||
"auth.required_password": ["请输入密码", "Enter your password.", "กรุณากรอกรหัสผ่าน"],
|
||
"time.business": ["页面日期时间统一按泰国曼谷(UTC+7)显示。", "All page dates and times use Bangkok time (UTC+7).", "วันที่และเวลาทั้งหมดใช้เวลาที่กรุงเทพฯ (UTC+7)"],
|
||
"time.business_short": ["业务时间统一按泰国曼谷(UTC+7)显示", "Business time uses Bangkok time (UTC+7)", "เวลาธุรกิจใช้เวลาที่กรุงเทพฯ (UTC+7)"],
|
||
"upload.xml.title": ["上传 ARR.XML", "Upload ARR.XML", "อัปโหลด ARR.XML"],
|
||
"upload.xml.drop": ["拖放 XML 文件到这里", "Drop an XML file here", "ลากไฟล์ XML มาที่นี่"],
|
||
"upload.xml.choose": ["或点击选择文件,单个文件最大 25 MB", "or click to choose a file; maximum 25 MB", "หรือคลิกเพื่อเลือกไฟล์ ขนาดไม่เกิน 25 MB"],
|
||
"upload.file_not_selected": ["尚未选择文件", "No file selected", "ยังไม่ได้เลือกไฟล์"],
|
||
"upload.start": ["开始处理", "Start processing", "เริ่มประมวลผล"],
|
||
"upload.progress": ["ARR.XML 处理进度", "ARR.XML processing progress", "ความคืบหน้าการประมวลผล ARR.XML"],
|
||
"upload.waiting": ["等待开始", "Waiting to start", "รอเริ่ม"],
|
||
"upload.uploading": ["正在上传 ARR.XML", "Uploading ARR.XML", "กำลังอัปโหลด ARR.XML"],
|
||
"upload.processing": ["正在处理ARR.XML文件", "Processing ARR.XML", "กำลังประมวลผล ARR.XML"],
|
||
"upload.processing_detail": ["正在安全上传、运行固定处理器、独立验收并提交数据库。", "Uploading securely, running the fixed processor, validating independently and committing to the database.", "กำลังอัปโหลดอย่างปลอดภัย เรียกใช้ตัวประมวลผล ตรวจสอบผล และบันทึกลงฐานข้อมูล"],
|
||
"upload.after_processing": ["处理完成后将立即读取服务端全流程日志…", "The server trace will be read as soon as processing finishes…", "ระบบจะอ่านบันทึกกระบวนการจากเซิร์ฟเวอร์เมื่อเสร็จสิ้น…"],
|
||
"upload.creating_task": ["正在创建任务", "Creating task", "กำลังสร้างงาน"],
|
||
"upload.processing_done": ["处理完成", "Processing complete", "ประมวลผลเสร็จแล้ว"],
|
||
"upload.processing_failed": ["处理失败", "Processing failed", "ประมวลผลไม่สำเร็จ"],
|
||
"upload.success": ["ARR.XML 已处理并完成入库", "ARR.XML was processed and committed.", "ประมวลผลและบันทึก ARR.XML แล้ว"],
|
||
"upload.failure_log": ["ARR.XML 处理失败,请查看任务日志", "ARR.XML processing failed. Check the task log.", "การประมวลผล ARR.XML ไม่สำเร็จ โปรดดูบันทึกงาน"],
|
||
"upload.service_unready": ["文件接收服务尚未完成生产接线。", "The file intake service is not ready for production.", "บริการรับไฟล์ยังไม่พร้อมใช้งานจริง"],
|
||
"upload.stage_processor": ["正在运行固定处理器", "Running the fixed processor", "กำลังเรียกใช้ตัวประมวลผล"],
|
||
"upload.stage_validate": ["正在独立验收结果", "Independently validating the result", "กำลังตรวจสอบผลอย่างอิสระ"],
|
||
"upload.stage_commit": ["正在提交数据库", "Committing to the database", "กำลังบันทึกลงฐานข้อมูล"],
|
||
"metric.arrival_date": ["最近成功处理日期", "Latest successful processing date", "วันที่ประมวลผลสำเร็จล่าสุด"],
|
||
"metric.duration": ["处理耗时", "Processing time", "เวลาประมวลผล"],
|
||
"metric.duration_from_submit": ["从提交至完成", "From submission to completion", "ตั้งแต่ส่งจนเสร็จ"],
|
||
"metric.rooms": ["保留记录的售出房数", "Sold rooms in retained records", "จำนวนห้องที่ขายในรายการที่เก็บไว้"],
|
||
"daily.history": ["Daily Report", "Daily Report", "Daily Report"],
|
||
"daily.refresh": ["刷新", "Refresh", "รีเฟรช"],
|
||
"daily.download": ["日报", "Daily report", "รายงานรายวัน"],
|
||
"daily.empty": ["本月暂无 Daily Report 记录", "No Daily Report records this month", "เดือนนี้ยังไม่มีรายการ Daily Report"],
|
||
"daily.loading": ["正在读取本月记录…", "Loading this month’s records…", "กำลังโหลดรายการของเดือนนี้…"],
|
||
"daily.unavailable": ["日报记录暂时无法读取", "Daily records are temporarily unavailable", "ไม่สามารถอ่านรายการรายวันได้ชั่วคราว"],
|
||
"daily.no_selected_log": ["请上传文件,或从下方选择一个历史任务。", "Upload a file or select a historical task below.", "อัปโหลดไฟล์หรือเลือกงานเก่าด้านล่าง"],
|
||
"daily.overview": ["日报处理概览", "Daily processing overview", "ภาพรวมการประมวลผลรายวัน"],
|
||
"daily.pagination_label": ["Daily Report 分页", "Daily Report pagination", "การแบ่งหน้า Daily Report"],
|
||
"daily.file": ["文件", "File", "ไฟล์"],
|
||
"daily.submitted_at": ["提交时间", "Submitted", "เวลาส่ง"],
|
||
"daily.output": ["日报", "Daily report", "รายงานรายวัน"],
|
||
"monthly.processing": ["月报处理", "Monthly processing", "ประมวลผลรายงานประจำเดือน"],
|
||
"monthly.auto_update": ["自动更新 · 4 秒", "Auto-refresh · 4 sec", "อัปเดตอัตโนมัติ · 4 วินาที"],
|
||
"monthly.loading": ["正在读取月报记录…", "Loading monthly report records…", "กำลังโหลดรายการรายงานประจำเดือน…"],
|
||
"monthly.empty": ["该月份暂无月报处理记录", "No monthly processing records for this month", "เดือนนี้ยังไม่มีรายการประมวลผลรายเดือน"],
|
||
"monthly.updated_to": ["更新至", "Updated through", "อัปเดตถึง"],
|
||
"monthly.rows": ["数据行", "Data rows", "แถวข้อมูล"],
|
||
"monthly.pagination_label": ["月报记录分页", "Monthly report pagination", "การแบ่งหน้ารายงานประจำเดือน"],
|
||
"monthly.unavailable": ["月报记录暂时无法读取", "Monthly records are temporarily unavailable", "ไม่สามารถอ่านรายการรายเดือนได้ชั่วคราว"],
|
||
"monthly.new": ["新月报已自动加入列表", "A new monthly report was added automatically", "เพิ่มรายงานประจำเดือนใหม่ในรายการแล้ว"],
|
||
"monthly.retry": ["自动更新重试中", "Auto-refresh retrying", "กำลังลองอัปเดตอัตโนมัติอีกครั้ง"],
|
||
"bi.title": ["渠道BI", "Channel BI", "Channel BI"],
|
||
"bi.month": ["查看月份", "Month", "เดือน"],
|
||
"bi.metrics": ["本月核心指标", "This month’s key metrics", "ตัวชี้วัดหลักของเดือนนี้"],
|
||
"bi.rooms": ["售出房数", "Rooms sold", "จำนวนห้องที่ขาย"],
|
||
"bi.revenue": ["销售金额", "Sales amount", "ยอดขาย"],
|
||
"bi.nights": ["间夜", "Room nights", "คืนห้องพัก"],
|
||
"bi.companies": ["公司数", "Companies", "จำนวนบริษัท"],
|
||
"bi.rooms_total": ["NO. OF ROOM 合计", "Total NO. OF ROOM", "รวม NO. OF ROOM"],
|
||
"bi.revenue_total": ["销售金额合计", "Total sales amount", "ยอดขายรวม"],
|
||
"bi.nights_formula": ["房数 × 入住晚数", "Rooms × nights", "ห้อง × จำนวนคืน"],
|
||
"bi.channel_count": ["月报子表数量", "Monthly report worksheet count", "จำนวนชีตรายงานประจำเดือน"],
|
||
"bi.sales_by_company": ["各公司本月销售情况", "Monthly sales by company", "ยอดขายรายเดือนของแต่ละบริษัท"],
|
||
"bi.sales_ranking": ["公司销售金额排名", "Company sales ranking", "อันดับยอดขายบริษัท"],
|
||
"bi.sales_share": ["公司销售金额占比", "Company sales share", "สัดส่วนยอดขายบริษัท"],
|
||
"bi.matrix": ["渠道 × 房型售出房数", "Rooms sold by channel × room type", "จำนวนห้องที่ขายตามช่องทาง × ประเภทห้อง"],
|
||
"bi.channel": ["渠道", "Channel", "ช่องทาง"],
|
||
"bi.empty": ["暂无渠道与房型数据", "No channel or room-type data", "ไม่มีข้อมูลช่องทางหรือประเภทห้อง"],
|
||
"bi.empty_sales": ["暂无公司销售数据", "No company sales data", "ไม่มีข้อมูลยอดขายบริษัท"],
|
||
"bi.loading": ["正在读取看板数据…", "Loading dashboard data…", "กำลังโหลดข้อมูลแดชบอร์ด…"],
|
||
"company.title": ["公司渠道明细", "Company channel detail", "รายละเอียดช่องทางบริษัท"],
|
||
"company.fixed": ["固定生成:LianTai、QBD、DY-AI-Easy-KB、FengRun、HanaTour", "Fixed scope: LianTai, QBD, DY-AI-Easy-KB, FengRun, HanaTour", "ขอบเขตคงที่: LianTai, QBD, DY-AI-Easy-KB, FengRun, HanaTour"],
|
||
"company.refresh_title": ["重新读取服务状态、Excel 来源和生成记录", "Reload service status, Excel source and job history", "โหลดสถานะบริการ แหล่ง Excel และประวัติงานใหม่"],
|
||
"company.refresh_jobs": ["刷新任务", "Refresh jobs", "รีเฟรชงาน"],
|
||
"company.service_unready": ["公司渠道明细服务暂未就绪", "Company channel detail service is not ready", "บริการรายละเอียดช่องทางบริษัทยังไม่พร้อม"],
|
||
"company.service_unready_detail": ["请检查本机数据库和 Excel 生成服务后刷新。", "Check the local database and Excel generation service, then refresh.", "ตรวจสอบฐานข้อมูลและบริการสร้าง Excel ในเครื่อง แล้วรีเฟรช"],
|
||
"company.generate": ["生成报表", "Generate reports", "สร้างรายงาน"],
|
||
"company.report_month": ["报表月份", "Report month", "เดือนรายงาน"],
|
||
"company.month_title": ["选择 C/O 所属报表月份,并筛选下方生成记录", "Choose the report month for C/O and filter the job history below", "เลือกเดือนรายงานของ C/O และกรองประวัติงานด้านล่าง"],
|
||
"company.upload_title": ["上传 Excel 报表", "Upload Excel report", "อัปโหลดรายงาน Excel"],
|
||
"company.choose_excel": ["选择或拖放 Excel 文件", "Choose or drop an Excel file", "เลือกหรือลากไฟล์ Excel"],
|
||
"company.xlsx_limit": [".xlsx 格式,单个文件最大 25 MB", ".xlsx format, maximum 25 MB", "รูปแบบ .xlsx ขนาดไม่เกิน 25 MB"],
|
||
"company.extract": ["提取并核对", "Extract and review", "แยกข้อมูลและตรวจสอบ"],
|
||
"company.checking": ["核对中", "Checking", "กำลังตรวจสอบ"],
|
||
"company.period_release_10": ["周期结束:当月 11 日 00:00(曼谷)", "Period ends: 00:00 on the 11th (Bangkok)", "รอบสิ้นสุด: 00:00 วันที่ 11 (กรุงเทพฯ)"],
|
||
"company.period_release_20": ["周期结束:当月 21 日 00:00(曼谷)", "Period ends: 00:00 on the 21st (Bangkok)", "รอบสิ้นสุด: 00:00 วันที่ 21 (กรุงเทพฯ)"],
|
||
"company.period_release_month_end": ["周期结束:次月 1 日 00:00(曼谷)", "Period ends: 00:00 on the first day of the next month (Bangkok)", "รอบสิ้นสุด: 00:00 วันที่ 1 ของเดือนถัดไป (กรุงเทพฯ)"],
|
||
"company.generate_short": ["生成", "Generate", "สร้าง"],
|
||
"company.review_title": ["Booking记录提取", "Booking record extraction", "การแยกรายการ Booking"],
|
||
"company.extracted_file": ["提取文件", "Source file", "ไฟล์ต้นทาง"],
|
||
"company.review_stats": ["提取统计", "Extraction summary", "สรุปการแยกข้อมูล"],
|
||
"company.confirmed": ["已确认", "Confirmed", "ยืนยันแล้ว"],
|
||
"company.pending_human": ["待人工", "Needs review", "รอตรวจสอบ"],
|
||
"company.deleted": ["已删除", "Deleted", "ลบแล้ว"],
|
||
"company.delete_selected": ["删除所选", "Delete selected", "ลบรายการที่เลือก"],
|
||
"company.discard": ["放弃本次提取", "Discard this extraction", "ยกเลิกการแยกข้อมูลครั้งนี้"],
|
||
"company.activate": ["确认并启用", "Confirm and activate", "ยืนยันและเปิดใช้งาน"],
|
||
"company.extract_rooms": ["提取房型记录", "Extracted room records", "รายการประเภทห้องที่แยกได้"],
|
||
"company.raw_identifier": ["原始标识", "Raw identifier", "ตัวระบุดิบ"],
|
||
"company.empty_review": ["没有可显示的提取记录。", "No extraction records to display.", "ไม่มีรายการแยกข้อมูลให้แสดง"],
|
||
"company.task_status": ["TASK STATUS", "TASK STATUS", "TASK STATUS"],
|
||
"company.preparing": ["正在准备公司渠道明细任务", "Preparing company channel detail job", "กำลังเตรียมงานรายละเอียดช่องทางบริษัท"],
|
||
"company.reading_status": ["正在读取任务状态。", "Reading job status.", "กำลังอ่านสถานะงาน"],
|
||
"company.queued": ["已排队", "Queued", "เข้าคิวแล้ว"],
|
||
"company.generating": ["生成中", "Generating", "กำลังสร้าง"],
|
||
"company.all_success": ["全部成功", "All succeeded", "สำเร็จทั้งหมด"],
|
||
"company.partial_success": ["部分成功", "Partially succeeded", "สำเร็จบางส่วน"],
|
||
"company.incomplete": ["未完成", "Incomplete", "ยังไม่เสร็จ"],
|
||
"company.waiting_generation": ["等待生成", "Waiting to generate", "รอสร้าง"],
|
||
"company.success": ["成功", "Success", "สำเร็จ"],
|
||
"company.success_review": ["成功 · 待复核", "Success · review needed", "สำเร็จ · ต้องตรวจสอบ"],
|
||
"company.failed": ["失败", "Failed", "ล้มเหลว"],
|
||
"company.not_generated": ["未生成", "Not generated", "ยังไม่สร้าง"],
|
||
"company.warning_count": ["需复核", "Needs review", "ต้องตรวจสอบ"],
|
||
"company.cutoff_10": ["截至 10 日", "Through the 10th", "ถึงวันที่ 10"],
|
||
"company.cutoff_20": ["截至 20 日", "Through the 20th", "ถึงวันที่ 20"],
|
||
"company.cutoff_month_end": ["截至月末", "Through month-end", "ถึงสิ้นเดือน"],
|
||
"company.history": ["生成记录", "Generation history", "ประวัติการสร้าง"],
|
||
"company.per_page": ["每页 50 条", "50 per page", "หน้าละ 50 รายการ"],
|
||
"company.history_loading": ["正在读取公司渠道明细任务…", "Loading company channel detail jobs…", "กำลังโหลดงานรายละเอียดช่องทางบริษัท…"],
|
||
"company.history_empty": ["该月份还没有公司渠道明细任务", "No company channel detail jobs for this month", "เดือนนี้ยังไม่มีงานรายละเอียดช่องทางบริษัท"],
|
||
"company.processing_title": ["公司渠道明细处理", "Company channel detail processing", "การประมวลผลรายละเอียดช่องทางบริษัท"],
|
||
"company.review_pagination": ["提取记录分页", "Extraction record pagination", "การแบ่งหน้ารายการแยกข้อมูล"],
|
||
"company.history_pagination": ["生成记录分页", "Generation history pagination", "การแบ่งหน้าประวัติการสร้าง"],
|
||
"company.result_caption": ["本次任务中五家公司的生成结果", "Generation results for the five companies in this job", "ผลการสร้างของห้าบริษัทในงานนี้"],
|
||
"company.select_all_page": ["选择本页全部记录", "Select all records on this page", "เลือกทุกรายการในหน้านี้"],
|
||
"company.generated": ["已生成", "Generated", "สร้างแล้ว"],
|
||
"company.as_of": ["截止日期", "As-of date", "วันที่ข้อมูล"],
|
||
"company.task_duration": ["任务耗时", "Job duration", "ระยะเวลางาน"],
|
||
"company.cutoff_period": ["截止期间", "Cutoff period", "ช่วงเวลาสิ้นสุด"],
|
||
"company.review_problem": ["复核 / 异常", "Review / issues", "ตรวจสอบ / ปัญหา"],
|
||
"company.warning_short": ["需复核", "Needs review", "ต้องตรวจสอบ"],
|
||
"company.report_default_description": ["2026年07月,01-10", "July 2026, 01-10", "กรกฎาคม 2026, 01-10"],
|
||
"company.progress_waiting": ["等待生成", "Waiting to generate", "รอสร้าง"],
|
||
"company.progress_failed": ["生成未完成", "Generation incomplete", "การสร้างไม่เสร็จ"],
|
||
"company.progress_loading_finance": ["正在读取已校验的 finance 数据", "Loading validated finance data", "กำลังโหลดข้อมูล finance ที่ตรวจสอบแล้ว"],
|
||
"company.progress_generating_excel": ["正在生成 5 家公司 Excel", "Generating Excel files for 5 companies", "กำลังสร้างไฟล์ Excel สำหรับ 5 บริษัท"],
|
||
"company.progress_complete": ["生成完成", "Generation complete", "สร้างเสร็จแล้ว"],
|
||
"company.progress_ended": ["生成已结束", "Generation ended", "การสร้างสิ้นสุดแล้ว"],
|
||
"company.job_queued_message": ["任务已进入生成队列。", "The job entered the generation queue.", "งานเข้าสู่คิวการสร้างแล้ว"],
|
||
"company.job_generating_message": ["正在按公司生成渠道明细表。", "Generating channel detail workbooks by company.", "กำลังสร้างตารางรายละเอียดช่องทางแยกตามบริษัท"],
|
||
"company.job_success_message": ["5 家公司渠道明细表已生成。", "Channel detail workbooks for 5 companies were generated.", "สร้างตารางรายละเอียดช่องทางสำหรับ 5 บริษัทแล้ว"],
|
||
"company.job_partial_message": ["部分公司已生成,失败公司可按错误代码复核后重跑。", "Some companies were generated. Review failed companies by error code and rerun.", "สร้างบางบริษัทแล้ว ตรวจสอบบริษัทที่ล้มเหลวตามรหัสข้อผิดพลาดแล้วลองใหม่"],
|
||
"company.job_execution_failed_message": ["渠道明细生成未完成;未成功发布的公司不会提供下载。", "Channel detail generation did not complete; companies not published successfully will not be available for download.", "การสร้างรายละเอียดช่องทางไม่เสร็จ บริษัทที่เผยแพร่ไม่สำเร็จจะไม่พร้อมให้ดาวน์โหลด"],
|
||
"company.discarding": ["正在放弃", "Discarding", "กำลังยกเลิก"],
|
||
"company.deleting": ["正在删除", "Deleting", "กำลังลบ"],
|
||
"company.confirm_delete_title": ["删除房型记录", "Delete room record", "ลบรายการประเภทห้อง"],
|
||
"company.confirm_delete_description": ["确认删除所选房型记录?", "Delete the selected room record?", "ยืนยันการลบรายการประเภทห้องที่เลือกหรือไม่"],
|
||
"company.confirm_discard_title": ["放弃本次提取", "Discard this extraction", "ยกเลิกการแยกข้อมูลครั้งนี้"],
|
||
"company.confirm_discard_description": ["确认放弃本次提取?", "Discard this extraction?", "ยืนยันการยกเลิกการแยกข้อมูลครั้งนี้หรือไม่"],
|
||
"company.confirm_discard_note": ["当前已做的人工修改和删除将被丢弃,原 Booking 数据源不会改变。", "Your edits and deletions will be discarded; the original Booking source will not change.", "การแก้ไขและการลบจะถูกทิ้ง แหล่งข้อมูล Booking เดิมจะไม่เปลี่ยนแปลง"],
|
||
"company.confirm_delete_note": ["此操作只影响本次提取结果,确认启用前原数据源不会改变。", "This affects only this extraction; the original source will not change until activation.", "การดำเนินการนี้มีผลเฉพาะผลการแยกครั้งนี้ แหล่งข้อมูลเดิมจะไม่เปลี่ยนจนกว่าจะเปิดใช้งาน"],
|
||
"company.confirm_delete": ["确认删除", "Confirm delete", "ยืนยันการลบ"],
|
||
"company.confirm_discard": ["确认放弃", "Confirm discard", "ยืนยันการยกเลิก"],
|
||
"company.confirm_generate": ["确认生成", "Confirm generation", "ยืนยันการสร้าง"],
|
||
"company.generate_five": ["将一次生成五家公司的正式 Excel。", "This will generate the official Excel files for five companies.", "ระบบจะสร้างไฟล์ Excel อย่างเป็นทางการสำหรับห้าบริษัท"],
|
||
"company.generate_early": ["周期尚未结束,将按当前已入库数据生成;后续新数据不会自动补入,可再次生成新版。", "The period is not complete. The current committed data will be used; later data will not be added automatically. Generate again for a new version.", "รอบยังไม่สิ้นสุด ระบบจะใช้ข้อมูลที่บันทึกแล้ว ข้อมูลใหม่จะไม่ถูกเพิ่มอัตโนมัติ สามารถสร้างเวอร์ชันใหม่ได้"],
|
||
"company.no_valid_month": ["请先选择有效的报表月份", "Choose a valid report month first", "เลือกเดือนรายงานที่ถูกต้องก่อน"],
|
||
"company.review_open": ["请先确认或放弃当前 Excel 提取结果", "Confirm or discard the current Excel extraction first", "ยืนยันหรือยกเลิกผลการแยก Excel ปัจจุบันก่อน"],
|
||
"company.source_required": ["请先提取并启用 Excel 报表", "Extract and activate the Excel report first", "แยกและเปิดใช้งานรายงาน Excel ก่อน"],
|
||
"company.service_required": ["公司渠道明细服务尚未就绪", "Company channel detail service is not ready", "บริการรายละเอียดช่องทางบริษัทยังไม่พร้อม"],
|
||
"company.future_month": ["未来报表月份暂不可生成", "Future report months cannot be generated yet", "ยังไม่สามารถสร้างเดือนรายงานในอนาคตได้"],
|
||
"company.select_month": ["选择月份", "Choose a month", "เลือกเดือน"],
|
||
"company.period_not_finished": ["周期未结束", "Period not complete", "รอบยังไม่สิ้นสุด"],
|
||
"company.period_finished": ["周期已结束", "Period complete", "รอบสิ้นสุดแล้ว"],
|
||
"company.source_error": ["当前 Excel 数据源暂时无法读取", "The current Excel source is temporarily unavailable", "ไม่สามารถอ่านแหล่ง Excel ปัจจุบันได้ชั่วคราว"],
|
||
"company.current_source": ["当前启用来源", "Active source", "แหล่งข้อมูลที่เปิดใช้งาน"],
|
||
"company.source_active": ["已启用", "Active", "เปิดใช้งานแล้ว"],
|
||
"company.source_meta": ["批次 #{batch} · {rows} 条记录 · {rooms} 间 · 启用于 {activated}", "Batch #{batch} · {rows} records · {rooms} rooms · Activated {activated}", "ชุดข้อมูล #{batch} · {rows} รายการ · {rooms} ห้อง · เปิดใช้งานเมื่อ {activated}"],
|
||
"company.source_file_missing": ["未记录文件名", "File name not recorded", "ไม่ได้บันทึกชื่อไฟล์"],
|
||
"company.source_already_active": ["这份 Excel 已是当前启用来源;请在下方按月份查看或生成对应报表", "This Excel is already the active source. Review or generate the corresponding report by month below.", "Excel นี้เป็นแหล่งข้อมูลที่เปิดใช้งานอยู่แล้ว ตรวจสอบหรือสร้างรายงานตามเดือนได้ด้านล่าง"],
|
||
"company.publish_failed_summary_all": ["数据已处理,但 5 家公司的正式 Excel 保存失败。请重启报表服务后再生成。", "The data was processed, but the official Excel files for all five companies could not be saved. Restart the report service and generate again.", "ประมวลผลข้อมูลแล้ว แต่ไม่สามารถบันทึกไฟล์ Excel อย่างเป็นทางการของทั้ง 5 บริษัทได้ โปรดรีสตาร์ตบริการรายงานแล้วสร้างใหม่"],
|
||
"company.publish_failed_summary_partial": ["数据已处理,但部分公司的正式 Excel 保存失败。请重启报表服务后再生成。", "The data was processed, but some official Excel files could not be saved. Restart the report service and generate again.", "ประมวลผลข้อมูลแล้ว แต่ไม่สามารถบันทึกไฟล์ Excel อย่างเป็นทางการของบางบริษัทได้ โปรดรีสตาร์ตบริการรายงานแล้วสร้างใหม่"],
|
||
"company.publish_failed_detail": ["数据已处理,但正式 Excel 保存失败。请重启报表服务后再生成。", "The data was processed, but the official Excel could not be saved. Restart the report service and generate again.", "ประมวลผลข้อมูลแล้ว แต่ไม่สามารถบันทึกไฟล์ Excel อย่างเป็นทางการได้ โปรดรีสตาร์ตบริการรายงานแล้วสร้างใหม่"],
|
||
"company.draft_error": ["当前提取结果暂时无法读取", "The current extraction is temporarily unavailable", "ไม่สามารถอ่านผลการแยกปัจจุบันได้ชั่วคราว"],
|
||
"company.file_xlsx": ["请选择 .xlsx 格式的 Excel 文件", "Choose an .xlsx Excel file", "เลือกไฟล์ Excel รูปแบบ .xlsx"],
|
||
"company.file_empty": ["Excel 文件为空", "The Excel file is empty", "ไฟล์ Excel ว่างเปล่า"],
|
||
"company.file_too_large": ["Excel 文件超过 25 MB", "The Excel file exceeds 25 MB", "ไฟล์ Excel มีขนาดเกิน 25 MB"],
|
||
"company.file_extract_failed": ["Excel 文件未能完成提取", "The Excel file could not be extracted", "แยกข้อมูลจากไฟล์ Excel ไม่สำเร็จ"],
|
||
"company.fill_room_type": ["请填写房型后再保存", "Enter a room type before saving", "กรอกประเภทห้องก่อนบันทึก"],
|
||
"company.quantity_invalid": ["房间数量必须是 1 至 9999 的整数", "Room quantity must be an integer from 1 to 9999", "จำนวนห้องต้องเป็นจำนวนเต็มตั้งแต่ 1 ถึง 9999"],
|
||
"company.review_saved": ["房型记录已保存", "Room record saved", "บันทึกรายการประเภทห้องแล้ว"],
|
||
"company.review_deleted": ["房型记录已删除", "Room record deleted", "ลบรายการประเภทห้องแล้ว"],
|
||
"company.review_discarded": ["本次提取已放弃,原数据源未改变", "Extraction discarded; the original source was not changed", "ยกเลิกการแยกแล้ว แหล่งข้อมูลเดิมไม่เปลี่ยนแปลง"],
|
||
"company.review_activated": ["人工核对完成,Booking 数据源已启用", "Review complete; the Booking source is active", "ตรวจสอบเสร็จแล้ว เปิดใช้งานแหล่งข้อมูล Booking"],
|
||
"company.review_discard_failed": ["本次提取未能放弃", "The extraction could not be discarded", "ยกเลิกการแยกข้อมูลไม่สำเร็จ"],
|
||
"company.review_delete_failed": ["房型记录未能删除", "The room record could not be deleted", "ลบรายการประเภทห้องไม่สำเร็จ"],
|
||
"company.review_save_failed": ["房型记录未能保存", "The room record could not be saved", "บันทึกรายการประเภทห้องไม่สำเร็จ"],
|
||
"company.source_activate_failed": ["Booking 数据源未能启用", "The Booking source could not be activated", "เปิดใช้งานแหล่งข้อมูล Booking ไม่สำเร็จ"],
|
||
"company.extract_done": ["提取完成,可以确认并启用", "Extraction complete. Confirm and activate.", "แยกข้อมูลเสร็จแล้ว ยืนยันและเปิดใช้งานได้"],
|
||
"company.extract_pending": ["提取完成,{count} 条需要人工确认", "Extraction complete. {count} records need review.", "แยกข้อมูลเสร็จแล้ว ต้องตรวจสอบ {count} รายการ"],
|
||
"company.file_name_missing": ["未记录文件名", "File name not recorded", "ไม่ได้บันทึกชื่อไฟล์"],
|
||
"company.review_pending_confirm": ["待人工确认", "Pending human review", "รอยืนยันโดยเจ้าหน้าที่"],
|
||
"company.review_save": ["保存", "Save", "บันทึก"],
|
||
"company.review_delete": ["删除", "Delete", "ลบ"],
|
||
"company.review_select": ["选择", "Select", "เลือก"],
|
||
"company.review_no_record": ["暂无", "None", "ไม่มีข้อมูล"],
|
||
"company.period_pending": ["待生成", "Pending", "รอสร้าง"],
|
||
"company.period_waiting": ["等待生成", "Waiting to generate", "รอสร้าง"],
|
||
"company.period_none": ["无结果", "No result", "ไม่มีผลลัพธ์"],
|
||
"company.period_not_generated": ["未生成", "Not generated", "ยังไม่สร้าง"],
|
||
"company.period_not_recorded": ["未记录", "Not recorded", "ไม่ได้บันทึก"],
|
||
"company.report_prepare": ["正在准备公司渠道明细任务", "Preparing company channel detail job", "กำลังเตรียมงานรายละเอียดช่องทางบริษัท"],
|
||
"company.report_waiting": ["等待生成", "Waiting to generate", "รอสร้าง"],
|
||
"company.report_warning_count": ["{count} 项", "{count} items", "{count} รายการ"],
|
||
"company.report_no_result": ["暂无", "None", "ไม่มีข้อมูล"],
|
||
"company.this_record": ["这条记录", "this record", "รายการนี้"],
|
||
"company.job_submitted": ["五家公司渠道明细任务已提交", "The five-company channel detail job was submitted", "ส่งงานรายละเอียดช่องทางของห้าบริษัทแล้ว"],
|
||
"company.jobs_refreshed": ["公司渠道明细任务已刷新", "Company channel detail jobs refreshed", "รีเฟรชงานรายละเอียดช่องทางบริษัทแล้ว"],
|
||
"company.job_submit_failed": ["公司渠道明细任务未能提交", "The company channel detail job could not be submitted", "ส่งงานรายละเอียดช่องทางบริษัทไม่สำเร็จ"],
|
||
"task.title": ["任务日志", "Task log", "บันทึกงาน"],
|
||
"task.description": ["显示当前选中的日报处理任务从上传到下游通知的处理日志。", "Shows the selected daily task from upload through downstream notification.", "แสดงบันทึกของงานรายวันที่เลือกตั้งแต่อัปโหลดจนถึงการแจ้งเตือนปลายทาง"],
|
||
"task.kicker": ["DAILY JOB / LOG", "DAILY JOB / LOG", "DAILY JOB / LOG"],
|
||
"task.raw_log": ["任务原始日志", "Raw task log", "บันทึกงานดิบ"],
|
||
"task.refresh": ["刷新日志", "Refresh log", "รีเฟรชบันทึก"],
|
||
"task.copy": ["复制全部日志", "Copy all logs", "คัดลอกบันทึกทั้งหมด"],
|
||
"task.copying": ["复制中…", "Copying…", "กำลังคัดลอก…"],
|
||
"task.copied": ["已复制", "Copied", "คัดลอกแล้ว"],
|
||
"task.copy_failed": ["复制失败", "Copy failed", "คัดลอกไม่สำเร็จ"],
|
||
"task.copy_none": ["暂无可复制的任务日志", "No task log to copy", "ไม่มีบันทึกงานให้คัดลอก"],
|
||
"task.copy_denied": ["无法复制日志,请检查浏览器剪贴板权限", "Could not copy the log. Check browser clipboard permission.", "คัดลอกบันทึกไม่ได้ โปรดตรวจสอบสิทธิ์คลิปบอร์ดของเบราว์เซอร์"],
|
||
"task.clipboard_denied": ["浏览器未允许访问剪贴板", "The browser did not allow clipboard access", "เบราว์เซอร์ไม่อนุญาตให้เข้าถึงคลิปบอร์ด"],
|
||
"task.reading": ["正在读取任务日志", "Reading task log", "กำลังอ่านบันทึกงาน"],
|
||
"task.reading_detail": ["正在聚合数据库中的处理证据…", "Aggregating processing evidence from the database…", "กำลังรวมหลักฐานการประมวลผลจากฐานข้อมูล…"],
|
||
"task.unavailable": ["任务日志暂时无法读取", "The task log is temporarily unavailable", "ไม่สามารถอ่านบันทึกงานได้ชั่วคราว"],
|
||
"task.no_server_log": ["服务端没有返回可用日志。", "The server returned no usable log.", "เซิร์ฟเวอร์ไม่ส่งบันทึกที่ใช้ได้กลับมา"],
|
||
"task.not_selected": ["未选择任务", "No task selected", "ยังไม่ได้เลือกงาน"],
|
||
"task.waiting_upload": ["等待上传 ARR.XML", "Waiting for ARR.XML upload", "รอการอัปโหลด ARR.XML"],
|
||
"task.after_create": ["新任务创建后,这里会显示服务端保存的全流程日志。", "The server-side trace will appear here after a new task is created.", "บันทึกจากเซิร์ฟเวอร์จะแสดงที่นี่หลังสร้างงานใหม่"],
|
||
"task.inline_prompt": ["请上传文件,或从 Daily Report 中选择一个任务。", "Upload a file or select a task from Daily Report.", "อัปโหลดไฟล์หรือเลือกงานจาก Daily Report"],
|
||
"task.refresh_failed": ["日志刷新失败", "Log refresh failed", "รีเฟรชบันทึกไม่สำเร็จ"],
|
||
"task.auto_refresh_failed": ["自动刷新失败", "Auto-refresh failed", "อัปเดตอัตโนมัติไม่สำเร็จ"],
|
||
"task.created": ["任务已登记到 ARR 程序化处理队列。", "The task was registered in the ARR processing queue.", "ลงทะเบียนงานในคิวประมวลผล ARR แล้ว"],
|
||
"task.processor_started": ["ARR 已开始处理已登记的源 XML。", "ARR started processing the registered source XML.", "ARR เริ่มประมวลผล XML ต้นทางที่ลงทะเบียนแล้ว"],
|
||
"task.processor_failed": ["程序化处理失败", "Programmatic processing failed", "การประมวลผลแบบโปรแกรมไม่สำเร็จ"],
|
||
"task.processor_cancelled": ["程序化处理已取消", "Programmatic processing cancelled", "ยกเลิกการประมวลผลแบบโปรแกรมแล้ว"],
|
||
"task.processor_complete": ["处理尝试已闭环", "Processing attempt completed", "การประมวลผลเสร็จสิ้น"],
|
||
"task.accepted": ["业务任务已完成", "Business task completed", "งานธุรกิจเสร็จสิ้น"],
|
||
"task.upload_registered": ["上传已安全登记", "Upload safely registered", "ลงทะเบียนการอัปโหลดอย่างปลอดภัยแล้ว"],
|
||
"task.trace_processing": ["处理已结束", "Processing ended", "การประมวลผลสิ้นสุดแล้ว"],
|
||
"task.trace_failed": ["处理未能完成", "Processing could not complete", "การประมวลผลไม่เสร็จสมบูรณ์"],
|
||
"task.refreshed": ["任务日志已刷新", "Task log refreshed", "รีเฟรชบันทึกงานแล้ว"],
|
||
"task.no_job_id": ["任务编号暂不可用,请刷新 Daily Report。", "The task ID is not available yet. Refresh Daily Report.", "รหัสงานยังไม่พร้อม โปรดรีเฟรช Daily Report"],
|
||
"task.failed_refresh_hint": ["请刷新 Daily Report,确认是否已登记失败任务。", "Refresh Daily Report to confirm whether a failed task was recorded.", "รีเฟรช Daily Report เพื่อตรวจสอบว่าบันทึกงานที่ล้มเหลวแล้วหรือไม่"],
|
||
"task.error_status": ["处理失败", "Processing failed", "ประมวลผลไม่สำเร็จ"],
|
||
"task.live_failed": ["日志刷新失败", "Log refresh failed", "รีเฟรชบันทึกไม่สำเร็จ"],
|
||
"task.live_auto_failed": ["自动刷新失败", "Auto-refresh failed", "อัปเดตอัตโนมัติไม่สำเร็จ"],
|
||
"task.live_reading": ["正在读取", "Reading", "กำลังอ่าน"],
|
||
"task.live_unselected": ["未选择任务", "No task selected", "ยังไม่ได้เลือกงาน"],
|
||
"task.trace_failed_status": ["FAILED", "FAILED", "FAILED"],
|
||
"task.trace_running_status": ["RUNNING / auto refresh {seconds}s", "RUNNING / auto refresh {seconds}s", "RUNNING / อัปเดตทุก {seconds}s"],
|
||
"task.trace_succeeded_status": ["SUCCEEDED", "SUCCEEDED", "SUCCEEDED"],
|
||
"task.trace_unknown_status": ["UNKNOWN", "UNKNOWN", "UNKNOWN"],
|
||
"task.no_events": ["0 events", "0 events", "0 เหตุการณ์"],
|
||
"task.events": ["{count} events", "{count} events", "{count} เหตุการณ์"],
|
||
"task.raw_log_count": ["0 lines", "0 lines", "0 บรรทัด"],
|
||
"task.raw_log_aria": ["任务原始日志", "Raw task log", "บันทึกงานดิบ"],
|
||
"task.browser_clipboard": ["浏览器未允许访问剪贴板", "The browser did not allow clipboard access", "เบราว์เซอร์ไม่อนุญาตให้เข้าถึงคลิปบอร์ด"],
|
||
"task.none_to_copy": ["暂无可复制的任务日志", "No task log to copy", "ไม่มีบันทึกงานให้คัดลอก"],
|
||
"task.copying_lines": ["已复制全部任务日志({count} 行)", "Copied all task logs ({count} lines)", "คัดลอกบันทึกงานทั้งหมดแล้ว ({count} บรรทัด)"],
|
||
"task.copy_button": ["复制全部日志", "Copy all logs", "คัดลอกบันทึกทั้งหมด"],
|
||
"task.copying_button": ["复制中…", "Copying…", "กำลังคัดลอก…"],
|
||
"task.copied_button": ["已复制", "Copied", "คัดลอกแล้ว"],
|
||
"task.copy_failed_button": ["复制失败", "Copy failed", "คัดลอกไม่สำเร็จ"],
|
||
"task.close_button": ["关闭", "Close", "ปิด"],
|
||
"error.generic": ["请求未完成", "The request could not be completed", "ดำเนินการตามคำขอไม่สำเร็จ"],
|
||
"error.unrecognized_response": ["服务返回了无法识别的结果", "The service returned an unrecognized response", "บริการส่งผลลัพธ์ที่ไม่รู้จักกลับมา"],
|
||
"error.database_unavailable": ["数据库连接失败", "Database connection failed", "เชื่อมต่อฐานข้อมูลไม่สำเร็จ"],
|
||
"error.database_read_unavailable": ["数据库读取服务暂不可用", "Database read service is temporarily unavailable", "บริการอ่านฐานข้อมูลไม่พร้อมใช้งานชั่วคราว"],
|
||
"error.query_invalid": ["查询参数无效", "Invalid query parameters", "พารามิเตอร์การค้นหาไม่ถูกต้อง"],
|
||
"error.auth_required": ["请先登录", "Please log in first", "กรุณาเข้าสู่ระบบก่อน"],
|
||
"error.login_failed": ["用户名或密码不正确", "Incorrect username or password", "ชื่อผู้ใช้หรือรหัสผ่านไม่ถูกต้อง"],
|
||
"error.login_rate_limited": ["登录尝试过于频繁,请稍后再试", "Too many login attempts. Try again later.", "พยายามเข้าสู่ระบบบ่อยเกินไป โปรดลองอีกครั้งภายหลัง"],
|
||
"error.request_invalid": ["请求内容不是有效 JSON", "The request is not valid JSON", "คำขอไม่ใช่ JSON ที่ถูกต้อง"],
|
||
"error.request_too_large": ["请求内容过大", "The request is too large", "คำขอมีขนาดใหญ่เกินไป"],
|
||
"error.upload_empty": ["XML 文件为空", "The XML file is empty", "ไฟล์ XML ว่างเปล่า"],
|
||
"error.upload_too_large": ["XML 文件超过 25 MB", "The XML file exceeds 25 MB", "ไฟล์ XML มีขนาดเกิน 25 MB"],
|
||
"error.upload_invalid": ["文件内容不是 XML", "The file content is not XML", "เนื้อหาไฟล์ไม่ใช่ XML"],
|
||
"error.upload_filename": ["请选择 XML 文件", "Choose an XML file", "เลือกไฟล์ XML"],
|
||
"error.job_id": ["任务编号无效", "Invalid task ID", "รหัสงานไม่ถูกต้อง"],
|
||
"error.route_not_found": ["页面或接口不存在", "The page or endpoint does not exist", "ไม่พบหน้าหรือปลายทาง"],
|
||
"error.static_not_found": ["页面资源不存在", "The page resource does not exist", "ไม่พบทรัพยากรของหน้า"],
|
||
"error.session_invalid": ["页面会话已失效", "The page session is no longer valid", "เซสชันของหน้าไม่ถูกต้องแล้ว"],
|
||
"error.download_not_found": ["文件不存在或尚未生成", "The file does not exist or has not been generated", "ไม่พบไฟล์หรือยังไม่ได้สร้าง"],
|
||
"error.download_invalid": ["文件身份无效", "Invalid file identity", "ข้อมูลระบุไฟล์ไม่ถูกต้อง"],
|
||
"error.download_unavailable": ["文件读取服务暂不可用", "The file read service is temporarily unavailable", "บริการอ่านไฟล์ไม่พร้อมใช้งานชั่วคราว"],
|
||
"error.download_integrity": ["文件完整性校验失败", "File integrity check failed", "ตรวจสอบความสมบูรณ์ของไฟล์ไม่ผ่าน"],
|
||
"error.company_source_required": ["请先上传并校验 Excel 报表", "Upload and validate the Excel report first", "อัปโหลดและตรวจสอบรายงาน Excel ก่อน"],
|
||
"error.company_review_open": ["请先完成或放弃当前 Excel 提取草稿", "Complete or discard the current Excel draft first", "ดำเนินการหรือยกเลิกร่าง Excel ปัจจุบันก่อน"],
|
||
"error.company_future": ["未来报表月份暂不可生成", "Future report months cannot be generated yet", "ยังไม่สามารถสร้างเดือนรายงานในอนาคตได้"],
|
||
"error.company_running": ["同一月份和期间已有生成任务", "A job already exists for this month and period", "มีงานสำหรับเดือนและช่วงเวลานี้อยู่แล้ว"],
|
||
"error.company_unavailable": ["公司渠道明细生成服务暂不可用", "Company channel detail generation is temporarily unavailable", "บริการสร้างรายละเอียดช่องทางบริษัทไม่พร้อมใช้งานชั่วคราว"],
|
||
"error.company_download": ["该公司的正式表格尚不可下载", "The official workbook for this company is not ready to download", "ไฟล์อย่างเป็นทางการของบริษัทนี้ยังไม่พร้อมดาวน์โหลด"],
|
||
"error.booking_unavailable": ["Excel 数据源服务暂未就绪", "The Excel source service is not ready", "บริการแหล่ง Excel ยังไม่พร้อม"],
|
||
"error.booking_extract_failed": ["Excel 文件未能完成提取", "The Excel file could not be extracted", "แยกข้อมูลจากไฟล์ Excel ไม่สำเร็จ"],
|
||
"error.processing_failed": ["文件已安全接收,但程序化处理未能完成", "The file was received safely, but processing did not complete", "รับไฟล์อย่างปลอดภัยแล้ว แต่การประมวลผลไม่เสร็จ"],
|
||
"error.invalid_request": ["请求字段无效", "Invalid request fields", "ฟิลด์คำขอไม่ถูกต้อง"],
|
||
"error.invalid_period": ["期间无效", "Invalid period", "ช่วงเวลาไม่ถูกต้อง"],
|
||
"error.invalid_company": ["公司名称无效", "Invalid company name", "ชื่อบริษัทไม่ถูกต้อง"],
|
||
"error.not_found": ["任务或数据不存在", "The task or data was not found", "ไม่พบงานหรือข้อมูล"],
|
||
"error.service_unavailable": ["服务暂时不可用", "The service is temporarily unavailable", "บริการไม่พร้อมใช้งานชั่วคราว"],
|
||
"error.internal": ["服务暂时无法完成请求", "The service could not complete the request", "บริการไม่สามารถดำเนินการตามคำขอได้"],
|
||
"error.month_invalid": ["报表月份无效", "Invalid report month", "เดือนรายงานไม่ถูกต้อง"],
|
||
"error.monthly_busy": ["该服务正在生成月报", "The monthly report service is already running", "บริการรายงานประจำเดือนกำลังทำงาน"],
|
||
"error.monthly_failed": ["月报生成未完成", "Monthly report generation did not complete", "การสร้างรายงานประจำเดือนไม่เสร็จ"],
|
||
"error.monthly_not_found": ["找不到该月份的正式月报。", "The published monthly report for this month was not found.", "ไม่พบรายงานประจำเดือนที่เผยแพร่ของเดือนนี้"],
|
||
"error.channel_invalid": ["渠道名称无效", "Invalid channel name", "ชื่อช่องทางไม่ถูกต้อง"],
|
||
"error.login_request_invalid": ["登录请求格式或字段无效", "The login request is invalid", "คำขอเข้าสู่ระบบไม่ถูกต้อง"],
|
||
"error.content_length_required": ["请求长度缺失", "Request length is missing", "ไม่มีความยาวคำขอ"],
|
||
"error.booking_filename": ["请选择 .xlsx 格式的 Excel 文件", "Choose an .xlsx Excel file", "เลือกไฟล์ Excel รูปแบบ .xlsx"],
|
||
"error.review_request_invalid": ["人工确认请求无效", "The review request is invalid", "คำขอตรวจสอบไม่ถูกต้อง"],
|
||
"error.review_item_invalid": ["待确认记录编号无效", "Invalid review item ID", "รหัสรายการตรวจสอบไม่ถูกต้อง"],
|
||
"error.company_request_invalid": ["渠道明细请求字段无效", "Invalid company detail request fields", "ฟิลด์คำขอรายละเอียดบริษัทไม่ถูกต้อง"],
|
||
"error.company_source_already_active": ["这份 Excel 已经启用,无需重复提取", "This Excel is already active; no duplicate extraction is needed", "Excel นี้เปิดใช้งานอยู่แล้ว ไม่จำเป็นต้องแยกข้อมูลซ้ำ"],
|
||
"error.company_period_invalid": ["期间必须是 01-10、11-20 或 21-month-end", "Period must be 01-10, 11-20 or 21-month-end", "ช่วงเวลาต้องเป็น 01-10, 11-20 หรือ 21-month-end"],
|
||
"error.company_job_not_found": ["任务不存在", "The job does not exist", "ไม่พบงาน"],
|
||
"error.download_too_large": ["文件超过下载限制", "The file exceeds the download limit", "ไฟล์มีขนาดเกินขีดจำกัดการดาวน์โหลด"],
|
||
"problem.multi_price": ["同房型存在不同静态价格,需人工复核", "The same room type has different static prices; review required", "ประเภทห้องเดียวกันมีราคาคงที่ต่างกัน ต้องตรวจสอบ"],
|
||
"problem.source_missing": ["数据源版本缺失", "Source version is missing", "ไม่มีเวอร์ชันแหล่งข้อมูล"],
|
||
"problem.group_missing": ["Group Code 缺失", "Group Code is missing", "ไม่มี Group Code"],
|
||
"problem.group_not_found": ["Group Code 未找到", "Group Code not found", "ไม่พบ Group Code"],
|
||
"problem.booking_parse": ["预订解析异常", "Booking parsing error", "เกิดข้อผิดพลาดในการแยก Booking"],
|
||
"problem.room_items_missing": ["房型明细缺失", "Room details are missing", "ไม่มีรายละเอียดประเภทห้อง"],
|
||
"problem.stay_date": ["住宿日期异常", "Invalid stay date", "วันที่เข้าพักไม่ถูกต้อง"],
|
||
"problem.nights_conflict": ["间夜数据冲突", "Room-night data conflict", "ข้อมูลคืนห้องพักขัดแย้งกัน"],
|
||
"problem.total_price": ["静态价格异常", "Invalid static price", "ราคาคงที่ไม่ถูกต้อง"],
|
||
"problem.output_validation": ["Excel 输出校验失败", "Excel output validation failed", "ตรวจสอบผลลัพธ์ Excel ไม่ผ่าน"],
|
||
"problem.publish_failed": ["正式 Excel 保存失败", "Official Excel save failed", "บันทึกไฟล์ Excel อย่างเป็นทางการไม่สำเร็จ"],
|
||
"problem.internal": ["处理服务异常", "Processing service error", "บริการประมวลผลขัดข้อง"],
|
||
"problem.execution": ["任务执行未完成", "Job execution did not complete", "การทำงานยังไม่เสร็จ"],
|
||
"problem.interrupted": ["服务重启中断任务", "The service restart interrupted the job", "การรีสตาร์ตบริการขัดจังหวะงาน"],
|
||
"problem.review_result": ["需复核处理结果", "Review the processing result", "โปรดตรวจสอบผลการประมวลผล"],
|
||
"units.second": ["秒", "sec", "วินาที"],
|
||
"units.seconds": ["秒", "sec", "วินาที"],
|
||
"units.minute": ["分", "min", "นาที"],
|
||
"units.minutes": ["分", "min", "นาที"],
|
||
"units.rows": ["条", "records", "รายการ"],
|
||
"units.items": ["项", "items", "รายการ"],
|
||
"pagination.zero": ["共 0 条", "0 records", "0 รายการ"],
|
||
"pagination.summary": ["共 {total} 条 · 本页 {start} 至 {end}", "{total} records · Showing {start}–{end}", "{total} รายการ · แสดง {start}–{end}"],
|
||
"pagination.page": ["第 {page} / {pages} 页", "Page {page} of {pages}", "หน้า {page} จาก {pages}"],
|
||
"history.pagination": ["{month} · {summary}", "{month} · {summary}", "{month} · {summary}"],
|
||
"history.daily_empty": ["{month} · 暂无 Daily Report 记录", "{month} · No Daily Report records", "{month} · ไม่มีรายการ Daily Report"],
|
||
"history.monthly_empty": ["{month} · 暂无月报处理记录", "{month} · No monthly processing records", "{month} · ไม่มีรายการประมวลผลรายเดือน"],
|
||
"history.company_empty": ["{month} · 暂无公司渠道明细任务", "{month} · No company channel detail jobs", "{month} · ไม่มีงานรายละเอียดช่องทางบริษัท"],
|
||
"history.view_recent": ["查看 {month}", "View {month}", "ดู {month}"],
|
||
"progress.auto_update": ["自动更新 · {seconds} 秒", "Auto-refresh · {seconds} sec", "อัปเดตอัตโนมัติ · {seconds} วินาที"],
|
||
"period.release": ["周期结束:{date} 00:00(曼谷)", "Period ends: {date} 00:00 (Bangkok)", "รอบสิ้นสุด: {date} 00:00 (กรุงเทพฯ)"],
|
||
"period.release_hint": ["周期尚未结束;可按当前已入库数据生成,周期结束时间为曼谷 {date} 00:00", "The period is not complete; current committed data can be used. The Bangkok completion time is {date} 00:00.", "รอบยังไม่สิ้นสุด ใช้ข้อมูลที่บันทึกแล้วสร้างได้ เวลาสิ้นสุดที่กรุงเทพฯ คือ {date} 00:00"],
|
||
"company.month_end": ["21-月末", "21-month-end", "21-สิ้นเดือน"],
|
||
"company.range_month_end": ["21-{day}", "21-{day}", "21-{day}"],
|
||
"company.range_co": ["C/O:{range}", "C/O:{range}", "C/O:{range}"],
|
||
"company.month_label": ["{year}年{month}月", "{monthName} {year}", "{monthName} {year}"],
|
||
"company.at_period": ["{month},{period}", "{month}, {period}", "{month}, {period}"],
|
||
"company.selected_count": ["已选 {count} 条", "{count} selected", "เลือก {count} รายการ"],
|
||
"company.deleted_count": ["已删除 {count} 条房型记录", "Deleted {count} room records", "ลบรายการประเภทห้อง {count} รายการแล้ว"],
|
||
"company.delete_count": ["删除 {count} 条", "Delete {count}", "ลบ {count} รายการ"],
|
||
"company.confirm_single_delete": ["确认删除 {tourCode} 的这条房型记录?", "Delete this room record for {tourCode}?", "ลบรายการประเภทห้องของ {tourCode} หรือไม่"],
|
||
"company.confirm_multi_delete": ["确认删除已选择的 {count} 条房型记录?", "Delete the {count} selected room records?", "ลบรายการประเภทห้องที่เลือก {count} รายการหรือไม่"],
|
||
"company.selected_file": ["{name} · {size} KB", "{name} · {size} KB", "{name} · {size} KB"],
|
||
"task.selected_job": ["任务 {jobId}", "Task {jobId}", "งาน {jobId}"],
|
||
"task.view_job": ["查看任务 {jobId} 的全流程日志", "View the full trace for task {jobId}", "ดูบันทึกทั้งหมดของงาน {jobId}"],
|
||
"task.copied_lines": ["已复制全部任务日志({count} 行)", "Copied all task logs ({count} lines)", "คัดลอกบันทึกงานทั้งหมดแล้ว ({count} บรรทัด)"],
|
||
"bi.data_range": ["数据范围:{start} – {end}", "Data coverage: {start} – {end}", "ช่วงข้อมูล: {start} – {end}"],
|
||
"company.warning_items": ["{count} 项", "{count} items", "{count} รายการ"],
|
||
"company.period_rows": ["✓ {count} 条", "✓ {count} records", "✓ {count} รายการ"],
|
||
"company.period_end_display": ["周期结束:{date} 00:00(曼谷)", "Period ends: {date} 00:00 (Bangkok)", "รอบสิ้นสุด: {date} 00:00 (กรุงเทพฯ)"],
|
||
"trace.events": ["0 events", "0 events", "0 เหตุการณ์"],
|
||
"trace.events_count": ["{count} events", "{count} events", "{count} เหตุการณ์"],
|
||
"trace.failed": ["ERROR", "ERROR", "ERROR"],
|
||
"trace.loading": ["LOADING", "LOADING", "LOADING"],
|
||
"trace.idle": ["IDLE", "IDLE", "IDLE"],
|
||
"trace.failed_status": ["FAILED / {stage} / {code}", "FAILED / {stage} / {code}", "FAILED / {stage} / {code}"],
|
||
"trace.running_status": ["RUNNING / auto refresh {seconds}s", "RUNNING / auto refresh {seconds}s", "RUNNING / อัปเดตทุก {seconds}s"],
|
||
"trace.succeeded_status": ["SUCCEEDED", "SUCCEEDED", "SUCCEEDED"],
|
||
"trace.unknown_status": ["{status}", "{status}", "{status}"],
|
||
"h5.snapshot": ["基于已发布月报快照", "Based on the published monthly snapshot", "อ้างอิงสแนปช็อตรายเดือนที่เผยแพร่แล้ว"],
|
||
"h5.nights_formula": ["房数 × 晚数", "Rooms × nights", "ห้อง × จำนวนคืน"],
|
||
"h5.monthly_sheet": ["月报子表", "Monthly worksheets", "ชีตรายงานประจำเดือน"],
|
||
"h5.loading": ["正在读取数据…", "Loading data…", "กำลังโหลดข้อมูล…"],
|
||
"h5.login_title": ["ARR Report 登录", "ARR Report login", "เข้าสู่ระบบ ARR Report"],
|
||
"login.title": ["登录 · ARR Report", "Log in · ARR Report", "เข้าสู่ระบบ · ARR Report"],
|
||
"h5.title": ["渠道BI · ARR Report", "Channel BI · ARR Report", "Channel BI · ARR Report"],
|
||
};
|
||
|
||
const ERROR_KEYS = {
|
||
LOGIN_FAILED: "error.login_failed",
|
||
LOGIN_RATE_LIMITED: "error.login_rate_limited",
|
||
AUTH_REQUIRED: "error.auth_required",
|
||
SESSION_INVALID: "error.session_invalid",
|
||
REQUEST_JSON_INVALID: "error.request_invalid",
|
||
REQUEST_TOO_LARGE: "error.request_too_large",
|
||
UPLOAD_FILENAME_INVALID: "error.upload_filename",
|
||
UPLOAD_EMPTY: "error.upload_empty",
|
||
UPLOAD_TOO_LARGE: "error.upload_too_large",
|
||
UPLOAD_CONTENT_INVALID: "error.upload_invalid",
|
||
JOB_ID_INVALID: "error.job_id",
|
||
ROUTE_NOT_FOUND: "error.route_not_found",
|
||
STATIC_NOT_FOUND: "error.static_not_found",
|
||
QUERY_INVALID: "error.query_invalid",
|
||
DATABASE_UNAVAILABLE: "error.database_unavailable",
|
||
DATABASE_DRIVER_UNAVAILABLE: "error.database_read_unavailable",
|
||
DATABASE_CONFIG_MISSING: "error.database_read_unavailable",
|
||
DATABASE_TARGET_INVALID: "error.database_read_unavailable",
|
||
DATABASE_QUERY_FAILED: "error.database_read_unavailable",
|
||
DOWNLOAD_NOT_FOUND: "error.download_not_found",
|
||
DOWNLOAD_REQUEST_INVALID: "error.download_invalid",
|
||
DOWNLOAD_REFERENCE_INVALID: "error.download_invalid",
|
||
DOWNLOAD_UNAVAILABLE: "error.download_unavailable",
|
||
DOWNLOAD_IDENTITY_MISMATCH: "error.download_integrity",
|
||
BOOKING_EXCEL_SERVICE_UNAVAILABLE: "error.booking_unavailable",
|
||
BOOKING_EXCEL_PROCESSING_FAILED: "error.booking_extract_failed",
|
||
BOOKING_EXCEL_REVIEW_OPEN: "error.company_review_open",
|
||
BOOKING_EXCEL_SOURCE_REQUIRED: "error.company_source_required",
|
||
BOOKING_EXCEL_SOURCE_ALREADY_ACTIVATED: "error.company_source_already_active",
|
||
COMPANY_REPORT_MONTH_IN_FUTURE: "error.company_future",
|
||
COMPANY_REPORT_ALREADY_RUNNING: "error.company_running",
|
||
COMPANY_REPORT_GENERATOR_UNAVAILABLE: "error.company_unavailable",
|
||
COMPANY_REPORT_DOWNLOAD_NOT_FOUND: "error.company_download",
|
||
PROGRAMMATIC_PROCESSING_FAILED: "error.processing_failed",
|
||
MONTH_INVALID: "error.month_invalid",
|
||
MONTH_KEY_INVALID: "error.month_invalid",
|
||
CHANNEL_INVALID: "error.channel_invalid",
|
||
LOGIN_REQUEST_INVALID: "error.login_request_invalid",
|
||
CONTENT_LENGTH_REQUIRED: "error.content_length_required",
|
||
INTERNAL_ERROR: "error.internal",
|
||
PROCESSING_GATEWAY_UNAVAILABLE: "error.service_unavailable",
|
||
MONTHLY_GENERATOR_UNAVAILABLE: "error.service_unavailable",
|
||
MONTHLY_REQUEST_INVALID: "error.invalid_request",
|
||
MONTHLY_ALREADY_RUNNING: "error.monthly_busy",
|
||
MONTHLY_GENERATION_FAILED: "error.monthly_failed",
|
||
MONTHLY_NOT_FOUND: "error.monthly_not_found",
|
||
BOOKING_EXCEL_FILENAME_INVALID: "error.booking_filename",
|
||
BOOKING_EXCEL_REVIEW_REQUEST_INVALID: "error.review_request_invalid",
|
||
BOOKING_EXCEL_REVIEW_ITEM_INVALID: "error.review_item_invalid",
|
||
COMPANY_REPORT_REQUEST_INVALID: "error.company_request_invalid",
|
||
COMPANY_REPORT_PERIOD_INVALID: "error.company_period_invalid",
|
||
COMPANY_REPORT_COMPANY_INVALID: "error.invalid_company",
|
||
COMPANY_REPORT_JOB_NOT_FOUND: "error.company_job_not_found",
|
||
COMPANY_REPORT_DOWNLOAD_INVALID: "error.download_invalid",
|
||
DOWNLOAD_TOO_LARGE: "error.download_too_large",
|
||
};
|
||
|
||
const sourceToKey = new Map();
|
||
Object.entries(CATALOG).forEach(([key, values]) => {
|
||
if (!sourceToKey.has(values[0])) sourceToKey.set(values[0], key);
|
||
});
|
||
|
||
let locale = DEFAULT_LOCALE;
|
||
let translating = false;
|
||
const sourceTextValues = new WeakMap();
|
||
const sourceAttributeValues = new WeakMap();
|
||
|
||
function validLocale(value) {
|
||
return Object.prototype.hasOwnProperty.call(LOCALES, value) ? value : null;
|
||
}
|
||
|
||
function readCookie(name) {
|
||
const match = document.cookie.match(new RegExp(`(?:^|; )${name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}=([^;]*)`));
|
||
return match ? decodeURIComponent(match[1]) : "";
|
||
}
|
||
|
||
function writeLocaleCookie(value) {
|
||
document.cookie = `${COOKIE_KEY}=${encodeURIComponent(value)}; Path=/; Max-Age=31536000; SameSite=Strict`;
|
||
}
|
||
|
||
function resolveLocale() {
|
||
let candidate = null;
|
||
try { candidate = validLocale(window.localStorage.getItem(STORAGE_KEY)); } catch (_) { /* private mode */ }
|
||
return candidate || validLocale(readCookie(COOKIE_KEY)) || DEFAULT_LOCALE;
|
||
}
|
||
|
||
function currentMeta() {
|
||
return LOCALES[locale] || LOCALES[DEFAULT_LOCALE];
|
||
}
|
||
|
||
function interpolate(value, params = {}) {
|
||
return String(value).replace(/\{([A-Za-z0-9_]+)\}/g, (_, name) => String(params[name] ?? ""));
|
||
}
|
||
|
||
function t(key, params = {}) {
|
||
const entry = CATALOG[key];
|
||
if (!entry) return interpolate(key, params);
|
||
const index = locale === "en" ? 1 : locale === "th" ? 2 : 0;
|
||
return interpolate(entry[index] || entry[0], params);
|
||
}
|
||
|
||
function keyForSource(value) {
|
||
const normalized = String(value ?? "").trim();
|
||
return sourceToKey.get(normalized) || null;
|
||
}
|
||
|
||
function patternText(value) {
|
||
const source = String(value ?? "");
|
||
const trimmed = source.trim();
|
||
const exact = keyForSource(trimmed);
|
||
if (exact) return t(exact);
|
||
|
||
let match = /^共\s*([\d,]+)\s*条\s*·\s*本页\s*([\d,]+)\s*至\s*([\d,]+)$/.exec(trimmed);
|
||
if (match) return t("pagination.summary", { total: match[1], start: match[2], end: match[3] });
|
||
match = /^第\s*([\d,]+)\s*\/\s*([\d,]+)\s*页$/.exec(trimmed);
|
||
if (match) return t("pagination.page", { page: match[1], pages: match[2] });
|
||
match = /^已选\s*([\d,]+)\s*条$/.exec(trimmed);
|
||
if (match) return t("company.selected_count", { count: match[1] });
|
||
match = /^已删除\s*([\d,]+)\s*条房型记录$/.exec(trimmed);
|
||
if (match) return t("company.deleted_count", { count: match[1] });
|
||
match = /^删除\s*([\d,]+)\s*条$/.exec(trimmed);
|
||
if (match) return t("company.delete_count", { count: match[1] });
|
||
match = /^提取完成,\s*([\d,]+)\s*条需要人工确认$/.exec(trimmed);
|
||
if (match) return t("company.extract_pending", { count: match[1] });
|
||
match = /^✓\s*([\d,]+)\s*条$/.exec(trimmed);
|
||
if (match) return t("company.period_rows", { count: match[1] });
|
||
match = /^([\d,]+)\s*项$/.exec(trimmed);
|
||
if (match) return t("company.warning_items", { count: match[1] });
|
||
match = /^自动更新\s*·\s*([\d,]+)\s*秒$/.exec(trimmed);
|
||
if (match) return t("progress.auto_update", { seconds: match[1] });
|
||
match = /^周期结束:(.+?)\s*00:00(曼谷)$/.exec(trimmed);
|
||
if (match) return t("company.period_end_display", { date: match[1] });
|
||
match = /^周期尚未结束;可按当前已入库数据生成,周期结束时间为曼谷\s*(.+?)\s*00:00$/.exec(trimmed);
|
||
if (match) return t("period.release_hint", { date: match[1] });
|
||
match = /^(\d{4})年(\d{2})月,(.+)$/.exec(trimmed);
|
||
if (match) return t("company.at_period", { month: `${match[1]}年${match[2]}月`, period: match[3] });
|
||
match = /^(\d{4})年(\d{2})月$/.exec(trimmed);
|
||
if (match) return t("company.month_label", { year: match[1], month: match[2], monthName: `${match[1]}年${match[2]}月` });
|
||
match = /^任务\s+(.+)$/.exec(trimmed);
|
||
if (match) return t("task.selected_job", { jobId: match[1] });
|
||
match = /^查看任务\s+(.+?)\s+的全流程日志$/.exec(trimmed);
|
||
if (match) return t("task.view_job", { jobId: match[1] });
|
||
match = /^([\d,]+)\s*秒$/.exec(trimmed);
|
||
if (match) return `${match[1]} ${t("units.seconds")}`;
|
||
match = /^([\d,]+)\s*分\s*([\d,]+)\s*秒$/.exec(trimmed);
|
||
if (match) return `${match[1]} ${t("units.minutes")} ${match[2]} ${t("units.seconds")}`;
|
||
return source;
|
||
}
|
||
|
||
function translateValue(value) {
|
||
if (typeof value !== "string") return value;
|
||
if (value.includes("\n")) {
|
||
return value.split("\n").map((line) => patternText(line)).join("\n");
|
||
}
|
||
const leading = value.match(/^\s*/)?.[0] || "";
|
||
const trailing = value.match(/\s*$/)?.[0] || "";
|
||
return `${leading}${patternText(value)}${trailing}`;
|
||
}
|
||
|
||
function translateAttribute(value) {
|
||
const translated = translateValue(value);
|
||
if (translated !== value) return translated;
|
||
let match = /^ARR Report 首页$/.exec(value);
|
||
if (match) return t("brand.home");
|
||
match = /^查看任务\s+(.+?)\s+的全流程日志$/.exec(value);
|
||
if (match) return t("task.view_job", { jobId: match[1] });
|
||
match = /^选择\s+(.+)$/.exec(value);
|
||
if (match) return `${t("common.select")} ${match[1]}`;
|
||
match = /^(.+?)\s+房型$/.exec(value);
|
||
if (match) return `${match[1]} ${t("common.room_type")}`;
|
||
match = /^(.+?)\s+数量$/.exec(value);
|
||
if (match) return `${match[1]} ${t("common.quantity")}`;
|
||
match = /^(.+?)\s*·\s*退出登录$/.exec(value);
|
||
if (match) return `${match[1]} · ${t("auth.logout")}`;
|
||
return value;
|
||
}
|
||
|
||
function translateDom(root = document) {
|
||
if (translating || !root) return;
|
||
translating = true;
|
||
try {
|
||
const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
|
||
const nodes = [];
|
||
let node;
|
||
while ((node = walker.nextNode())) {
|
||
const parent = node.parentElement;
|
||
if (!parent || ["SCRIPT", "STYLE"].includes(parent.tagName)) continue;
|
||
nodes.push(node);
|
||
}
|
||
nodes.forEach((textNode) => {
|
||
const source = sourceTextValues.has(textNode) ? sourceTextValues.get(textNode) : textNode.nodeValue;
|
||
sourceTextValues.set(textNode, source);
|
||
const value = translateValue(source);
|
||
if (value !== textNode.nodeValue) textNode.nodeValue = value;
|
||
});
|
||
root.querySelectorAll?.("[aria-label], [title], [placeholder], [aria-valuetext]").forEach((element) => {
|
||
["aria-label", "title", "placeholder", "aria-valuetext"].forEach((attribute) => {
|
||
if (!element.hasAttribute(attribute)) return;
|
||
const values = sourceAttributeValues.get(element) || new Map();
|
||
const current = element.getAttribute(attribute);
|
||
const source = values.has(attribute) ? values.get(attribute) : current;
|
||
values.set(attribute, source);
|
||
sourceAttributeValues.set(element, values);
|
||
const next = translateAttribute(source);
|
||
if (next !== current) element.setAttribute(attribute, next);
|
||
});
|
||
});
|
||
root.querySelectorAll?.("[data-arr-locale-selector]").forEach((select) => {
|
||
select.value = locale;
|
||
select.setAttribute("aria-label", t("common.language"));
|
||
});
|
||
} finally {
|
||
translating = false;
|
||
}
|
||
}
|
||
|
||
function formatInteger(value) {
|
||
return new Intl.NumberFormat(currentMeta().intl, { maximumFractionDigits: 0 }).format(Number(value || 0));
|
||
}
|
||
|
||
function formatMoney(value, compact = false) {
|
||
const amount = Number(value || 0);
|
||
const formatter = new Intl.NumberFormat(currentMeta().intl, compact
|
||
? { notation: "compact", maximumFractionDigits: 2 }
|
||
: { maximumFractionDigits: 0 });
|
||
return `¥${formatter.format(amount)}`;
|
||
}
|
||
|
||
function formatDate(value, withTime = false) {
|
||
if (!value) return "—";
|
||
const date = new Date(value);
|
||
if (Number.isNaN(date.getTime())) return translateValue(String(value));
|
||
return new Intl.DateTimeFormat(currentMeta().intl, withTime
|
||
? { timeZone: "Asia/Bangkok", calendar: "gregory", year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", hourCycle: "h23" }
|
||
: { timeZone: "Asia/Bangkok", calendar: "gregory", year: "numeric", month: "2-digit", day: "2-digit" }
|
||
).format(date);
|
||
}
|
||
|
||
function formatMonth(value) {
|
||
const match = /^(\d{4})-(\d{2})$/.exec(value || "");
|
||
if (!match) return t("common.year_month_unselected");
|
||
const date = new Date(Date.UTC(Number(match[1]), Number(match[2]) - 1, 1));
|
||
return new Intl.DateTimeFormat(currentMeta().intl, { timeZone: "UTC", calendar: "gregory", year: "numeric", month: "long" }).format(date);
|
||
}
|
||
|
||
function formatDurationSeconds(value) {
|
||
const seconds = Number(value);
|
||
if (!Number.isFinite(seconds)) return t("common.none");
|
||
if (seconds < 60) return `${Math.max(1, Math.round(seconds))} ${t("units.seconds")}`;
|
||
return `${Math.floor(seconds / 60)} ${t("units.minutes")} ${Math.round(seconds % 60)} ${t("units.seconds")}`;
|
||
}
|
||
|
||
function errorMessage(code, fallback = "") {
|
||
const normalizedCode = String(code || "");
|
||
const key = ERROR_KEYS[normalizedCode] || (
|
||
normalizedCode.endsWith("_NOT_FOUND") ? "error.not_found"
|
||
: normalizedCode.includes("UNAVAILABLE") || normalizedCode.endsWith("_TIMEOUT") ? "error.service_unavailable"
|
||
: normalizedCode.endsWith("_INVALID") || normalizedCode.includes("REQUEST") ? "error.invalid_request"
|
||
: normalizedCode.endsWith("_FAILED") || normalizedCode.endsWith("_ERROR") ? "error.generic"
|
||
: null
|
||
);
|
||
if (key) return t(key);
|
||
const translated = fallback ? translateValue(fallback) : "";
|
||
const containsCjk = /[\u4e00-\u9fff]/.test(translated);
|
||
const containsThai = /[\u0e00-\u0e7f]/.test(translated);
|
||
if (translated && locale === DEFAULT_LOCALE) return translated;
|
||
if (translated && locale === "en" && !containsCjk && !containsThai) return translated;
|
||
if (translated && locale === "th" && !containsCjk && containsThai) return translated;
|
||
return t("error.generic");
|
||
}
|
||
|
||
function setLocale(next) {
|
||
const resolved = validLocale(next) || DEFAULT_LOCALE;
|
||
locale = resolved;
|
||
try { window.localStorage.setItem(STORAGE_KEY, resolved); } catch (_) { /* private mode */ }
|
||
writeLocaleCookie(resolved);
|
||
document.documentElement.lang = resolved;
|
||
translateDom();
|
||
document.dispatchEvent(new CustomEvent("arr:locale-change", { detail: { locale: resolved } }));
|
||
}
|
||
|
||
function bindSelectors() {
|
||
document.querySelectorAll("[data-arr-locale-selector]").forEach((select) => {
|
||
select.value = locale;
|
||
select.addEventListener("change", (event) => setLocale(event.currentTarget.value));
|
||
});
|
||
}
|
||
|
||
function init() {
|
||
locale = resolveLocale();
|
||
document.documentElement.lang = locale;
|
||
bindSelectors();
|
||
translateDom();
|
||
const observer = new MutationObserver((mutations) => {
|
||
if (locale === DEFAULT_LOCALE || translating) return;
|
||
mutations.forEach((mutation) => {
|
||
mutation.addedNodes.forEach((node) => {
|
||
if (node.nodeType === Node.ELEMENT_NODE) translateDom(node);
|
||
else if (node.nodeType === Node.TEXT_NODE) {
|
||
sourceTextValues.set(node, node.nodeValue);
|
||
node.nodeValue = translateValue(node.nodeValue);
|
||
}
|
||
});
|
||
});
|
||
});
|
||
observer.observe(document.body, { childList: true, subtree: true });
|
||
}
|
||
|
||
window.ARRI18n = {
|
||
CATALOG,
|
||
LOCALES,
|
||
get locale() { return locale; },
|
||
t,
|
||
text: translateValue,
|
||
translateDom,
|
||
errorMessage,
|
||
formatInteger,
|
||
formatMoney,
|
||
formatDate,
|
||
formatMonth,
|
||
formatDurationSeconds,
|
||
setLocale,
|
||
};
|
||
|
||
if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", init, { once: true });
|
||
else init();
|
||
})();
|