{"id":25631,"date":"2025-06-13T14:55:38","date_gmt":"2025-06-13T06:55:38","guid":{"rendered":"https:\/\/www.hkmu.edu.hk\/oetools\/?page_id=25631"},"modified":"2025-11-07T10:01:43","modified_gmt":"2025-11-07T02:01:43","slug":"agent-2-open-cc","status":"publish","type":"page","link":"https:\/\/www.hkmu.edu.hk\/oetools\/agent-2-open-cc\/","title":{"rendered":"Agent 2 Chinese Conversion"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"25631\" class=\"elementor elementor-25631\" data-elementor-settings=\"[]\">\n\t\t\t\t\t\t\t<div class=\"elementor-section-wrap\">\n\t\t\t\t\t\t\t<section class=\"has_eae_slider wavo-column-gap-default elementor-section elementor-top-section elementor-element elementor-element-c98372f elementor-section-boxed elementor-section-height-default elementor-section-height-default\" data-id=\"c98372f\" data-element_type=\"section\" data-settings=\"{&quot;jet_parallax_layout_list&quot;:[]}\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"has_eae_slider elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-55584bd\" data-id=\"55584bd\" data-element_type=\"column\" data-settings=\"{&quot;background_background&quot;:&quot;classic&quot;}\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t\t\t<div class=\"elementor-element elementor-element-816b5a8 elementor-widget__width-inherit elementor-widget elementor-widget-html\" data-id=\"816b5a8\" data-element_type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t<!DOCTYPE html>\r\n<html lang=\"zh\">\r\n<head>\r\n    <meta charset=\"UTF-8\">\r\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n    <title>Cross\u2011Region Chinese Convertor<\/title>\r\n    <script src=\"https:\/\/cdn.jsdelivr.net\/npm\/opencc-js@1.0.5\/dist\/umd\/full.min.js\"><\/script>\r\n    <script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/mammoth\/1.4.2\/mammoth.browser.min.js\"><\/script>\r\n    <script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/jszip\/3.10.1\/jszip.min.js\"><\/script>\r\n    <script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/iconv-lite\/0.6.3\/iconv-lite.min.js\"><\/script>\r\n    <script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/pdf.js\/3.11.174\/pdf.min.js\"><\/script>\r\n    <style>\r\n\t\t\/* ================ GLOBAL STYLES ================ *\/\r\n\t\t:root {\r\n\t\t\t--primary-color: #fa975e;\r\n\t\t\t--primary-light: #e8864a;\r\n\t\t\t--primary-dark: #d87a40;\r\n\t\t\t--primary-blue: #6495ED;\r\n\t\t\t--accent-color: #fa975e;\r\n\t\t\t--accent-green: #7AC143;\r\n\t\t\t--light-gray: #f9f5f3;\r\n\t\t\t--medium-gray: #e0e0e0;\r\n\t\t\t--dark-gray: #707070;\r\n\t\t\t--success-color: #7AC143;\r\n\t\t\t--danger-color: #f44336;\r\n\t\t\t--warning-color: #ff9800;\r\n\t\t\t--info-color: #fa975e;\r\n\t\t\t--primary-red: #e53e3e;\r\n\t\t\t--accent-orange: #ed8936;\r\n\t\t\t--dark-text: #333;\r\n\t\t\t--light-text: #666;\r\n\t\t\t--card-bg: rgba(255, 255, 255, 0.95);\r\n\t\t\t--bg-light: linear-gradient(135deg, #fa975e, #e8864a);\r\n\t\t\t--shadow: 0 15px 40px rgba(0, 0, 0, 0.2);\r\n\t\t}\r\n\r\n\t\t* {\r\n\t\t\tbox-sizing: border-box;\r\n\t\t\tmargin: 0;\r\n\t\t\tpadding: 0;\r\n\t\t}\r\n\r\n\t\tbody {\r\n\t\t\tfont-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;\r\n\t\t\tbackground: var(--bg-light);\r\n\t\t\tcolor: var(--dark-text);\r\n\t\t\tline-height: 1.6;\r\n\t\t\tpadding: 0px;\r\n\t\t\tmin-height: 100vh;\r\n\t\t}\r\n\r\n\t\t.container {\r\n\t\t\tmax-width: 1800px;\r\n\t\t\tmargin: 0 auto;\r\n\t\t\tbackground: var(--card-bg);\r\n\t\t\tborder-radius: 15px;\r\n\t\t\tbox-shadow: var(--shadow);\r\n\t\t\toverflow: hidden;\r\n\t\t\tpadding: 10px; \/* Added 10px internal margin *\/\r\n\t\t}\r\n\r\n\t\t\/* ================ HEADER STYLES ================ *\/\r\n\t\theader {\r\n\t\t\ttext-align: center;\r\n\t\t\tmargin-bottom: 30px;\r\n\t\t\tpadding: 20px 0;\r\n\t\t\tbackground: linear-gradient(45deg, var(--primary-color), var(--primary-light));\r\n\t\t\tposition: relative;\r\n\t\t\toverflow: hidden;\r\n\t\t\tborder-radius: 15px; \/* Added border radius since container now has padding *\/\r\n\t\t}\r\n\r\n\t\theader::before {\r\n\t\t\tcontent: \"\";\r\n\t\t\tposition: absolute;\r\n\t\t\ttop: -50%;\r\n\t\t\tleft: -50%;\r\n\t\t\twidth: 200%;\r\n\t\t\theight: 200%;\r\n\t\t\tbackground: radial-gradient(circle, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0) 70%);\r\n\t\t\ttransform: rotate(30deg);\r\n\t\t}\r\n\r\n\t\th1 {\r\n\t\t\tcolor: #fff;\r\n\t\t\tfont-size: 2.5rem;\r\n\t\t\tmargin-bottom: 10px;\r\n\t\t\ttext-shadow: 0 2px 4px rgba(0,0,0,0.3);\r\n\t\t}\r\n\r\n\t\t.subtitle {\r\n\t\t\tcolor: #fff;\r\n\t\t\tfont-size: 1.1rem;\r\n\t\t\tmax-width: 800px;\r\n\t\t\tmargin: 0 auto;\r\n\t\t\topacity: 0.9;\r\n\t\t\tposition: relative;\r\n\t\t}\r\n\r\n\t\t\/* ================ CARD STYLES ================ *\/\r\n\t\t.card {\r\n\t\t\tbackground: var(--card-bg);\r\n\t\t\tborder-radius: 12px;\r\n\t\t\tbox-shadow: var(--shadow);\r\n\t\t\tpadding: 25px;\r\n\t\t\tmargin-bottom: 25px;\r\n\t\t\ttransition: all 0.3s ease;\r\n\t\t}\r\n\r\n\t\t.card:hover {\r\n\t\t\ttransform: translateY(-3px);\r\n\t\t\tbox-shadow: 0 8px 25px rgba(0,0,0,0.15);\r\n\t\t}\r\n\r\n\t\t.card-title {\r\n\t\t\tfont-size: 1.25rem;\r\n\t\t\tcolor: var(--primary-color);\r\n\t\t\tmargin-bottom: 20px;\r\n\t\t\tpadding-bottom: 10px;\r\n\t\t\tborder-bottom: 2px solid var(--light-gray);\r\n\t\t\tfont-weight: 600;\r\n\t\t\tdisplay: flex;\r\n\t\t\talign-items: center;\r\n\t\t\tgap: 10px;\r\n\t\t}\r\n\r\n\t\t\/* ================ CONVERSION SETTINGS ================ *\/\r\n\t\t.settings-grid {\r\n\t\t\tdisplay: grid;\r\n\t\t\tgrid-template-columns: repeat(auto-fit, minmax(300px, 1fr));\r\n\t\t\tgap: 25px;\r\n\t\t\tmargin-bottom: 15px;\r\n\t\t}\r\n\r\n\t\t\/* Second row for regional conversion - single column *\/\r\n\t\t.settings-grid.second-row {\r\n\t\t\tgrid-template-columns: 1fr;\r\n\t\t}\r\n\r\n\t\t.option-group {\r\n\t\t\tmargin-bottom: 20px;\r\n\t\t}\r\n\r\n\t\t.option-group h3 {\r\n\t\t\tfont-size: 1.8rem;\r\n\t\t\tcolor: var(--primary-light);\r\n\t\t\tfont-weight: 700;\r\n\t\t\tmargin-bottom: 12px;\r\n\t\t}\r\n\r\n\t\t.radio-group {\r\n\t\t\tdisplay: flex;\r\n\t\t\tflex-wrap: wrap;\r\n\t\t\tgap: 10px;\r\n\t\t}\r\n\r\n\t\t.radio-option {\r\n\t\t\tdisplay: flex;\r\n\t\t\tfont-size: 1.1rem;\r\n\t\t\talign-items: center;\r\n\t\t\tbackground: var(--light-gray);\r\n\t\t\tpadding: 10px 16px;\r\n\t\t\tborder-radius: 30px;\r\n\t\t\tcursor: pointer;\r\n\t\t\ttransition: all 0.2s ease;\r\n\t\t\tfont-weight: 500;\r\n\t\t}\r\n\r\n\t\t.radio-option:hover {\r\n\t\t\tbackground: var(--medium-gray);\r\n\t\t}\r\n\r\n\t\t.radio-option.active {\r\n\t\t\tbackground: var(--primary-color);\r\n\t\t\tcolor: white;\r\n\t\t}\r\n\r\n\t\t.radio-option.disabled {\r\n\t\t\tbackground: var(--medium-gray);\r\n\t\t\tcolor: var(--light-text);\r\n\t\t\tcursor: not-allowed;\r\n\t\t\topacity: 0.5;\r\n\t\t}\r\n\r\n\t\t.radio-option input {\r\n\t\t\tmargin-right: 8px;\r\n\t\t}\r\n\r\n\t\t.radio-option input:disabled {\r\n\t\t\tcursor: not-allowed;\r\n\t\t}\r\n\r\n\t\t\/* ================ CONVERTER CONTAINER ================ *\/\r\n\t\t.converter-container {\r\n\t\t\tdisplay: grid;\r\n\t\t\tgrid-template-rows: auto 1fr;\r\n\t\t\tgap: 25px;\r\n\t\t\tmargin-top: 15px;\r\n\t\t}\r\n\r\n\t\t.file-upload-row {\r\n\t\t\twidth: 100%;\r\n\t\t}\r\n\r\n\t\t.text-panels-row {\r\n\t\t\tdisplay: grid;\r\n\t\t\tgrid-template-columns: 1fr 1fr;\r\n\t\t\tgap: 25px;\r\n\t\t}\r\n\r\n\t\t@media (max-width: 768px) {\r\n\t\t\t.text-panels-row {\r\n\t\t\t\tgrid-template-columns: 1fr;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t.text-panel {\r\n\t\t\tdisplay: flex;\r\n\t\t\tflex-direction: column;\r\n\t\t}\r\n\r\n\t\t.text-panel h3 {\r\n\t\t\tfont-size: 1.8rem;\r\n\t\t\tcolor: var(--primary-color);\r\n\t\t\tmargin-bottom: 15px;\r\n\t\t\tfont-weight: 700;\r\n\t\t}\r\n\r\n\t\ttextarea {\r\n\t\t\twidth: 100%;\r\n\t\t\theight: 250px;\r\n\t\t\tpadding: 15px;\r\n\t\t\tborder: 1px solid var(--medium-gray);\r\n\t\t\tborder-radius: 8px;\r\n\t\t\tresize: vertical;\r\n\t\t\tfont-size: 16px;\r\n\t\t\tfont-family: inherit;\r\n\t\t\tbackground: var(--card-bg);\r\n\t\t\ttransition: border-color 0.2s;\r\n\t\t}\r\n\r\n\t\ttextarea:focus {\r\n\t\t\toutline: none;\r\n\t\t\tborder-color: var(--primary-color);\r\n\t\t\tbox-shadow: 0 0 0 3px rgba(250, 151, 94, 0.2);\r\n\t\t}\r\n\r\n\t\ttextarea#outputText {\r\n\t\t\tbackground-color: #f9fbfd;\r\n\t\t}\r\n\r\n\t\t\/* ================ FONT STYLES FOR DIFFERENT CHINESE VARIANTS ================ *\/\r\n\t\t.font-simplified {\r\n\t\t\tfont-family: \"Microsoft YaHei\", \"\u5fae\u8f6f\u96c5\u9ed1\", sans-serif;\r\n\t\t}\r\n\r\n\t\t.font-traditional {\r\n\t\t\tfont-family: \"Microsoft JhengHei\", \"\u5fae\u8edf\u6b63\u9ed1\u9ad4\", sans-serif;\r\n\t\t}\r\n\r\n\t\t\/* ================ BUTTON STYLES ================ *\/\r\n\t\t.button-group {\r\n\t\t\tdisplay: flex;\r\n\t\t\tjustify-content: flex-end;\r\n\t\t\tgap: 10px;\r\n\t\t\tmargin-top: 15px;\r\n\t\t}\r\n\r\n\t\t.button-group.left-aligned {\r\n\t\t\tjustify-content: flex-start;\r\n\t\t}\r\n\r\n\t\tbutton {\r\n\t\t\tpadding: 8px 28px;\r\n\t\t\tborder: none;\r\n\t\t\tborder-radius: 8px;\r\n\t\t\tcursor: pointer;\r\n\t\t\tfont-size: 24px;\r\n\t\t\tfont-weight: 600;\r\n\t\t\ttransition: all 0.3s ease;\r\n\t\t\tdisplay: flex;\r\n\t\t\talign-items: center;\r\n\t\t\tjustify-content: center;\r\n\t\t\tbox-shadow: 0 4px 8px rgba(250, 151, 94, 0.3);\r\n\t\t\tmin-width: 120px; \/* Ensure consistent button sizes *\/\r\n\t\t}\r\n\r\n\t\t#convertBtn {\r\n\t\t\tbackground-color: #7AC143;\r\n\t\t\tcolor: white;\r\n\t\t}\r\n\r\n\t\t#convertBtn:hover {\r\n\t\t\tbackground-color: #5a8f32;\r\n\t\t\ttransform: translateY(-2px);\r\n\t\t\tbox-shadow: 0 6px 12px rgba(250, 151, 94, 0.4);\r\n\t\t}\r\n\r\n\t\t#convertBtn:disabled {\r\n\t\t\tbackground-color: var(--medium-gray);\r\n\t\t\tcursor: not-allowed;\r\n\t\t\ttransform: none;\r\n\t\t\tbox-shadow: none;\r\n\t\t}\r\n\r\n\t\t.copy-btn {\r\n\t\t\tbackground-color: #7AC143;\r\n\t\t\tcolor: white;\r\n\t\t}\r\n\r\n\t\t.copy-btn:hover {\r\n\t\t\tbackground-color: #5a8f32;\r\n\t\t\ttransform: translateY(-2px);\r\n\t\t\tbox-shadow: 0 6px 12px rgba(122, 193, 67, 0.4);\r\n\t\t}\r\n\r\n\t\t.copy-btn:disabled {\r\n\t\t\tbackground-color: var(--medium-gray);\r\n\t\t\tcursor: not-allowed;\r\n\t\t\ttransform: none;\r\n\t\t\tbox-shadow: none;\r\n\t\t}\r\n\r\n\t\t.download-btn {\r\n\t\t\tbackground-color: #7AC143;\r\n\t\t\tcolor: white;\r\n\t\t\tdisplay: none; \/* Hidden by default, shown when file is uploaded *\/\r\n\t\t}\r\n\r\n\t\t.download-btn:hover {\r\n\t\t\tbackground-color: #5a8f32;\r\n\t\t\ttransform: translateY(-2px);\r\n\t\t\tbox-shadow: 0 6px 12px rgba(250, 151, 94, 0.4);\r\n\t\t}\r\n\r\n\t\t\/* ================ FILE UPLOAD ================ *\/\r\n\t\t.file-upload {\r\n\t\t\tpadding: 20px;\r\n\t\t\tborder: 2px dashed var(--primary-color);\r\n\t\t\tborder-radius: 8px;\r\n\t\t\tfont-size:1.2rem;\r\n\t\t\ttext-align: center;\r\n\t\t\tcursor: pointer;\r\n\t\t\ttransition: all 0.3s;\r\n\t\t\tbackground: rgba(250, 151, 94, 0.05);\r\n\t\t\twidth: 100%;\r\n\t\t}\r\n\r\n\t\t.file-upload:hover {\r\n\t\t\tborder-color: var(--primary-light);\r\n\t\t\tbackground-color: rgba(232, 134, 74, 0.15);\r\n\t\t}\r\n\r\n\t\t.file-upload input {\r\n\t\t\tdisplay: none;\r\n\t\t}\r\n\r\n\t\t.file-upload label {\r\n\t\t\tdisplay: block;\r\n\t\t\tcursor: pointer;\r\n\t\t\tfont-weight: 500;\r\n\t\t\tcolor: var(--primary-light);\r\n\t\t}\r\n\r\n\t\t.file-info {\r\n\t\t\tmargin-top: 10px;\r\n\t\t\tfont-size: 14px;\r\n\t\t\tcolor: var(--light-text);\r\n\t\t}\r\n\r\n\t\t\/* ================ STATUS & PROGRESS ================ *\/\r\n\t\t.status {\r\n\t\t\tmargin-top: 15px;\r\n\t\t\tpadding: 12px;\r\n\t\t\tborder-radius: 8px;\r\n\t\t\tfont-style: normal;\r\n\t\t\tmin-height: 20px;\r\n\t\t\tfont-weight: 500;\r\n\t\t\tborder: 1px solid transparent;\r\n\t\t}\r\n\r\n\t\t.status.info {\r\n\t\t\tbackground-color: #fff3cd;\r\n\t\t\tcolor: #856404;\r\n\t\t\tborder-color: #ffeaa7;\r\n\t\t}\r\n\r\n\t\t.status.error {\r\n\t\t\tbackground-color: #f8d7da;\r\n\t\t\tcolor: #721c24;\r\n\t\t\tborder-color: #f5c6cb;\r\n\t\t}\r\n\r\n\t\t.status.success {\r\n\t\t\tbackground-color: #d4edda;\r\n\t\t\tcolor: #155724;\r\n\t\t\tborder-color: #c3e6cb;\r\n\t\t}\r\n\r\n\t\t.loading {\r\n\t\t\tdisplay: none;\r\n\t\t\tmargin-top: 15px;\r\n\t\t\ttext-align: center;\r\n\t\t\tcolor: var(--primary-light);\r\n\t\t}\r\n\r\n\t\t.spinner {\r\n\t\t\tborder: 4px solid rgba(0, 0, 0, 0.1);\r\n\t\t\tborder-top: 4px solid var(--primary-color);\r\n\t\t\tborder-radius: 50%;\r\n\t\t\twidth: 24px;\r\n\t\t\theight: 24px;\r\n\t\t\tanimation: spin 1s linear infinite;\r\n\t\t\tdisplay: inline-block;\r\n\t\t\tmargin-right: 10px;\r\n\t\t}\r\n\r\n\t\t@keyframes spin {\r\n\t\t\t0% { transform: rotate(0deg); }\r\n\t\t\t100% { transform: rotate(360deg); }\r\n\t\t}\r\n\r\n\t\t.progress-bar {\r\n\t\t\twidth: 100%;\r\n\t\t\theight: 8px;\r\n\t\t\tbackground-color: var(--light-gray);\r\n\t\t\tborder-radius: 4px;\r\n\t\t\tmargin-top: 15px;\r\n\t\t\toverflow: hidden;\r\n\t\t\tdisplay: none;\r\n\t\t}\r\n\r\n\t\t.progress-fill {\r\n\t\t\theight: 100%;\r\n\t\t\tbackground-color: var(--primary-color);\r\n\t\t\twidth: 0%;\r\n\t\t\ttransition: width 0.3s ease;\r\n\t\t}\r\n\r\n\t\t\/* ================ EXAMPLES SECTION ================ *\/\r\n\t\t.examples {\r\n\t\t\tbackground: var(--card-bg);\r\n\t\t\tborder-radius: 12px;\r\n\t\t\tpadding: 20px;\r\n\t\t\tmargin-top: 25px;\r\n\t\t\tbox-shadow: var(--shadow);\r\n\t\t\tborder-left: 4px solid var(--primary-color);\r\n\t\t}\r\n\r\n\r\n\t\t.examples h3 {\r\n\t\t\tcolor: var(--primary-color);\r\n\t\t\tmargin-bottom: 15px;\r\n\t\t\tfont-weight: 600;\r\n\t\t\tfont-size: 1.5rem;\r\n\t\t}\r\n\r\n\t\t.examples th {\r\n\t\t\tpadding-left: 2px;\r\n\t\t\tcolor: var(--dark-text);\r\n\t\t\tfont-size: 1.1rem;\r\n\t\t}\r\n\r\n\t\t.examples ul {\r\n\t\t\tpadding-left: 25px;\r\n\t\t\tcolor: var(--dark-text);\r\n\t\t\tfont-size: 1.1rem;\r\n\t\t}\r\n\r\n\t\t\/* Font styling for example rows *\/\r\n\t\t.examples .example-simplified td {\r\n\t\t\tfont-family: \"Microsoft YaHei\", \"\u5fae\u8f6f\u96c5\u9ed1\", sans-serif;\r\n\t\t}\r\n\r\n\t\t.examples .example-traditional td {\r\n\t\t\tfont-family: \"Microsoft JhengHei\", \"\u5fae\u8edf\u6b63\u9ed1\u9ad4\", sans-serif;\r\n\t\t}\r\n\r\n\r\n\t\t\/* ================ ACKNOWLEDGEMENT ================ *\/\r\n\t\t.acknowledgement {\r\n\t\t\ttext-align: center;\r\n\t\t\tmargin-top: 40px;\r\n\t\t\tpadding-top: 20px;\r\n\t\t\tcolor: var(--light-text);\r\n\t\t\tfont-size: 0.9rem;\r\n\t\t\tborder-top: 1px solid var(--light-gray);\r\n\t\t}\r\n\r\n\t\t.acknowledgement a {\r\n\t\t\tcolor: var(--primary-light);\r\n\t\t\ttext-decoration: none;\r\n\t\t\tfont-weight: 600;\r\n\t\t}\r\n\r\n\t\t.acknowledgement a:hover {\r\n\t\t\ttext-decoration: underline;\r\n\t\t}\r\n    <\/style>\r\n<\/head>\r\n<body>\r\n    <div class=\"container\">\r\n        <header>\r\n            <h1>Cross\u2011Region Chinese Convertor<\/h1>\r\n            <p class=\"subtitle\">Effortlessly convert across Hong Kong Traditional, Taiwan Traditional, and Simplified Chinese in 12 file formats while preserving original layout and nuance.<\/p>\r\n        <\/header>\r\n\r\n        <div class=\"card\">\r\n            <!-- First row: Source and Target -->\r\n            <div class=\"settings-grid\">\r\n                <div class=\"option-group\">\r\n                    <h3>\u539f\u6587<\/h3>\r\n                    <div class=\"radio-group\">\r\n                        <label class=\"radio-option\">\r\n                            <input type=\"radio\" name=\"source\" value=\"hk\"> \u4e2d\u570b\u9999\u6e2f\u7e41\u9ad4\r\n                        <\/label>\r\n                        <label class=\"radio-option\">\r\n                            <input type=\"radio\" name=\"source\" value=\"cn\" checked> \u7c21\u9ad4\u4e2d\u6587\r\n                        <\/label>\r\n                        <label class=\"radio-option\">\r\n                            <input type=\"radio\" name=\"source\" value=\"tw\"> \u4e2d\u570b\u81fa\u7063\u7e41\u9ad4\r\n                        <\/label>\r\n                    <\/div>\r\n                <\/div>\r\n                \r\n                <div class=\"option-group\">\r\n                    <h3>\u76ee\u6a19<\/h3>\r\n                    <div class=\"radio-group\">\r\n                        <label class=\"radio-option\">\r\n                            <input type=\"radio\" name=\"target\" value=\"hk\" checked> \u4e2d\u570b\u9999\u6e2f\u7e41\u9ad4\r\n                        <\/label>\r\n                        <label class=\"radio-option\">\r\n                            <input type=\"radio\" name=\"target\" value=\"cn\"> \u7c21\u9ad4\u4e2d\u6587\r\n                        <\/label>\r\n                        <label class=\"radio-option\">\r\n                            <input type=\"radio\" name=\"target\" value=\"tw\"> \u4e2d\u570b\u81fa\u7063\u7e41\u9ad4\r\n                        <\/label>\r\n                    <\/div>\r\n                <\/div>\r\n            <\/div>\r\n\r\n            <!-- Second row: Regional conversion (merged) -->\r\n            <div class=\"settings-grid second-row\">\r\n                <div class=\"option-group\">\r\n                    <h3>\u5730\u5340\u7570\u9ad4\u5b57\u8f49\u63db<\/h3>\r\n                    <div class=\"radio-group\">\r\n                        <label class=\"radio-option\">\r\n                            <input type=\"radio\" name=\"regional\" value=\"hk\" data-variant=\"hk\" data-phrase=\"none\" checked> \u4e2d\u570b\u9999\u6e2f\u6a21\u5f0f\r\n                        <\/label>\r\n                        <label class=\"radio-option\">\r\n                            <input type=\"radio\" name=\"regional\" value=\"tw\" data-variant=\"tw\" data-phrase=\"tw\"> \u4e2d\u570b\u81fa\u7063\u6a21\u5f0f\r\n                        <\/label>\r\n                        <label class=\"radio-option\">\r\n                            <input type=\"radio\" name=\"regional\" value=\"none\" data-variant=\"opencc\" data-phrase=\"none\"> \u4e0d\u8f49\u63db\u7570\u9ad4\u5b57\r\n                        <\/label>\r\n                    <\/div>\r\n                <\/div>\r\n            <\/div>\r\n            \r\n            <div class=\"status info\" id=\"status\">\u6e96\u5099\u5c31\u7dd2<\/div>\r\n            <div class=\"loading\" id=\"loading\">\r\n                <div class=\"spinner\"><\/div>\r\n                <span>\u8655\u7406\u4e2d...<\/span>\r\n            <\/div>\r\n            <div class=\"progress-bar\" id=\"progressBar\">\r\n                <div class=\"progress-fill\" id=\"progressFill\"><\/div>\r\n            <\/div>\r\n        <\/div>\r\n        \r\n        <div class=\"converter-container\">\r\n            <!-- First row: File upload (full width) -->\r\n            <div class=\"file-upload-row\">\r\n                <div class=\"file-upload\" id=\"inputFileUpload\">\r\n                    <input type=\"file\" id=\"inputFile\" accept=\".txt,.srt,.json,.xml,.html,.md,.csv,.ass,.ssa,.docx,.pptx,.pdf\">\r\n                    <label for=\"inputFile\">\u62d6\u653e\u6587\u4ef6\u5230\u9019\u88e1\u6216\u9ede\u64ca\u4e0a\u50b3<\/label>\r\n                    <div class=\"file-info\" id=\"inputFileInfo\">\u652f\u6301\u683c\u5f0f: txt, srt, json, xml, html, md, csv, ass, ssa, docx, pptx, pdf<\/div>\r\n                <\/div>\r\n            <\/div>\r\n            \r\n            <!-- Second row: Text panels -->\r\n            <div class=\"text-panels-row\">\r\n                <div class=\"text-panel\">\r\n                    <h3>\u8f38\u5165\u6587\u672c<\/h3>\r\n                    <textarea id=\"inputText\" placeholder=\"\u8acb\u8f38\u5165\u8981\u8f49\u63db\u7684\u6587\u672c...\"><\/textarea>\r\n                    <div class=\"button-group\">\r\n                        <button id=\"convertBtn\">\u8f49\u63db<\/button>\r\n                    <\/div>\r\n                <\/div>\r\n                \r\n                <div class=\"text-panel\">\r\n                    <h3>\u8f49\u63db\u7d50\u679c<\/h3>\r\n                    <textarea id=\"outputText\" readonly placeholder=\"\u8f49\u63db\u7d50\u679c\u5c07\u986f\u793a\u5728\u9019\u88e1...\"><\/textarea>\r\n                    <div class=\"button-group left-aligned\">\r\n\t\t\t\t\t\t<button class=\"copy-btn\" id=\"copyBtn\" disabled>\u8907\u88fd\u7d50\u679c<\/button>\r\n\t\t\t\t\t\t<button class=\"download-btn\" id=\"downloadBtn\">\u4e0b\u8f09\u7d50\u679c<\/button>\r\n\t\t\t\t\t<\/div>\r\n\r\n                <\/div>\r\n            <\/div>\r\n        <\/div>\r\n\r\n        <div class=\"examples\">\r\n            <h3><strong>\u5730\u5340\u7570\u9ad4\u5b57\u8f49\u63db<\/strong>\uff1a\u6839\u64da\u7576\u5730\u6163\u7528\u8a5e\u5f59\u53ca\u5b57\u5f62\u8f49\u63db<\/h3>\r\n\t\t\t\t<table>\r\n\t\t\t\t  <thead>\r\n\t\t\t\t\t<tr>\r\n\t\t\t\t\t  <th>\u5730\u5340<\/th>\r\n\t\t\t\t\t  <td> <\/td>\t\t\t\t\t  \r\n\t\t\t\t\t  <th>\u7570\u9ad4\u5b57\u8f49\u63db\u7bc4\u4f8b<\/th>\r\n\t\t\t\t\t<\/tr>\r\n\t\t\t\t  <\/thead>\r\n\t\t\t\t  <tbody>\r\n\t\t\t\t\t<tr class=\"example-simplified\">\r\n\t\t\t\t\t  <td>\u4e2d\u56fd\u5185\u5730\u7b80\u4f53<\/td>\r\n\t\t\t\t\t  <td>\uff1a <\/td>\t\t\t\t\t  \r\n\t\t\t\t\t  <td>\u9879\u76ee\u7684\u7b14\u8bb0\u672c\u7535\u8111\u9644\u8d60\u7684\u9f20\u6807\u91cc\u7684\u7845\u4e8c\u6781\u7ba1\u574f\u4e86\uff0c\u5bfc\u81f4\u5149\u6807\u5cf0\u503c\u5206\u8fa8\u7387\u964d\u4f4e\uff0c\u8bf7\u4fdd\u6301\u529e\u516c\u5ba4\u536b\u751f\u3002<\/td>\r\n\t\t\t\t\t<\/tr>\r\n\t\t\t\t\t<tr class=\"example-traditional\">\r\n\t\t\t\t\t  <td>\u4e2d\u570b\u81fa\u7063\u7e41\u9ad4<\/td>\r\n\t\t\t\t\t  <td>\uff1a <\/td>\r\n\t\t\t\t\t  <td>\u5c08\u6848\u7684\u819d\u4e0a\u578b\u96fb\u8166\u9644\u8d08\u7684\u6ed1\u9f20\u88e1\u7684\u77fd\u4e8c\u6975\u9ad4\u58de\u4e86\uff0c\u5c0e\u81f4\u6e38\u6a19\u5cf0\u503c\u89e3\u6790\u5ea6\u964d\u4f4e\uff0c\u8acb\u4fdd\u6301\u8fa6\u516c\u5ba4\u885b\u751f\u3002<\/td>\r\n\t\t\t\t\t<\/tr>\r\n\t\t\t\t\t<tr class=\"example-traditional\">\r\n\t\t\t\t\t  <td>\u4e2d\u570b\u9999\u6e2f\u7e41\u9ad4<\/td>\r\n\t\t\t\t\t  <td>\uff1a <\/td>\t\t\t\t\t  \r\n\t\t\t\t\t  <td>\u9805\u76ee\u7684\u7b46\u8a18\u672c\u96fb\u8166\u9644\u8d08\u7684\u9f20\u6a19\u88cf\u7684\u7845\u4e8c\u6975\u7ba1\u58de\u4e86\uff0c\u5c0e\u81f4\u5149\u6a19\u5cef\u503c\u5206\u8fa8\u7387\u964d\u4f4e\uff0c\u8acb\u4fdd\u6301\u8fa6\u516c\u5ba4\u885e\u751f\u3002<\/td>\r\n\t\t\t\t\t<\/tr>\r\n\t\t\t\t  <\/tbody>\r\n\t\t\t\t<\/table>\t\r\n        <\/div>\r\n\r\n        <div class=\"acknowledgement\">\r\n            \u672c\u5de5\u5177\u57fa\u65bc <a href=\"https:\/\/github.com\/BYVoid\/OpenCC\" target=\"_blank\">OpenCC<\/a> \u958b\u767c\r\n        <\/div>\r\n    <\/div>\r\n\r\n    <script>\r\n        document.addEventListener('DOMContentLoaded', function() {\r\n            \/\/ Initialize PDF.js worker\r\n            pdfjsLib.GlobalWorkerOptions.workerSrc = 'https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/pdf.js\/3.11.174\/pdf.worker.min.js';\r\n            \r\n            const inputText = document.getElementById('inputText');\r\n            const outputText = document.getElementById('outputText');\r\n            const convertBtn = document.getElementById('convertBtn');\r\n            const copyBtn = document.getElementById('copyBtn');\r\n            const downloadBtn = document.getElementById('downloadBtn');\r\n            const status = document.getElementById('status');\r\n            const loading = document.getElementById('loading');\r\n            const progressBar = document.getElementById('progressBar');\r\n            const progressFill = document.getElementById('progressFill');\r\n            const inputFile = document.getElementById('inputFile');\r\n            const inputFileUpload = document.getElementById('inputFileUpload');\r\n            const inputFileInfo = document.getElementById('inputFileInfo');\r\n            \r\n            let currentFileName = '';\r\n            let currentFileType = '';\r\n            let currentFileExtension = '';\r\n            \r\n            \/\/ Enhanced status display\r\n            function setStatus(message, type = 'info') {\r\n                status.textContent = message;\r\n                status.className = `status ${type}`;\r\n            }\r\n            \r\n            function showLoading(show = true) {\r\n                loading.style.display = show ? 'block' : 'none';\r\n                convertBtn.disabled = show;\r\n            }\r\n            \r\n            function showProgress(show = true, progress = 0) {\r\n                progressBar.style.display = show ? 'block' : 'none';\r\n                if (show) {\r\n                    progressFill.style.width = `${progress}%`;\r\n                }\r\n            }\r\n            \r\n            \/\/ Calculate ratio of valid text characters\r\n            function calculateValidTextRatio(text) {\r\n                if (!text || text.length === 0) return 0;\r\n                \r\n                let validChars = 0;\r\n                for (let i = 0; i < text.length; i++) {\r\n                    const char = text[i];\r\n                    const code = char.charCodeAt(0);\r\n                    \r\n                    \/\/ Count as valid: ASCII printable, Chinese characters, common punctuation\r\n                    if ((code >= 32 && code <= 126) ||  \/\/ ASCII printable\r\n                        (code >= 0x4E00 && code <= 0x9FFF) ||  \/\/ CJK Unified Ideographs\r\n                        (code >= 0x3400 && code <= 0x4DBF) ||  \/\/ CJK Extension A\r\n                        (code >= 0x3000 && code <= 0x303F) ||  \/\/ CJK Symbols and Punctuation\r\n                        (code >= 0xFF00 && code <= 0xFFEF) ||  \/\/ Halfwidth and Fullwidth Forms\r\n                        char === '\\n' || char === '\\r' || char === '\\t') {\r\n                        validChars++;\r\n                    }\r\n                }\r\n                \r\n                return validChars \/ text.length;\r\n            }\r\n            \r\n            \/\/ Auto-detect file encoding with improved heuristics\r\n            function detectEncoding(buffer) {\r\n                const bytes = new Uint8Array(buffer);\r\n                \r\n                \/\/ Check for BOM\r\n                if (bytes.length >= 3 && bytes[0] === 0xEF && bytes[1] === 0xBB && bytes[2] === 0xBF) {\r\n                    return 'utf-8';\r\n                }\r\n                if (bytes.length >= 2 && bytes[0] === 0xFF && bytes[1] === 0xFE) {\r\n                    return 'utf-16le';\r\n                }\r\n                if (bytes.length >= 2 && bytes[0] === 0xFE && bytes[1] === 0xFF) {\r\n                    return 'utf-16be';\r\n                }\r\n                \r\n                \/\/ Check if all bytes are ASCII\r\n                let isAscii = true;\r\n                for (let i = 0; i < Math.min(1000, bytes.length); i++) {\r\n                    if (bytes[i] > 127) {\r\n                        isAscii = false;\r\n                        break;\r\n                    }\r\n                }\r\n                \r\n                if (isAscii) return 'utf-8';\r\n                \r\n                \/\/ Try to detect Chinese encodings using byte patterns\r\n                \/\/ Test for valid UTF-8 sequences\r\n                let isValidUTF8 = true;\r\n                for (let i = 0; i < bytes.length - 1; i++) {\r\n                    if (bytes[i] > 127) {\r\n                        if ((bytes[i] & 0xE0) === 0xC0) {\r\n                            \/\/ 2-byte sequence\r\n                            if (i + 1 >= bytes.length || (bytes[i + 1] & 0xC0) !== 0x80) {\r\n                                isValidUTF8 = false;\r\n                                break;\r\n                            }\r\n                            i++;\r\n                        } else if ((bytes[i] & 0xF0) === 0xE0) {\r\n                            \/\/ 3-byte sequence\r\n                            if (i + 2 >= bytes.length || \r\n                                (bytes[i + 1] & 0xC0) !== 0x80 || \r\n                                (bytes[i + 2] & 0xC0) !== 0x80) {\r\n                                isValidUTF8 = false;\r\n                                break;\r\n                            }\r\n                            i += 2;\r\n                        } else if ((bytes[i] & 0xF8) === 0xF0) {\r\n                            \/\/ 4-byte sequence\r\n                            if (i + 3 >= bytes.length || \r\n                                (bytes[i + 1] & 0xC0) !== 0x80 || \r\n                                (bytes[i + 2] & 0xC0) !== 0x80 || \r\n                                (bytes[i + 3] & 0xC0) !== 0x80) {\r\n                                isValidUTF8 = false;\r\n                                break;\r\n                            }\r\n                            i += 3;\r\n                        } else {\r\n                            isValidUTF8 = false;\r\n                            break;\r\n                        }\r\n                    }\r\n                }\r\n                \r\n                if (isValidUTF8) return 'utf-8';\r\n                \r\n                \/\/ Check for GBK patterns (common Chinese characters)\r\n                let gbkLikeCount = 0;\r\n                for (let i = 0; i < bytes.length - 1; i++) {\r\n                    if (bytes[i] >= 0xA1 && bytes[i] <= 0xFE && \r\n                        bytes[i + 1] >= 0xA1 && bytes[i + 1] <= 0xFE) {\r\n                        gbkLikeCount++;\r\n                        i++; \/\/ Skip next byte\r\n                    }\r\n                }\r\n                \r\n                \/\/ Check for Big5 patterns\r\n                let big5LikeCount = 0;\r\n                for (let i = 0; i < bytes.length - 1; i++) {\r\n                    if (bytes[i] >= 0xA4 && bytes[i] <= 0xF9 && \r\n                        ((bytes[i + 1] >= 0x40 && bytes[i + 1] <= 0x7E) || \r\n                         (bytes[i + 1] >= 0xA1 && bytes[i + 1] <= 0xFE))) {\r\n                        big5LikeCount++;\r\n                        i++; \/\/ Skip next byte\r\n                    }\r\n                }\r\n                \r\n                \/\/ Return most likely encoding\r\n                if (big5LikeCount > gbkLikeCount) {\r\n                    return 'big5';\r\n                } else if (gbkLikeCount > 0) {\r\n                    return 'gbk';\r\n                }\r\n                \r\n                \/\/ Default fallback\r\n                return 'gbk';\r\n            }\r\n            \r\n            \/\/ Read file with auto-detected encoding using built-in TextDecoder\r\n            function readFileWithEncoding(file, encoding) {\r\n                return new Promise((resolve, reject) => {\r\n                    const reader = new FileReader();\r\n                    reader.onload = function(e) {\r\n                        try {\r\n                            const buffer = e.target.result;\r\n                            let text;\r\n                            \r\n                            if (encoding === 'utf-8' || encoding === 'utf-16le' || encoding === 'utf-16be') {\r\n                                text = new TextDecoder(encoding).decode(buffer);\r\n                            } else {\r\n                                \/\/ For other encodings, try TextDecoder first\r\n                                try {\r\n                                    text = new TextDecoder(encoding).decode(buffer);\r\n                                } catch (decoderError) {\r\n                                    \/\/ If TextDecoder fails, try reading as binary and convert manually\r\n                                    console.warn(`TextDecoder failed for ${encoding}, trying fallback method`);\r\n                                    \r\n                                    if (encoding === 'gbk' || encoding === 'gb2312') {\r\n                                        \/\/ Simple GBK\/GB2312 fallback - may not be perfect but better than failure\r\n                                        const bytes = new Uint8Array(buffer);\r\n                                        text = '';\r\n                                        for (let i = 0; i < bytes.length; i++) {\r\n                                            if (bytes[i] < 128) {\r\n                                                text += String.fromCharCode(bytes[i]);\r\n                                            } else if (i + 1 < bytes.length) {\r\n                                                \/\/ Convert GBK bytes to Unicode (simplified approach)\r\n                                                const code = (bytes[i] << 8) | bytes[i + 1];\r\n                                                text += String.fromCharCode(code);\r\n                                                i++; \/\/ Skip next byte\r\n                                            }\r\n                                        }\r\n                                    } else if (encoding === 'big5') {\r\n                                        \/\/ Simple Big5 fallback\r\n                                        const bytes = new Uint8Array(buffer);\r\n                                        text = '';\r\n                                        for (let i = 0; i < bytes.length; i++) {\r\n                                            if (bytes[i] < 128) {\r\n                                                text += String.fromCharCode(bytes[i]);\r\n                                            } else if (i + 1 < bytes.length) {\r\n                                                const code = (bytes[i] << 8) | bytes[i + 1];\r\n                                                text += String.fromCharCode(code);\r\n                                                i++;\r\n                                            }\r\n                                        }\r\n                                    } else {\r\n                                        \/\/ Ultimate fallback - try UTF-8\r\n                                        text = new TextDecoder('utf-8', { fatal: false }).decode(buffer);\r\n                                    }\r\n                                }\r\n                            }\r\n                            \r\n                            resolve(text);\r\n                        } catch (error) {\r\n                            reject(error);\r\n                        }\r\n                    };\r\n                    reader.onerror = () => reject(reader.error);\r\n                    reader.readAsArrayBuffer(file);\r\n                });\r\n            }\r\n            \r\n            \/\/ Extract text from DOCX\r\n            async function extractDocxText(file) {\r\n                try {\r\n                    const arrayBuffer = await file.arrayBuffer();\r\n                    const result = await mammoth.extractRawText({ arrayBuffer });\r\n                    return result.value;\r\n                } catch (error) {\r\n                    throw new Error('\u7121\u6cd5\u8b80\u53d6DOCX\u6587\u4ef6: ' + error.message);\r\n                }\r\n            }\r\n            \r\n            \/\/ Extract text from PPTX\r\n            async function extractPptxText(file) {\r\n                try {\r\n                    const arrayBuffer = await file.arrayBuffer();\r\n                    const zip = await JSZip.loadAsync(arrayBuffer);\r\n                    let text = '';\r\n                    \r\n                    \/\/ Extract text from slides\r\n                    const slideFiles = Object.keys(zip.files).filter(name => \r\n                        name.startsWith('ppt\/slides\/slide') && name.endsWith('.xml')\r\n                    );\r\n                    \r\n                    for (const slideFile of slideFiles) {\r\n                        const content = await zip.file(slideFile).async('string');\r\n                        \/\/ Simple XML parsing to extract text\r\n                        const textMatches = content.match(\/<a:t[^>]*>([^<]*)<\\\/a:t>\/g);\r\n                        if (textMatches) {\r\n                            textMatches.forEach(match => {\r\n                                const textContent = match.replace(\/<[^>]*>\/g, '');\r\n                                if (textContent.trim()) {\r\n                                    text += textContent + '\\n';\r\n                                }\r\n                            });\r\n                        }\r\n                    }\r\n                    \r\n                    return text;\r\n                } catch (error) {\r\n                    throw new Error('\u7121\u6cd5\u8b80\u53d6PPTX\u6587\u4ef6: ' + error.message);\r\n                }\r\n            }\r\n            \r\n            \/\/ Extract text from PDF\r\n            async function extractPdfText(file) {\r\n                try {\r\n                    const arrayBuffer = await file.arrayBuffer();\r\n                    setStatus('\u6b63\u5728\u8f09\u5165PDF\u6587\u4ef6...', 'info');\r\n                    showProgress(true, 10);\r\n                    \r\n                    \/\/ Load PDF document\r\n                    const pdf = await pdfjsLib.getDocument(arrayBuffer).promise;\r\n                    \r\n                    \/\/ Get metadata\r\n                    const metadata = await pdf.getMetadata();\r\n                    console.log('PDF Metadata:', metadata);\r\n                    \r\n                    const numPages = pdf.numPages;\r\n                    let fullText = '';\r\n                    \r\n                    setStatus(`\u6b63\u5728\u89e3\u6790PDF\u6587\u4ef6 (\u5171${numPages}\u9801)...`, 'info');\r\n                    \r\n                    \/\/ Process each page\r\n                    for (let pageNum = 1; pageNum <= numPages; pageNum++) {\r\n                        const page = await pdf.getPage(pageNum);\r\n                        const textContent = await page.getTextContent();\r\n                        \r\n                        \/\/ Extract text from page\r\n                        const pageText = textContent.items\r\n                            .filter(item => item.str && item.str.trim())\r\n                            .map(item => item.str)\r\n                            .join(' ');\r\n                        \r\n                        if (pageText.trim()) {\r\n                            fullText += pageText + '\\n\\n';\r\n                        }\r\n                        \r\n                        \/\/ Update progress\r\n                        const progress = Math.round((pageNum \/ numPages) * 80) + 10;\r\n                        showProgress(true, progress);\r\n                        setStatus(`\u6b63\u5728\u89e3\u6790\u7b2c${pageNum}\/${numPages}\u9801...`, 'info');\r\n                        \r\n                        \/\/ Allow UI to update\r\n                        await new Promise(resolve => setTimeout(resolve, 10));\r\n                    }\r\n                    \r\n                    showProgress(true, 100);\r\n                    \r\n                    if (!fullText.trim()) {\r\n                        throw new Error('PDF\u6587\u4ef6\u4e2d\u672a\u627e\u5230\u53ef\u63d0\u53d6\u7684\u6587\u672c\u5167\u5bb9');\r\n                    }\r\n                    \r\n                    return fullText.trim();\r\n                } catch (error) {\r\n                    if (error.name === 'InvalidPDFException') {\r\n                        throw new Error('\u7121\u6548\u7684PDF\u6587\u4ef6\u683c\u5f0f');\r\n                    } else if (error.name === 'MissingPDFException') {\r\n                        throw new Error('PDF\u6587\u4ef6\u640d\u58de\u6216\u4e0d\u5b8c\u6574');\r\n                    } else if (error.name === 'UnexpectedResponseException') {\r\n                        throw new Error('PDF\u6587\u4ef6\u8b80\u53d6\u5931\u6557');\r\n                    } else {\r\n                        throw new Error('PDF\u89e3\u6790\u932f\u8aa4: ' + error.message);\r\n                    }\r\n                }\r\n            }\r\n            \r\n            \/\/ Updated map configuration based on new merged selections\r\n            function getConversionConfig() {\r\n                const source = document.querySelector('input[name=\"source\"]:checked').value;\r\n                const target = document.querySelector('input[name=\"target\"]:checked').value;\r\n                const regional = document.querySelector('input[name=\"regional\"]:checked');\r\n                const variant = regional.getAttribute('data-variant');\r\n                const phrase = regional.getAttribute('data-phrase');\r\n                \r\n                let config = { from: source, to: target };\r\n                \r\n                \/\/ Handle variants and phrases based on merged options\r\n                if (regional.value === 'tw' && target === 'tw') {\r\n                    config.to = 'twp'; \/\/ Taiwan variant with phrases\r\n                } else if (regional.value === 'hk' && target === 'hk') {\r\n                    config.to = 'hk'; \/\/ Hong Kong variant\r\n                } else if (regional.value === 'none') {\r\n                    \/\/ Keep original target, no variant conversion\r\n                    config.to = target;\r\n                }\r\n                \r\n                return config;\r\n            }\r\n            \r\n            \/\/ Validate and update regional options based on target selection\r\n            function updateRegionalOptions() {\r\n                const target = document.querySelector('input[name=\"target\"]:checked').value;\r\n                const regionalOptions = document.querySelectorAll('input[name=\"regional\"]');\r\n                \r\n                regionalOptions.forEach(option => {\r\n                    const optionValue = option.value;\r\n                    const label = option.parentNode;\r\n                    \r\n                    \/\/ Reset all options first\r\n                    option.disabled = false;\r\n                    label.classList.remove('disabled');\r\n                    \r\n                    \/\/ Apply validation rules\r\n                    if (target === 'cn') {\r\n                        \/\/ When target is cn, only allow \"\u4e0d\u8f49\u63db\u7570\u9ad4\u5b57\"\r\n                        if (optionValue !== 'none') {\r\n                            option.disabled = true;\r\n                            label.classList.add('disabled');\r\n                        }\r\n                    } else if (target === 'tw') {\r\n                        \/\/ When target is tw, only allow \"\u4e0d\u8f49\u63db\u7570\u9ad4\u5b57\" or \"\u4e2d\u570b\u81fa\u7063\u6a21\u5f0f\"\r\n                        if (optionValue === 'hk') {\r\n                            option.disabled = true;\r\n                            label.classList.add('disabled');\r\n                        }\r\n                    } else if (target === 'hk') {\r\n                        \/\/ When target is hk, only allow \"\u4e0d\u8f49\u63db\u7570\u9ad4\u5b57\" or \"\u4e2d\u570b\u9999\u6e2f\u6a21\u5f0f\"\r\n                        if (optionValue === 'tw') {\r\n                            option.disabled = true;\r\n                            label.classList.add('disabled');\r\n                        }\r\n                    }\r\n                });\r\n                \r\n                \/\/ If current selection is disabled, switch to \"\u4e0d\u8f49\u63db\u7570\u9ad4\u5b57\"\r\n                const currentRegional = document.querySelector('input[name=\"regional\"]:checked');\r\n                if (currentRegional.disabled) {\r\n                    const noneOption = document.querySelector('input[name=\"regional\"][value=\"none\"]');\r\n                    noneOption.checked = true;\r\n                    \/\/ Update visual state\r\n                    document.querySelectorAll('input[name=\"regional\"]').forEach(r => {\r\n                        r.parentNode.classList.remove('active');\r\n                    });\r\n                    noneOption.parentNode.classList.add('active');\r\n                }\r\n            }\r\n\t\t\t\r\n            \/\/ Update textarea fonts based on source and target selections\r\n            function updateTextareaFonts() {\r\n                const source = document.querySelector('input[name=\"source\"]:checked').value;\r\n                const target = document.querySelector('input[name=\"target\"]:checked').value;\r\n                \r\n                \/\/ Update input textarea font based on SOURCE selection\r\n                if (source === 'cn') {\r\n                    inputText.classList.remove('font-traditional');\r\n                    inputText.classList.add('font-simplified');\r\n                } else if (source === 'tw' || source === 'hk') {\r\n                    inputText.classList.remove('font-simplified');\r\n                    inputText.classList.add('font-traditional');\r\n                }\r\n                \r\n                \/\/ Update output textarea font based on TARGET selection\r\n                if (target === 'cn') {\r\n                    outputText.classList.remove('font-traditional');\r\n                    outputText.classList.add('font-simplified');\r\n                } else if (target === 'tw' || target === 'hk') {\r\n                    outputText.classList.remove('font-simplified');\r\n                    outputText.classList.add('font-traditional');\r\n                }\r\n            }\r\n\t\t\t\r\n            \/\/ Update download button text based on file format\r\n\t\t\tfunction updateDownloadButtonText() {\r\n\t\t\t\tif (!currentFileName) {\r\n\t\t\t\t\tdownloadBtn.textContent = '\u4e0b\u8f09\u7d50\u679c';\r\n\t\t\t\t\treturn;\r\n\t\t\t\t}\r\n\t\t\t\t\r\n\t\t\t\t\/\/ Determine output format based on input file type\r\n\t\t\t\tlet outputFormat;\r\n\t\t\t\tif (currentFileExtension === 'docx' || currentFileExtension === 'pptx' || currentFileExtension === 'pdf') {\r\n\t\t\t\t\toutputFormat = 'txt'; \/\/ These formats are converted to txt\r\n\t\t\t\t} else {\r\n\t\t\t\t\toutputFormat = currentFileExtension; \/\/ Keep original format\r\n\t\t\t\t}\r\n\t\t\t\t\r\n\t\t\t\tdownloadBtn.textContent = `\u4e0b\u8f09${outputFormat.toUpperCase()}`;\r\n\t\t\t}\r\n\r\n            \/\/ Handle file upload\r\n            inputFileUpload.addEventListener('dragover', (e) => {\r\n                e.preventDefault();\r\n                inputFileUpload.style.borderColor = '#4CAF50';\r\n                inputFileUpload.style.backgroundColor = '#f0fff0';\r\n            });\r\n            \r\n            inputFileUpload.addEventListener('dragleave', () => {\r\n                inputFileUpload.style.borderColor = '#ddd';\r\n                inputFileUpload.style.backgroundColor = '';\r\n            });\r\n            \r\n            inputFileUpload.addEventListener('drop', (e) => {\r\n                e.preventDefault();\r\n                inputFileUpload.style.borderColor = '#ddd';\r\n                inputFileUpload.style.backgroundColor = '';\r\n                \r\n                if (e.dataTransfer.files.length) {\r\n                    inputFile.files = e.dataTransfer.files;\r\n                    handleFileUpload();\r\n                }\r\n            });\r\n            \r\n            inputFile.addEventListener('change', handleFileUpload);\r\n            \r\n            async function handleFileUpload() {\r\n                const file = inputFile.files[0];\r\n                if (!file) return;\r\n                \r\n                currentFileName = file.name;\r\n                currentFileExtension = file.name.split('.').pop().toLowerCase();\r\n                currentFileType = currentFileExtension;\r\n                \r\n                \/\/ Check if file type is supported\r\n                const supportedTypes = ['txt', 'srt', 'json', 'xml', 'html', 'md', 'csv', 'ass', 'ssa', 'docx', 'pptx', 'pdf'];\r\n                if (!supportedTypes.includes(currentFileExtension)) {\r\n                    setStatus('\u932f\u8aa4: \u4e0d\u652f\u6301\u7684\u6587\u4ef6\u985e\u578b', 'error');\r\n                    return;\r\n                }\r\n                \r\n                showLoading(true);\r\n                showProgress(false);\r\n                \r\n                try {\r\n                    let text = '';\r\n                    \r\n                    if (currentFileExtension === 'pdf') {\r\n                        text = await extractPdfText(file);\r\n                    } else if (currentFileExtension === 'docx') {\r\n                        text = await extractDocxText(file);\r\n                    } else if (currentFileExtension === 'pptx') {\r\n                        text = await extractPptxText(file);\r\n                    } else {\r\n                        \/\/ Auto-detect encoding for text files\r\n                        const buffer = await file.arrayBuffer();\r\n                        const detectedEncoding = detectEncoding(buffer);\r\n                        setStatus(`\u81ea\u52d5\u6aa2\u6e2c\u7de8\u78bc: ${detectedEncoding.toUpperCase()}`, 'info');\r\n                        \r\n                        try {\r\n                            text = await readFileWithEncoding(file, detectedEncoding);\r\n                            \r\n                            \/\/ Validate the text - check if it contains mostly valid characters\r\n                            const validTextRatio = calculateValidTextRatio(text);\r\n                            if (validTextRatio < 0.7) {\r\n                                throw new Error('\u7de8\u78bc\u6aa2\u6e2c\u53ef\u80fd\u4e0d\u6b63\u78ba\uff0c\u5617\u8a66UTF-8\u7de8\u78bc');\r\n                            }\r\n                            \r\n                        } catch (encodingError) {\r\n                            if (detectedEncoding !== 'utf-8') {\r\n                                \/\/ Fallback to UTF-8\r\n                                setStatus('\u7de8\u78bc\u6aa2\u6e2c\u5931\u6557\uff0c\u5617\u8a66UTF-8...', 'info');\r\n                                text = await readFileWithEncoding(file, 'utf-8');\r\n                            } else {\r\n                                throw encodingError;\r\n                            }\r\n                        }\r\n                    }\r\n                    \r\n                    inputText.value = text;\r\n\t\t\t\t\tinputFileInfo.textContent = `\u5df2\u52a0\u8f09: ${file.name}`;\r\n\t\t\t\t\tsetStatus('\u6587\u4ef6\u5df2\u52a0\u8f09\uff0c\u8acb\u9ede\u64ca\u8f49\u63db\u6309\u9215', 'success');\r\n\r\n\t\t\t\t\t\/\/ Update download button text based on file type\r\n\t\t\t\t\tupdateDownloadButtonText();\r\n\r\n                    \r\n                } catch (error) {\r\n                    setStatus('\u932f\u8aa4: \u8b80\u53d6\u6587\u4ef6\u5931\u6557 - ' + error.message, 'error');\r\n                    console.error('File reading error:', error);\r\n                } finally {\r\n                    showLoading(false);\r\n                    showProgress(false);\r\n                }\r\n            }\r\n            \r\n            convertBtn.addEventListener('click', async function() {\r\n                const text = inputText.value.trim();\r\n                if (!text) {\r\n                    setStatus('\u8acb\u8f38\u5165\u8981\u8f49\u63db\u7684\u6587\u672c', 'error');\r\n                    return;\r\n                }\r\n                \r\n                showLoading(true);\r\n                \r\n                try {\r\n                    const config = getConversionConfig();\r\n                    const converter = OpenCC.Converter(config);\r\n                    const convertedText = converter(text);\r\n                    outputText.value = convertedText;\r\n                    copyBtn.disabled = false;\r\n                    \r\n\t\t\t\t\t\/\/ Show download button if file was uploaded\r\n\t\t\t\t\tif (currentFileName) {\r\n\t\t\t\t\t\tdownloadBtn.style.display = 'inline-flex';\r\n\t\t\t\t\t\tupdateDownloadButtonText();\r\n\t\t\t\t\t} else {\r\n\t\t\t\t\t\tdownloadBtn.style.display = 'none';\r\n}\r\n                    \r\n                    setStatus('\u8f49\u63db\u5b8c\u6210\uff01', 'success');\r\n                } catch (error) {\r\n                    setStatus('\u8f49\u63db\u6642\u51fa\u932f: ' + error.message, 'error');\r\n                    console.error('Conversion error:', error);\r\n                } finally {\r\n                    showLoading(false);\r\n                }\r\n            });\r\n            \r\n            copyBtn.addEventListener('click', async function() {\r\n                if (!outputText.value) return;\r\n                \r\n                try {\r\n                    await navigator.clipboard.writeText(outputText.value);\r\n                    \r\n                    \/\/ Visual feedback\r\n                    const originalText = copyBtn.textContent;\r\n                    copyBtn.textContent = '\u5df2\u8907\u88fd\uff01';\r\n                    setStatus('\u7d50\u679c\u5df2\u8907\u88fd\u5230\u526a\u8cbc\u7c3f', 'success');\r\n                    \r\n                    setTimeout(() => {\r\n                        copyBtn.textContent = originalText;\r\n                    }, 2000);\r\n                } catch (error) {\r\n                    \/\/ Fallback for older browsers\r\n                    outputText.select();\r\n                    document.execCommand('copy');\r\n                    setStatus('\u7d50\u679c\u5df2\u8907\u88fd\u5230\u526a\u8cbc\u7c3f', 'success');\r\n                }\r\n            });\r\n            \r\n            downloadBtn.addEventListener('click', function() {\r\n                if (!outputText.value || !currentFileName) return;\r\n                \r\n                try {\r\n                    const blob = new Blob([outputText.value], { type: 'text\/plain;charset=utf-8' });\r\n                    const url = URL.createObjectURL(blob);\r\n                    const a = document.createElement('a');\r\n                    a.href = url;\r\n                    \r\n                    \/\/ Create new filename with conversion info\r\n                    const source = document.querySelector('input[name=\"source\"]:checked').value;\r\n                    const target = document.querySelector('input[name=\"target\"]:checked').value;\r\n                    const fileNameParts = currentFileName.split('.');\r\n                    const baseName = fileNameParts.slice(0, -1).join('.');\r\n                    const extension = (currentFileExtension === 'docx' || currentFileExtension === 'pptx' || currentFileExtension === 'pdf') ? 'txt' : currentFileExtension;\r\n                    const newFileName = `${baseName}_${source}2${target}.${extension}`;\r\n                    \r\n                    a.download = newFileName;\r\n                    document.body.appendChild(a);\r\n                    a.click();\r\n                    document.body.removeChild(a);\r\n                    URL.revokeObjectURL(url);\r\n                    \r\n                    setStatus(`\u6587\u4ef6\u5df2\u4e0b\u8f09: ${newFileName}`, 'success');\r\n                } catch (error) {\r\n                    setStatus('\u4e0b\u8f09\u5931\u6557: ' + error.message, 'error');\r\n                    console.error('Download error:', error);\r\n                }\r\n            });\r\n            \r\n            \/\/ Update radio button styles when selected\r\n            document.querySelectorAll('.radio-option input').forEach(radio => {\r\n                radio.addEventListener('change', function() {\r\n                    \/\/ Remove active class from all options in the same group\r\n                    document.querySelectorAll(`input[name=\"${this.name}\"]`).forEach(r => {\r\n                        r.parentNode.classList.remove('active');\r\n                    });\r\n                    \/\/ Add active class to selected option\r\n                    this.parentNode.classList.add('active');\r\n                    \r\n                    \/\/ Update regional options when target changes\r\n                    if (this.name === 'target') {\r\n                        updateRegionalOptions();\r\n                    }\r\n                    \r\n                    \/\/ Update textarea fonts when source or target changes\r\n                    if (this.name === 'source' || this.name === 'target') {\r\n                        updateTextareaFonts();\r\n                    }\r\n                });\r\n            });\r\n            \r\n            \/\/ Initialize active states and regional options\r\n            document.querySelectorAll('.radio-option input:checked').forEach(radio => {\r\n                radio.parentNode.classList.add('active');\r\n            });\r\n            \r\n            \/\/ Initialize regional options validation\r\n            updateRegionalOptions();\r\n\t\t\t\r\n\t\t\t\/\/ Initialize textarea fonts based on default selections\r\n\t\t\tupdateTextareaFonts();\r\n\t\t\t\r\n\t\t\t\/\/ Initialize download button text\r\n\t\t\tupdateDownloadButtonText();\r\n\r\n        });\r\n    <\/script>\r\n<\/body>\r\n<\/html>\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>Cross\u2011Region Chinese Convertor Cross\u2011Region Chinese Convertor Effortlessly convert across Hong Kong Traditional, Taiwan Traditional, and Simplified Chinese in 12 file formats while preserving original layout and nuance. \u539f\u6587 \u4e2d\u570b\u9999\u6e2f\u7e41\u9ad4 \u7c21\u9ad4\u4e2d\u6587 \u4e2d\u570b\u81fa\u7063\u7e41\u9ad4 \u76ee\u6a19 \u4e2d\u570b\u9999\u6e2f\u7e41\u9ad4 \u7c21\u9ad4\u4e2d\u6587 \u4e2d\u570b\u81fa\u7063\u7e41\u9ad4 \u5730\u5340\u7570\u9ad4\u5b57\u8f49\u63db \u4e2d\u570b\u9999\u6e2f\u6a21\u5f0f \u4e2d\u570b\u81fa\u7063\u6a21\u5f0f \u4e0d\u8f49\u63db\u7570\u9ad4\u5b57 \u6e96\u5099\u5c31\u7dd2 \u8655\u7406\u4e2d... \u62d6\u653e\u6587\u4ef6\u5230\u9019\u88e1\u6216\u9ede\u64ca\u4e0a\u50b3 \u652f\u6301\u683c\u5f0f: txt, srt, json, xml, html, md, csv, ass, ssa, docx, pptx, pdf...<\/p>\n","protected":false},"author":748,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"redux-templates_canvas","meta":{"_expiration-date-status":"","_expiration-date":0,"_expiration-date-type":"","_expiration-date-categories":[],"_expiration-date-options":[]},"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v17.3 (Yoast SEO v21.2) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Agent 2 Chinese Conversion - Hong Kong Metropolitan University<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.hkmu.edu.hk\/oetools\/agent-2-open-cc\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Agent 2 Chinese Conversion\" \/>\n<meta property=\"og:description\" content=\"Cross\u2011Region Chinese Convertor Cross\u2011Region Chinese Convertor Effortlessly convert across Hong Kong Traditional, Taiwan Traditional, and Simplified\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.hkmu.edu.hk\/oetools\/agent-2-open-cc\/\" \/>\n<meta property=\"og:site_name\" content=\"Hong Kong Metropolitan University\" \/>\n<meta property=\"article:modified_time\" content=\"2025-11-07T02:01:43+00:00\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data1\" content=\"1 minute\" \/>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Agent 2 Chinese Conversion - Hong Kong Metropolitan University","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.hkmu.edu.hk\/oetools\/agent-2-open-cc\/","og_locale":"en_US","og_type":"article","og_title":"Agent 2 Chinese Conversion","og_description":"Cross\u2011Region Chinese Convertor Cross\u2011Region Chinese Convertor Effortlessly convert across Hong Kong Traditional, Taiwan Traditional, and Simplified","og_url":"https:\/\/www.hkmu.edu.hk\/oetools\/agent-2-open-cc\/","og_site_name":"Hong Kong Metropolitan University","article_modified_time":"2025-11-07T02:01:43+00:00","twitter_card":"summary_large_image","twitter_misc":{"Est. reading time":"1 minute"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.hkmu.edu.hk\/oetools\/agent-2-open-cc\/","url":"https:\/\/www.hkmu.edu.hk\/oetools\/agent-2-open-cc\/","name":"Agent 2 Chinese Conversion - Hong Kong Metropolitan University","isPartOf":{"@id":"https:\/\/www.hkmu.edu.hk\/oetools\/#website"},"datePublished":"2025-06-13T06:55:38+00:00","dateModified":"2025-11-07T02:01:43+00:00","breadcrumb":{"@id":"https:\/\/www.hkmu.edu.hk\/oetools\/agent-2-open-cc\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.hkmu.edu.hk\/oetools\/agent-2-open-cc\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.hkmu.edu.hk\/oetools\/agent-2-open-cc\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Open Educational Tools","item":"\/oetools\/"},{"@type":"ListItem","position":2,"name":"Agent 2 Chinese Conversion"}]},{"@type":"WebSite","@id":"https:\/\/www.hkmu.edu.hk\/oetools\/#website","url":"https:\/\/www.hkmu.edu.hk\/oetools\/","name":"Hong Kong Metropolitan University","description":"Open Educational Tools - Hong Kong Metropolitan University","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.hkmu.edu.hk\/oetools\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"en-US"}]}},"_links":{"self":[{"href":"https:\/\/www.hkmu.edu.hk\/oetools\/wp-json\/wp\/v2\/pages\/25631"}],"collection":[{"href":"https:\/\/www.hkmu.edu.hk\/oetools\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.hkmu.edu.hk\/oetools\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.hkmu.edu.hk\/oetools\/wp-json\/wp\/v2\/users\/748"}],"replies":[{"embeddable":true,"href":"https:\/\/www.hkmu.edu.hk\/oetools\/wp-json\/wp\/v2\/comments?post=25631"}],"version-history":[{"count":119,"href":"https:\/\/www.hkmu.edu.hk\/oetools\/wp-json\/wp\/v2\/pages\/25631\/revisions"}],"predecessor-version":[{"id":31091,"href":"https:\/\/www.hkmu.edu.hk\/oetools\/wp-json\/wp\/v2\/pages\/25631\/revisions\/31091"}],"wp:attachment":[{"href":"https:\/\/www.hkmu.edu.hk\/oetools\/wp-json\/wp\/v2\/media?parent=25631"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}