/**
* AlaskahQR - QR Code Generator
* Pure JavaScript implementation with custom canvas drawing
*/
const QRState = {
text: 'https://qr.alaskah.com',
dotStyle: 'square',
eyeStyle: 'square',
fgColor: '#000000',
bgColor: '#ffffff',
gradientType: 'none',
gradientColor1: '#4F46E5',
gradientColor2: '#7C3AED',
logoImage: null,
logoSize: 20,
logoPadding: 8,
logoRadius: 10,
errorLevel: 'M',
margin: 20,
downloadSize: 512,
};
let renderTimer = null;
const QR_UI_I18N = {
ko: {
empty: '내용을 입력하면 QR코드가 생성됩니다',
failed: '생성 실패: 내용을 확인해주세요',
infoModules: '모듈 수',
infoError: '오류 수정',
infoChars: '문자 수',
copyOk: '클립보드에 복사되었습니다!',
copyFail: '복사 실패: 브라우저가 지원하지 않습니다',
uiOptions: '⚙️ 옵션 설정',
uiTabInput: '내용',
uiTabShape: '모양',
uiTabColor: '색상',
uiTabLogo: '로고',
uiContentType: '콘텐츠 유형',
uiTypeUrl: 'URL / 웹사이트',
uiTypeText: '텍스트',
uiTypeEmail: '이메일',
uiTypePhone: '전화번호',
uiTypeSms: 'SMS',
uiTypeWifi: 'Wi-Fi',
uiTypeVcard: '연락처 (vCard)',
uiUrlAddress: 'URL 주소',
uiTextContent: '텍스트 내용',
uiTextPlaceholder: 'QR코드에 담을 내용을 입력하세요',
uiEmailAddress: '이메일 주소',
uiSubjectOptional: '제목 (선택)',
uiBodyOptional: '본문 (선택)',
uiEmailSubjectPlaceholder: '메일 제목',
uiEmailBodyPlaceholder: '메일 내용',
uiPhoneNumber: '전화번호',
uiMessageContent: '메시지 내용',
uiSmsPlaceholder: '전송할 메시지',
uiWifiSsid: '네트워크 이름 (SSID)',
uiWifiNamePlaceholder: 'Wi-Fi 이름',
uiPassword: '비밀번호',
uiWifiPasswordPlaceholder: 'Wi-Fi 비밀번호',
uiSecurityType: '보안 유형',
uiSecurityNone: '없음',
uiName: '이름',
uiCompany: '회사',
uiWebsite: '웹사이트',
uiErrorLevel: '오류 수정 수준',
uiErrorLevelNote: '로고 추가 시 자동으로 H로 설정됩니다',
uiEcL: 'L - 낮음 (7%)',
uiEcM: 'M - 중간 (15%)',
uiEcQ: 'Q - 높음 (25%)',
uiEcH: 'H - 최고 (30%)',
uiPreview: '👁️ 미리보기',
uiDownloadSize: '다운로드 크기',
uiDownloadPng: '⬇️ PNG 다운로드',
uiCopy: '📋 클립보드 복사',
uiAd: '광고 영역 (AdSense)',
uiHowtoTitle: 'QR 코드 활용 방법',
uiHow1Title: '웹사이트 링크',
uiHow1Desc: '명함, 전단지, 포스터에 QR코드를 삽입해 웹사이트로 쉽게 연결하세요.',
uiHow2Title: 'Wi-Fi 공유',
uiHow2Desc: '카페나 사무실에서 Wi-Fi 비밀번호를 QR코드로 간편하게 공유하세요.',
uiHow3Title: '연락처 공유',
uiHow3Desc: '명함 대신 QR코드로 연락처를 공유하세요. 스캔 한 번으로 저장됩니다.',
uiHow4Title: '제품 정보',
uiHow4Desc: '제품 패키지에 QR코드를 추가해 상세 정보나 사용 설명서로 연결하세요.',
uiDotStyle: '데이터 점 모양',
uiEyeStyle: '눈(Eye) 모양',
uiMarginSize: '여백 크기',
uiDotSquare: '사각형',
uiDotRounded: '둥근',
uiDotExtraRounded: '더 둥근',
uiDotCircle: '원형',
uiDotDiamond: '다이아몬드',
uiEyeSquare: '사각형',
uiEyeRounded: '둥근',
uiEyeCircle: '원형',
uiFgColorLabel: '전경색 (QR 색상)',
uiFgColorHelp: 'QR 점 색상',
uiBgColorLabel: '배경색',
uiBgColorHelp: '배경 색상',
uiGradientLabel: '그라데이션',
uiGradNone: '없음',
uiGradLinear: '선형',
uiGradRadial: '방사형',
uiGradStart: '시작 색상',
uiGradEnd: '끝 색상',
uiColorPresetLabel: '빠른 색상 프리셋',
uiPresetClassic: '클래식',
uiPresetBlue: '블루',
uiPresetGreen: '그린',
uiPresetAmber: '에임버',
uiPresetPurple: '퍼플',
uiPresetRose: '로즈',
uiLogoLabel: '가운데 로고/아이콘',
uiLogoAutoNote: '로고 추가 시 오류 수정이 H로 자동 설정됩니다',
uiUploadClick: '클릭하여 이미지 선택',
uiUploadFormats: 'PNG, JPG, SVG, WebP 지원',
uiUploadDone: '✓ 로고가 업로드되었습니다',
uiRemove: '제거',
uiLogoSizeLabel: '로고 크기',
uiLogoPaddingLabel: '로고 배경 여백',
uiLogoRadiusLabel: '로고 배경 모서리',
uiTipStrong: '💡',
uiTipLabel: '팁:',
uiTipDesc: '로고 추가 시 QR코드의 오류 수정 수준이 자동으로 H(30%)로 설정됩니다. 로고가 가운데 약 20% 영역을 가리더라도 QR코드가 정상 인식됩니다.',
uiSupportTitle: '마음에 드셨다면 후원으로 응원해주세요',
uiSupportDesc: '무료 운영에 도움이 됩니다. 카카오페이와 토스로 간편하게 후원할 수 있습니다.',
uiSupportButton: '☕ 후원하기',
},
en: {
empty: 'Enter content to generate your QR code',
failed: 'Generation failed: please check your content',
infoModules: 'Modules',
infoError: 'Error Correction',
infoChars: 'Characters',
copyOk: 'Copied to clipboard!',
copyFail: 'Copy failed: browser not supported',
uiOptions: '⚙️ Options',
uiTabInput: 'Content',
uiTabShape: 'Shape',
uiTabColor: 'Color',
uiTabLogo: 'Logo',
uiContentType: 'Content Type',
uiTypeUrl: 'URL / Website',
uiTypeText: 'Text',
uiTypeEmail: 'Email',
uiTypePhone: 'Phone Number',
uiTypeSms: 'SMS',
uiTypeWifi: 'Wi-Fi',
uiTypeVcard: 'Contact (vCard)',
uiUrlAddress: 'URL Address',
uiTextContent: 'Text Content',
uiTextPlaceholder: 'Enter the text to include in your QR code',
uiEmailAddress: 'Email Address',
uiSubjectOptional: 'Subject (Optional)',
uiBodyOptional: 'Body (Optional)',
uiEmailSubjectPlaceholder: 'Email subject',
uiEmailBodyPlaceholder: 'Email body',
uiPhoneNumber: 'Phone Number',
uiMessageContent: 'Message',
uiSmsPlaceholder: 'Message to send',
uiWifiSsid: 'Network Name (SSID)',
uiWifiNamePlaceholder: 'Wi-Fi name',
uiPassword: 'Password',
uiWifiPasswordPlaceholder: 'Wi-Fi password',
uiSecurityType: 'Security Type',
uiSecurityNone: 'None',
uiName: 'Name',
uiCompany: 'Company',
uiWebsite: 'Website',
uiErrorLevel: 'Error Correction Level',
uiErrorLevelNote: 'Automatically set to H when a logo is added',
uiEcL: 'L - Low (7%)',
uiEcM: 'M - Medium (15%)',
uiEcQ: 'Q - High (25%)',
uiEcH: 'H - Best (30%)',
uiPreview: '👁️ Preview',
uiDownloadSize: 'Download Size',
uiDownloadPng: '⬇️ Download PNG',
uiCopy: '📋 Copy to Clipboard',
uiAd: 'Ad Area (AdSense)',
uiHowtoTitle: 'How to Use QR Codes',
uiHow1Title: 'Website Link',
uiHow1Desc: 'Insert QR codes into business cards, flyers, and posters to link to your website easily.',
uiHow2Title: 'Wi-Fi Sharing',
uiHow2Desc: 'Share Wi-Fi passwords quickly in cafes or offices using a QR code.',
uiHow3Title: 'Contact Sharing',
uiHow3Desc: 'Share contacts with a QR code instead of business cards. Save with one scan.',
uiHow4Title: 'Product Information',
uiHow4Desc: 'Add QR codes to product packaging to link to details or user guides.',
uiDotStyle: 'Dot Style',
uiEyeStyle: 'Eye Style',
uiMarginSize: 'Margin Size',
uiDotSquare: 'Square',
uiDotRounded: 'Rounded',
uiDotExtraRounded: 'Extra Rounded',
uiDotCircle: 'Circle',
uiDotDiamond: 'Diamond',
uiEyeSquare: 'Square',
uiEyeRounded: 'Rounded',
uiEyeCircle: 'Circle',
uiFgColorLabel: 'Foreground Color (QR Color)',
uiFgColorHelp: 'QR dot color',
uiBgColorLabel: 'Background Color',
uiBgColorHelp: 'Background color',
uiGradientLabel: 'Gradient',
uiGradNone: 'None',
uiGradLinear: 'Linear',
uiGradRadial: 'Radial',
uiGradStart: 'Start Color',
uiGradEnd: 'End Color',
uiColorPresetLabel: 'Quick Color Presets',
uiPresetClassic: 'Classic',
uiPresetBlue: 'Blue',
uiPresetGreen: 'Green',
uiPresetAmber: 'Amber',
uiPresetPurple: 'Purple',
uiPresetRose: 'Rose',
uiLogoLabel: 'Center Logo/Icon',
uiLogoAutoNote: 'Error correction is automatically set to H when a logo is added',
uiUploadClick: 'Click to select an image',
uiUploadFormats: 'PNG, JPG, SVG, WebP supported',
uiUploadDone: '✓ Logo uploaded',
uiRemove: 'Remove',
uiLogoSizeLabel: 'Logo Size',
uiLogoPaddingLabel: 'Logo Background Padding',
uiLogoRadiusLabel: 'Logo Background Radius',
uiTipStrong: '💡',
uiTipLabel: 'Tip:',
uiTipDesc: 'When a logo is added, error correction is automatically set to H (30%). Even if the center area is covered by about 20%, the QR code can still be scanned.',
uiSupportTitle: 'If this helped, consider supporting the project',
uiSupportDesc: 'Your support helps keep AlaskahQR free. You can donate easily with KakaoPay or Toss.',
uiSupportButton: '☕ Support',
},
ja: {
empty: '内容を入力するとQRコードが生成されます',
failed: '生成に失敗しました: 内容を確認してください',
infoModules: 'モジュール数',
infoError: '誤り訂正',
infoChars: '文字数',
copyOk: 'クリップボードにコピーしました',
copyFail: 'コピーに失敗しました: ブラウザ未対応',
uiOptions: '⚙️ オプション',
uiTabInput: '内容',
uiTabShape: '形',
uiTabColor: '色',
uiTabLogo: 'ロゴ',
uiContentType: 'コンテンツタイプ',
uiTypeUrl: 'URL / ウェブサイト',
uiTypeText: 'テキスト',
uiTypeEmail: 'メール',
uiTypePhone: '電話番号',
uiTypeSms: 'SMS',
uiTypeWifi: 'Wi-Fi',
uiTypeVcard: '連絡先 (vCard)',
uiUrlAddress: 'URLアドレス',
uiTextContent: 'テキスト内容',
uiTextPlaceholder: 'QRコードに入れる内容を入力してください',
uiEmailAddress: 'メールアドレス',
uiSubjectOptional: '件名 (任意)',
uiBodyOptional: '本文 (任意)',
uiEmailSubjectPlaceholder: 'メール件名',
uiEmailBodyPlaceholder: 'メール本文',
uiPhoneNumber: '電話番号',
uiMessageContent: 'メッセージ内容',
uiSmsPlaceholder: '送信するメッセージ',
uiWifiSsid: 'ネットワーク名 (SSID)',
uiWifiNamePlaceholder: 'Wi-Fi名',
uiPassword: 'パスワード',
uiWifiPasswordPlaceholder: 'Wi-Fiパスワード',
uiSecurityType: 'セキュリティ種類',
uiSecurityNone: 'なし',
uiName: '名前',
uiCompany: '会社',
uiWebsite: 'ウェブサイト',
uiErrorLevel: '誤り訂正レベル',
uiErrorLevelNote: 'ロゴ追加時は自動でHに設定されます',
uiEcL: 'L - 低 (7%)',
uiEcM: 'M - 中 (15%)',
uiEcQ: 'Q - 高 (25%)',
uiEcH: 'H - 最高 (30%)',
uiPreview: '👁️ プレビュー',
uiDownloadSize: 'ダウンロードサイズ',
uiDownloadPng: '⬇️ PNGをダウンロード',
uiCopy: '📋 クリップボードにコピー',
uiAd: '広告エリア (AdSense)',
uiHowtoTitle: 'QRコードの活用方法',
uiHow1Title: 'ウェブサイトリンク',
uiHow1Desc: '名刺、チラシ、ポスターにQRコードを載せて、サイトへ簡単に誘導できます。',
uiHow2Title: 'Wi-Fi共有',
uiHow2Desc: 'カフェやオフィスでWi-FiパスワードをQRコードで手軽に共有できます。',
uiHow3Title: '連絡先共有',
uiHow3Desc: '名刺の代わりにQRコードで連絡先を共有できます。1回のスキャンで保存できます。',
uiHow4Title: '製品情報',
uiHow4Desc: '製品パッケージにQRコードを追加して、詳細情報やガイドに接続できます。',
uiDotStyle: 'ドットスタイル',
uiEyeStyle: 'アイスタイル',
uiMarginSize: '余白サイズ',
uiDotSquare: '四角',
uiDotRounded: '角丸',
uiDotExtraRounded: 'より丸い',
uiDotCircle: '円',
uiDotDiamond: 'ダイヤモンド',
uiEyeSquare: '四角',
uiEyeRounded: '角丸',
uiEyeCircle: '円',
uiFgColorLabel: '前景色 (QR色)',
uiFgColorHelp: 'QRドット色',
uiBgColorLabel: '背景色',
uiBgColorHelp: '背景色',
uiGradientLabel: 'グラデーション',
uiGradNone: 'なし',
uiGradLinear: '線形',
uiGradRadial: '放射状',
uiGradStart: '開始色',
uiGradEnd: '終了色',
uiColorPresetLabel: 'クイックカラープリセット',
uiPresetClassic: 'クラシック',
uiPresetBlue: 'ブルー',
uiPresetGreen: 'グリーン',
uiPresetAmber: 'アンバー',
uiPresetPurple: 'パープル',
uiPresetRose: 'ローズ',
uiLogoLabel: '中央ロゴ/アイコン',
uiLogoAutoNote: 'ロゴ追加時、誤り訂正は自動でHに設定されます',
uiUploadClick: 'クリックして画像を選択',
uiUploadFormats: 'PNG, JPG, SVG, WebP 対応',
uiUploadDone: '✓ ロゴがアップロードされました',
uiRemove: '削除',
uiLogoSizeLabel: 'ロゴサイズ',
uiLogoPaddingLabel: 'ロゴ背景余白',
uiLogoRadiusLabel: 'ロゴ背景角丸',
uiTipStrong: '💡',
uiTipLabel: 'ヒント:',
uiTipDesc: 'ロゴを追加すると誤り訂正レベルは自動的にH(30%)になります。中央の約20%が隠れても読み取り可能です。',
},
zh: {
empty: '输入内容后将自动生成二维码',
failed: '生成失败: 请检查输入内容',
infoModules: '模块数',
infoError: '纠错级别',
infoChars: '字符数',
copyOk: '已复制到剪贴板',
copyFail: '复制失败: 浏览器不支持',
uiOptions: '⚙️ 选项设置',
uiTabInput: '内容',
uiTabShape: '形状',
uiTabColor: '颜色',
uiTabLogo: '标志',
uiContentType: '内容类型',
uiTypeUrl: 'URL / 网站',
uiTypeText: '文本',
uiTypeEmail: '电子邮箱',
uiTypePhone: '电话号码',
uiTypeSms: '短信',
uiTypeWifi: 'Wi-Fi',
uiTypeVcard: '联系人 (vCard)',
uiUrlAddress: 'URL地址',
uiTextContent: '文本内容',
uiTextPlaceholder: '请输入要写入二维码的内容',
uiEmailAddress: '电子邮箱地址',
uiSubjectOptional: '主题 (可选)',
uiBodyOptional: '正文 (可选)',
uiEmailSubjectPlaceholder: '邮件主题',
uiEmailBodyPlaceholder: '邮件正文',
uiPhoneNumber: '电话号码',
uiMessageContent: '消息内容',
uiSmsPlaceholder: '要发送的消息',
uiWifiSsid: '网络名称 (SSID)',
uiWifiNamePlaceholder: 'Wi-Fi 名称',
uiPassword: '密码',
uiWifiPasswordPlaceholder: 'Wi-Fi 密码',
uiSecurityType: '安全类型',
uiSecurityNone: '无',
uiName: '姓名',
uiCompany: '公司',
uiWebsite: '网站',
uiErrorLevel: '纠错级别',
uiErrorLevelNote: '添加Logo时将自动设置为H',
uiEcL: 'L - 低 (7%)',
uiEcM: 'M - 中 (15%)',
uiEcQ: 'Q - 高 (25%)',
uiEcH: 'H - 最高 (30%)',
uiPreview: '👁️ 预览',
uiDownloadSize: '下载尺寸',
uiDownloadPng: '⬇️ 下载 PNG',
uiCopy: '📋 复制到剪贴板',
uiAd: '广告区域 (AdSense)',
uiHowtoTitle: '二维码使用方式',
uiHow1Title: '网站链接',
uiHow1Desc: '将二维码放在名片、传单和海报上,轻松跳转到网站。',
uiHow2Title: 'Wi-Fi 共享',
uiHow2Desc: '在咖啡店或办公室用二维码快速共享 Wi-Fi 密码。',
uiHow3Title: '联系人共享',
uiHow3Desc: '用二维码代替名片共享联系人,扫码即可保存。',
uiHow4Title: '产品信息',
uiHow4Desc: '在产品包装上添加二维码,连接到详细信息或使用说明。',
uiDotStyle: '点样式',
uiEyeStyle: '定位点样式',
uiMarginSize: '留白大小',
uiDotSquare: '方形',
uiDotRounded: '圆角',
uiDotExtraRounded: '更圆',
uiDotCircle: '圆形',
uiDotDiamond: '菱形',
uiEyeSquare: '方形',
uiEyeRounded: '圆角',
uiEyeCircle: '圆形',
uiFgColorLabel: '前景色 (二维码颜色)',
uiFgColorHelp: '二维码点颜色',
uiBgColorLabel: '背景色',
uiBgColorHelp: '背景颜色',
uiGradientLabel: '渐变',
uiGradNone: '无',
uiGradLinear: '线性',
uiGradRadial: '径向',
uiGradStart: '起始颜色',
uiGradEnd: '结束颜色',
uiColorPresetLabel: '快速颜色预设',
uiPresetClassic: '经典',
uiPresetBlue: '蓝色',
uiPresetGreen: '绿色',
uiPresetAmber: '琥珀',
uiPresetPurple: '紫色',
uiPresetRose: '玫瑰',
uiLogoLabel: '中间标志/图标',
uiLogoAutoNote: '添加标志后纠错级别会自动设为 H',
uiUploadClick: '点击选择图片',
uiUploadFormats: '支持 PNG, JPG, SVG, WebP',
uiUploadDone: '✓ 标志已上传',
uiRemove: '移除',
uiLogoSizeLabel: '标志大小',
uiLogoPaddingLabel: '标志背景内边距',
uiLogoRadiusLabel: '标志背景圆角',
uiTipStrong: '💡',
uiTipLabel: '提示:',
uiTipDesc: '添加标志后,纠错级别会自动设置为 H(30%)。即使中心约20%被遮挡,二维码通常仍可识别。',
},
es: {
empty: 'Introduce contenido para generar tu codigo QR',
failed: 'Error al generar: revisa el contenido',
infoModules: 'Modulos',
infoError: 'Correccion de errores',
infoChars: 'Caracteres',
copyOk: 'Copiado al portapapeles',
copyFail: 'Error al copiar: navegador no compatible',
uiOptions: '⚙️ Opciones',
uiTabInput: 'Contenido',
uiTabShape: 'Forma',
uiTabColor: 'Color',
uiTabLogo: 'Logo',
uiContentType: 'Tipo de contenido',
uiTypeUrl: 'URL / Sitio web',
uiTypeText: 'Texto',
uiTypeEmail: 'Correo',
uiTypePhone: 'Numero de telefono',
uiTypeSms: 'SMS',
uiTypeWifi: 'Wi-Fi',
uiTypeVcard: 'Contacto (vCard)',
uiUrlAddress: 'Direccion URL',
uiTextContent: 'Contenido de texto',
uiTextPlaceholder: 'Ingresa el contenido para el codigo QR',
uiEmailAddress: 'Correo electronico',
uiSubjectOptional: 'Asunto (Opcional)',
uiBodyOptional: 'Cuerpo (Opcional)',
uiEmailSubjectPlaceholder: 'Asunto del correo',
uiEmailBodyPlaceholder: 'Contenido del correo',
uiPhoneNumber: 'Numero de telefono',
uiMessageContent: 'Mensaje',
uiSmsPlaceholder: 'Mensaje a enviar',
uiWifiSsid: 'Nombre de red (SSID)',
uiWifiNamePlaceholder: 'Nombre de Wi-Fi',
uiPassword: 'Contrasena',
uiWifiPasswordPlaceholder: 'Contrasena de Wi-Fi',
uiSecurityType: 'Tipo de seguridad',
uiSecurityNone: 'Ninguno',
uiName: 'Nombre',
uiCompany: 'Empresa',
uiWebsite: 'Sitio web',
uiErrorLevel: 'Nivel de correccion de errores',
uiErrorLevelNote: 'Se establece automaticamente en H al agregar un logo',
uiEcL: 'L - Bajo (7%)',
uiEcM: 'M - Medio (15%)',
uiEcQ: 'Q - Alto (25%)',
uiEcH: 'H - Maximo (30%)',
uiPreview: '👁️ Vista previa',
uiDownloadSize: 'Tamano de descarga',
uiDownloadPng: '⬇️ Descargar PNG',
uiCopy: '📋 Copiar al portapapeles',
uiAd: 'Area de anuncios (AdSense)',
uiHowtoTitle: 'Como usar codigos QR',
uiHow1Title: 'Enlace web',
uiHow1Desc: 'Inserta codigos QR en tarjetas, folletos y posters para dirigir facilmente a tu sitio web.',
uiHow2Title: 'Compartir Wi-Fi',
uiHow2Desc: 'Comparte rapidamente la contrasena Wi-Fi en cafeterias u oficinas con un codigo QR.',
uiHow3Title: 'Compartir contacto',
uiHow3Desc: 'Comparte contactos con un codigo QR en lugar de tarjetas. Se guarda con un escaneo.',
uiHow4Title: 'Informacion del producto',
uiHow4Desc: 'Agrega codigos QR al empaque para enlazar detalles o guias de uso.',
uiDotStyle: 'Estilo de punto',
uiEyeStyle: 'Estilo de ojo',
uiMarginSize: 'Tamano de margen',
uiDotSquare: 'Cuadrado',
uiDotRounded: 'Redondeado',
uiDotExtraRounded: 'Muy redondeado',
uiDotCircle: 'Circulo',
uiDotDiamond: 'Diamante',
uiEyeSquare: 'Cuadrado',
uiEyeRounded: 'Redondeado',
uiEyeCircle: 'Circulo',
uiFgColorLabel: 'Color frontal (QR)',
uiFgColorHelp: 'Color de los puntos QR',
uiBgColorLabel: 'Color de fondo',
uiBgColorHelp: 'Color de fondo',
uiGradientLabel: 'Degradado',
uiGradNone: 'Ninguno',
uiGradLinear: 'Lineal',
uiGradRadial: 'Radial',
uiGradStart: 'Color inicial',
uiGradEnd: 'Color final',
uiColorPresetLabel: 'Preajustes rapidos de color',
uiPresetClassic: 'Clasico',
uiPresetBlue: 'Azul',
uiPresetGreen: 'Verde',
uiPresetAmber: 'Ambar',
uiPresetPurple: 'Purpura',
uiPresetRose: 'Rosa',
uiLogoLabel: 'Logo/Icono central',
uiLogoAutoNote: 'La correccion se establece automaticamente en H al agregar un logo',
uiUploadClick: 'Haz clic para seleccionar una imagen',
uiUploadFormats: 'Compatible con PNG, JPG, SVG, WebP',
uiUploadDone: '✓ Logo subido',
uiRemove: 'Eliminar',
uiLogoSizeLabel: 'Tamano del logo',
uiLogoPaddingLabel: 'Relleno del fondo del logo',
uiLogoRadiusLabel: 'Radio del fondo del logo',
uiTipStrong: '💡',
uiTipLabel: 'Consejo:',
uiTipDesc: 'Cuando agregas un logo, la correccion de errores se ajusta automaticamente a H(30%). Incluso si se cubre cerca del 20% del centro, el QR suele seguir siendo legible.',
},
fr: {
empty: 'Saisissez du contenu pour generer votre code QR',
failed: 'Echec de generation : verifiez le contenu',
infoModules: 'Modules',
infoError: 'Correction d erreurs',
infoChars: 'Caracteres',
copyOk: 'Copie dans le presse-papiers',
copyFail: 'Echec de copie : navigateur non pris en charge',
uiOptions: '⚙️ Options',
uiTabInput: 'Contenu',
uiTabShape: 'Forme',
uiTabColor: 'Couleur',
uiTabLogo: 'Logo',
uiContentType: 'Type de contenu',
uiTypeUrl: 'URL / Site web',
uiTypeText: 'Texte',
uiTypeEmail: 'E-mail',
uiTypePhone: 'Numero de telephone',
uiTypeSms: 'SMS',
uiTypeWifi: 'Wi-Fi',
uiTypeVcard: 'Contact (vCard)',
uiUrlAddress: 'Adresse URL',
uiTextContent: 'Contenu texte',
uiTextPlaceholder: 'Saisissez le contenu du code QR',
uiEmailAddress: 'Adresse e-mail',
uiSubjectOptional: 'Objet (Optionnel)',
uiBodyOptional: 'Message (Optionnel)',
uiEmailSubjectPlaceholder: 'Objet de l e-mail',
uiEmailBodyPlaceholder: 'Contenu de l e-mail',
uiPhoneNumber: 'Numero de telephone',
uiMessageContent: 'Message',
uiSmsPlaceholder: 'Message a envoyer',
uiWifiSsid: 'Nom du reseau (SSID)',
uiWifiNamePlaceholder: 'Nom du Wi-Fi',
uiPassword: 'Mot de passe',
uiWifiPasswordPlaceholder: 'Mot de passe Wi-Fi',
uiSecurityType: 'Type de securite',
uiSecurityNone: 'Aucun',
uiName: 'Nom',
uiCompany: 'Entreprise',
uiWebsite: 'Site web',
uiErrorLevel: 'Niveau de correction d erreurs',
uiErrorLevelNote: 'Defini automatiquement sur H lors de l ajout d un logo',
uiEcL: 'L - Faible (7%)',
uiEcM: 'M - Moyen (15%)',
uiEcQ: 'Q - Eleve (25%)',
uiEcH: 'H - Maximum (30%)',
uiPreview: '👁️ Apercu',
uiDownloadSize: 'Taille de telechargement',
uiDownloadPng: '⬇️ Telecharger PNG',
uiCopy: '📋 Copier dans le presse-papiers',
uiAd: 'Zone publicitaire (AdSense)',
uiHowtoTitle: 'Comment utiliser les QR codes',
uiHow1Title: 'Lien vers un site web',
uiHow1Desc: 'Ajoutez des QR codes sur cartes, flyers et affiches pour diriger facilement vers votre site.',
uiHow2Title: 'Partage Wi-Fi',
uiHow2Desc: 'Partagez rapidement le mot de passe Wi-Fi dans un cafe ou un bureau via un QR code.',
uiHow3Title: 'Partage de contact',
uiHow3Desc: 'Partagez vos contacts avec un QR code au lieu d une carte de visite. Un scan suffit.',
uiHow4Title: 'Informations produit',
uiHow4Desc: 'Ajoutez un QR code sur l emballage pour ouvrir les details ou le guide d utilisation.',
uiDotStyle: 'Style des points',
uiEyeStyle: 'Style des yeux',
uiMarginSize: 'Taille de marge',
uiDotSquare: 'Carre',
uiDotRounded: 'Arrondi',
uiDotExtraRounded: 'Tres arrondi',
uiDotCircle: 'Cercle',
uiDotDiamond: 'Diamant',
uiEyeSquare: 'Carre',
uiEyeRounded: 'Arrondi',
uiEyeCircle: 'Cercle',
uiFgColorLabel: 'Couleur du premier plan (QR)',
uiFgColorHelp: 'Couleur des points QR',
uiBgColorLabel: 'Couleur d arriere-plan',
uiBgColorHelp: 'Couleur de fond',
uiGradientLabel: 'Degrade',
uiGradNone: 'Aucun',
uiGradLinear: 'Lineaire',
uiGradRadial: 'Radial',
uiGradStart: 'Couleur de debut',
uiGradEnd: 'Couleur de fin',
uiColorPresetLabel: 'Preselections de couleurs rapides',
uiPresetClassic: 'Classique',
uiPresetBlue: 'Bleu',
uiPresetGreen: 'Vert',
uiPresetAmber: 'Ambre',
uiPresetPurple: 'Violet',
uiPresetRose: 'Rose',
uiLogoLabel: 'Logo/Icone central(e)',
uiLogoAutoNote: 'La correction est automatiquement reglee sur H lors de l ajout d un logo',
uiUploadClick: 'Cliquez pour choisir une image',
uiUploadFormats: 'PNG, JPG, SVG, WebP pris en charge',
uiUploadDone: '✓ Logo televerse',
uiRemove: 'Supprimer',
uiLogoSizeLabel: 'Taille du logo',
uiLogoPaddingLabel: 'Marge de fond du logo',
uiLogoRadiusLabel: 'Rayon du fond du logo',
uiTipStrong: '💡',
uiTipLabel: 'Conseil:',
uiTipDesc: 'Quand vous ajoutez un logo, la correction d erreurs passe automatiquement a H(30%). Meme si environ 20% de la zone centrale est couverte, le QR reste en general lisible.',
},
pt: {
empty: 'Insira o conteudo para gerar seu codigo QR',
failed: 'Falha na geracao: verifique o conteudo',
infoModules: 'Modulos',
infoError: 'Correcao de erros',
infoChars: 'Caracteres',
copyOk: 'Copiado para a area de transferencia',
copyFail: 'Falha ao copiar: navegador nao suportado',
uiOptions: '⚙️ Opcoes',
uiTabInput: 'Conteudo',
uiTabShape: 'Forma',
uiTabColor: 'Cor',
uiTabLogo: 'Logo',
uiContentType: 'Tipo de conteudo',
uiTypeUrl: 'URL / Website',
uiTypeText: 'Texto',
uiTypeEmail: 'Email',
uiTypePhone: 'Numero de telefone',
uiTypeSms: 'SMS',
uiTypeWifi: 'Wi-Fi',
uiTypeVcard: 'Contato (vCard)',
uiUrlAddress: 'Endereco URL',
uiTextContent: 'Conteudo de texto',
uiTextPlaceholder: 'Digite o conteudo para o codigo QR',
uiEmailAddress: 'Endereco de email',
uiSubjectOptional: 'Assunto (Opcional)',
uiBodyOptional: 'Corpo (Opcional)',
uiEmailSubjectPlaceholder: 'Assunto do email',
uiEmailBodyPlaceholder: 'Corpo do email',
uiPhoneNumber: 'Numero de telefone',
uiMessageContent: 'Mensagem',
uiSmsPlaceholder: 'Mensagem para enviar',
uiWifiSsid: 'Nome da rede (SSID)',
uiWifiNamePlaceholder: 'Nome do Wi-Fi',
uiPassword: 'Senha',
uiWifiPasswordPlaceholder: 'Senha do Wi-Fi',
uiSecurityType: 'Tipo de seguranca',
uiSecurityNone: 'Nenhum',
uiName: 'Nome',
uiCompany: 'Empresa',
uiWebsite: 'Website',
uiErrorLevel: 'Nivel de correcao de erros',
uiErrorLevelNote: 'Definido automaticamente para H ao adicionar logo',
uiEcL: 'L - Baixo (7%)',
uiEcM: 'M - Medio (15%)',
uiEcQ: 'Q - Alto (25%)',
uiEcH: 'H - Maximo (30%)',
uiPreview: '👁️ Pre-visualizacao',
uiDownloadSize: 'Tamanho de download',
uiDownloadPng: '⬇️ Baixar PNG',
uiCopy: '📋 Copiar para a area de transferencia',
uiAd: 'Area de anuncios (AdSense)',
uiHowtoTitle: 'Como usar codigos QR',
uiHow1Title: 'Link de site',
uiHow1Desc: 'Insira codigos QR em cartoes, panfletos e posters para levar ao seu site com facilidade.',
uiHow2Title: 'Compartilhar Wi-Fi',
uiHow2Desc: 'Compartilhe a senha do Wi-Fi em cafeterias ou escritorios com um QR code.',
uiHow3Title: 'Compartilhar contato',
uiHow3Desc: 'Compartilhe contatos com QR code em vez de cartao de visita. Salva com um scan.',
uiHow4Title: 'Informacoes do produto',
uiHow4Desc: 'Adicione QR codes na embalagem para abrir detalhes ou guias de uso.',
uiDotStyle: 'Estilo do ponto',
uiEyeStyle: 'Estilo dos olhos',
uiMarginSize: 'Tamanho da margem',
uiDotSquare: 'Quadrado',
uiDotRounded: 'Arredondado',
uiDotExtraRounded: 'Mais arredondado',
uiDotCircle: 'Circulo',
uiDotDiamond: 'Diamante',
uiEyeSquare: 'Quadrado',
uiEyeRounded: 'Arredondado',
uiEyeCircle: 'Circulo',
uiFgColorLabel: 'Cor de primeiro plano (QR)',
uiFgColorHelp: 'Cor dos pontos do QR',
uiBgColorLabel: 'Cor de fundo',
uiBgColorHelp: 'Cor de fundo',
uiGradientLabel: 'Gradiente',
uiGradNone: 'Nenhum',
uiGradLinear: 'Linear',
uiGradRadial: 'Radial',
uiGradStart: 'Cor inicial',
uiGradEnd: 'Cor final',
uiColorPresetLabel: 'Predefinicoes rapidas de cor',
uiPresetClassic: 'Classico',
uiPresetBlue: 'Azul',
uiPresetGreen: 'Verde',
uiPresetAmber: 'Ambar',
uiPresetPurple: 'Roxo',
uiPresetRose: 'Rosa',
uiLogoLabel: 'Logo/Icone central',
uiLogoAutoNote: 'A correcao de erros e ajustada automaticamente para H ao adicionar logo',
uiUploadClick: 'Clique para selecionar uma imagem',
uiUploadFormats: 'Suporta PNG, JPG, SVG, WebP',
uiUploadDone: '✓ Logo enviado',
uiRemove: 'Remover',
uiLogoSizeLabel: 'Tamanho do logo',
uiLogoPaddingLabel: 'Espacamento do fundo do logo',
uiLogoRadiusLabel: 'Raio do fundo do logo',
uiTipStrong: '💡',
uiTipLabel: 'Dica:',
uiTipDesc: 'Ao adicionar um logo, a correcao de erros e definida automaticamente para H(30%). Mesmo com cerca de 20% da area central coberta, o QR geralmente continua legivel.',
},
vi: {
empty: 'Nhap noi dung de tao ma QR',
failed: 'Tao that bai: vui long kiem tra noi dung',
infoModules: 'So module',
infoError: 'Muc sua loi',
infoChars: 'So ky tu',
copyOk: 'Da sao chep vao bo nho tam',
copyFail: 'Sao chep that bai: trinh duyet khong ho tro',
uiOptions: '⚙️ Tuy chon',
uiTabInput: 'Noi dung',
uiTabShape: 'Hinh dang',
uiTabColor: 'Mau sac',
uiTabLogo: 'Logo',
uiContentType: 'Loai noi dung',
uiTypeUrl: 'URL / Trang web',
uiTypeText: 'Van ban',
uiTypeEmail: 'Email',
uiTypePhone: 'So dien thoai',
uiTypeSms: 'SMS',
uiTypeWifi: 'Wi-Fi',
uiTypeVcard: 'Lien he (vCard)',
uiUrlAddress: 'Dia chi URL',
uiTextContent: 'Noi dung van ban',
uiTextPlaceholder: 'Nhap noi dung cho ma QR',
uiEmailAddress: 'Dia chi email',
uiSubjectOptional: 'Tieu de (Tuy chon)',
uiBodyOptional: 'Noi dung (Tuy chon)',
uiEmailSubjectPlaceholder: 'Tieu de email',
uiEmailBodyPlaceholder: 'Noi dung email',
uiPhoneNumber: 'So dien thoai',
uiMessageContent: 'Noi dung tin nhan',
uiSmsPlaceholder: 'Tin nhan se gui',
uiWifiSsid: 'Ten mang (SSID)',
uiWifiNamePlaceholder: 'Ten Wi-Fi',
uiPassword: 'Mat khau',
uiWifiPasswordPlaceholder: 'Mat khau Wi-Fi',
uiSecurityType: 'Kieu bao mat',
uiSecurityNone: 'Khong',
uiName: 'Ten',
uiCompany: 'Cong ty',
uiWebsite: 'Trang web',
uiErrorLevel: 'Muc sua loi',
uiErrorLevelNote: 'Tu dong dat H khi them logo',
uiEcL: 'L - Thap (7%)',
uiEcM: 'M - Trung binh (15%)',
uiEcQ: 'Q - Cao (25%)',
uiEcH: 'H - Cao nhat (30%)',
uiPreview: '👁️ Xem truoc',
uiDownloadSize: 'Kich thuoc tai xuong',
uiDownloadPng: '⬇️ Tai PNG',
uiCopy: '📋 Sao chep vao bo nho tam',
uiAd: 'Khu vuc quang cao (AdSense)',
uiHowtoTitle: 'Cach su dung ma QR',
uiHow1Title: 'Lien ket website',
uiHow1Desc: 'Chen ma QR vao danh thiep, to roi, poster de dieu huong den website de dang.',
uiHow2Title: 'Chia se Wi-Fi',
uiHow2Desc: 'Chia se mat khau Wi-Fi nhanh trong quan ca phe hoac van phong bang ma QR.',
uiHow3Title: 'Chia se lien he',
uiHow3Desc: 'Chia se lien he bang ma QR thay vi danh thiep. Quet mot lan la luu duoc.',
uiHow4Title: 'Thong tin san pham',
uiHow4Desc: 'Them ma QR len bao bi de lien ket den thong tin chi tiet hoac huong dan su dung.',
uiDotStyle: 'Kieu cham',
uiEyeStyle: 'Kieu mat',
uiMarginSize: 'Kich thuoc le',
uiDotSquare: 'Vuong',
uiDotRounded: 'Bo goc',
uiDotExtraRounded: 'Bo goc nhieu',
uiDotCircle: 'Tron',
uiDotDiamond: 'Kim cuong',
uiEyeSquare: 'Vuong',
uiEyeRounded: 'Bo goc',
uiEyeCircle: 'Tron',
uiFgColorLabel: 'Mau tien canh (mau QR)',
uiFgColorHelp: 'Mau cham QR',
uiBgColorLabel: 'Mau nen',
uiBgColorHelp: 'Mau nen',
uiGradientLabel: 'Chuyen mau',
uiGradNone: 'Khong',
uiGradLinear: 'Tuyen tinh',
uiGradRadial: 'Huong tam',
uiGradStart: 'Mau bat dau',
uiGradEnd: 'Mau ket thuc',
uiColorPresetLabel: 'Mau san co nhanh',
uiPresetClassic: 'Co dien',
uiPresetBlue: 'Xanh duong',
uiPresetGreen: 'Xanh la',
uiPresetAmber: 'Ho phach',
uiPresetPurple: 'Tim',
uiPresetRose: 'Hong',
uiLogoLabel: 'Logo/Bieu tuong o giua',
uiLogoAutoNote: 'Muc sua loi duoc tu dong dat H khi them logo',
uiUploadClick: 'Nhan de chon anh',
uiUploadFormats: 'Ho tro PNG, JPG, SVG, WebP',
uiUploadDone: '✓ Da tai logo len',
uiRemove: 'Xoa',
uiLogoSizeLabel: 'Kich thuoc logo',
uiLogoPaddingLabel: 'Le nen logo',
uiLogoRadiusLabel: 'Bo goc nen logo',
uiTipStrong: '💡',
uiTipLabel: 'Meo:',
uiTipDesc: 'Khi them logo, muc sua loi se tu dong dat H(30%). Ngay ca khi logo che khoang 20% o giua, ma QR thuong van quet duoc.',
},
th: {
empty: 'ป้อนเนื้อหาเพื่อสร้างคิวอาร์โค้ด',
failed: 'สร้างไม่สำเร็จ: โปรดตรวจสอบเนื้อหา',
infoModules: 'จำนวนโมดูล',
infoError: 'ระดับแก้ไขข้อผิดพลาด',
infoChars: 'จำนวนอักขระ',
copyOk: 'คัดลอกไปยังคลิปบอร์ดแล้ว',
copyFail: 'คัดลอกไม่สำเร็จ: เบราว์เซอร์ไม่รองรับ',
uiOptions: '⚙️ ตัวเลือก',
uiTabInput: 'เนื้อหา',
uiTabShape: 'รูปร่าง',
uiTabColor: 'สี',
uiTabLogo: 'โลโก้',
uiContentType: 'ประเภทเนื้อหา',
uiTypeUrl: 'URL / เว็บไซต์',
uiTypeText: 'ข้อความ',
uiTypeEmail: 'อีเมล',
uiTypePhone: 'หมายเลขโทรศัพท์',
uiTypeSms: 'SMS',
uiTypeWifi: 'Wi-Fi',
uiTypeVcard: 'รายชื่อผู้ติดต่อ (vCard)',
uiUrlAddress: 'ที่อยู่ URL',
uiTextContent: 'เนื้อหาข้อความ',
uiTextPlaceholder: 'ป้อนเนื้อหาสำหรับคิวอาร์โค้ด',
uiEmailAddress: 'ที่อยู่อีเมล',
uiSubjectOptional: 'หัวข้อ (ไม่บังคับ)',
uiBodyOptional: 'เนื้อความ (ไม่บังคับ)',
uiEmailSubjectPlaceholder: 'หัวข้ออีเมล',
uiEmailBodyPlaceholder: 'เนื้อหาอีเมล',
uiPhoneNumber: 'หมายเลขโทรศัพท์',
uiMessageContent: 'เนื้อหาข้อความ',
uiSmsPlaceholder: 'ข้อความที่จะส่ง',
uiWifiSsid: 'ชื่อเครือข่าย (SSID)',
uiWifiNamePlaceholder: 'ชื่อ Wi-Fi',
uiPassword: 'รหัสผ่าน',
uiWifiPasswordPlaceholder: 'รหัสผ่าน Wi-Fi',
uiSecurityType: 'ประเภทความปลอดภัย',
uiSecurityNone: 'ไม่มี',
uiName: 'ชื่อ',
uiCompany: 'บริษัท',
uiWebsite: 'เว็บไซต์',
uiErrorLevel: 'ระดับการแก้ไขข้อผิดพลาด',
uiErrorLevelNote: 'เมื่อลงโลโก้จะตั้งเป็น H อัตโนมัติ',
uiEcL: 'L - ต่ำ (7%)',
uiEcM: 'M - ปานกลาง (15%)',
uiEcQ: 'Q - สูง (25%)',
uiEcH: 'H - สูงสุด (30%)',
uiPreview: '👁️ ตัวอย่าง',
uiDownloadSize: 'ขนาดดาวน์โหลด',
uiDownloadPng: '⬇️ ดาวน์โหลด PNG',
uiCopy: '📋 คัดลอกไปยังคลิปบอร์ด',
uiAd: 'พื้นที่โฆษณา (AdSense)',
uiHowtoTitle: 'วิธีใช้งานคิวอาร์โค้ด',
uiHow1Title: 'ลิงก์เว็บไซต์',
uiHow1Desc: 'ใส่คิวอาร์โค้ดบนนามบัตร ใบปลิว และโปสเตอร์ เพื่อพาไปยังเว็บไซต์ได้ง่าย',
uiHow2Title: 'แชร์ Wi-Fi',
uiHow2Desc: 'แชร์รหัสผ่าน Wi-Fi ในคาเฟ่หรือออฟฟิศได้อย่างรวดเร็วด้วยคิวอาร์โค้ด',
uiHow3Title: 'แชร์ข้อมูลติดต่อ',
uiHow3Desc: 'แชร์ข้อมูลติดต่อด้วยคิวอาร์โค้ดแทนนามบัตร สแกนครั้งเดียวบันทึกได้',
uiHow4Title: 'ข้อมูลสินค้า',
uiHow4Desc: 'เพิ่มคิวอาร์โค้ดบนบรรจุภัณฑ์เพื่อเชื่อมไปยังรายละเอียดหรือคู่มือการใช้งาน',
uiDotStyle: 'สไตล์จุด',
uiEyeStyle: 'สไตล์ตา',
uiMarginSize: 'ขนาดขอบ',
uiDotSquare: 'สี่เหลี่ยม',
uiDotRounded: 'ขอบมน',
uiDotExtraRounded: 'มนมาก',
uiDotCircle: 'วงกลม',
uiDotDiamond: 'เพชร',
uiEyeSquare: 'สี่เหลี่ยม',
uiEyeRounded: 'ขอบมน',
uiEyeCircle: 'วงกลม',
uiFgColorLabel: 'สีด้านหน้า (สี QR)',
uiFgColorHelp: 'สีจุด QR',
uiBgColorLabel: 'สีพื้นหลัง',
uiBgColorHelp: 'สีพื้นหลัง',
uiGradientLabel: 'ไล่สี',
uiGradNone: 'ไม่มี',
uiGradLinear: 'เชิงเส้น',
uiGradRadial: 'แบบรัศมี',
uiGradStart: 'สีเริ่มต้น',
uiGradEnd: 'สีสิ้นสุด',
uiColorPresetLabel: 'พรีเซ็ตสีด่วน',
uiPresetClassic: 'คลาสสิก',
uiPresetBlue: 'น้ำเงิน',
uiPresetGreen: 'เขียว',
uiPresetAmber: 'อำพัน',
uiPresetPurple: 'ม่วง',
uiPresetRose: 'กุหลาบ',
uiLogoLabel: 'โลโก้/ไอคอนตรงกลาง',
uiLogoAutoNote: 'เมื่อเพิ่มโลโก้ ระบบจะตั้งค่าการแก้ไขข้อผิดพลาดเป็น H อัตโนมัติ',
uiUploadClick: 'คลิกเพื่อเลือกภาพ',
uiUploadFormats: 'รองรับ PNG, JPG, SVG, WebP',
uiUploadDone: '✓ อัปโหลดโลโก้แล้ว',
uiRemove: 'ลบ',
uiLogoSizeLabel: 'ขนาดโลโก้',
uiLogoPaddingLabel: 'ระยะห่างพื้นหลังโลโก้',
uiLogoRadiusLabel: 'ความโค้งมุมพื้นหลังโลโก้',
uiTipStrong: '💡',
uiTipLabel: 'เคล็ดลับ:',
uiTipDesc: 'เมื่อเพิ่มโลโก้ ระดับการแก้ไขข้อผิดพลาดจะถูกตั้งเป็น H(30%) อัตโนมัติ แม้บริเวณกลางภาพถูกบังประมาณ 20% โดยทั่วไปก็ยังสแกนได้',
},
};
QR_UI_I18N.de = {
...QR_UI_I18N.en,
empty: 'Geben Sie Inhalte ein, um Ihr QR code zu generieren',
failed: 'Generierung fehlgeschlagen: Bitte überprüfen Sie Ihren Inhalt',
infoModules: 'Module',
infoError: 'Fehlerkorrektur',
infoChars: 'Charaktere',
copyOk: 'In die Zwischenablage kopiert!',
copyFail: 'Kopieren fehlgeschlagen: Browser wird nicht unterstützt',
uiOptions: '⚙️ Optionen',
uiTabInput: 'Inhalt',
uiTabShape: 'Form',
uiTabColor: 'Farbe',
uiTabLogo: 'Logo',
uiContentType: 'Inhaltstyp',
uiTypeUrl: 'URL / Website',
uiTypeText: 'Text',
uiTypeEmail: 'E-Mail',
uiTypePhone: 'Telefonnummer',
uiTypeSms: 'SMS',
uiTypeWifi: 'Wi-Fi',
uiTypeVcard: 'Kontakt (vCard)',
uiUrlAddress: 'URL Adresse',
uiTextContent: 'Textinhalt',
uiTextPlaceholder: 'Geben Sie den Text ein, der in Ihr QR code aufgenommen werden soll.',
uiEmailAddress: 'E-Mail-Adresse',
uiSubjectOptional: 'Betreff (optional)',
uiBodyOptional: 'Körper (optional)',
uiEmailSubjectPlaceholder: 'E-Mail-Betreff',
uiEmailBodyPlaceholder: 'E-Mail-Text',
uiPhoneNumber: 'Telefonnummer',
uiMessageContent: 'Nachricht',
uiSmsPlaceholder: 'Nachricht zum Senden',
uiWifiSsid: 'Netzwerkname (SSID)',
uiWifiNamePlaceholder: 'Wi-Fi Name',
uiPassword: 'Passwort',
uiWifiPasswordPlaceholder: 'Wi-Fi Passwort',
uiSecurityType: 'Sicherheitstyp',
uiSecurityNone: 'Keiner',
uiName: 'Name',
uiCompany: 'Unternehmen',
uiWebsite: 'Webseite',
uiErrorLevel: 'Fehlerkorrekturstufe',
uiErrorLevelNote: 'Wird automatisch auf H gesetzt, wenn ein Logo hinzugefügt wird',
uiEcL: 'L – Niedrig (7 %)',
uiEcM: 'M – Mittel (15 %)',
uiEcQ: 'Q – Hoch (25 %)',
uiEcH: 'H – Am besten (30 %)',
uiPreview: '👁️ Vorschau',
uiDownloadSize: 'Downloadgröße',
uiDownloadPng: '⬇️ Laden Sie PNG herunter',
uiCopy: '📋 In die Zwischenablage kopieren',
uiAd: 'Werbefläche (AdSense)',
uiHowtoTitle: 'So verwenden Sie QR Codes',
uiHow1Title: 'Website-Link',
uiHow1Desc: 'Fügen Sie QR codes in Visitenkarten, Flyer und Poster ein, um ganz einfach eine Verknüpfung zu Ihrer Website herzustellen.',
uiHow2Title: 'Wi-Fi Teilen',
uiHow2Desc: 'Teilen Sie Wi-Fi Passwörter schnell in Cafés oder Büros mit einem QR code.',
uiHow3Title: 'Kontaktfreigabe',
uiHow3Desc: 'Teilen Sie Kontakte mit einem QR code anstelle von Visitenkarten. Sparen Sie mit einem Scan.',
uiHow4Title: 'Produktinformationen',
uiHow4Desc: 'Fügen Sie QR codes zur Produktverpackung hinzu, um auf Details oder Benutzerhandbücher zu verweisen.',
uiDotStyle: 'Punktstil',
uiEyeStyle: 'Augenstil',
uiMarginSize: 'Randgröße',
uiDotSquare: 'Quadrat',
uiDotRounded: 'Gerundet',
uiDotExtraRounded: 'Extra abgerundet',
uiDotCircle: 'Kreis',
uiDotDiamond: 'Diamant',
uiEyeSquare: 'Quadrat',
uiEyeRounded: 'Gerundet',
uiEyeCircle: 'Kreis',
uiFgColorLabel: 'Vordergrundfarbe (QR Farbe)',
uiFgColorHelp: 'QR Punktfarbe',
uiBgColorLabel: 'Hintergrundfarbe',
uiBgColorHelp: 'Hintergrundfarbe',
uiGradientLabel: 'Gradient',
uiGradNone: 'Keiner',
uiGradLinear: 'Linear',
uiGradRadial: 'Radial',
uiGradStart: 'Starten Sie Farbe',
uiGradEnd: 'Endfarbe',
uiColorPresetLabel: 'Schnelle Farbvoreinstellungen',
uiPresetClassic: 'Klassiker',
uiPresetBlue: 'Blau',
uiPresetGreen: 'Grün',
uiPresetAmber: 'Bernstein',
uiPresetPurple: 'Lila',
uiPresetRose: 'Rose',
uiLogoLabel: 'Logo/Symbol in der Mitte',
uiLogoAutoNote: 'Beim Hinzufügen eines Logos wird die Fehlerkorrektur automatisch auf H gesetzt',
uiUploadClick: 'Klicken Sie, um ein Bild auszuwählen',
uiUploadFormats: 'PNG, JPG, SVG, WebP unterstützt',
uiUploadDone: '✓ Logo hochgeladen',
uiRemove: 'Entfernen',
uiLogoSizeLabel: 'Logogröße',
uiLogoPaddingLabel: 'Logo-Hintergrundpolsterung',
uiLogoRadiusLabel: 'Logo-Hintergrundradius',
uiTipStrong: '💡',
uiTipLabel: 'Tipp:',
uiTipDesc: 'Wenn ein Logo hinzugefügt wird, wird die Fehlerkorrektur automatisch auf H (30 %) eingestellt. Selbst wenn der mittlere Bereich zu etwa 20 % abgedeckt ist, kann der QR code noch gescannt werden.',
};
function getCurrentLangForQR() {
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 qrt(key) {
const lang = getCurrentLangForQR();
return QR_UI_I18N[lang]?.[key] || QR_UI_I18N.en[key] || QR_UI_I18N.ko[key] || key;
}
function applyQRStaticTexts() {
const panelTitle = document.querySelector('.options-col .panel .panel-title');
if (panelTitle) panelTitle.textContent = qrt('uiOptions');
const tabInput = document.querySelector('.tab-btn[data-tab="tab-input"]');
if (tabInput) tabInput.textContent = qrt('uiTabInput');
const tabShape = document.querySelector('.tab-btn[data-tab="tab-shape"]');
if (tabShape) tabShape.textContent = qrt('uiTabShape');
const tabColor = document.querySelector('.tab-btn[data-tab="tab-color"]');
if (tabColor) tabColor.textContent = qrt('uiTabColor');
const tabLogo = document.querySelector('.tab-btn[data-tab="tab-logo"]');
if (tabLogo) tabLogo.textContent = qrt('uiTabLogo');
const contentTypeLabel = document.querySelector('#tab-input .form-group .form-label');
if (contentTypeLabel) contentTypeLabel.textContent = qrt('uiContentType');
const urlOpt = document.querySelector('#content-type option[value="url"]');
if (urlOpt) urlOpt.textContent = qrt('uiTypeUrl');
const textOpt = document.querySelector('#content-type option[value="text"]');
if (textOpt) textOpt.textContent = qrt('uiTypeText');
const emailOpt = document.querySelector('#content-type option[value="email"]');
if (emailOpt) emailOpt.textContent = qrt('uiTypeEmail');
const phoneOpt = document.querySelector('#content-type option[value="phone"]');
if (phoneOpt) phoneOpt.textContent = qrt('uiTypePhone');
const smsOpt = document.querySelector('#content-type option[value="sms"]');
if (smsOpt) smsOpt.textContent = qrt('uiTypeSms');
const wifiOpt = document.querySelector('#content-type option[value="wifi"]');
if (wifiOpt) wifiOpt.textContent = qrt('uiTypeWifi');
const vcardOpt = document.querySelector('#content-type option[value="vcard"]');
if (vcardOpt) vcardOpt.textContent = qrt('uiTypeVcard');
const urlAddressLabel = document.querySelector('#field-url .form-label');
if (urlAddressLabel) urlAddressLabel.textContent = qrt('uiUrlAddress');
const urlInput = document.getElementById('input-url');
if (urlInput) urlInput.placeholder = 'https://example.com';
const textLabel = document.querySelector('#field-text .form-label');
if (textLabel) textLabel.textContent = qrt('uiTextContent');
const textInput = document.getElementById('input-text');
if (textInput) textInput.placeholder = qrt('uiTextPlaceholder');
const emailLabel = document.querySelector('#field-email #input-email')?.closest('.form-group')?.querySelector('.form-label');
if (emailLabel) emailLabel.textContent = qrt('uiEmailAddress');
const emailInput = document.getElementById('input-email');
if (emailInput) emailInput.placeholder = 'example@email.com';
const emailSubjLabel = document.querySelector('#field-email #input-email-subject')?.closest('.form-group')?.querySelector('.form-label');
if (emailSubjLabel) emailSubjLabel.textContent = qrt('uiSubjectOptional');
const emailSubjInput = document.getElementById('input-email-subject');
if (emailSubjInput) emailSubjInput.placeholder = qrt('uiEmailSubjectPlaceholder');
const emailBodyLabel = document.querySelector('#field-email #input-email-body')?.closest('.form-group')?.querySelector('.form-label');
if (emailBodyLabel) emailBodyLabel.textContent = qrt('uiBodyOptional');
const emailBodyInput = document.getElementById('input-email-body');
if (emailBodyInput) emailBodyInput.placeholder = qrt('uiEmailBodyPlaceholder');
const phoneLabel = document.querySelector('#field-phone .form-label');
if (phoneLabel) phoneLabel.textContent = qrt('uiPhoneNumber');
const phoneInput = document.getElementById('input-phone');
if (phoneInput) phoneInput.placeholder = '010-1234-5678';
const smsPhoneLabel = document.querySelector('#field-sms #input-sms-phone')?.closest('.form-group')?.querySelector('.form-label');
if (smsPhoneLabel) smsPhoneLabel.textContent = qrt('uiPhoneNumber');
const smsInput = document.getElementById('input-sms-msg');
const smsLabel = document.querySelector('#field-sms #input-sms-msg')?.closest('.form-group')?.querySelector('.form-label');
if (smsLabel) smsLabel.textContent = qrt('uiMessageContent');
if (smsInput) smsInput.placeholder = qrt('uiSmsPlaceholder');
const wifiSsidLabel = document.querySelector('#field-wifi #input-wifi-ssid')?.closest('.form-group')?.querySelector('.form-label');
if (wifiSsidLabel) wifiSsidLabel.textContent = qrt('uiWifiSsid');
const wifiSsidInput = document.getElementById('input-wifi-ssid');
if (wifiSsidInput) wifiSsidInput.placeholder = qrt('uiWifiNamePlaceholder');
const wifiPassLabel = document.querySelector('#field-wifi #input-wifi-pass')?.closest('.form-group')?.querySelector('.form-label');
if (wifiPassLabel) wifiPassLabel.textContent = qrt('uiPassword');
const wifiPassInput = document.getElementById('input-wifi-pass');
if (wifiPassInput) wifiPassInput.placeholder = qrt('uiWifiPasswordPlaceholder');
const wifiEncLabel = document.querySelector('#field-wifi #input-wifi-enc')?.closest('.form-group')?.querySelector('.form-label');
if (wifiEncLabel) wifiEncLabel.textContent = qrt('uiSecurityType');
const wifiEncNone = document.querySelector('#input-wifi-enc option[value="nopass"]');
if (wifiEncNone) wifiEncNone.textContent = qrt('uiSecurityNone');
const vcardNameLabel = document.querySelector('#field-vcard #input-vcard-name')?.closest('.form-group')?.querySelector('.form-label');
if (vcardNameLabel) vcardNameLabel.textContent = qrt('uiName');
const vcardPhoneLabel = document.querySelector('#field-vcard #input-vcard-phone')?.closest('.form-group')?.querySelector('.form-label');
if (vcardPhoneLabel) vcardPhoneLabel.textContent = qrt('uiPhoneNumber');
const vcardEmailLabel = document.querySelector('#field-vcard #input-vcard-email')?.closest('.form-group')?.querySelector('.form-label');
if (vcardEmailLabel) vcardEmailLabel.textContent = qrt('uiEmailAddress');
const vcardOrgLabel = document.querySelector('#field-vcard #input-vcard-org')?.closest('.form-group')?.querySelector('.form-label');
if (vcardOrgLabel) vcardOrgLabel.textContent = qrt('uiCompany');
const vcardUrlLabel = document.querySelector('#field-vcard #input-vcard-url')?.closest('.form-group')?.querySelector('.form-label');
if (vcardUrlLabel) vcardUrlLabel.textContent = qrt('uiWebsite');
const errorLevelSelect = document.getElementById('error-level');
const errorLevelLabel = errorLevelSelect
? errorLevelSelect.closest('.form-group')?.querySelector('.form-label')
: null;
if (errorLevelLabel && errorLevelLabel.querySelector('small')) {
const small = errorLevelLabel.querySelector('small');
errorLevelLabel.childNodes[0].textContent = qrt('uiErrorLevel') + ' ';
small.textContent = qrt('uiErrorLevelNote');
}
const optL = document.querySelector('#error-level option[value="L"]');
if (optL) optL.textContent = qrt('uiEcL');
const optM = document.querySelector('#error-level option[value="M"]');
if (optM) optM.textContent = qrt('uiEcM');
const optQ = document.querySelector('#error-level option[value="Q"]');
if (optQ) optQ.textContent = qrt('uiEcQ');
const optH = document.querySelector('#error-level option[value="H"]');
if (optH) optH.textContent = qrt('uiEcH');
const dotLabel = document.querySelector('#tab-shape .form-group .form-label');
if (dotLabel) dotLabel.textContent = qrt('uiDotStyle');
const dotSquare = document.querySelector('#dot-style-grid .style-opt[data-style="square"] span');
if (dotSquare) dotSquare.textContent = qrt('uiDotSquare');
const dotRounded = document.querySelector('#dot-style-grid .style-opt[data-style="rounded"] span');
if (dotRounded) dotRounded.textContent = qrt('uiDotRounded');
const dotExtraRounded = document.querySelector('#dot-style-grid .style-opt[data-style="extra-rounded"] span');
if (dotExtraRounded) dotExtraRounded.textContent = qrt('uiDotExtraRounded');
const dotCircle = document.querySelector('#dot-style-grid .style-opt[data-style="dot"] span');
if (dotCircle) dotCircle.textContent = qrt('uiDotCircle');
const dotDiamond = document.querySelector('#dot-style-grid .style-opt[data-style="diamond"] span');
if (dotDiamond) dotDiamond.textContent = qrt('uiDotDiamond');
const eyeLabel = document.querySelector('#eye-style-grid')?.closest('.form-group')?.querySelector('.form-label');
if (eyeLabel) eyeLabel.textContent = qrt('uiEyeStyle');
const eyeSquare = document.querySelector('#eye-style-grid .style-opt[data-style="square"] span');
if (eyeSquare) eyeSquare.textContent = qrt('uiEyeSquare');
const eyeRounded = document.querySelector('#eye-style-grid .style-opt[data-style="rounded"] span');
if (eyeRounded) eyeRounded.textContent = qrt('uiEyeRounded');
const eyeCircle = document.querySelector('#eye-style-grid .style-opt[data-style="circle"] span');
if (eyeCircle) eyeCircle.textContent = qrt('uiEyeCircle');
const marginLabel = document.getElementById('margin-slider')?.closest('.form-group')?.querySelector('.form-label');
if (marginLabel) marginLabel.textContent = qrt('uiMarginSize');
const fgLabel = document.getElementById('fg-color')?.closest('.form-group')?.querySelector('.form-label');
if (fgLabel) fgLabel.textContent = qrt('uiFgColorLabel');
const fgHelp = document.getElementById('fg-hex')?.parentElement?.querySelector('span');
if (fgHelp) fgHelp.textContent = qrt('uiFgColorHelp');
const bgLabel = document.getElementById('bg-color')?.closest('.form-group')?.querySelector('.form-label');
if (bgLabel) bgLabel.textContent = qrt('uiBgColorLabel');
const bgHelp = document.getElementById('bg-hex')?.parentElement?.querySelector('span');
if (bgHelp) bgHelp.textContent = qrt('uiBgColorHelp');
const gradLabel = document.querySelector('#tab-color .gradient-grid')?.closest('.form-group')?.querySelector('.form-label');
if (gradLabel) gradLabel.textContent = qrt('uiGradientLabel');
const gradNone = document.querySelector('.gradient-opt[data-grad="none"]');
if (gradNone) gradNone.textContent = qrt('uiGradNone');
const gradLinear = document.querySelector('.gradient-opt[data-grad="linear"]');
if (gradLinear) gradLinear.textContent = qrt('uiGradLinear');
const gradRadial = document.querySelector('.gradient-opt[data-grad="radial"]');
if (gradRadial) gradRadial.textContent = qrt('uiGradRadial');
const gradStart = document.getElementById('grad-hex1')?.parentElement?.querySelector('span');
if (gradStart) gradStart.textContent = qrt('uiGradStart');
const gradEnd = document.getElementById('grad-hex2')?.parentElement?.querySelector('span');
if (gradEnd) gradEnd.textContent = qrt('uiGradEnd');
const presetLabel = document.querySelector('#tab-color .color-preset')?.closest('.form-group')?.querySelector('.form-label');
if (presetLabel) presetLabel.textContent = qrt('uiColorPresetLabel');
const presets = document.querySelectorAll('#tab-color .color-preset');
if (presets[0]) presets[0].title = qrt('uiPresetClassic');
if (presets[1]) presets[1].title = qrt('uiPresetBlue');
if (presets[2]) presets[2].title = qrt('uiPresetGreen');
if (presets[3]) presets[3].title = qrt('uiPresetAmber');
if (presets[4]) presets[4].title = qrt('uiPresetPurple');
if (presets[5]) presets[5].title = qrt('uiPresetRose');
const logoLabel = document.getElementById('logo-upload-area')?.closest('.form-group')?.querySelector('.form-label');
if (logoLabel && logoLabel.querySelector('small')) {
const small = logoLabel.querySelector('small');
logoLabel.childNodes[0].textContent = qrt('uiLogoLabel') + ' ';
small.textContent = qrt('uiLogoAutoNote');
}
const uploadText = document.querySelector('#upload-placeholder .upload-text');
if (uploadText) {
uploadText.innerHTML = `${qrt('uiUploadClick')}
${qrt('uiUploadFormats')}`;
}
const uploaded = document.querySelector('#upload-preview-wrap > div:last-child');
if (uploaded) uploaded.textContent = qrt('uiUploadDone');
const removeBtn = document.querySelector('.upload-remove');
if (removeBtn) removeBtn.title = qrt('uiRemove');
const logoSizeLabel = document.getElementById('logo-size')?.closest('.form-group')?.querySelector('.form-label');
if (logoSizeLabel) logoSizeLabel.textContent = qrt('uiLogoSizeLabel');
const logoPaddingLabel = document.getElementById('logo-padding')?.closest('.form-group')?.querySelector('.form-label');
if (logoPaddingLabel) logoPaddingLabel.textContent = qrt('uiLogoPaddingLabel');
const logoRadiusLabel = document.getElementById('logo-radius')?.closest('.form-group')?.querySelector('.form-label');
if (logoRadiusLabel) logoRadiusLabel.textContent = qrt('uiLogoRadiusLabel');
const logoTipBox = document.querySelector('#tab-logo > div[style*="line-height:1.7"]');
if (logoTipBox) {
logoTipBox.innerHTML = `${qrt('uiTipStrong')} ${qrt('uiTipLabel')} ${qrt('uiTipDesc')}`;
}
const previewTitle = document.querySelector('.preview-panel .panel-title');
if (previewTitle) previewTitle.textContent = qrt('uiPreview');
const dlSizeLabel = document.querySelector('.preview-panel .preview-area > div[style*="width:100%"] > div[style*="font-size:.8rem"]');
if (dlSizeLabel) dlSizeLabel.textContent = qrt('uiDownloadSize');
const pngBtn = document.querySelector('.download-group .btn-dl-primary');
if (pngBtn) pngBtn.textContent = qrt('uiDownloadPng');
const copyBtn = document.querySelector('.download-group .btn-dl-secondary');
if (copyBtn) copyBtn.textContent = qrt('uiCopy');
const supportTitle = document.getElementById('qr-support-title');
if (supportTitle) supportTitle.textContent = qrt('uiSupportTitle');
const supportDesc = document.getElementById('qr-support-desc');
if (supportDesc) supportDesc.textContent = qrt('uiSupportDesc');
const supportLink = document.getElementById('qr-support-link');
if (supportLink) supportLink.textContent = qrt('uiSupportButton');
const adWrap = document.querySelector('.ad-wrap');
if (adWrap) adWrap.textContent = qrt('uiAd');
const howTitle = document.querySelector('.features-bg .section-title');
if (howTitle) howTitle.textContent = qrt('uiHowtoTitle');
const featureItems = document.querySelectorAll('.features-bg .feature-item');
if (featureItems[0]) {
const h4 = featureItems[0].querySelector('h4');
const p = featureItems[0].querySelector('p');
if (h4) h4.textContent = qrt('uiHow1Title');
if (p) p.textContent = qrt('uiHow1Desc');
}
if (featureItems[1]) {
const h4 = featureItems[1].querySelector('h4');
const p = featureItems[1].querySelector('p');
if (h4) h4.textContent = qrt('uiHow2Title');
if (p) p.textContent = qrt('uiHow2Desc');
}
if (featureItems[2]) {
const h4 = featureItems[2].querySelector('h4');
const p = featureItems[2].querySelector('p');
if (h4) h4.textContent = qrt('uiHow3Title');
if (p) p.textContent = qrt('uiHow3Desc');
}
if (featureItems[3]) {
const h4 = featureItems[3].querySelector('h4');
const p = featureItems[3].querySelector('p');
if (h4) h4.textContent = qrt('uiHow4Title');
if (p) p.textContent = qrt('uiHow4Desc');
}
}
/* ========== HELPERS ========== */
function roundRectPath(ctx, x, y, w, h, r) {
r = Math.min(r, Math.min(w, h) / 2);
ctx.beginPath();
ctx.moveTo(x + r, y);
ctx.lineTo(x + w - r, y);
ctx.quadraticCurveTo(x + w, y, x + w, y + r);
ctx.lineTo(x + w, y + h - r);
ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
ctx.lineTo(x + r, y + h);
ctx.quadraticCurveTo(x, y + h, x, y + h - r);
ctx.lineTo(x, y + r);
ctx.quadraticCurveTo(x, y, x + r, y);
ctx.closePath();
}
function drawModule(ctx, x, y, size, style) {
const pad = size * 0.08;
const s = size - pad * 2;
const cx = x + size / 2;
const cy = y + size / 2;
switch (style) {
case 'rounded':
roundRectPath(ctx, x + pad, y + pad, s, s, s * 0.3);
ctx.fill();
break;
case 'extra-rounded':
roundRectPath(ctx, x + pad, y + pad, s, s, s * 0.5);
ctx.fill();
break;
case 'dot':
ctx.beginPath();
ctx.arc(cx, cy, s / 2, 0, Math.PI * 2);
ctx.fill();
break;
case 'diamond':
ctx.beginPath();
ctx.moveTo(cx, y + pad);
ctx.lineTo(x + size - pad, cy);
ctx.lineTo(cx, y + size - pad);
ctx.lineTo(x + pad, cy);
ctx.closePath();
ctx.fill();
break;
default: // square
ctx.fillRect(x + pad, y + pad, s, s);
}
}
function drawEye(ctx, x, y, ms, style, bgColor) {
const total = ms * 7;
const cx = x + total / 2;
const cy = y + total / 2;
switch (style) {
case 'rounded': {
// Outer frame (rounded)
roundRectPath(ctx, x, y, total, total, ms * 1.8);
ctx.fill();
const savedFill = ctx.fillStyle;
ctx.fillStyle = bgColor;
roundRectPath(ctx, x + ms, y + ms, ms * 5, ms * 5, ms * 1.0);
ctx.fill();
ctx.fillStyle = savedFill;
// Inner dot (rounded)
roundRectPath(ctx, x + ms * 2, y + ms * 2, ms * 3, ms * 3, ms * 0.7);
ctx.fill();
break;
}
case 'circle': {
// Outer ring
ctx.beginPath();
ctx.arc(cx, cy, total / 2, 0, Math.PI * 2);
ctx.fill();
const savedFill2 = ctx.fillStyle;
ctx.fillStyle = bgColor;
ctx.beginPath();
ctx.arc(cx, cy, total / 2 - ms, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = savedFill2;
// Inner circle
ctx.beginPath();
ctx.arc(cx, cy, ms * 1.5, 0, Math.PI * 2);
ctx.fill();
break;
}
default: { // square
ctx.fillRect(x, y, total, total);
const savedFill3 = ctx.fillStyle;
ctx.fillStyle = bgColor;
ctx.fillRect(x + ms, y + ms, ms * 5, ms * 5);
ctx.fillStyle = savedFill3;
ctx.fillRect(x + ms * 2, y + ms * 2, ms * 3, ms * 3);
}
}
}
/* ========== MAIN RENDER ========== */
function renderQR(canvas, overrideSize) {
const size = overrideSize || 512;
canvas.width = size;
canvas.height = size;
const ctx = canvas.getContext('2d');
const text = QRState.text;
// Empty state
if (!text || text.trim() === '') {
ctx.fillStyle = '#f3f4f6';
ctx.fillRect(0, 0, size, size);
ctx.fillStyle = '#9ca3af';
ctx.font = `${size * 0.035}px sans-serif`;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(qrt('empty'), size / 2, size / 2);
toggleQRSupportPrompt(false);
return false;
}
// Choose error correction (H when logo present)
const ec = QRState.logoImage ? 'H' : QRState.errorLevel;
try {
// qrcode-generator uses type number 0 = auto
const qr = qrcode(0, ec);
qr.addData(text);
qr.make();
const count = qr.getModuleCount();
const margin = QRState.margin * (size / 512);
const moduleSize = (size - margin * 2) / count;
// Background
ctx.fillStyle = QRState.bgColor;
ctx.fillRect(0, 0, size, size);
// Foreground fill (solid or gradient)
let fgFill;
if (QRState.gradientType === 'none') {
fgFill = QRState.fgColor;
} else if (QRState.gradientType === 'linear') {
const g = ctx.createLinearGradient(margin, margin, size - margin, size - margin);
g.addColorStop(0, QRState.gradientColor1);
g.addColorStop(1, QRState.gradientColor2);
fgFill = g;
} else {
const g = ctx.createRadialGradient(size / 2, size / 2, 0, size / 2, size / 2, (size - margin * 2) / 2);
g.addColorStop(0, QRState.gradientColor1);
g.addColorStop(1, QRState.gradientColor2);
fgFill = g;
}
// Eye zones (top-left, top-right, bottom-left)
const eyes = [
{ row: 0, col: 0 },
{ row: 0, col: count - 7 },
{ row: count - 7, col: 0 },
];
function isEye(r, c) {
for (const e of eyes) {
if (r >= e.row && r < e.row + 7 && c >= e.col && c < e.col + 7) return true;
}
return false;
}
// Draw data modules
ctx.fillStyle = fgFill;
for (let row = 0; row < count; row++) {
for (let col = 0; col < count; col++) {
if (!qr.isDark(row, col) || isEye(row, col)) continue;
const x = margin + col * moduleSize;
const y = margin + row * moduleSize;
drawModule(ctx, x, y, moduleSize, QRState.dotStyle);
}
}
// Draw eyes
ctx.fillStyle = fgFill;
for (const e of eyes) {
const x = margin + e.col * moduleSize;
const y = margin + e.row * moduleSize;
drawEye(ctx, x, y, moduleSize, QRState.eyeStyle, QRState.bgColor);
}
// Draw logo
if (QRState.logoImage) {
const logoFraction = QRState.logoSize / 100;
const lw = size * logoFraction;
const lh = size * logoFraction;
const lx = (size - lw) / 2;
const ly = (size - lh) / 2;
const pad = QRState.logoPadding * (size / 512);
const r = (lw + pad * 2) * (QRState.logoRadius / 100);
ctx.fillStyle = QRState.bgColor;
roundRectPath(ctx, lx - pad, ly - pad, lw + pad * 2, lh + pad * 2, r);
ctx.fill();
ctx.save();
roundRectPath(ctx, lx - pad, ly - pad, lw + pad * 2, lh + pad * 2, r);
ctx.clip();
ctx.drawImage(QRState.logoImage, lx, ly, lw, lh);
ctx.restore();
}
// Update info
updateQRInfo(count, ec, text.length);
toggleQRSupportPrompt(true);
return true;
} catch (err) {
ctx.fillStyle = '#FEF2F2';
ctx.fillRect(0, 0, size, size);
ctx.fillStyle = '#EF4444';
ctx.font = `${size * 0.03}px sans-serif`;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(qrt('failed'), size / 2, size / 2);
toggleQRSupportPrompt(false);
return false;
}
}
function toggleQRSupportPrompt(show) {
const prompt = document.getElementById('qr-support-prompt');
if (!prompt) return;
prompt.hidden = !show;
}
function updateQRInfo(modules, ec, chars) {
const el = document.getElementById('qr-info');
if (el) {
el.textContent = `${qrt('infoModules')}: ${modules}×${modules} | ${qrt('infoError')}: ${ec} | ${qrt('infoChars')}: ${chars}`;
}
}
function scheduleRender() {
clearTimeout(renderTimer);
renderTimer = setTimeout(() => {
const canvas = document.getElementById('qr-canvas');
if (canvas) renderQR(canvas);
}, 120);
}
/* ========== CONTENT BUILDER ========== */
function buildQRText() {
const type = document.getElementById('content-type').value;
switch (type) {
case 'url': {
let url = document.getElementById('input-url').value.trim();
if (url && !url.match(/^https?:\/\//i)) url = 'https://' + url;
return url;
}
case 'text':
return document.getElementById('input-text').value;
case 'email': {
const mail = document.getElementById('input-email').value.trim();
const subj = document.getElementById('input-email-subject').value.trim();
const body = document.getElementById('input-email-body').value.trim();
let str = `mailto:${mail}`;
const params = [];
if (subj) params.push('subject=' + encodeURIComponent(subj));
if (body) params.push('body=' + encodeURIComponent(body));
if (params.length) str += '?' + params.join('&');
return str;
}
case 'phone':
return 'tel:' + document.getElementById('input-phone').value.trim();
case 'sms': {
const ph = document.getElementById('input-sms-phone').value.trim();
const msg = document.getElementById('input-sms-msg').value.trim();
return `sms:${ph}${msg ? '?body=' + encodeURIComponent(msg) : ''}`;
}
case 'wifi': {
const ssid = document.getElementById('input-wifi-ssid').value.trim();
const pass = document.getElementById('input-wifi-pass').value;
const enc = document.getElementById('input-wifi-enc').value;
return `WIFI:T:${enc};S:${ssid};P:${pass};;`;
}
case 'vcard': {
const name = document.getElementById('input-vcard-name').value.trim();
const phone = document.getElementById('input-vcard-phone').value.trim();
const email = document.getElementById('input-vcard-email').value.trim();
const org = document.getElementById('input-vcard-org').value.trim();
const url = document.getElementById('input-vcard-url').value.trim();
return [
'BEGIN:VCARD', 'VERSION:3.0',
name ? `FN:${name}` : '',
phone ? `TEL:${phone}` : '',
email ? `EMAIL:${email}` : '',
org ? `ORG:${org}` : '',
url ? `URL:${url}` : '',
'END:VCARD'
].filter(Boolean).join('\n');
}
default:
return '';
}
}
function showContentFields(type) {
document.querySelectorAll('.content-fields').forEach(el => el.style.display = 'none');
const target = document.getElementById('field-' + type);
if (target) target.style.display = 'block';
}
/* ========== DOWNLOAD ========== */
function downloadQR(format) {
const size = QRState.downloadSize;
const tempCanvas = document.createElement('canvas');
const ok = renderQR(tempCanvas, size);
if (!ok) return;
const link = document.createElement('a');
link.download = `quickqr-${Date.now()}.png`;
link.href = tempCanvas.toDataURL('image/png');
link.click();
}
function copyQRToClipboard() {
const canvas = document.getElementById('qr-canvas');
canvas.toBlob(blob => {
try {
navigator.clipboard.write([
new ClipboardItem({ 'image/png': blob })
]).then(() => {
showToast(qrt('copyOk'));
}).catch(() => {
showToast(qrt('copyFail'));
});
} catch (e) {
showToast(qrt('copyFail'));
}
});
}
function showToast(msg) {
let toast = document.getElementById('qr-toast');
if (!toast) {
toast = document.createElement('div');
toast.id = 'qr-toast';
toast.style.cssText = 'position:fixed;bottom:24px;left:50%;transform:translateX(-50%);background:#1F2937;color:white;padding:12px 24px;border-radius:8px;font-size:.9rem;font-weight:600;z-index:9999;transition:opacity .3s';
document.body.appendChild(toast);
}
toast.textContent = msg;
toast.style.opacity = '1';
setTimeout(() => { toast.style.opacity = '0'; }, 2500);
}
/* ========== EVENT LISTENERS ========== */
document.addEventListener('DOMContentLoaded', () => {
// Initial render
QRState.text = buildQRText();
applyQRStaticTexts();
scheduleRender();
const htmlEl = document.documentElement;
if (htmlEl) {
const observer = new MutationObserver(mutations => {
for (const m of mutations) {
if (m.type === 'attributes' && m.attributeName === 'lang') {
applyQRStaticTexts();
scheduleRender();
break;
}
}
});
observer.observe(htmlEl, { attributes: true, attributeFilter: ['lang'] });
}
// Content type switcher
document.getElementById('content-type').addEventListener('change', e => {
showContentFields(e.target.value);
QRState.text = buildQRText();
scheduleRender();
});
// All text inputs trigger regeneration
document.querySelectorAll('.form-input').forEach(el => {
el.addEventListener('input', () => {
QRState.text = buildQRText();
scheduleRender();
});
});
// Error level
document.getElementById('error-level').addEventListener('change', e => {
QRState.errorLevel = e.target.value;
scheduleRender();
});
// Dot style
document.querySelectorAll('#dot-style-grid .style-opt').forEach(opt => {
opt.addEventListener('click', () => {
document.querySelectorAll('#dot-style-grid .style-opt').forEach(o => o.classList.remove('active'));
opt.classList.add('active');
QRState.dotStyle = opt.dataset.style;
scheduleRender();
});
});
// Eye style
document.querySelectorAll('#eye-style-grid .style-opt').forEach(opt => {
opt.addEventListener('click', () => {
document.querySelectorAll('#eye-style-grid .style-opt').forEach(o => o.classList.remove('active'));
opt.classList.add('active');
QRState.eyeStyle = opt.dataset.style;
scheduleRender();
});
});
// Margin slider
const marginSlider = document.getElementById('margin-slider');
marginSlider.addEventListener('input', e => {
QRState.margin = parseInt(e.target.value);
document.getElementById('margin-val').textContent = e.target.value + 'px';
scheduleRender();
});
// FG Color
const fgColorInput = document.getElementById('fg-color');
const fgHexInput = document.getElementById('fg-hex');
fgColorInput.addEventListener('input', e => {
QRState.fgColor = e.target.value;
fgHexInput.value = e.target.value;
scheduleRender();
});
fgHexInput.addEventListener('input', e => {
if (/^#[0-9A-Fa-f]{6}$/.test(e.target.value)) {
QRState.fgColor = e.target.value;
fgColorInput.value = e.target.value;
scheduleRender();
}
});
// BG Color
const bgColorInput = document.getElementById('bg-color');
const bgHexInput = document.getElementById('bg-hex');
bgColorInput.addEventListener('input', e => {
QRState.bgColor = e.target.value;
bgHexInput.value = e.target.value;
scheduleRender();
});
bgHexInput.addEventListener('input', e => {
if (/^#[0-9A-Fa-f]{6}$/.test(e.target.value)) {
QRState.bgColor = e.target.value;
bgColorInput.value = e.target.value;
scheduleRender();
}
});
// Gradient
document.querySelectorAll('.gradient-opt').forEach(opt => {
opt.addEventListener('click', () => {
document.querySelectorAll('.gradient-opt').forEach(o => o.classList.remove('active'));
opt.classList.add('active');
QRState.gradientType = opt.dataset.grad;
const gc = document.getElementById('gradient-colors');
if (QRState.gradientType === 'none') {
gc.classList.add('hidden');
} else {
gc.classList.remove('hidden');
}
scheduleRender();
});
});
// Gradient colors
['1','2'].forEach(n => {
const col = document.getElementById(`grad-color${n}`);
const hex = document.getElementById(`grad-hex${n}`);
col.addEventListener('input', e => {
QRState[`gradientColor${n}`] = e.target.value;
hex.value = e.target.value;
scheduleRender();
});
hex.addEventListener('input', e => {
if (/^#[0-9A-Fa-f]{6}$/.test(e.target.value)) {
QRState[`gradientColor${n}`] = e.target.value;
col.value = e.target.value;
scheduleRender();
}
});
});
// Color presets
document.querySelectorAll('.color-preset').forEach(preset => {
preset.addEventListener('click', () => {
const fg = preset.dataset.fg;
const bg = preset.dataset.bg;
QRState.fgColor = fg;
QRState.bgColor = bg;
document.getElementById('fg-color').value = fg;
document.getElementById('fg-hex').value = fg;
document.getElementById('bg-color').value = bg;
document.getElementById('bg-hex').value = bg;
// Reset gradient
QRState.gradientType = 'none';
document.querySelectorAll('.gradient-opt').forEach(o => o.classList.remove('active'));
document.querySelector('.gradient-opt[data-grad="none"]').classList.add('active');
document.getElementById('gradient-colors').classList.add('hidden');
scheduleRender();
});
});
// Logo upload
document.getElementById('logo-file').addEventListener('change', e => {
const file = e.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = ev => {
const img = new Image();
img.onload = () => {
QRState.logoImage = img;
document.getElementById('upload-placeholder').style.display = 'none';
document.getElementById('upload-preview-wrap').style.display = 'block';
document.getElementById('logo-preview-img').src = ev.target.result;
document.getElementById('logo-upload-area').classList.add('has-file');
document.getElementById('logo-size-group').style.display = 'block';
document.getElementById('logo-padding-group').style.display = 'block';
document.getElementById('logo-radius-group').style.display = 'block';
scheduleRender();
};
img.src = ev.target.result;
};
reader.readAsDataURL(file);
});
// Logo size slider
document.getElementById('logo-size').addEventListener('input', e => {
QRState.logoSize = parseInt(e.target.value);
document.getElementById('logo-size-val').textContent = e.target.value + '%';
scheduleRender();
});
// Logo padding slider
document.getElementById('logo-padding').addEventListener('input', e => {
QRState.logoPadding = parseInt(e.target.value);
document.getElementById('logo-padding-val').textContent = e.target.value + 'px';
scheduleRender();
});
// Logo radius slider
document.getElementById('logo-radius').addEventListener('input', e => {
QRState.logoRadius = parseInt(e.target.value);
document.getElementById('logo-radius-val').textContent = e.target.value + '%';
scheduleRender();
});
// Download size buttons
document.querySelectorAll('.size-btn').forEach(btn => {
btn.addEventListener('click', () => {
document.querySelectorAll('.size-btn').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
QRState.downloadSize = parseInt(btn.dataset.size);
});
});
// 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');
});
});
});
// Remove logo
function removeLogo(e) {
e.stopPropagation();
QRState.logoImage = null;
document.getElementById('upload-placeholder').style.display = 'block';
document.getElementById('upload-preview-wrap').style.display = 'none';
document.getElementById('logo-upload-area').classList.remove('has-file');
document.getElementById('logo-size-group').style.display = 'none';
document.getElementById('logo-padding-group').style.display = 'none';
document.getElementById('logo-radius-group').style.display = 'none';
document.getElementById('logo-file').value = '';
scheduleRender();
}