{"id":2413,"date":"2025-01-20T12:21:25","date_gmt":"2025-01-20T12:21:25","guid":{"rendered":"https:\/\/textsnapper.com\/?page_id=2413"},"modified":"2025-06-18T05:07:15","modified_gmt":"2025-06-18T05:07:15","slug":"settings","status":"publish","type":"page","link":"https:\/\/textsnapper.com\/en\/settings\/","title":{"rendered":"settings"},"content":{"rendered":"<div data-elementor-type=\"wp-page\" data-elementor-id=\"2413\" class=\"elementor elementor-2413\" data-elementor-post-type=\"page\">\n\t\t\t\t<div class=\"elementor-element elementor-element-cdf9c30 e-flex e-con-boxed e-con e-parent\" data-id=\"cdf9c30\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-4ff58ca elementor-widget elementor-widget-heading\" data-id=\"4ff58ca\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">language settings<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-ca4def1 elementor-widget elementor-widget-text-editor\" data-id=\"ca4def1\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>Select the voice, language, and quality you'd like your texts to be read aloud in.<br \/>Set your favorite voice for an even better listening experience.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-f6c1e31 elementor-widget elementor-widget-html\" data-id=\"f6c1e31\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<!-- \u00dcbersetzbare Popup-Texte (f\u00fcr TranslatePress) -->\r\n<div id=\"translatepress-popups\" style=\"display:none;\">\r\n  <span id=\"confirm-delete-voice\">Do you really want to delete this saved voice?<\/span>\r\n  <span id=\"alert-voice-deleted\">Voice deleted!<\/span>\r\n  <span id=\"alert-delete-error\">Error deleting:<\/span>\r\n  <span id=\"alert-delete-catch-error\">Error deleting:<\/span>\r\n  <span id=\"alert-no-audio-data\">No audio data received:<\/span>\r\n  <span id=\"alert-tts-error\">TTS errors:<\/span>\r\n  <span id=\"alert-voice-saved\">Voice &quot;{voice}&quot; has been saved!<\/span>\r\n  <span id=\"alert-save-error\">Error saving:<\/span>\r\n  <span id=\"alert-catch-save-error\">Error with &#039;Remember&#039;:<\/span>\r\n<\/div>\r\n<div id=\"my-voice-settings-widget\">\r\n  <style>\r\n    \/* ---------------------- STYLES ---------------------- *\/\r\n    #my-voice-settings-widget .settings-container {\r\n  max-width: 800px;\r\n  width: 100%;\r\n  margin: 0 auto;\r\n  background: #fff;\r\n  padding: 20px;\r\n  border-radius: 8px;\r\n  box-shadow: 0 2px 5px rgba(0,0,0,0.1);\r\n}\r\n#my-voice-settings-widget h2 {\r\n  text-align: center;\r\n  color: #2F5591;\r\n  font-size: 2.5rem;\r\n  margin-top: 0;\r\n}\r\n#my-voice-settings-widget label {\r\n  font-weight: bold;\r\n  font-size: 20px;\r\n}\r\n#my-voice-settings-widget select,\r\n#my-voice-settings-widget textarea {\r\n  width: 100%;\r\n  box-sizing: border-box;\r\n  padding: 10px;\r\n  margin-top: 10px;\r\n  margin-bottom: 20px;\r\n  border: 1px solid #ccc;\r\n  border-radius: 4px;\r\n  font-size: 18px;\r\n}\r\n#my-voice-settings-widget button {\r\n  padding: 10px 20px;\r\n  background: #2F5591;\r\n  color: #fff;\r\n  border: none;\r\n  border-radius: 4px;\r\n  cursor: pointer;\r\n  transition: background 0.3s;\r\n  margin-right: 10px;\r\n}\r\n#my-voice-settings-widget button:hover {\r\n  background: #1d3b6c;\r\n}\r\n#my-voice-settings-widget .voice-option {\r\n  display: flex;\r\n  align-items: center;\r\n  margin-bottom: 15px;\r\n  padding: 10px;\r\n  border: 1px solid #ccc;\r\n  border-radius: 5px;\r\n}\r\n#my-voice-settings-widget .voice-option img {\r\n  width: 50px;\r\n  height: 50px;\r\n  border-radius: 10%;\r\n  margin-right: 10px;\r\n}\r\n#my-voice-settings-widget .voice-name {\r\n  flex: 1;\r\n  font-weight: bold;\r\n}\r\n#my-voice-settings-widget .playing {\r\n  background-color: #ff9800;\r\n  color: #fff;\r\n  animation: glow 1s infinite;\r\n}\r\n@keyframes glow {\r\n  0% { box-shadow: 0 0 5px #ff9800; }\r\n  50% { box-shadow: 0 0 20px #ff9800; }\r\n  100% { box-shadow: 0 0 5px #ff9800; }\r\n}\r\n#my-voice-settings-widget #saved-voices-container {\r\n  margin-top: 20px;\r\n  padding: 10px;\r\n  border: 1px solid #ccc;\r\n  border-radius: 5px;\r\n  display: none;\r\n}\r\n#my-voice-settings-widget #saved-voices-container h3 {\r\n  margin-top: 0;\r\n}\r\n#my-voice-settings-widget .saved-voice-item {\r\n  display: flex;\r\n  align-items: center;\r\n  margin-bottom: 10px;\r\n  padding: 8px;\r\n  border: 1px solid #ccc;\r\n  border-radius: 6px;\r\n  flex-wrap: wrap;\r\n}\r\n#my-voice-settings-widget .saved-voice-item img {\r\n  width: 50px;\r\n  height: 50px;\r\n  border-radius: 10%;\r\n  margin-right: 10px;\r\n  margin-bottom: 5px;\r\n}\r\n#my-voice-settings-widget .saved-voice-item .voice-info {\r\n  flex: 1;\r\n  min-width: 120px;\r\n  margin-bottom: 5px;\r\n}\r\n#my-voice-settings-widget .saved-voice-item .voice-buttons {\r\n  display: flex;\r\n  gap: 6px;\r\n  margin-left: auto;\r\n}\r\n#my-voice-settings-widget #voices-container p {\r\n  font-size: 18px;\r\n}\r\n@media (max-width: 600px) {\r\n  #my-voice-settings-widget .voice-option {\r\n    flex-direction: column;\r\n    align-items: flex-start;\r\n  }\r\n  #my-voice-settings-widget .voice-option button {\r\n    width: 100%;\r\n    margin-top: 8px;\r\n  }\r\n}\r\n\r\n  <\/style>\r\n\r\n  <div class=\"settings-container\">\r\n\r\n    <label for=\"language-select\">Select language:<\/label>\r\n    <select id=\"language-select\">\r\n      <option value=\"\" disabled selected>Select language<\/option>\r\n    <\/select>\r\n\r\n    <label for=\"dialect-select\">Select dialect:<\/label>\r\n    <select id=\"dialect-select\" disabled>\r\n      <option value=\"\" disabled selected>Please select a language first<\/option>\r\n    <\/select>\r\n\r\n    <label for=\"gender-select\">Select gender:<\/label>\r\n    <select id=\"gender-select\" disabled>\r\n      <option value=\"\" disabled selected>Select gender<\/option>\r\n      <option value=\"WEIBLICH\">Female<\/option>\r\n      <option value=\"M\u00c4NNLICH\">Masculine<\/option>\r\n    <\/select>\r\n\r\n    <label for=\"quality-select\">Select quality:<\/label>\r\n    <select id=\"quality-select\" disabled>\r\n      <option value=\"\" disabled selected>Select quality<\/option>\r\n    <\/select>\r\n\r\n    <label for=\"preview-text\">Audio sample text:<\/label>\r\n    <textarea id=\"preview-text\" rows=\"3\">\r\nDies ist eine Testnachricht auf Textsnapper.com. Sie k\u00f6nnen diesen Text \u00e4ndern.\r\n    <\/textarea>\r\n\r\n    <div id=\"voices-container\">\r\n      <h3>Available voices:<\/h3>\r\n      <p>Please select a dialect, gender and quality to view available voices.<\/p>\r\n    <\/div>\r\n\r\n    <div id=\"saved-voices-container\">\r\n      <h3>My saved voices<\/h3>\r\n      <div id=\"saved-voices-list\"><\/div>\r\n    <\/div>\r\n  <\/div>\r\n\r\n  <script>\r\n    \r\n    \r\n    const apiKey = (window.myVoiceKeys && window.myVoiceKeys.ttsApiKey) || \"\";\r\n    const translationApiKey = (window.myVoiceKeys && window.myVoiceKeys.translationApiKey) || \"\";\r\n\r\n    if (typeof window.ajaxurl === \"undefined\") {\r\n      window.ajaxurl = \"\/wp-admin\/admin-ajax.php\";\r\n    }\r\n    let userSavedVoices = (typeof window.userVoiceSettings === \"object\") ? window.userVoiceSettings : {};\r\n    let voiceData = {};\r\n    let currentAudio = null;\r\n    let originalText = \"Dies ist eine Testnachricht auf Textsnapper.com. Sie k\u00f6nnen diesen Text \u00e4ndern.\";\r\n\r\n    document.addEventListener(\"DOMContentLoaded\", () => {\r\n      loadVoiceData();\r\n    });\r\n\r\n    async function loadVoiceData() {\r\n      try {\r\n        const resp = await fetch(\"https:\/\/textsnapper.com\/wp-content\/textsnapper\/google_stimmen.json\");\r\n        if (!resp.ok) {\r\n          throw new Error(\"Fehler beim Laden der JSON-Daten\");\r\n        }\r\n        const data = await resp.json();\r\n\r\n        data.forEach(entry => {\r\n          let mainLanguage = entry.Sprache;\r\n          if (mainLanguage.startsWith(\"Englisch\")) mainLanguage = \"Englisch\";\r\n          else if (mainLanguage.startsWith(\"Deutsch\")) mainLanguage = \"Deutsch\";\r\n          else if (mainLanguage.startsWith(\"Spanisch\")) mainLanguage = \"Spanisch\";\r\n\r\n          if (!voiceData[mainLanguage]) {\r\n            voiceData[mainLanguage] = {\r\n              Dialekte: {},\r\n              Translation_Code: entry.Translation_Code\r\n            };\r\n          }\r\n\r\n          const dialCode = entry.code;\r\n          if (!voiceData[mainLanguage].Dialekte[dialCode]) {\r\n            voiceData[mainLanguage].Dialekte[dialCode] = {\r\n              Dialekt_Name: entry.Dialekt_Name,\r\n              Geschlechter: {}\r\n            };\r\n          }\r\n          const gender = entry.Geschlecht;\r\n          const quality = entry.Qualit\u00e4t;\r\n\r\n          if (!voiceData[mainLanguage].Dialekte[dialCode].Geschlechter[gender]) {\r\n            voiceData[mainLanguage].Dialekte[dialCode].Geschlechter[gender] = {};\r\n          }\r\n          if (!voiceData[mainLanguage].Dialekte[dialCode].Geschlechter[gender][quality]) {\r\n            voiceData[mainLanguage].Dialekte[dialCode].Geschlechter[gender][quality] = [];\r\n          }\r\n\r\n          voiceData[mainLanguage].Dialekte[dialCode].Geschlechter[gender][quality].push(entry);\r\n        });\r\n\r\n        populateLanguageDropdown();\r\n        await loadUserSavedVoices();\r\n\r\n      } catch (err) {\r\n        console.error(\"Fehler in loadVoiceData:\", err);\r\n      }\r\n    }\r\n    \r\n    document.addEventListener(\"DOMContentLoaded\", () => {\r\n  const previewText = document.getElementById(\"preview-text\");\r\n  \r\n  previewText.addEventListener(\"input\", () => {\r\n    if (previewText.value.length > 125) {\r\n      previewText.value = previewText.value.slice(0, 125);\r\n    }\r\n  });\r\n});\r\n\r\n\r\n    async function loadUserSavedVoices() {\r\n      const formData = new FormData();\r\n      formData.append(\"action\", \"my_get_user_voices\");\r\n\r\n      try {\r\n        const r = await fetch(window.ajaxurl, {\r\n          method: \"POST\",\r\n          credentials: \"include\",\r\n          body: formData\r\n        });\r\n        const j = await r.json();\r\n        if (j.success && j.data && j.data.voices) {\r\n          userSavedVoices = j.data.voices;\r\n          updateSavedVoicesUI();\r\n        }\r\n      } catch (err) {\r\n        console.error(\"Fehler beim Laden userSavedVoices:\", err);\r\n      }\r\n    }\r\n\r\n    function updateSavedVoicesUI() {\r\n      const savedContainer = document.getElementById(\"saved-voices-container\");\r\n      const savedList = document.getElementById(\"saved-voices-list\");\r\n      const keys = Object.keys(userSavedVoices);\r\n      if (keys.length === 0) {\r\n        savedContainer.style.display = \"none\";\r\n        savedList.innerHTML = \"\";\r\n        return;\r\n      }\r\n      savedContainer.style.display = \"block\";\r\n\r\n      let html = \"\";\r\n      keys.forEach(dial => {\r\n        const { voiceId, gender, quality } = userSavedVoices[dial] || {};\r\n        const info = findVoiceDataById(voiceId);\r\n        const displayImg   = info?.Bild || \"https:\/\/via.placeholder.com\/50\";\r\n        const displayTitle = info?.Dialekt_Name || dial;\r\n\r\n        html += `\r\n          <div class=\"saved-voice-item\">\r\n            <img decoding=\"async\" src=\"${displayImg}\" alt=\"Bild\" \/>\r\n            <div class=\"voice-info\">\r\n              <strong>${displayTitle}<\/strong><br>\r\n              Stimme: ${voiceId}<br>\r\n              Geschlecht: ${gender}, Qualit\u00e4t: ${quality}\r\n            <\/div>\r\n            <div class=\"voice-buttons\">\r\n              <button onclick=\"deleteSingleVoiceAjax('${dial}')\">L\u00f6schen<\/button>\r\n            <\/div>\r\n          <\/div>\r\n        `;\r\n      });\r\n      savedList.innerHTML = html;\r\n    }\r\n\r\n    function deleteSingleVoiceAjax(dialCode) {\r\n      if (!confirm(document.getElementById(\"confirm-delete-voice\").textContent.trim())) {\r\n  return;\r\n}\r\n      const f = new FormData();\r\n      f.append(\"action\", \"my_delete_single_voice\");\r\n      f.append(\"dialCode\", dialCode);\r\n\r\n      fetch(window.ajaxurl, {\r\n        method: \"POST\",\r\n        credentials: \"include\",\r\n        body: f\r\n      })\r\n      .then(r => r.json())\r\n      .then(res => {\r\n        if (res.success) {\r\n          alert(document.getElementById(\"alert-voice-deleted\").textContent.trim());\r\n          delete userSavedVoices[dialCode];\r\n          updateSavedVoicesUI();\r\n        } else {\r\n          alert(document.getElementById(\"alert-delete-error\").textContent.trim() + \" \" + JSON.stringify(res));\r\n        }\r\n      })\r\n      .catch(err => alert(document.getElementById(\"alert-delete-catch-error\").textContent.trim() + \" \" + err));\r\n    }\r\n\r\n    function findVoiceDataById(voiceId) {\r\n      for (const lang in voiceData) {\r\n        const dialObj = voiceData[lang].Dialekte;\r\n        for (const dialKey in dialObj) {\r\n          const gObj = dialObj[dialKey].Geschlechter;\r\n          for (const gKey in gObj) {\r\n            const qObj = gObj[gKey];\r\n            for (const qKey in qObj) {\r\n              const arr = qObj[qKey];\r\n              for (const v of arr) {\r\n                if (v.code2 === voiceId) {\r\n                  return v;\r\n                }\r\n              }\r\n            }\r\n          }\r\n        }\r\n      }\r\n      return null;\r\n    }\r\n\r\n    function populateLanguageDropdown() {\r\n      const languageSelect = document.getElementById(\"language-select\");\r\n      languageSelect.innerHTML = '<option value=\"\" disabled selected>Sprache ausw\u00e4hlen<\/option>';\r\n\r\n      const langs = Object.keys(voiceData).sort();\r\n      langs.forEach(lang => {\r\n        const opt = document.createElement(\"option\");\r\n        opt.value = lang;\r\n        opt.textContent = lang;\r\n        languageSelect.appendChild(opt);\r\n      });\r\n\r\n      languageSelect.addEventListener(\"change\", async function() {\r\n        const selLang = this.value;\r\n        await translatePreviewText(selLang);\r\n        resetSelections();\r\n        populateDialectDropdown(selLang);\r\n      });\r\n    }\r\n\r\n    async function translatePreviewText(language) {\r\n    const txtArea = document.getElementById(\"preview-text\");\r\n    const translationCode = voiceData[language]?.Translation_Code || \"en\"; \r\n    const textToTranslate = originalText;\r\n\r\n    const payload = new URLSearchParams({\r\n        action: \"ts_translate_text\",\r\n        q: textToTranslate,\r\n        target: translationCode,\r\n        source: \"auto\" \/\/ optional automatische Erkennung\r\n    });\r\n\r\n    try {\r\n        const resp = await fetch(window.ajaxurl, {\r\n            method: \"POST\",\r\n            headers: { \"Content-Type\": \"application\/x-www-form-urlencoded\" },\r\n            body: payload\r\n        });\r\n\r\n        const data = await resp.json();\r\n        console.log(\"\ud83d\udd0e Translate API Response:\", data);\r\n\r\n        if (!data.success || !data.data?.translatedText) {\r\n            console.error(\"Fehler bei der \u00dcbersetzung:\", data);\r\n            txtArea.value = \"\u00dcbersetzung fehlgeschlagen.\";\r\n            return;\r\n        }\r\n\r\n        txtArea.value = data.data.translatedText;\r\n\r\n    } catch (err) {\r\n        console.error(\"\u274c Fehler in translatePreviewText:\", err);\r\n    }\r\n}\r\n\r\n\r\n    async function getMappedDialectCode(shortLangCode) {\r\n      try {\r\n        const resp = await fetch(\"https:\/\/textsnapper.com\/wp-content\/textsnapper\/google_stimmen.json\");\r\n        if (!resp.ok) throw new Error(\"Fehler beim Laden der JSON-Daten\");\r\n\r\n        const data = await resp.json();\r\n\r\n        for (const entry of data) {\r\n          if (entry.Translation_Code === shortLangCode) {\r\n            return entry.code;\r\n          }\r\n        }\r\n\r\n        console.warn(\"\u26a0\ufe0f Kein Mapping f\u00fcr\", shortLangCode, \"gefunden. Standardwert wird verwendet.\");\r\n        return shortLangCode;\r\n      } catch (err) {\r\n        console.error(\"\u274c Fehler in getMappedDialectCode:\", err);\r\n        return shortLangCode;\r\n      }\r\n    }\r\n\r\n    function populateDialectDropdown(language) {\r\n      const dialectSelect = document.getElementById(\"dialect-select\");\r\n      dialectSelect.disabled = false;\r\n      dialectSelect.innerHTML = '<option value=\"\" disabled selected>Dialekt ausw\u00e4hlen<\/option>';\r\n\r\n      const dialObj = voiceData[language]?.Dialekte || {};\r\n      Object.keys(dialObj).forEach(dialKey => {\r\n        const dialName = dialObj[dialKey].Dialekt_Name || dialKey;\r\n        const opt = document.createElement(\"option\");\r\n        opt.value = dialKey;\r\n        opt.textContent = dialName;\r\n        dialectSelect.appendChild(opt);\r\n      });\r\n\r\n      dialectSelect.addEventListener(\"change\", function() {\r\n        populateGenderDropdown(language, this.value);\r\n      });\r\n    }\r\n\r\n    function populateGenderDropdown(language, dialCode) {\r\n      const genderSelect = document.getElementById(\"gender-select\");\r\n      genderSelect.disabled = false;\r\n      genderSelect.innerHTML = `\r\n        <option value=\"\" disabled selected>Geschlecht ausw\u00e4hlen<\/option>\r\n        <option value=\"WEIBLICH\">Weiblich<\/option>\r\n        <option value=\"M\u00c4NNLICH\">M\u00e4nnlich<\/option>\r\n      `;\r\n\r\n      genderSelect.addEventListener(\"change\", function() {\r\n        populateQualityDropdown(language, dialCode, this.value);\r\n      });\r\n    }\r\n\r\n    function populateQualityDropdown(language, dialCode, gender) {\r\n      const qSelect = document.getElementById(\"quality-select\");\r\n      qSelect.disabled = false;\r\n      qSelect.innerHTML = '<option value=\"\" disabled selected>Qualit\u00e4t ausw\u00e4hlen<\/option>';\r\n\r\n      const qObj = voiceData[language]?.Dialekte[dialCode]?.Geschlechter[gender] || {};\r\n      Object.keys(qObj).forEach(qual => {\r\n        const opt = document.createElement(\"option\");\r\n        opt.value = qual;\r\n        opt.textContent = qual;\r\n        qSelect.appendChild(opt);\r\n      });\r\n\r\n      qSelect.addEventListener(\"change\", function() {\r\n        populateVoiceOptions(language, dialCode, gender, this.value);\r\n      });\r\n    }\r\n\r\n    function populateVoiceOptions(language, dialCode, gender, quality) {\r\n      const container = document.getElementById(\"voices-container\");\r\n      container.innerHTML = \"<h3>Verf\u00fcgbare Stimmen:<\/h3>\";\r\n\r\n      const arr = voiceData[language]?.Dialekte[dialCode]?.Geschlechter[gender]?.[quality] || [];\r\n      if (arr.length === 0) {\r\n        container.innerHTML += \"<p>Keine Stimmen gefunden.<\/p>\";\r\n        return;\r\n      }\r\n\r\n      arr.forEach(voice => {\r\n        const div = document.createElement(\"div\");\r\n        div.className = \"voice-option\";\r\n        div.innerHTML = `\r\n          <img decoding=\"async\" src=\"${voice.Bild}\" alt=\"Stimmenbild\">\r\n          <span class=\"voice-name\">${voice.code2}<\/span>\r\n          <button onclick=\"playVoice('${voice.code2}', '${voice.code}', '${gender}', '${quality}', this)\">Abspielen<\/button>\r\n          <button onclick=\"saveSingleVoiceAjax('${language}', '${voice.code}', '${voice.code2}', '${gender}', '${quality}')\">Merken<\/button>\r\n        `;\r\n        container.appendChild(div);\r\n      });\r\n    }\r\n\r\n    function playVoice(voiceId, dialCode, gender, quality, btn) {\r\n      const text = document.getElementById(\"preview-text\").value;\r\n      const url = `https:\/\/texttospeech.googleapis.com\/v1\/text:synthesize?key=${apiKey}`;\r\n\r\n      if (currentAudio) {\r\n        currentAudio.pause();\r\n        currentAudio = null;\r\n        document.querySelectorAll(\".playing\").forEach(b => {\r\n          b.classList.remove(\"playing\");\r\n          b.textContent = \"Abspielen\";\r\n        });\r\n        return;\r\n      }\r\n\r\n      const payload = {\r\n        input: { text },\r\n        voice: {\r\n          name: voiceId,\r\n          languageCode: dialCode\r\n        },\r\n        audioConfig: { audioEncoding: \"MP3\" }\r\n      };\r\n\r\n      fetch(url, {\r\n        method: \"POST\",\r\n        headers: { \"Content-Type\": \"application\/json\" },\r\n        body: JSON.stringify(payload)\r\n      })\r\n      .then(r => r.json())\r\n      .then(result => {\r\n        if (result.audioContent) {\r\n          const audio = new Audio(`data:audio\/mp3;base64,${result.audioContent}`);\r\n          currentAudio = audio;\r\n          btn.classList.add(\"playing\");\r\n          btn.textContent = \"Stopp\";\r\n          audio.play();\r\n          audio.addEventListener(\"ended\", () => {\r\n            btn.classList.remove(\"playing\");\r\n            btn.textContent = \"Abspielen\";\r\n            currentAudio = null;\r\n          });\r\n        } else {\r\n          alert(document.getElementById(\"alert-no-audio-data\").textContent.trim() + \" \" + JSON.stringify(result));\r\n        }\r\n      })\r\n      .catch(err => alert(document.getElementById(\"alert-tts-error\").textContent.trim() + \" \" + err));\r\n    }\r\n\r\nfunction saveSingleVoiceAjax(language, dialCode, voiceId, gender, quality) {\r\n  const f = new FormData();\r\n  f.append(\"action\", \"my_save_single_voice\");\r\n  f.append(\"voice\", JSON.stringify({ dialCode, voiceId, gender, quality, language }));\r\n\r\n  fetch(window.ajaxurl, {\r\n    method: \"POST\",\r\n    credentials: \"include\",\r\n    body: f\r\n  })\r\n  .then(r => r.json())\r\n  .then(res => {\r\n    if (res.success) {\r\n      let alertText = document.getElementById(\"alert-voice-saved\").textContent.trim();\r\n      alert(alertText.replace(\"{voice}\", voiceId));\r\n      updateSavedVoicesUI();\r\n      loadUserSavedVoices(); \r\n    } else {\r\n      if (res.data === 'Premium- oder Diamond-Mitgliedschaft erforderlich, um Premium-Stimmen zu speichern.') {\r\n        alert(\"Diese Premium-Stimme ist nur f\u00fcr Premium- oder Diamond-Mitglieder speicherbar. Bitte upgraden Sie Ihre Mitgliedschaft.\");\r\n      } else {\r\n        alert(document.getElementById(\"alert-save-error\").textContent.trim() + \" \" + res.data);\r\n      }\r\n    }\r\n  })\r\n  .catch(error => alert(document.getElementById(\"alert-catch-save-error\").textContent.trim() + \" \" + error));\r\n}\r\n\r\n\r\n    function resetSelections() {\r\n      const dialEl = document.getElementById(\"dialect-select\");\r\n      dialEl.disabled = true;\r\n      dialEl.innerHTML = '<option value=\"\" disabled selected>Dialekt ausw\u00e4hlen<\/option>';\r\n\r\n      const gEl = document.getElementById(\"gender-select\");\r\n      gEl.disabled = true;\r\n      gEl.innerHTML = `\r\n        <option value=\"\" disabled selected>Geschlecht ausw\u00e4hlen<\/option>\r\n        <option value=\"WEIBLICH\">Weiblich<\/option>\r\n        <option value=\"M\u00c4NNLICH\">M\u00e4nnlich<\/option>\r\n      `;\r\n\r\n      const qEl = document.getElementById(\"quality-select\");\r\n      qEl.disabled = true;\r\n      qEl.innerHTML = '<option value=\"\" disabled selected>Qualit\u00e4t ausw\u00e4hlen<\/option>';\r\n\r\n      const container = document.getElementById(\"voices-container\");\r\n      container.innerHTML = `\r\n        <h3>Verf\u00fcgbare Stimmen:<\/h3>\r\n        <p>Bitte w\u00e4hlen Sie einen Dialekt, ein Geschlecht und eine Qualit\u00e4t aus, um verf\u00fcgbare Stimmen anzuzeigen.<\/p>\r\n      `;\r\n    }\r\n    \r\n  \r\n\r\n    \r\n  <\/script>\r\n<\/div>\r\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-d927e7e e-flex e-con-boxed e-con e-parent\" data-id=\"d927e7e\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-a7e16b8 elementor-widget__width-initial elementor-widget elementor-widget-text-editor\" data-id=\"a7e16b8\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<h3>Description of voice qualities (credits per character):<\/h3>\n<ol>\n  <li>\n    <p><strong>Standard (1 Credit)<\/strong><br>A solid, basic synthetic voice that is clear and easy to understand but relatively neutral and emotionless. Ideal for simple read-aloud functions or short texts.<\/p>\n  <\/li>\n  <li>\n    <p><strong>Wavenet (4 Credits)<\/strong><br>Natural and smooth voices based on advanced technology, providing particularly human-like and authentic speech. They offer clear emphasis and a pleasant, natural melody.<\/p>\n  <\/li>\n  <li>\n    <p><strong>Chirp (4 Credits)<\/strong><br>High-quality, fresh voices with a friendly and bright tone. They sound lively and are especially suited for casual content like podcasts or promotional videos.<\/p>\n  <\/li>\n  <li>\n    <p><strong>Chirp3 (4 Credits)<\/strong><br>An enhanced version of Chirp voices featuring even clearer pronunciation and particularly pleasant emphasis. Perfectly suited for longer texts or high-quality content that requires a professional touch.<\/p>\n  <\/li>\n  <li>\n    <p><strong>Neural2 (4 Credits)<\/strong><br>Extremely realistic and natural-sounding voices that closely mimic real human speech. These voices are particularly suited for interactive applications, audiobooks, or professional presentations.<\/p>\n  <\/li>\n  <li>\n    <p><strong>Casual (4 Credits)<\/strong><br>Relaxed, informal voices with a casual tone. Perfect for everyday texts, social media posts, entertainment, or personal assistants intended to sound approachable and friendly.<\/p>\n  <\/li>\n  <li>\n    <p><strong>Polyglot (4 Credits)<\/strong><br>Multilingual voices that excel at mixing and correctly pronouncing words or phrases from multiple languages within the same text. Ideal for international content, foreign-language quotes, or texts containing many loanwords.<\/p>\n  <\/li>\n  <li>\n    <p><strong>News (6 Credits)<\/strong><br>Voices specifically developed for reading news. They sound neutral, professional, and matter-of-fact, making them excellent for informative content such as articles, reports, or news broadcasts.<\/p>\n  <\/li>\n  <li>\n    <p><strong>Studio (40 Credits)<\/strong><br>Exceptionally high-quality premium voices that sound like they were recorded in a professional studio. Ideal for professional content, advertisements, or movies where sound quality and clarity are essential.<\/p>\n  <\/li>\n<\/ol>\n\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-57f0beb elementor-align-center elementor-widget elementor-widget-button\" data-id=\"57f0beb\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"button.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<div class=\"elementor-button-wrapper\">\n\t\t\t\t\t<a class=\"elementor-button elementor-button-link elementor-size-sm\" href=\"https:\/\/textsnapper.com\/en\/library\/\">\n\t\t\t\t\t\t<span class=\"elementor-button-content-wrapper\">\n\t\t\t\t\t\t\t\t\t<span class=\"elementor-button-text\">My Archive \ud83d\udcda<\/span>\n\t\t\t\t\t<\/span>\n\t\t\t\t\t<\/a>\n\t\t\t\t<\/div>\n\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>","protected":false},"excerpt":{"rendered":"<p>Spracheinstellungen W\u00e4hle hier die Stimme, Sprache und Qualit\u00e4t aus, in der Texte vorgelesen werden sollen.Lege deine Lieblingsstimme f\u00fcr noch besseres Vorlese-Erlebnis fest. M\u00f6chten Sie diese gespeicherte Stimme wirklich l\u00f6schen? Stimme gel\u00f6scht! Fehler beim L\u00f6schen: Fehler bei L\u00f6schen: Keine Audiodaten erhalten: Fehler bei TTS: Stimme &#8222;{voice}&#8220; wurde gespeichert! Fehler beim Speichern: Fehler bei &#8218;Merken&#8216;: Sprache ausw\u00e4hlen:&hellip;&nbsp;<a href=\"https:\/\/textsnapper.com\/en\/settings\/\" rel=\"bookmark\">Read More \u00bb<span class=\"screen-reader-text\">settings<\/span><\/a><\/p>","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"page-templates\/template-pagebuilder-full-width.php","meta":{"neve_meta_sidebar":"","neve_meta_container":"","neve_meta_enable_content_width":"","neve_meta_content_width":0,"neve_meta_title_alignment":"","neve_meta_author_avatar":"","neve_post_elements_order":"","neve_meta_disable_header":"","neve_meta_disable_footer":"","neve_meta_disable_title":"","footnotes":""},"class_list":["post-2413","page","type-page","status-publish","hentry"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.4 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>settings - TextSnapper<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/textsnapper.com\/en\/settings\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"settings - TextSnapper\" \/>\n<meta property=\"og:description\" content=\"Spracheinstellungen W\u00e4hle hier die Stimme, Sprache und Qualit\u00e4t aus, in der Texte vorgelesen werden sollen.Lege deine Lieblingsstimme f\u00fcr noch besseres Vorlese-Erlebnis fest. M\u00f6chten Sie diese gespeicherte Stimme wirklich l\u00f6schen? Stimme gel\u00f6scht! Fehler beim L\u00f6schen: Fehler bei L\u00f6schen: Keine Audiodaten erhalten: Fehler bei TTS: Stimme &#8222;{voice}&#8220; wurde gespeichert! Fehler beim Speichern: Fehler bei &#8218;Merken&#8216;: Sprache ausw\u00e4hlen:&hellip;&nbsp;Read More &raquo;settings\" \/>\n<meta property=\"og:url\" content=\"https:\/\/textsnapper.com\/en\/settings\/\" \/>\n<meta property=\"og:site_name\" content=\"TextSnapper\" \/>\n<meta property=\"article:modified_time\" content=\"2025-06-18T05:07:15+00:00\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data1\" content=\"2 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/textsnapper.com\\\/settings\\\/\",\"url\":\"https:\\\/\\\/textsnapper.com\\\/settings\\\/\",\"name\":\"settings - TextSnapper\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/textsnapper.com\\\/#website\"},\"datePublished\":\"2025-01-20T12:21:25+00:00\",\"dateModified\":\"2025-06-18T05:07:15+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/textsnapper.com\\\/settings\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/textsnapper.com\\\/settings\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/textsnapper.com\\\/settings\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/textsnapper.com\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"settings\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/textsnapper.com\\\/#website\",\"url\":\"https:\\\/\\\/textsnapper.com\\\/\",\"name\":\"TextSnapper\",\"description\":\"Texte aus Bildern vorlesen, \u00fcbersetzen und einfach h\u00f6ren.\",\"publisher\":{\"@id\":\"https:\\\/\\\/textsnapper.com\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/textsnapper.com\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/textsnapper.com\\\/#organization\",\"name\":\"Textsnapper\",\"url\":\"https:\\\/\\\/textsnapper.com\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/textsnapper.com\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/textsnapper.com\\\/wp-content\\\/uploads\\\/2024\\\/12\\\/cropped-cropped-cropped-textsnapper.png\",\"contentUrl\":\"https:\\\/\\\/textsnapper.com\\\/wp-content\\\/uploads\\\/2024\\\/12\\\/cropped-cropped-cropped-textsnapper.png\",\"width\":200,\"height\":200,\"caption\":\"Textsnapper\"},\"image\":{\"@id\":\"https:\\\/\\\/textsnapper.com\\\/#\\\/schema\\\/logo\\\/image\\\/\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"settings - TextSnapper","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/textsnapper.com\/en\/settings\/","og_locale":"en_US","og_type":"article","og_title":"settings - TextSnapper","og_description":"Spracheinstellungen W\u00e4hle hier die Stimme, Sprache und Qualit\u00e4t aus, in der Texte vorgelesen werden sollen.Lege deine Lieblingsstimme f\u00fcr noch besseres Vorlese-Erlebnis fest. M\u00f6chten Sie diese gespeicherte Stimme wirklich l\u00f6schen? Stimme gel\u00f6scht! Fehler beim L\u00f6schen: Fehler bei L\u00f6schen: Keine Audiodaten erhalten: Fehler bei TTS: Stimme &#8222;{voice}&#8220; wurde gespeichert! Fehler beim Speichern: Fehler bei &#8218;Merken&#8216;: Sprache ausw\u00e4hlen:&hellip;&nbsp;Read More &raquo;settings","og_url":"https:\/\/textsnapper.com\/en\/settings\/","og_site_name":"TextSnapper","article_modified_time":"2025-06-18T05:07:15+00:00","twitter_card":"summary_large_image","twitter_misc":{"Est. reading time":"2 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/textsnapper.com\/settings\/","url":"https:\/\/textsnapper.com\/settings\/","name":"settings - TextSnapper","isPartOf":{"@id":"https:\/\/textsnapper.com\/#website"},"datePublished":"2025-01-20T12:21:25+00:00","dateModified":"2025-06-18T05:07:15+00:00","breadcrumb":{"@id":"https:\/\/textsnapper.com\/settings\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/textsnapper.com\/settings\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/textsnapper.com\/settings\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/textsnapper.com\/"},{"@type":"ListItem","position":2,"name":"settings"}]},{"@type":"WebSite","@id":"https:\/\/textsnapper.com\/#website","url":"https:\/\/textsnapper.com\/","name":"TextSnapper","description":"Texte aus Bildern vorlesen, \u00fcbersetzen und einfach h\u00f6ren.","publisher":{"@id":"https:\/\/textsnapper.com\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/textsnapper.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/textsnapper.com\/#organization","name":"Textsnapper","url":"https:\/\/textsnapper.com\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/textsnapper.com\/#\/schema\/logo\/image\/","url":"https:\/\/textsnapper.com\/wp-content\/uploads\/2024\/12\/cropped-cropped-cropped-textsnapper.png","contentUrl":"https:\/\/textsnapper.com\/wp-content\/uploads\/2024\/12\/cropped-cropped-cropped-textsnapper.png","width":200,"height":200,"caption":"Textsnapper"},"image":{"@id":"https:\/\/textsnapper.com\/#\/schema\/logo\/image\/"}}]}},"_links":{"self":[{"href":"https:\/\/textsnapper.com\/en\/wp-json\/wp\/v2\/pages\/2413","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/textsnapper.com\/en\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/textsnapper.com\/en\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/textsnapper.com\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/textsnapper.com\/en\/wp-json\/wp\/v2\/comments?post=2413"}],"version-history":[{"count":355,"href":"https:\/\/textsnapper.com\/en\/wp-json\/wp\/v2\/pages\/2413\/revisions"}],"predecessor-version":[{"id":10552,"href":"https:\/\/textsnapper.com\/en\/wp-json\/wp\/v2\/pages\/2413\/revisions\/10552"}],"wp:attachment":[{"href":"https:\/\/textsnapper.com\/en\/wp-json\/wp\/v2\/media?parent=2413"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}