/** * AlaskahQR - Barcode Generator */ const BCState = { data: '1234567890128', format: 'CODE128', productName: '', displayValue: true, fgColor: '#000000', bgColor: '#ffffff', titleColor: '#000000', titleSize: 16, titleWeight: 'normal', barWidth: 2, barHeight: 100, margin: 10, fontSize: 16, downloadScale: 1, }; const BC_UI_I18N = { ko: { uiOptions: '⚙️ 옵션 설정', uiTabInput: '내용', uiTabStyle: '스타일', uiTabSize: '크기', uiFormatLabel: '바코드 형식', uiDataLabel: '바코드 데이터', uiDataPlaceholder: '바코드에 담을 숫자 또는 텍스트', uiProductLabel: '제품명', uiProductSmall: '바코드 위에 표시됩니다', uiProductPlaceholder: '예: 홍길동 과자 300g', uiProductHelp: '비워두면 제품명이 표시되지 않습니다', uiDisplayValue: '하단 숫자 표시', uiShow: '표시', uiHide: '숨김', uiBarColorLabel: '바 색상 (전경색)', uiBarColorHelp: '바코드 색상', uiBgColorLabel: '배경색', uiBgColorHelp: '배경 색상', uiTitleColorLabel: '제품명 색상', uiTitleColorHelp: '제품명 텍스트 색상', uiTitleSizeLabel: '제품명 글꼴 크기', uiTitleWeightLabel: '제품명 글꼴 두께', uiWeightNormal: '보통', uiWeightBold: '굵게', uiWeightBolder: '아주 굵게', uiPresetLabel: '색상 프리셋', uiPresetClassic: '클래식', uiPresetBlue: '블루', uiPresetGreen: '그린', uiPresetBrown: '브라운', uiPresetDark: '다크', uiBarWidthLabel: '바 너비', uiBarHeightLabel: '바코드 높이', uiMarginLabel: '여백 (margin)', uiFontSizeLabel: '하단 숫자 크기', uiPreview: '👁️ 미리보기', uiDownloadScale: '다운로드 배율', uiDownloadPng: '⬇️ PNG 다운로드', uiDownloadSvg: '📄 SVG 다운로드', uiInfoEmpty: '내용을 입력하면 바코드가 자동으로 생성됩니다', uiAd: '광고 영역 (AdSense)', uiGuideTitle: '바코드 형식 안내', uiGuide1Desc: '가장 범용적인 형식. 영문, 숫자, 특수문자를 모두 지원하며 길이 제한이 없습니다.', uiGuide2Desc: '국제 표준 소매 상품 코드. 마트, 편의점 등 소매점 상품에 주로 사용됩니다 (13자리).', uiGuide3Desc: '미국/캐나다 소매 상품 코드. 북미 시장 진출 시 필요한 12자리 바코드입니다.', uiGuide4Desc: '물류 박스용 바코드. 여러 개의 소매 상품을 묶은 포장 단위에 사용됩니다.', uiFmtCode128: 'CODE128 - 범용 (영문/숫자)', uiFmtCode39: 'CODE39 - 산업용', uiFmtEan13: 'EAN-13 - 소매 상품 (13자리)', uiFmtEan8: 'EAN-8 - 소매 상품 소형 (8자리)', uiFmtUpc: 'UPC-A - 미국 소매 (12자리)', uiFmtItf14: 'ITF-14 - 물류/박스', uiFmtMsi: 'MSI - 재고 관리', hints: { CODE128: 'CODE128: 영문 대소문자, 숫자, 특수문자 자유롭게 사용 가능', CODE39: 'CODE39: 대문자 영문(A-Z), 숫자(0-9), 일부 특수문자(-. *$/+%)', EAN13: 'EAN-13: 숫자 13자리만 입력 (마지막 자리는 체크섬 자동 계산)', EAN8: 'EAN-8: 숫자 8자리만 입력', UPC: 'UPC-A: 숫자 12자리만 입력', ITF14: 'ITF-14: 숫자 14자리 (짝수 개수)', MSI: 'MSI: 숫자만 입력 가능', }, errEan13: 'EAN-13은 숫자 12자리(체크섬 자동) 또는 13자리만 가능합니다.', errEan8: 'EAN-8은 숫자 7자리(체크섬 자동) 또는 8자리만 가능합니다.', errUpc: 'UPC-A는 숫자 11자리(체크섬 자동) 또는 12자리만 가능합니다.', errItf14: 'ITF-14는 숫자 14자리(또는 13자리+체크섬)만 가능합니다.', errMsi: 'MSI는 숫자만 입력 가능합니다.', emptyData: '바코드 데이터를 입력해주세요', invalidDataPrefix: '유효하지 않은 데이터입니다: ', invalidDataFallback: '형식을 확인해주세요', renderFailed: '생성 실패', infoFormat: '형식', infoData: '데이터', alertGenerateFirst: '먼저 바코드를 생성해주세요', uiSupportTitle: '생성이 잘 되었다면 후원으로 응원해주세요', uiSupportDesc: '무료 운영과 기능 개선에 도움이 됩니다. 카카오페이와 토스로 후원할 수 있습니다.', uiSupportButton: '☕ 후원하기', }, en: { uiOptions: '⚙️ Options', uiTabInput: 'Content', uiTabStyle: 'Style', uiTabSize: 'Size', uiFormatLabel: 'Barcode Format', uiDataLabel: 'Barcode Data', uiDataPlaceholder: 'Enter numbers or text for the barcode', uiProductLabel: 'Product Name', uiProductSmall: 'Displayed above the barcode', uiProductPlaceholder: 'e.g. Chocolate Snack 300g', uiProductHelp: 'If left blank, no product name is displayed', uiDisplayValue: 'Show Numbers Below', uiShow: 'Show', uiHide: 'Hide', uiBarColorLabel: 'Bar Color (Foreground)', uiBarColorHelp: 'Barcode color', uiBgColorLabel: 'Background Color', uiBgColorHelp: 'Background color', uiTitleColorLabel: 'Product Name Color', uiTitleColorHelp: 'Product name text color', uiTitleSizeLabel: 'Product Name Font Size', uiTitleWeightLabel: 'Product Name Font Weight', uiWeightNormal: 'Normal', uiWeightBold: 'Bold', uiWeightBolder: 'Extra Bold', uiPresetLabel: 'Color Presets', uiPresetClassic: 'Classic', uiPresetBlue: 'Blue', uiPresetGreen: 'Green', uiPresetBrown: 'Brown', uiPresetDark: 'Dark', uiBarWidthLabel: 'Bar Width', uiBarHeightLabel: 'Barcode Height', uiMarginLabel: 'Margin', uiFontSizeLabel: 'Number Font Size', uiPreview: '👁️ Preview', uiDownloadScale: 'Download Scale', uiDownloadPng: '⬇️ Download PNG', uiDownloadSvg: '📄 Download SVG', uiInfoEmpty: 'Enter content to generate your barcode automatically', uiAd: 'Ad Area (AdSense)', uiGuideTitle: 'Barcode Format Guide', uiGuide1Desc: 'Most versatile format. Supports letters, numbers, and symbols with no practical length limit.', uiGuide2Desc: 'International retail standard. Commonly used for products in supermarkets and convenience stores (13 digits).', uiGuide3Desc: 'Retail code used in the US and Canada. Required 12-digit barcode for North American markets.', uiGuide4Desc: 'Used for logistics boxes. Applied to grouped shipping units containing multiple retail items.', uiFmtCode128: 'CODE128 - General (Text/Numbers)', uiFmtCode39: 'CODE39 - Industrial', uiFmtEan13: 'EAN-13 - Retail Product (13 digits)', uiFmtEan8: 'EAN-8 - Small Retail Product (8 digits)', uiFmtUpc: 'UPC-A - US Retail (12 digits)', uiFmtItf14: 'ITF-14 - Logistics/Box', uiFmtMsi: 'MSI - Inventory', hints: { CODE128: 'CODE128: letters, numbers, and symbols are supported', CODE39: 'CODE39: uppercase A-Z, digits 0-9, and limited symbols (-. *$/+%)', EAN13: 'EAN-13: enter 13 digits (last digit auto-checksummed)', EAN8: 'EAN-8: enter 8 digits', UPC: 'UPC-A: enter 12 digits', ITF14: 'ITF-14: 14 digits (even count)', MSI: 'MSI: digits only', }, errEan13: 'EAN-13 requires 12 digits (auto checksum) or 13 digits.', errEan8: 'EAN-8 requires 7 digits (auto checksum) or 8 digits.', errUpc: 'UPC-A requires 11 digits (auto checksum) or 12 digits.', errItf14: 'ITF-14 requires 14 digits (or 13 digits + checksum).', errMsi: 'MSI accepts digits only.', emptyData: 'Please enter barcode data', invalidDataPrefix: 'Invalid data: ', invalidDataFallback: 'Please check the format', renderFailed: 'Generation failed', infoFormat: 'Format', infoData: 'Data', alertGenerateFirst: 'Generate a barcode first', uiSupportTitle: 'If this worked well, consider supporting the project', uiSupportDesc: 'Your support helps keep the generator free and improves the service. Donations are available via KakaoPay or Toss.', uiSupportButton: '☕ Support', }, ja: { uiOptions: '⚙️ オプション', uiTabInput: '内容', uiTabStyle: 'スタイル', uiTabSize: 'サイズ', uiFormatLabel: 'バーコード形式', uiDataLabel: 'バーコードデータ', uiDataPlaceholder: 'バーコードに入れる数字またはテキスト', uiProductLabel: '商品名', uiProductSmall: 'バーコード上に表示されます', uiProductPlaceholder: '例: チョコスナック 300g', uiProductHelp: '空欄の場合、商品名は表示されません', uiDisplayValue: '下部の数字表示', uiShow: '表示', uiHide: '非表示', uiBarColorLabel: 'バーの色 (前景色)', uiBarColorHelp: 'バーコード色', uiBgColorLabel: '背景色', uiBgColorHelp: '背景色', uiTitleColorLabel: '商品名の色', uiTitleColorHelp: '商品名テキスト色', uiTitleSizeLabel: '商品名フォントサイズ', uiTitleWeightLabel: '商品名フォントの太さ', uiWeightNormal: '標準', uiWeightBold: '太字', uiWeightBolder: '極太', uiPresetLabel: 'カラープリセット', uiPresetClassic: 'クラシック', uiPresetBlue: 'ブルー', uiPresetGreen: 'グリーン', uiPresetBrown: 'ブラウン', uiPresetDark: 'ダーク', uiBarWidthLabel: 'バー幅', uiBarHeightLabel: 'バーコード高さ', uiMarginLabel: '余白 (margin)', uiFontSizeLabel: '下部数字サイズ', uiPreview: '👁️ プレビュー', uiDownloadScale: 'ダウンロード倍率', uiDownloadPng: '⬇️ PNGをダウンロード', uiDownloadSvg: '📄 SVGをダウンロード', uiInfoEmpty: '内容を入力するとバーコードが自動生成されます', uiAd: '広告エリア (AdSense)', uiGuideTitle: 'バーコード形式ガイド', uiGuide1Desc: '最も汎用的な形式。英字・数字・記号に対応し、実用上の長さ制限がほぼありません。', uiGuide2Desc: '国際標準の小売商品コード。スーパーやコンビニの商品に広く使われます (13桁)。', uiGuide3Desc: '米国/カナダの小売商品コード。北米市場向けに必要な12桁バーコードです。', uiGuide4Desc: '物流箱向けバーコード。複数の小売商品をまとめた梱包単位に使用されます。', uiFmtCode128: 'CODE128 - 汎用 (英字/数字)', uiFmtCode39: 'CODE39 - 産業用', uiFmtEan13: 'EAN-13 - 小売商品 (13桁)', uiFmtEan8: 'EAN-8 - 小型小売商品 (8桁)', uiFmtUpc: 'UPC-A - 米国小売 (12桁)', uiFmtItf14: 'ITF-14 - 物流/箱', uiFmtMsi: 'MSI - 在庫管理', hints: { CODE128: 'CODE128: 英数字と記号を使用できます', CODE39: 'CODE39: 英大文字(A-Z)、数字(0-9)、一部記号(-. *$/+%)', EAN13: 'EAN-13: 13桁の数字を入力(末尾チェックサムは自動計算)', EAN8: 'EAN-8: 8桁の数字を入力', UPC: 'UPC-A: 12桁の数字を入力', ITF14: 'ITF-14: 14桁の数字(偶数桁)', MSI: 'MSI: 数字のみ入力可能', }, errEan13: 'EAN-13は12桁(チェックサム自動)または13桁の数字のみ対応です。', errEan8: 'EAN-8は7桁(チェックサム自動)または8桁の数字のみ対応です。', errUpc: 'UPC-Aは11桁(チェックサム自動)または12桁の数字のみ対応です。', errItf14: 'ITF-14は14桁(または13桁+チェックサム)の数字のみ対応です。', errMsi: 'MSIは数字のみ入力できます。', emptyData: 'バーコードデータを入力してください', invalidDataPrefix: '無効なデータです: ', invalidDataFallback: '形式を確認してください', renderFailed: '生成に失敗しました', infoFormat: '形式', infoData: 'データ', alertGenerateFirst: '先にバーコードを生成してください', }, zh: { uiOptions: '⚙️ 选项', uiTabInput: '内容', uiTabStyle: '样式', uiTabSize: '大小', uiFormatLabel: '条码格式', uiDataLabel: '条码数据', uiDataPlaceholder: '输入条码数字或文本', uiProductLabel: '产品名称', uiProductSmall: '显示在条码上方', uiProductPlaceholder: '例如: 巧克力饼干 300g', uiProductHelp: '留空时不显示产品名称', uiDisplayValue: '显示底部数字', uiShow: '显示', uiHide: '隐藏', uiBarColorLabel: '条纹颜色 (前景色)', uiBarColorHelp: '条码颜色', uiBgColorLabel: '背景色', uiBgColorHelp: '背景颜色', uiTitleColorLabel: '产品名称颜色', uiTitleColorHelp: '产品名称文字颜色', uiTitleSizeLabel: '产品名称字体大小', uiTitleWeightLabel: '产品名称字重', uiWeightNormal: '常规', uiWeightBold: '加粗', uiWeightBolder: '特粗', uiPresetLabel: '颜色预设', uiPresetClassic: '经典', uiPresetBlue: '蓝色', uiPresetGreen: '绿色', uiPresetBrown: '棕色', uiPresetDark: '深色', uiBarWidthLabel: '条宽', uiBarHeightLabel: '条码高度', uiMarginLabel: '边距 (margin)', uiFontSizeLabel: '底部数字大小', uiPreview: '👁️ 预览', uiDownloadScale: '下载倍率', uiDownloadPng: '⬇️ 下载 PNG', uiDownloadSvg: '📄 下载 SVG', uiInfoEmpty: '输入内容后将自动生成条码', uiAd: '广告区域 (AdSense)', uiGuideTitle: '条码格式说明', uiGuide1Desc: '最通用的格式。支持字母、数字和符号,长度限制很宽松。', uiGuide2Desc: '国际零售标准。常用于商超与便利店商品 (13位)。', uiGuide3Desc: '美国/加拿大零售商品编码。进入北美市场常用的12位条码。', uiGuide4Desc: '用于物流外箱。适合多个零售商品合并包装的运输单元。', uiFmtCode128: 'CODE128 - 通用 (字母/数字)', uiFmtCode39: 'CODE39 - 工业用', uiFmtEan13: 'EAN-13 - 零售商品 (13位)', uiFmtEan8: 'EAN-8 - 小型零售商品 (8位)', uiFmtUpc: 'UPC-A - 美国零售 (12位)', uiFmtItf14: 'ITF-14 - 物流/箱装', uiFmtMsi: 'MSI - 库存管理', hints: { CODE128: 'CODE128: 支持字母、数字和符号', CODE39: 'CODE39: 支持大写字母(A-Z)、数字(0-9)及部分符号(-. *$/+%)', EAN13: 'EAN-13: 输入13位数字(最后一位校验码自动计算)', EAN8: 'EAN-8: 输入8位数字', UPC: 'UPC-A: 输入12位数字', ITF14: 'ITF-14: 输入14位数字(偶数位)', MSI: 'MSI: 仅支持数字', }, errEan13: 'EAN-13仅支持12位(自动校验)或13位数字。', errEan8: 'EAN-8仅支持7位(自动校验)或8位数字。', errUpc: 'UPC-A仅支持11位(自动校验)或12位数字。', errItf14: 'ITF-14仅支持14位(或13位+校验位)数字。', errMsi: 'MSI仅支持数字。', emptyData: '请输入条码数据', invalidDataPrefix: '无效数据: ', invalidDataFallback: '请检查格式', renderFailed: '生成失败', infoFormat: '格式', infoData: '数据', alertGenerateFirst: '请先生成条码', }, es: { uiOptions: '⚙️ Opciones', uiTabInput: 'Contenido', uiTabStyle: 'Estilo', uiTabSize: 'Tamano', uiFormatLabel: 'Formato de codigo de barras', uiDataLabel: 'Datos del codigo de barras', uiDataPlaceholder: 'Ingresa numeros o texto para el codigo de barras', uiProductLabel: 'Nombre del producto', uiProductSmall: 'Se muestra sobre el codigo de barras', uiProductPlaceholder: 'Ej: Galletas de chocolate 300g', uiProductHelp: 'Si esta vacio, no se muestra el nombre del producto', uiDisplayValue: 'Mostrar numeros inferiores', uiShow: 'Mostrar', uiHide: 'Ocultar', uiBarColorLabel: 'Color de barras (primer plano)', uiBarColorHelp: 'Color del codigo de barras', uiBgColorLabel: 'Color de fondo', uiBgColorHelp: 'Color de fondo', uiTitleColorLabel: 'Color del nombre del producto', uiTitleColorHelp: 'Color del texto del producto', uiTitleSizeLabel: 'Tamano de fuente del producto', uiTitleWeightLabel: 'Grosor de fuente del producto', uiWeightNormal: 'Normal', uiWeightBold: 'Negrita', uiWeightBolder: 'Muy negrita', uiPresetLabel: 'Preajustes de color', uiPresetClassic: 'Clasico', uiPresetBlue: 'Azul', uiPresetGreen: 'Verde', uiPresetBrown: 'Marron', uiPresetDark: 'Oscuro', uiBarWidthLabel: 'Ancho de barra', uiBarHeightLabel: 'Altura del codigo', uiMarginLabel: 'Margen', uiFontSizeLabel: 'Tamano de numeros inferiores', uiPreview: '👁️ Vista previa', uiDownloadScale: 'Escala de descarga', uiDownloadPng: '⬇️ Descargar PNG', uiDownloadSvg: '📄 Descargar SVG', uiInfoEmpty: 'Ingresa contenido para generar el codigo automaticamente', uiAd: 'Area de anuncios (AdSense)', uiGuideTitle: 'Guia de formatos de codigo', uiGuide1Desc: 'Formato mas versatil. Soporta letras, numeros y simbolos sin limite practico de longitud.', uiGuide2Desc: 'Estandar internacional de retail. Usado en supermercados y tiendas (13 digitos).', uiGuide3Desc: 'Codigo retail de EE. UU./Canada. Requerido para mercado norteamericano (12 digitos).', uiGuide4Desc: 'Para cajas logisticas. Se usa en unidades de envio con multiples productos.', uiFmtCode128: 'CODE128 - General (texto/numeros)', uiFmtCode39: 'CODE39 - Industrial', uiFmtEan13: 'EAN-13 - Producto retail (13 digitos)', uiFmtEan8: 'EAN-8 - Producto retail pequeno (8 digitos)', uiFmtUpc: 'UPC-A - Retail EE. UU. (12 digitos)', uiFmtItf14: 'ITF-14 - Logistica/Caja', uiFmtMsi: 'MSI - Inventario', hints: { CODE128: 'CODE128: admite letras, numeros y simbolos', CODE39: 'CODE39: letras mayusculas (A-Z), numeros (0-9) y algunos simbolos (-. *$/+%)', EAN13: 'EAN-13: ingresa 13 digitos (el checksum final se calcula automaticamente)', EAN8: 'EAN-8: ingresa 8 digitos', UPC: 'UPC-A: ingresa 12 digitos', ITF14: 'ITF-14: 14 digitos (cantidad par)', MSI: 'MSI: solo numeros', }, errEan13: 'EAN-13 requiere 12 digitos (checksum automatico) o 13 digitos.', errEan8: 'EAN-8 requiere 7 digitos (checksum automatico) o 8 digitos.', errUpc: 'UPC-A requiere 11 digitos (checksum automatico) o 12 digitos.', errItf14: 'ITF-14 requiere 14 digitos (o 13 digitos + checksum).', errMsi: 'MSI solo acepta numeros.', emptyData: 'Ingresa los datos del codigo de barras', invalidDataPrefix: 'Datos no validos: ', invalidDataFallback: 'Revisa el formato', renderFailed: 'Error de generacion', infoFormat: 'Formato', infoData: 'Datos', alertGenerateFirst: 'Primero genera un codigo de barras', }, fr: { uiOptions: '⚙️ Options', uiTabInput: 'Contenu', uiTabStyle: 'Style', uiTabSize: 'Taille', uiFormatLabel: 'Format de code-barres', uiDataLabel: 'Donnees du code-barres', uiDataPlaceholder: 'Saisissez des chiffres ou du texte pour le code-barres', uiProductLabel: 'Nom du produit', uiProductSmall: 'Affiche au-dessus du code-barres', uiProductPlaceholder: 'Ex: Biscuits chocolat 300g', uiProductHelp: 'Si vide, le nom du produit n est pas affiche', uiDisplayValue: 'Afficher les chiffres en bas', uiShow: 'Afficher', uiHide: 'Masquer', uiBarColorLabel: 'Couleur des barres (premier plan)', uiBarColorHelp: 'Couleur du code-barres', uiBgColorLabel: 'Couleur d arriere-plan', uiBgColorHelp: 'Couleur de fond', uiTitleColorLabel: 'Couleur du nom du produit', uiTitleColorHelp: 'Couleur du texte produit', uiTitleSizeLabel: 'Taille de police du produit', uiTitleWeightLabel: 'Epaisseur de police du produit', uiWeightNormal: 'Normal', uiWeightBold: 'Gras', uiWeightBolder: 'Tres gras', uiPresetLabel: 'Preselections de couleur', uiPresetClassic: 'Classique', uiPresetBlue: 'Bleu', uiPresetGreen: 'Vert', uiPresetBrown: 'Marron', uiPresetDark: 'Sombre', uiBarWidthLabel: 'Largeur des barres', uiBarHeightLabel: 'Hauteur du code-barres', uiMarginLabel: 'Marge', uiFontSizeLabel: 'Taille des chiffres du bas', uiPreview: '👁️ Apercu', uiDownloadScale: 'Echelle de telechargement', uiDownloadPng: '⬇️ Telecharger PNG', uiDownloadSvg: '📄 Telecharger SVG', uiInfoEmpty: 'Saisissez du contenu pour generer automatiquement le code-barres', uiAd: 'Zone publicitaire (AdSense)', uiGuideTitle: 'Guide des formats de code-barres', uiGuide1Desc: 'Format le plus polyvalent. Prend en charge lettres, chiffres et symboles sans limite pratique de longueur.', uiGuide2Desc: 'Standard international du retail. Utilise pour les produits en magasin (13 chiffres).', uiGuide3Desc: 'Code retail des Etats-Unis/Canada. Necessaire pour le marche nord-americain (12 chiffres).', uiGuide4Desc: 'Pour les cartons logistiques. Utilise pour les unites d expedition regroupant plusieurs produits.', uiFmtCode128: 'CODE128 - General (texte/chiffres)', uiFmtCode39: 'CODE39 - Industriel', uiFmtEan13: 'EAN-13 - Produit retail (13 chiffres)', uiFmtEan8: 'EAN-8 - Petit produit retail (8 chiffres)', uiFmtUpc: 'UPC-A - Retail US (12 chiffres)', uiFmtItf14: 'ITF-14 - Logistique/Carton', uiFmtMsi: 'MSI - Inventaire', hints: { CODE128: 'CODE128 : lettres, chiffres et symboles pris en charge', CODE39: 'CODE39 : lettres majuscules (A-Z), chiffres (0-9) et symboles limites (-. *$/+%)', EAN13: 'EAN-13 : saisir 13 chiffres (checksum final calcule automatiquement)', EAN8: 'EAN-8 : saisir 8 chiffres', UPC: 'UPC-A : saisir 12 chiffres', ITF14: 'ITF-14 : 14 chiffres (nombre pair)', MSI: 'MSI : chiffres uniquement', }, errEan13: 'EAN-13 exige 12 chiffres (checksum auto) ou 13 chiffres.', errEan8: 'EAN-8 exige 7 chiffres (checksum auto) ou 8 chiffres.', errUpc: 'UPC-A exige 11 chiffres (checksum auto) ou 12 chiffres.', errItf14: 'ITF-14 exige 14 chiffres (ou 13 chiffres + checksum).', errMsi: 'MSI accepte uniquement les chiffres.', emptyData: 'Veuillez saisir les donnees du code-barres', invalidDataPrefix: 'Donnees invalides : ', invalidDataFallback: 'Veuillez verifier le format', renderFailed: 'Echec de generation', infoFormat: 'Format', infoData: 'Donnees', alertGenerateFirst: 'Generez d abord un code-barres', }, pt: { uiOptions: '⚙️ Opcoes', uiTabInput: 'Conteudo', uiTabStyle: 'Estilo', uiTabSize: 'Tamanho', uiFormatLabel: 'Formato do codigo de barras', uiDataLabel: 'Dados do codigo de barras', uiDataPlaceholder: 'Digite numeros ou texto para o codigo de barras', uiProductLabel: 'Nome do produto', uiProductSmall: 'Exibido acima do codigo de barras', uiProductPlaceholder: 'Ex: Biscoito de chocolate 300g', uiProductHelp: 'Se ficar vazio, o nome do produto nao sera exibido', uiDisplayValue: 'Mostrar numeros abaixo', uiShow: 'Mostrar', uiHide: 'Ocultar', uiBarColorLabel: 'Cor das barras (primeiro plano)', uiBarColorHelp: 'Cor do codigo de barras', uiBgColorLabel: 'Cor de fundo', uiBgColorHelp: 'Cor de fundo', uiTitleColorLabel: 'Cor do nome do produto', uiTitleColorHelp: 'Cor do texto do produto', uiTitleSizeLabel: 'Tamanho da fonte do produto', uiTitleWeightLabel: 'Espessura da fonte do produto', uiWeightNormal: 'Normal', uiWeightBold: 'Negrito', uiWeightBolder: 'Extra negrito', uiPresetLabel: 'Predefinicoes de cor', uiPresetClassic: 'Classico', uiPresetBlue: 'Azul', uiPresetGreen: 'Verde', uiPresetBrown: 'Marrom', uiPresetDark: 'Escuro', uiBarWidthLabel: 'Largura das barras', uiBarHeightLabel: 'Altura do codigo de barras', uiMarginLabel: 'Margem', uiFontSizeLabel: 'Tamanho dos numeros inferiores', uiPreview: '👁️ Pre-visualizacao', uiDownloadScale: 'Escala de download', uiDownloadPng: '⬇️ Baixar PNG', uiDownloadSvg: '📄 Baixar SVG', uiInfoEmpty: 'Digite conteudo para gerar o codigo automaticamente', uiAd: 'Area de anuncios (AdSense)', uiGuideTitle: 'Guia de formatos de codigo de barras', uiGuide1Desc: 'Formato mais versatil. Suporta letras, numeros e simbolos sem limite pratico de tamanho.', uiGuide2Desc: 'Padrao internacional de varejo. Muito usado em supermercados e lojas (13 digitos).', uiGuide3Desc: 'Codigo de varejo dos EUA/Canada. Necessario para o mercado norte-americano (12 digitos).', uiGuide4Desc: 'Para caixas logisticas. Usado em unidades de envio com varios produtos.', uiFmtCode128: 'CODE128 - Geral (texto/numeros)', uiFmtCode39: 'CODE39 - Industrial', uiFmtEan13: 'EAN-13 - Produto de varejo (13 digitos)', uiFmtEan8: 'EAN-8 - Produto pequeno de varejo (8 digitos)', uiFmtUpc: 'UPC-A - Varejo EUA (12 digitos)', uiFmtItf14: 'ITF-14 - Logistica/Caixa', uiFmtMsi: 'MSI - Inventario', hints: { CODE128: 'CODE128: aceita letras, numeros e simbolos', CODE39: 'CODE39: letras maiusculas (A-Z), numeros (0-9) e alguns simbolos (-. *$/+%)', EAN13: 'EAN-13: informe 13 digitos (checksum final calculado automaticamente)', EAN8: 'EAN-8: informe 8 digitos', UPC: 'UPC-A: informe 12 digitos', ITF14: 'ITF-14: 14 digitos (quantidade par)', MSI: 'MSI: apenas numeros', }, errEan13: 'EAN-13 exige 12 digitos (checksum automatico) ou 13 digitos.', errEan8: 'EAN-8 exige 7 digitos (checksum automatico) ou 8 digitos.', errUpc: 'UPC-A exige 11 digitos (checksum automatico) ou 12 digitos.', errItf14: 'ITF-14 exige 14 digitos (ou 13 digitos + checksum).', errMsi: 'MSI aceita apenas numeros.', emptyData: 'Digite os dados do codigo de barras', invalidDataPrefix: 'Dados invalidos: ', invalidDataFallback: 'Verifique o formato', renderFailed: 'Falha na geracao', infoFormat: 'Formato', infoData: 'Dados', alertGenerateFirst: 'Gere um codigo de barras primeiro', }, vi: { uiOptions: '⚙️ Tuy chon', uiTabInput: 'Noi dung', uiTabStyle: 'Kieu', uiTabSize: 'Kich thuoc', uiFormatLabel: 'Dinh dang ma vach', uiDataLabel: 'Du lieu ma vach', uiDataPlaceholder: 'Nhap so hoac van ban cho ma vach', uiProductLabel: 'Ten san pham', uiProductSmall: 'Hien thi phia tren ma vach', uiProductPlaceholder: 'Vi du: Banh quy socola 300g', uiProductHelp: 'De trong thi ten san pham se khong hien thi', uiDisplayValue: 'Hien so ben duoi', uiShow: 'Hien', uiHide: 'An', uiBarColorLabel: 'Mau vach (tien canh)', uiBarColorHelp: 'Mau ma vach', uiBgColorLabel: 'Mau nen', uiBgColorHelp: 'Mau nen', uiTitleColorLabel: 'Mau ten san pham', uiTitleColorHelp: 'Mau chu ten san pham', uiTitleSizeLabel: 'Co chu ten san pham', uiTitleWeightLabel: 'Do dam chu ten san pham', uiWeightNormal: 'Thuong', uiWeightBold: 'Dam', uiWeightBolder: 'Rat dam', uiPresetLabel: 'Mau san co', uiPresetClassic: 'Co dien', uiPresetBlue: 'Xanh duong', uiPresetGreen: 'Xanh la', uiPresetBrown: 'Nau', uiPresetDark: 'Toi', uiBarWidthLabel: 'Do rong vach', uiBarHeightLabel: 'Chieu cao ma vach', uiMarginLabel: 'Le', uiFontSizeLabel: 'Co chu so ben duoi', uiPreview: '👁️ Xem truoc', uiDownloadScale: 'Ti le tai xuong', uiDownloadPng: '⬇️ Tai PNG', uiDownloadSvg: '📄 Tai SVG', uiInfoEmpty: 'Nhap noi dung de tu dong tao ma vach', uiAd: 'Khu vuc quang cao (AdSense)', uiGuideTitle: 'Huong dan dinh dang ma vach', uiGuide1Desc: 'Dinh dang da dung nhat. Ho tro chu cai, so va ky tu dac biet, gan nhu khong gioi han do dai.', uiGuide2Desc: 'Tieu chuan ban le quoc te. Thuong dung cho san pham tai sieu thi va cua hang (13 chu so).', uiGuide3Desc: 'Ma ban le My/Canada. Ma 12 chu so can thiet cho thi truong Bac My.', uiGuide4Desc: 'Danh cho thung logistics. Dung cho don vi van chuyen gom nhieu san pham.', uiFmtCode128: 'CODE128 - Tong quat (chu/so)', uiFmtCode39: 'CODE39 - Cong nghiep', uiFmtEan13: 'EAN-13 - San pham ban le (13 chu so)', uiFmtEan8: 'EAN-8 - San pham nho (8 chu so)', uiFmtUpc: 'UPC-A - Ban le My (12 chu so)', uiFmtItf14: 'ITF-14 - Logistics/Thung', uiFmtMsi: 'MSI - Ton kho', hints: { CODE128: 'CODE128: ho tro chu cai, so va ky tu dac biet', CODE39: 'CODE39: chu in hoa (A-Z), so (0-9) va mot so ky tu (-. *$/+%)', EAN13: 'EAN-13: nhap 13 chu so (so kiem tra cuoi tu dong tinh)', EAN8: 'EAN-8: nhap 8 chu so', UPC: 'UPC-A: nhap 12 chu so', ITF14: 'ITF-14: 14 chu so (so luong chan)', MSI: 'MSI: chi nhap so', }, errEan13: 'EAN-13 yeu cau 12 chu so (tu dong checksum) hoac 13 chu so.', errEan8: 'EAN-8 yeu cau 7 chu so (tu dong checksum) hoac 8 chu so.', errUpc: 'UPC-A yeu cau 11 chu so (tu dong checksum) hoac 12 chu so.', errItf14: 'ITF-14 yeu cau 14 chu so (hoac 13 chu so + checksum).', errMsi: 'MSI chi cho phep chu so.', emptyData: 'Vui long nhap du lieu ma vach', invalidDataPrefix: 'Du lieu khong hop le: ', invalidDataFallback: 'Vui long kiem tra dinh dang', renderFailed: 'Tao that bai', infoFormat: 'Dinh dang', infoData: 'Du lieu', alertGenerateFirst: 'Hay tao ma vach truoc', }, th: { uiOptions: '⚙️ ตัวเลือก', uiTabInput: 'เนื้อหา', uiTabStyle: 'สไตล์', uiTabSize: 'ขนาด', uiFormatLabel: 'รูปแบบบาร์โค้ด', uiDataLabel: 'ข้อมูลบาร์โค้ด', uiDataPlaceholder: 'ป้อนตัวเลขหรือข้อความสำหรับบาร์โค้ด', uiProductLabel: 'ชื่อสินค้า', uiProductSmall: 'แสดงเหนือบาร์โค้ด', uiProductPlaceholder: 'เช่น คุกกี้ช็อกโกแลต 300g', uiProductHelp: 'หากเว้นว่าง จะไม่แสดงชื่อสินค้า', uiDisplayValue: 'แสดงตัวเลขด้านล่าง', uiShow: 'แสดง', uiHide: 'ซ่อน', uiBarColorLabel: 'สีแท่งบาร์ (สีด้านหน้า)', uiBarColorHelp: 'สีบาร์โค้ด', uiBgColorLabel: 'สีพื้นหลัง', uiBgColorHelp: 'สีพื้นหลัง', uiTitleColorLabel: 'สีชื่อสินค้า', uiTitleColorHelp: 'สีข้อความชื่อสินค้า', uiTitleSizeLabel: 'ขนาดฟอนต์ชื่อสินค้า', uiTitleWeightLabel: 'ความหนาฟอนต์ชื่อสินค้า', uiWeightNormal: 'ปกติ', uiWeightBold: 'หนา', uiWeightBolder: 'หนามาก', uiPresetLabel: 'พรีเซ็ตสี', uiPresetClassic: 'คลาสสิก', uiPresetBlue: 'น้ำเงิน', uiPresetGreen: 'เขียว', uiPresetBrown: 'น้ำตาล', uiPresetDark: 'เข้ม', uiBarWidthLabel: 'ความกว้างแท่ง', uiBarHeightLabel: 'ความสูงบาร์โค้ด', uiMarginLabel: 'ระยะขอบ', uiFontSizeLabel: 'ขนาดตัวเลขด้านล่าง', uiPreview: '👁️ ตัวอย่าง', uiDownloadScale: 'สเกลดาวน์โหลด', uiDownloadPng: '⬇️ ดาวน์โหลด PNG', uiDownloadSvg: '📄 ดาวน์โหลด SVG', uiInfoEmpty: 'ป้อนเนื้อหาเพื่อสร้างบาร์โค้ดอัตโนมัติ', uiAd: 'พื้นที่โฆษณา (AdSense)', uiGuideTitle: 'คู่มือรูปแบบบาร์โค้ด', uiGuide1Desc: 'รูปแบบที่ใช้งานได้หลากหลายที่สุด รองรับตัวอักษร ตัวเลข และสัญลักษณ์ โดยแทบไม่มีข้อจำกัดความยาว', uiGuide2Desc: 'มาตรฐานค้าปลีกสากล ใช้กับสินค้าในซูเปอร์มาร์เก็ตและร้านสะดวกซื้อ (13 หลัก)', uiGuide3Desc: 'รหัสค้าปลีกสหรัฐ/แคนาดา บาร์โค้ด 12 หลักที่จำเป็นสำหรับตลาดอเมริกาเหนือ', uiGuide4Desc: 'สำหรับกล่องโลจิสติกส์ ใช้กับหน่วยขนส่งที่รวมสินค้าหลายรายการ', uiFmtCode128: 'CODE128 - ทั่วไป (ข้อความ/ตัวเลข)', uiFmtCode39: 'CODE39 - อุตสาหกรรม', uiFmtEan13: 'EAN-13 - สินค้าค้าปลีก (13 หลัก)', uiFmtEan8: 'EAN-8 - สินค้าค้าปลีกขนาดเล็ก (8 หลัก)', uiFmtUpc: 'UPC-A - ค้าปลีกสหรัฐ (12 หลัก)', uiFmtItf14: 'ITF-14 - โลจิสติกส์/กล่อง', uiFmtMsi: 'MSI - คลังสินค้า', hints: { CODE128: 'CODE128: รองรับตัวอักษร ตัวเลข และสัญลักษณ์', CODE39: 'CODE39: ตัวพิมพ์ใหญ่ (A-Z), ตัวเลข (0-9) และสัญลักษณ์บางส่วน (-. *$/+%)', EAN13: 'EAN-13: กรอกตัวเลข 13 หลัก (หลักสุดท้ายคำนวณเช็คซัมอัตโนมัติ)', EAN8: 'EAN-8: กรอกตัวเลข 8 หลัก', UPC: 'UPC-A: กรอกตัวเลข 12 หลัก', ITF14: 'ITF-14: ตัวเลข 14 หลัก (จำนวนหลักเป็นเลขคู่)', MSI: 'MSI: กรอกได้เฉพาะตัวเลข', }, errEan13: 'EAN-13 ต้องเป็นตัวเลข 12 หลัก (เช็คซัมอัตโนมัติ) หรือ 13 หลักเท่านั้น', errEan8: 'EAN-8 ต้องเป็นตัวเลข 7 หลัก (เช็คซัมอัตโนมัติ) หรือ 8 หลักเท่านั้น', errUpc: 'UPC-A ต้องเป็นตัวเลข 11 หลัก (เช็คซัมอัตโนมัติ) หรือ 12 หลักเท่านั้น', errItf14: 'ITF-14 ต้องเป็นตัวเลข 14 หลัก (หรือ 13 หลัก+เช็คซัม) เท่านั้น', errMsi: 'MSI รองรับเฉพาะตัวเลขเท่านั้น', emptyData: 'กรุณาป้อนข้อมูลบาร์โค้ด', invalidDataPrefix: 'ข้อมูลไม่ถูกต้อง: ', invalidDataFallback: 'กรุณาตรวจสอบรูปแบบ', renderFailed: 'สร้างไม่สำเร็จ', infoFormat: 'รูปแบบ', infoData: 'ข้อมูล', alertGenerateFirst: 'โปรดสร้างบาร์โค้ดก่อน', }, }; BC_UI_I18N.de = { ...BC_UI_I18N.en, uiOptions: '⚙️ Optionen', uiTabInput: 'Inhalt', uiTabStyle: 'Stil', uiTabSize: 'Größe', uiFormatLabel: 'Barcode Format', uiDataLabel: 'Barcode Daten', uiDataPlaceholder: 'Geben Sie Zahlen oder Text für den Barcode ein', uiProductLabel: 'Produktname', uiProductSmall: 'Wird über dem Barcode angezeigt', uiProductPlaceholder: 'z.B. Schokoladensnack 300g', uiProductHelp: 'Bleibt das Feld leer, wird kein Produktname angezeigt', uiDisplayValue: 'Zahlen unten anzeigen', uiShow: 'Zeigen', uiHide: 'Verstecken', uiBarColorLabel: 'Balkenfarbe (Vordergrund)', uiBarColorHelp: 'Barcode Farbe', uiBgColorLabel: 'Hintergrundfarbe', uiBgColorHelp: 'Hintergrundfarbe', uiTitleColorLabel: 'Produktname Farbe', uiTitleColorHelp: 'Textfarbe des Produktnamens', uiTitleSizeLabel: 'Schriftgröße des Produktnamens', uiTitleWeightLabel: 'Produktname, Schriftstärke', uiWeightNormal: 'Normal', uiWeightBold: 'Deutlich', uiWeightBolder: 'Extra fett', uiPresetLabel: 'Farbvoreinstellungen', uiPresetClassic: 'Klassiker', uiPresetBlue: 'Blau', uiPresetGreen: 'Grün', uiPresetBrown: 'Braun', uiPresetDark: 'Dunkel', uiBarWidthLabel: 'Balkenbreite', uiBarHeightLabel: 'Barcode Höhe', uiMarginLabel: 'Marge', uiFontSizeLabel: 'Schriftgröße für Zahlen', uiPreview: '👁️ Vorschau', uiDownloadScale: 'Laden Sie Scale herunter', uiDownloadPng: '⬇️ Laden Sie PNG herunter', uiDownloadSvg: '📄 Laden Sie SVG herunter', uiInfoEmpty: 'Geben Sie Inhalte ein, um Ihren Barcode automatisch zu generieren', uiAd: 'Werbefläche (AdSense)', uiGuideTitle: 'Barcode Formatanleitung', uiGuide1Desc: 'Vielseitigstes Format. Unterstützt Buchstaben, Zahlen und Symbole ohne praktische Längenbeschränkung.', uiGuide2Desc: 'Internationaler Einzelhandelsstandard. Wird häufig für Produkte in Supermärkten und Convenience-Stores verwendet (13 Ziffern).', uiGuide3Desc: 'In den USA und Kanada verwendeter Einzelhandelscode. Erforderlicher 12-stelliger Barcode für nordamerikanische Märkte.', uiGuide4Desc: 'Wird für Logistikboxen verwendet. Wird auf gruppierte Versandeinheiten angewendet, die mehrere Einzelhandelsartikel enthalten.', uiFmtCode128: 'CODE128 – Allgemein (Text/Zahlen)', uiFmtCode39: 'CODE39 - ​​Industriell', uiFmtEan13: 'EAN-13 – Einzelhandelsprodukt (13 Ziffern)', uiFmtEan8: 'EAN-8 – Kleines Einzelhandelsprodukt (8 Ziffern)', uiFmtUpc: 'UPC-A – US-Einzelhandel (12 Ziffern)', uiFmtItf14: 'ITF-14 - ​​Logistik/Box', uiFmtMsi: 'MSI - ​​Inventar', hints: { CODE128: 'CODE128: Buchstaben, Zahlen und Symbole werden unterstützt', CODE39: 'CODE39: Großbuchstaben A–Z, Ziffern 0–9 und begrenzte Symbole (-. *$/+%)', EAN13: 'EAN-13: Geben Sie 13 Ziffern ein (letzte Ziffer wird automatisch geprüft)', EAN8: 'EAN-8: Geben Sie 8 Ziffern ein', UPC: 'UPC-A: Geben Sie 12 Ziffern ein', ITF14: 'ITF-14: 14 Ziffern (gerade Zahl)', MSI: 'MSI: nur Ziffern', }, errEan13: 'EAN-13 erfordert 12 Ziffern (automatische Prüfsumme) oder 13 Ziffern.', errEan8: 'EAN-8 erfordert 7 Ziffern (automatische Prüfsumme) oder 8 Ziffern.', errUpc: 'UPC-A erfordert 11 Ziffern (automatische Prüfsumme) oder 12 Ziffern.', errItf14: 'ITF-14 erfordert 14 Ziffern (oder 13 Ziffern + Prüfsumme).', errMsi: 'MSI akzeptiert nur Ziffern.', emptyData: 'Bitte geben Sie Barcode-Daten ein', invalidDataPrefix: 'Ungültige Daten:', invalidDataFallback: 'Bitte überprüfen Sie das Format', renderFailed: 'Die Generierung ist fehlgeschlagen', infoFormat: 'Format', infoData: 'Daten', alertGenerateFirst: 'Generieren Sie zunächst einen Barcode', }; function getCurrentLangForBC() { const supported = ['ko', 'en', 'ja', 'zh', 'es', 'fr', 'pt', 'de', 'vi', 'th']; const saved = localStorage.getItem('alaskahqr_lang'); if (saved && supported.includes(saved)) return saved; const docLang = (document.documentElement.lang || '').slice(0, 2); if (supported.includes(docLang)) return docLang; const browserLang = (navigator.language || '').slice(0, 2); return supported.includes(browserLang) ? browserLang : 'ko'; } function bct(key) { const lang = getCurrentLangForBC(); return BC_UI_I18N[lang]?.[key] || BC_UI_I18N.en[key] || BC_UI_I18N.ko[key] || key; } function getFormatHint(format) { const lang = getCurrentLangForBC(); return ( BC_UI_I18N[lang]?.hints?.[format] || BC_UI_I18N.en.hints[format] || BC_UI_I18N.ko.hints[format] || '' ); } function applyBCStaticTexts() { const setRadioLabelText = (labelEl, text) => { if (!labelEl) return; let textNode = null; for (const node of labelEl.childNodes) { if (node.nodeType === Node.TEXT_NODE) { textNode = node; } } if (textNode) { textNode.textContent = ` ${text}`; } else { labelEl.appendChild(document.createTextNode(` ${text}`)); } }; const panelTitle = document.querySelector('.options-col .panel .panel-title'); if (panelTitle) panelTitle.textContent = bct('uiOptions'); const tabInput = document.querySelector('.tab-btn[data-tab="bc-tab-input"]'); if (tabInput) tabInput.textContent = bct('uiTabInput'); const tabStyle = document.querySelector('.tab-btn[data-tab="bc-tab-style"]'); if (tabStyle) tabStyle.textContent = bct('uiTabStyle'); const tabSize = document.querySelector('.tab-btn[data-tab="bc-tab-size"]'); if (tabSize) tabSize.textContent = bct('uiTabSize'); const formatLabel = document.getElementById('bc-format')?.closest('.form-group')?.querySelector('.form-label'); if (formatLabel) formatLabel.textContent = bct('uiFormatLabel'); const optCode128 = document.querySelector('#bc-format option[value="CODE128"]'); if (optCode128) optCode128.textContent = bct('uiFmtCode128'); const optCode39 = document.querySelector('#bc-format option[value="CODE39"]'); if (optCode39) optCode39.textContent = bct('uiFmtCode39'); const optEan13 = document.querySelector('#bc-format option[value="EAN13"]'); if (optEan13) optEan13.textContent = bct('uiFmtEan13'); const optEan8 = document.querySelector('#bc-format option[value="EAN8"]'); if (optEan8) optEan8.textContent = bct('uiFmtEan8'); const optUpc = document.querySelector('#bc-format option[value="UPC"]'); if (optUpc) optUpc.textContent = bct('uiFmtUpc'); const optItf14 = document.querySelector('#bc-format option[value="ITF14"]'); if (optItf14) optItf14.textContent = bct('uiFmtItf14'); const optMsi = document.querySelector('#bc-format option[value="MSI"]'); if (optMsi) optMsi.textContent = bct('uiFmtMsi'); const dataLabel = document.getElementById('bc-data')?.closest('.form-group')?.querySelector('.form-label'); if (dataLabel) dataLabel.textContent = bct('uiDataLabel'); const dataInput = document.getElementById('bc-data'); if (dataInput) dataInput.placeholder = bct('uiDataPlaceholder'); const productLabel = document.getElementById('bc-product-name')?.closest('.form-group')?.querySelector('.form-label'); if (productLabel && productLabel.querySelector('small')) { const small = productLabel.querySelector('small'); productLabel.childNodes[0].textContent = `${bct('uiProductLabel')} `; small.textContent = bct('uiProductSmall'); } const productInput = document.getElementById('bc-product-name'); if (productInput) productInput.placeholder = bct('uiProductPlaceholder'); const productHelp = document.getElementById('bc-product-name')?.closest('.form-group')?.querySelector('div[style*="font-size:.78rem"]'); if (productHelp) productHelp.textContent = bct('uiProductHelp'); const displayLabel = document.querySelector('input[name="bc-display-value"]')?.closest('.form-group')?.querySelector('.form-label'); if (displayLabel) displayLabel.textContent = bct('uiDisplayValue'); const showLabel = document.querySelector('input[name="bc-display-value"][value="true"]')?.closest('label'); setRadioLabelText(showLabel, bct('uiShow')); const hideLabel = document.querySelector('input[name="bc-display-value"][value="false"]')?.closest('label'); setRadioLabelText(hideLabel, bct('uiHide')); const fgLabel = document.getElementById('bc-fg-color')?.closest('.form-group')?.querySelector('.form-label'); if (fgLabel) fgLabel.textContent = bct('uiBarColorLabel'); const fgHelp = document.getElementById('bc-fg-hex')?.parentElement?.querySelector('span'); if (fgHelp) fgHelp.textContent = bct('uiBarColorHelp'); const bgLabel = document.getElementById('bc-bg-color')?.closest('.form-group')?.querySelector('.form-label'); if (bgLabel) bgLabel.textContent = bct('uiBgColorLabel'); const bgHelp = document.getElementById('bc-bg-hex')?.parentElement?.querySelector('span'); if (bgHelp) bgHelp.textContent = bct('uiBgColorHelp'); const titleColorLabel = document.getElementById('bc-title-color')?.closest('.form-group')?.querySelector('.form-label'); if (titleColorLabel) titleColorLabel.textContent = bct('uiTitleColorLabel'); const titleColorHelp = document.getElementById('bc-title-hex')?.parentElement?.querySelector('span'); if (titleColorHelp) titleColorHelp.textContent = bct('uiTitleColorHelp'); const titleSizeLabel = document.getElementById('bc-title-size')?.closest('.form-group')?.querySelector('.form-label'); if (titleSizeLabel) titleSizeLabel.textContent = bct('uiTitleSizeLabel'); const titleWeightLabel = document.querySelector('input[name="bc-title-weight"]')?.closest('.form-group')?.querySelector('.form-label'); if (titleWeightLabel) titleWeightLabel.textContent = bct('uiTitleWeightLabel'); const weightNormal = document.querySelector('input[name="bc-title-weight"][value="normal"]')?.closest('label'); setRadioLabelText(weightNormal, bct('uiWeightNormal')); const weightBold = document.querySelector('input[name="bc-title-weight"][value="bold"]')?.closest('label'); setRadioLabelText(weightBold, bct('uiWeightBold')); const weightBolder = document.querySelector('input[name="bc-title-weight"][value="800"]')?.closest('label'); setRadioLabelText(weightBolder, bct('uiWeightBolder')); const presetLabel = document.querySelector('#bc-tab-style .bc-preset')?.closest('.form-group')?.querySelector('.form-label'); if (presetLabel) presetLabel.textContent = bct('uiPresetLabel'); const presets = document.querySelectorAll('#bc-tab-style .bc-preset'); if (presets[0]) presets[0].title = bct('uiPresetClassic'); if (presets[1]) presets[1].title = bct('uiPresetBlue'); if (presets[2]) presets[2].title = bct('uiPresetGreen'); if (presets[3]) presets[3].title = bct('uiPresetBrown'); if (presets[4]) presets[4].title = bct('uiPresetDark'); const widthLabel = document.getElementById('bc-width')?.closest('.form-group')?.querySelector('.form-label'); if (widthLabel) widthLabel.textContent = bct('uiBarWidthLabel'); const heightLabel = document.getElementById('bc-height')?.closest('.form-group')?.querySelector('.form-label'); if (heightLabel) heightLabel.textContent = bct('uiBarHeightLabel'); const marginLabel = document.getElementById('bc-margin')?.closest('.form-group')?.querySelector('.form-label'); if (marginLabel) marginLabel.textContent = bct('uiMarginLabel'); const fontSizeLabel = document.getElementById('bc-font-size')?.closest('.form-group')?.querySelector('.form-label'); if (fontSizeLabel) fontSizeLabel.textContent = bct('uiFontSizeLabel'); const previewTitle = document.querySelector('.preview-panel .panel-title'); if (previewTitle) previewTitle.textContent = bct('uiPreview'); const dlScaleLabel = document.querySelector('.preview-panel .preview-area > div[style*="width:100%"] > div[style*="font-size:.8rem"]'); if (dlScaleLabel) dlScaleLabel.textContent = bct('uiDownloadScale'); const pngBtn = document.querySelector(".download-group .btn-dl[onclick*=\"downloadBarcode('png')\"]"); if (pngBtn) pngBtn.textContent = bct('uiDownloadPng'); const svgBtn = document.querySelector(".download-group .btn-dl[onclick*=\"downloadBarcode('svg')\"]"); if (svgBtn) svgBtn.textContent = bct('uiDownloadSvg'); const supportTitle = document.getElementById('bc-support-title'); if (supportTitle) supportTitle.textContent = bct('uiSupportTitle'); const supportDesc = document.getElementById('bc-support-desc'); if (supportDesc) supportDesc.textContent = bct('uiSupportDesc'); const supportLink = document.getElementById('bc-support-link'); if (supportLink) supportLink.textContent = bct('uiSupportButton'); const infoEl = document.getElementById('bc-info'); if (infoEl && !infoEl.textContent.includes('|')) infoEl.textContent = bct('uiInfoEmpty'); const adWrap = document.querySelector('.ad-wrap'); if (adWrap) adWrap.textContent = bct('uiAd'); const guideTitle = document.querySelector('.features-bg .section-title'); if (guideTitle) guideTitle.textContent = bct('uiGuideTitle'); const guideItems = document.querySelectorAll('.features-bg .feature-item'); if (guideItems[0]) { const p = guideItems[0].querySelector('p'); if (p) p.textContent = bct('uiGuide1Desc'); } if (guideItems[1]) { const p = guideItems[1].querySelector('p'); if (p) p.textContent = bct('uiGuide2Desc'); } if (guideItems[2]) { const p = guideItems[2].querySelector('p'); if (p) p.textContent = bct('uiGuide3Desc'); } if (guideItems[3]) { const p = guideItems[3].querySelector('p'); if (p) p.textContent = bct('uiGuide4Desc'); } } let renderTimer = null; function calcMod10CheckDigit(digits) { let sum = 0; for (let i = 0; i < digits.length; i += 1) { const n = Number(digits[i]); sum += i % 2 === 0 ? n * 3 : n; } return (10 - (sum % 10)) % 10; } function calcEAN13CheckDigit(base12) { let sum = 0; for (let i = 0; i < base12.length; i += 1) { const n = Number(base12[i]); sum += i % 2 === 0 ? n : n * 3; } return (10 - (sum % 10)) % 10; } function calcEAN8CheckDigit(base7) { let sum = 0; for (let i = 0; i < base7.length; i += 1) { const n = Number(base7[i]); sum += i % 2 === 0 ? n * 3 : n; } return (10 - (sum % 10)) % 10; } function normalizeDataForFormat(rawData, format) { const data = String(rawData || ''); const digits = data.replace(/\D/g, ''); if (format === 'EAN13') { if (digits.length === 12) { return digits + String(calcEAN13CheckDigit(digits)); } if (digits.length === 13) { const base = digits.slice(0, 12); return base + String(calcEAN13CheckDigit(base)); } throw new Error(bct('errEan13')); } if (format === 'EAN8') { if (digits.length === 7) { return digits + String(calcEAN8CheckDigit(digits)); } if (digits.length === 8) { const base = digits.slice(0, 7); return base + String(calcEAN8CheckDigit(base)); } throw new Error(bct('errEan8')); } if (format === 'UPC') { if (digits.length === 11) { return digits + String(calcEAN8CheckDigit(digits)); } if (digits.length === 12) { const base = digits.slice(0, 11); return base + String(calcEAN8CheckDigit(base)); } throw new Error(bct('errUpc')); } if (format === 'ITF14') { if (digits.length === 13) { return digits + String(calcMod10CheckDigit(digits)); } if (digits.length === 14) { return digits; } throw new Error(bct('errItf14')); } if (format === 'MSI') { if (!digits.length) { throw new Error(bct('errMsi')); } return digits; } if (format === 'CODE39') { return data.toUpperCase(); } return data; } /* ========== RENDER ========== */ function renderBarcode() { const svgEl = document.getElementById('barcode-svg'); const errorEl = document.getElementById('bc-error-msg'); const productDisplay = document.getElementById('bc-product-display'); const infoEl = document.getElementById('bc-info'); if (!BCState.data || BCState.data.trim() === '') { svgEl.innerHTML = ''; errorEl.style.display = 'block'; errorEl.textContent = bct('emptyData'); productDisplay.style.display = 'none'; toggleBCSupportPrompt(false); return; } try { const normalizedData = normalizeDataForFormat(BCState.data, BCState.format); JsBarcode(svgEl, normalizedData, { format: BCState.format, lineColor: BCState.fgColor, background: BCState.bgColor, width: BCState.barWidth, height: BCState.barHeight, displayValue: BCState.displayValue, fontSize: BCState.fontSize, margin: BCState.margin, valid: () => true, }); // Keep preview responsive even when encoded data is very long. svgEl.setAttribute('preserveAspectRatio', 'xMidYMid meet'); errorEl.style.display = 'none'; // Product name display if (BCState.productName.trim()) { productDisplay.style.display = 'block'; productDisplay.textContent = BCState.productName; productDisplay.style.color = BCState.titleColor; productDisplay.style.fontSize = BCState.titleSize + 'px'; productDisplay.style.fontWeight = BCState.titleWeight; } else { productDisplay.style.display = 'none'; } // Info const svgRect = svgEl.getBoundingClientRect(); if (infoEl) { infoEl.textContent = `${bct('infoFormat')}: ${BCState.format} | ${bct('infoData')}: ${normalizedData}`; } toggleBCSupportPrompt(true); } catch (err) { svgEl.innerHTML = ''; productDisplay.style.display = 'none'; errorEl.style.display = 'block'; errorEl.textContent = bct('invalidDataPrefix') + (err.message || bct('invalidDataFallback')); if (infoEl) infoEl.textContent = bct('renderFailed'); toggleBCSupportPrompt(false); } } function toggleBCSupportPrompt(show) { const prompt = document.getElementById('bc-support-prompt'); if (!prompt) return; prompt.hidden = !show; } function scheduleRender() { clearTimeout(renderTimer); renderTimer = setTimeout(renderBarcode, 150); } /* ========== DOWNLOAD ========== */ function downloadBarcode(format) { const productName = BCState.productName.trim(); const svgEl = document.getElementById('barcode-svg'); if (!svgEl || !svgEl.querySelector('rect')) { alert(bct('alertGenerateFirst')); return; } if (format === 'svg') { downloadSVG(svgEl, productName); } else { downloadPNG(svgEl, productName, BCState.downloadScale); } } function downloadSVG(svgEl, productName) { // Clone svg and optionally add product name as text const clone = svgEl.cloneNode(true); if (productName) { // Add product name text above the barcode const titleFontSize = BCState.titleSize; const svgW = parseInt(svgEl.getAttribute('width') || svgEl.viewBox?.baseVal?.width || 200); const titleEl = document.createElementNS('http://www.w3.org/2000/svg', 'text'); titleEl.setAttribute('x', svgW / 2); titleEl.setAttribute('y', titleFontSize + 4); titleEl.setAttribute('text-anchor', 'middle'); titleEl.setAttribute('font-size', titleFontSize); titleEl.setAttribute('font-weight', BCState.titleWeight); titleEl.setAttribute('fill', BCState.titleColor); titleEl.setAttribute('font-family', 'sans-serif'); titleEl.textContent = productName; clone.insertBefore(titleEl, clone.firstChild); } const svgData = new XMLSerializer().serializeToString(clone); const blob = new Blob([svgData], { type: 'image/svg+xml' }); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = `barcode-${Date.now()}.svg`; link.click(); URL.revokeObjectURL(url); } function downloadPNG(svgEl, productName, scale) { const svgW = svgEl.width?.baseVal?.value || 200; const svgH = svgEl.height?.baseVal?.value || 120; const titleFontSize = BCState.titleSize; const titlePad = productName ? titleFontSize + 16 : 0; const canvas = document.createElement('canvas'); canvas.width = (svgW) * scale; canvas.height = (svgH + titlePad) * scale; const ctx = canvas.getContext('2d'); ctx.scale(scale, scale); // Background ctx.fillStyle = BCState.bgColor; ctx.fillRect(0, 0, svgW, svgH + titlePad); // Product name if (productName) { ctx.fillStyle = BCState.titleColor; ctx.font = `${BCState.titleWeight} ${titleFontSize}px sans-serif`; ctx.textAlign = 'center'; ctx.textBaseline = 'top'; ctx.fillText(productName, svgW / 2, 8); } // Render SVG to image then draw on canvas const svgData = new XMLSerializer().serializeToString(svgEl); const svgBlob = new Blob([svgData], { type: 'image/svg+xml;charset=utf-8' }); const url = URL.createObjectURL(svgBlob); const img = new Image(); img.onload = () => { ctx.drawImage(img, 0, titlePad, svgW, svgH); URL.revokeObjectURL(url); const link = document.createElement('a'); link.download = `barcode-${Date.now()}.png`; link.href = canvas.toDataURL('image/png'); link.click(); }; img.src = url; } /* ========== EVENT LISTENERS ========== */ document.addEventListener('DOMContentLoaded', () => { const htmlEl = document.documentElement; if (htmlEl) { const observer = new MutationObserver(mutations => { for (const m of mutations) { if (m.type === 'attributes' && m.attributeName === 'lang') { applyBCStaticTexts(); const hint = document.getElementById('bc-format-hint'); if (hint) hint.textContent = getFormatHint(BCState.format); scheduleRender(); break; } } }); observer.observe(htmlEl, { attributes: true, attributeFilter: ['lang'] }); } // Initial render applyBCStaticTexts(); const formatHint = document.getElementById('bc-format-hint'); if (formatHint) formatHint.textContent = getFormatHint(BCState.format); scheduleRender(); // Format document.getElementById('bc-format').addEventListener('change', e => { BCState.format = e.target.value; const hint = document.getElementById('bc-format-hint'); if (hint) hint.textContent = getFormatHint(e.target.value); scheduleRender(); }); // Data input document.getElementById('bc-data').addEventListener('input', e => { BCState.data = e.target.value; scheduleRender(); }); // Product name document.getElementById('bc-product-name').addEventListener('input', e => { BCState.productName = e.target.value; scheduleRender(); }); // Display value (show/hide numbers below barcode) document.querySelectorAll('input[name="bc-display-value"]').forEach(radio => { radio.addEventListener('change', e => { BCState.displayValue = e.target.value === 'true'; scheduleRender(); }); }); // FG Color const fgColor = document.getElementById('bc-fg-color'); const fgHex = document.getElementById('bc-fg-hex'); fgColor.addEventListener('input', e => { BCState.fgColor = e.target.value; fgHex.value = e.target.value; scheduleRender(); }); fgHex.addEventListener('input', e => { if (/^#[0-9A-Fa-f]{6}$/.test(e.target.value)) { BCState.fgColor = e.target.value; fgColor.value = e.target.value; scheduleRender(); } }); // BG Color const bgColor = document.getElementById('bc-bg-color'); const bgHex = document.getElementById('bc-bg-hex'); bgColor.addEventListener('input', e => { BCState.bgColor = e.target.value; bgHex.value = e.target.value; scheduleRender(); }); bgHex.addEventListener('input', e => { if (/^#[0-9A-Fa-f]{6}$/.test(e.target.value)) { BCState.bgColor = e.target.value; bgColor.value = e.target.value; scheduleRender(); } }); // Title color const titleColor = document.getElementById('bc-title-color'); const titleHex = document.getElementById('bc-title-hex'); titleColor.addEventListener('input', e => { BCState.titleColor = e.target.value; titleHex.value = e.target.value; scheduleRender(); }); titleHex.addEventListener('input', e => { if (/^#[0-9A-Fa-f]{6}$/.test(e.target.value)) { BCState.titleColor = e.target.value; titleColor.value = e.target.value; scheduleRender(); } }); // Title size document.getElementById('bc-title-size').addEventListener('input', e => { BCState.titleSize = parseInt(e.target.value); document.getElementById('bc-title-size-val').textContent = e.target.value + 'px'; scheduleRender(); }); // Title weight document.querySelectorAll('input[name="bc-title-weight"]').forEach(radio => { radio.addEventListener('change', e => { BCState.titleWeight = e.target.value; scheduleRender(); }); }); // Bar width document.getElementById('bc-width').addEventListener('input', e => { BCState.barWidth = parseFloat(e.target.value); document.getElementById('bc-width-val').textContent = e.target.value; scheduleRender(); }); // Bar height document.getElementById('bc-height').addEventListener('input', e => { BCState.barHeight = parseInt(e.target.value); document.getElementById('bc-height-val').textContent = e.target.value + 'px'; scheduleRender(); }); // Margin document.getElementById('bc-margin').addEventListener('input', e => { BCState.margin = parseInt(e.target.value); document.getElementById('bc-margin-val').textContent = e.target.value + 'px'; scheduleRender(); }); // Font size document.getElementById('bc-font-size').addEventListener('input', e => { BCState.fontSize = parseInt(e.target.value); document.getElementById('bc-font-size-val').textContent = e.target.value + 'px'; scheduleRender(); }); // Color presets document.querySelectorAll('.bc-preset').forEach(preset => { preset.addEventListener('click', () => { const fg = preset.dataset.fg; const bg = preset.dataset.bg; BCState.fgColor = fg; BCState.bgColor = bg; document.getElementById('bc-fg-color').value = fg; document.getElementById('bc-fg-hex').value = fg; document.getElementById('bc-bg-color').value = bg; document.getElementById('bc-bg-hex').value = bg; scheduleRender(); }); }); // Download scale document.querySelectorAll('.size-btn[data-scale]').forEach(btn => { btn.addEventListener('click', () => { document.querySelectorAll('.size-btn[data-scale]').forEach(b => b.classList.remove('active')); btn.classList.add('active'); BCState.downloadScale = parseInt(btn.dataset.scale); }); }); // Tab switching document.querySelectorAll('.tab-btn').forEach(btn => { btn.addEventListener('click', () => { document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active')); document.querySelectorAll('.tab-pane').forEach(p => p.classList.remove('active')); btn.classList.add('active'); document.getElementById(btn.dataset.tab).classList.add('active'); }); }); });