{"id":497,"date":"2026-03-10T20:58:45","date_gmt":"2026-03-10T20:58:45","guid":{"rendered":"http:\/\/sofapiano.com\/?page_id=497"},"modified":"2026-06-26T14:48:46","modified_gmt":"2026-06-26T14:48:46","slug":"b1-grammar-in-context","status":"publish","type":"page","link":"https:\/\/sofapiano.com\/es\/b1-grammar-in-context\/","title":{"rendered":"Gram\u00e1tica en contexto"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"497\" class=\"elementor elementor-497\">\n\t\t\t\t<div class=\"elementor-element elementor-element-31ccf11 stax-user_role_enabled-yes e-con-full stax-condition-yes e-flex e-con e-parent\" data-id=\"31ccf11\" data-element_type=\"container\" data-e-type=\"container\" data-settings=\"{&quot;background_background&quot;:&quot;classic&quot;,&quot;animation&quot;:&quot;none&quot;}\">\n\t\t<div class=\"elementor-element elementor-element-684a709 e-con-full animated-slow e-flex elementor-invisible e-con e-child\" data-id=\"684a709\" data-element_type=\"container\" data-e-type=\"container\" data-settings=\"{&quot;background_background&quot;:&quot;classic&quot;,&quot;animation&quot;:&quot;slideInLeft&quot;,&quot;animation_delay&quot;:500}\">\n\t\t\t\t<div class=\"elementor-element elementor-element-89fda1e elementor-widget elementor-widget-heading\" data-id=\"89fda1e\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">B1 Grammar in Context<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-069aae0 elementor-widget__width-initial elementor-hidden-tablet elementor-hidden-mobile premium-modal-dismissible-yes elementor-widget elementor-widget-premium-addon-modal-box\" data-id=\"069aae0\" data-element_type=\"widget\" data-e-type=\"widget\" data-settings=\"{&quot;premium_modal_box_animation&quot;:&quot;slideInLeft&quot;}\" data-widget_type=\"premium-addon-modal-box.default\">\n\t\t\t\t\t\n\t\t<div class=\"premium-modal-box-container\" data-settings=\"{&quot;trigger&quot;:&quot;button&quot;,&quot;show_on_exit&quot;:false}\">\n\t\t\t<div class=\"premium-modal-trigger-container\">\n\t\t\t\t\t\t\t\t\t<button data-toggle=\"premium-modal\" data-target=\"#premium-modal-069aae0\" type=\"button\" class=\"premium-modal-trigger-btn premium-btn-lg premium-button-none \" data-text=\"\">\n\n\t\t\t\t\t\t<svg class=\"svg-inline--fas-fa-info-circle premium-svg-nodraw\" aria-hidden=\"true\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewbox=\"0 0 512 512\"><path d=\"M256 8C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm0 110c23.196 0 42 18.804 42 42s-18.804 42-42 42-42-18.804-42-42 18.804-42 42-42zm56 254c0 6.627-5.373 12-12 12h-88c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h12v-64h-12c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h64c6.627 0 12 5.373 12 12v100h12c6.627 0 12 5.373 12 12v24z\"><\/path><\/svg>\n\t\t\t\t\t\t<div class=\"premium-button-text-icon-wrapper\">\n\t\t\t\t\t\t\t<span><\/span>\n\t\t\t\t\t\t<\/div>\n\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t<\/button>\n\t\t\t\t\t\t\t<\/div>\n\n\t\t\t<div id=\"premium-modal-069aae0\" class=\"premium-modal-box-modal\"\n\t\t\trole=\"dialog\"\n\t\t\tstyle=\"display: none\"\n\t\t\t>\n\t\t\t\t<div class=\"premium-modal-box-modal-dialog\" data-delay-animation=\"\" data-modal-animation=\"slideInLeft animated-\">\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"premium-modal-box-modal-header\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"premium-modal-box-close-button-container\">\n\t\t\t\t\t\t\t\t\t<button type=\"button\" class=\"premium-modal-box-modal-close\" data-dismiss=\"premium-modal\">&times;<\/button>\n\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t\t\t\t<div class=\"premium-modal-box-modal-body\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<h4><img decoding=\"async\" class=\"emoji\" role=\"img\" draggable=\"false\" src=\"https:\/\/s.w.org\/images\/core\/emoji\/17.0.2\/svg\/1f4cb.svg\" alt=\"\ud83d\udccb\" \/>\u00a0Grammar in Context<\/h4><p>Learn German grammar by translating sentences which include the selected topic.<\/p><ol><li>Select source language (<img decoding=\"async\" class=\"emoji\" role=\"img\" draggable=\"false\" src=\"https:\/\/s.w.org\/images\/core\/emoji\/17.0.2\/svg\/1f1fa-1f1f8.svg\" alt=\"\ud83c\uddfa\ud83c\uddf8\" \/>\/<img decoding=\"async\" class=\"emoji\" role=\"img\" draggable=\"false\" src=\"https:\/\/s.w.org\/images\/core\/emoji\/17.0.2\/svg\/1f1ea-1f1f8.svg\" alt=\"\ud83c\uddea\ud83c\uddf8\" \/>)<\/li><li>Select grammar topic<\/li><li>Enter the German translation<\/li><li>Press\u00a0<img decoding=\"async\" class=\"emoji\" role=\"img\" draggable=\"false\" src=\"https:\/\/s.w.org\/images\/core\/emoji\/17.0.2\/svg\/2705.svg\" alt=\"\u2705\" \/>\u00a0Check or Enter to verify.<\/li><li>Use\u00a0<img decoding=\"async\" class=\"emoji\" role=\"img\" draggable=\"false\" src=\"https:\/\/s.w.org\/images\/core\/emoji\/17.0.2\/svg\/1f4a1.svg\" alt=\"\ud83d\udca1\" \/>\u00a0for help,\u00a0<img decoding=\"async\" class=\"emoji\" role=\"img\" draggable=\"false\" src=\"https:\/\/s.w.org\/images\/core\/emoji\/17.0.2\/svg\/1f50a.svg\" alt=\"\ud83d\udd0a\" \/>\u00a0to listen,\u00a0<img decoding=\"async\" class=\"emoji\" role=\"img\" draggable=\"false\" src=\"https:\/\/s.w.org\/images\/core\/emoji\/17.0.2\/svg\/1f3a4.svg\" alt=\"\ud83c\udfa4\" \/>\u00a0to speak.<\/li><li>Use\u00a0<img decoding=\"async\" class=\"emoji\" role=\"img\" draggable=\"false\" src=\"https:\/\/s.w.org\/images\/core\/emoji\/17.0.2\/svg\/2b50.svg\" alt=\"\u2b50\" \/>\u00a0to save sentences.<\/li><li>Use\u00a0<img decoding=\"async\" class=\"emoji\" role=\"img\" draggable=\"false\" src=\"https:\/\/s.w.org\/images\/core\/emoji\/17.0.2\/svg\/1f504.svg\" alt=\"\ud83d\udd04\" \/>\u00a0for a new sentence.<\/li><li><img decoding=\"async\" class=\"emoji\" role=\"img\" draggable=\"false\" src=\"https:\/\/s.w.org\/images\/core\/emoji\/17.0.2\/svg\/1f50d.svg\" alt=\"\ud83d\udd0d\" \/>\u00a0shows grammer topic rules<\/li><\/ol><h4>Tips:<\/h4><ul><li>The alternative sentence under the solution uses synonyms or shows a grammatical variation.<\/li><li>Evaluation:<ul><li><img decoding=\"async\" class=\"emoji\" role=\"img\" draggable=\"false\" src=\"https:\/\/s.w.org\/images\/core\/emoji\/17.0.2\/svg\/1f7e2.svg\" alt=\"\ud83d\udfe2\" \/>\u00a0 correct<\/li><li><img decoding=\"async\" class=\"emoji\" role=\"img\" draggable=\"false\" src=\"https:\/\/s.w.org\/images\/core\/emoji\/17.0.2\/svg\/1f7e1.svg\" alt=\"\ud83d\udfe1\" \/>\u00a0 almost correct<\/li><li><img decoding=\"async\" class=\"emoji\" role=\"img\" draggable=\"false\" src=\"https:\/\/s.w.org\/images\/core\/emoji\/17.0.2\/svg\/1f534.svg\" alt=\"\ud83d\udd34\" \/>\u00a0incorrect or missing<\/li><\/ul><\/li><\/ul>\n\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-057bd11 e-con-full e-flex e-con e-child\" data-id=\"057bd11\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-d25c5c5 elementor-widget elementor-widget-html\" data-id=\"d25c5c5\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<div id=\"trainer-container\" class=\"trainer-app\">\n<style>\n\/* 1. Globaler Fix gegen das Wackeln *\/\nhtml, body {\n  max-width: 100% !important;\n  overflow-x: hidden !important;\n  width: 100% !important;\n  position: relative !important;\n}\n\/* CSS Reset & Variablen *\/\n#trainer-container.trainer-app {\n  display: flex !important;\n  max-width: 1000px !important; \n  margin: 10px auto !important;\n  gap: 10px !important;\n  font-family: 'Inter', 'Segoe UI', Roboto, Helvetica, Arial, sans-serif !important;\n  --source-size: 17px;\n  --input-size: 17px;\n  --feedback-size: 18px;\n  --alt-size: 16px;\n  --source-size-mobile: 14px;\n  --feedback-size-mobile: 15px;\n  --alt-size-mobile: 13px;\n  --input-width: 85%;\n  --sentence-gap: 10px;\n  --correct: #c8e6c9;\n  --tip: #fff59d;\n  --missing: #ffcdd2;\n  --light-blue: #F4FEFF;\n  --border-color: #ddd;\n  --brand-color: #6EC1E4;\n}\n\/* Tooltip Styling *\/\n#trainer-container [data-tooltip] {\n  position: relative !important;\n}\n#trainer-container [data-tooltip]::after {\n  content: attr(data-tooltip);\n  position: absolute;\n  bottom: 120%;\n  left: 50%;\n  transform: translateX(-50%);\n  background: #333;\n  color: #fff;\n  padding: 4px 8px;\n  border-radius: 4px;\n  font-size: 11px;\n  white-space: nowrap;\n  opacity: 0;\n  visibility: hidden;\n  transition: opacity 0.2s;\n  z-index: 100;\n  pointer-events: none;\n}\n#trainer-container [data-tooltip]:hover::after {\n  opacity: 1;\n  visibility: visible;\n}\n\/* Sidebar Styling *\/\n#trainer-container .sidebar {\n  width: 320px !important; \n  flex-shrink: 0 !important;\n  background-color: var(--light-blue) !important;\n  border: 1px solid var(--border-color) !important;\n  border-radius: 20px !important;\n  padding: 15px 10px !important; \n  text-align: center !important;\n  box-shadow: 0 4px 10px rgba(0,0,0,0.03) !important;\n}\n\/* Sidebar Items *\/\n#trainer-container .topic-item { \n    position: relative !important; \n    padding-right: 45px !important; \/* Platz f\u00fcr den Button rechts *\/\n}\n\/* Der Info\/Schloss Button *\/\n#trainer-container .topic-info-btn {\n    position: absolute !important; \n    right: -5px !important; \n    top: 50% !important;\n    transform: translateY(-50%) !important; \n    background: none !important;\n    border: none !important;\n    cursor: pointer !important; \n    display: flex !important; \n    align-items: center !important;\n    justify-content: center !important; \n    font-size: 10px !important; \n    font-weight: bold !important;\n    color: #666 !important;\n    transition: background 0.2s;\n}\n\n\/* Zustand: Gesperrt *\/\n#trainer-container .topic-item.locked {\n    background: #f8f8f8 !important;\n    opacity: 0.7 !important;\n    cursor: not-allowed !important;\n}\n#trainer-container .topic-item.locked b, \n#trainer-container .topic-item.locked span { color: #aaa !important; }\n#trainer-container.no-sidebar .sidebar { display: none !important; }\n#trainer-container .sidebar h3 { font-size: 12px !important; text-transform: uppercase !important; color: #666 !important; margin: 15px 0 10px 0 !important; letter-spacing: 1.8px !important; }\n#trainer-container .level-btn {\n  all: unset !important; box-sizing: border-box !important; flex: 1 !important; padding: 8px 0 !important;\n  border: 1px solid var(--border-color) !important; background: white !important; border-radius: 10px !important;\n  cursor: pointer !important; font-weight: bold !important; font-size: 12px !important; transition: 0.2s !important;\n  text-align: center !important; font-family: inherit !important;\n}\n#trainer-container .topic-text-wrapper {\n    display: flex !important;\n    font-size: 14px;\n    align-items: flex-start !important;\n    flex-grow: 1 !important;\n    overflow: hidden !important;\n    padding-right: 22px !important;\n}\n#trainer-container .level-btn.active { background: var(--brand-color) !important; color: white !important; border-color: var(--brand-color) !important; }\n#trainer-container .level-buttons { display: flex !important; justify-content: center !important; gap: 4px !important; margin-bottom: 5px !important; }\n#trainer-container .topic-list { list-style: none !important; padding: 0 !important; margin: 0 !important; max-height: 500px !important; overflow-y: auto !important; text-align: left !important; }\n#trainer-container .topic-item {\n  display: flex !important;justify-content: space-between !important;gap: 35px !important; padding: 10px 10px !important; margin-bottom: 5px !important; \n  background: white !important; border-radius: 10px !important; cursor: pointer !important; font-size: 14px !important;\n  border: 1px solid #eef0f2 !important; transition: 0.2s !important; line-height: 1.3 !important;\n}\n#trainer-container .topic-item.active { background: var(--brand-color) !important; color: white !important; }\n\n#trainer-container #topicDropdown { \n  display: none; \n  width: 100% !important; \n  padding: 12px !important; \n  border-radius: 12px !important; \n  border: 1px solid var(--border-color) !important;\n  background: white !important;\n  font-family: inherit !important;\n  font-size: 14px !important;\n  margin-top: 10px !important;\n}\n\/* SATZ-BLOCK DESIGN *\/\n#trainer-container .main-content { flex-grow: 1 !important; }\n#trainer-container .sentence-block {\n  position: relative !important; margin-bottom: var(--sentence-gap) !important; padding: 15px 10px 15px 10px !important;\n  border: 1px solid var(--border-color) !important; border-radius: 15px !important; background-color: #fcfcfc !important;\n  text-align: center !important; font-family: inherit !important; box-sizing: border-box !important;\n}\n#trainer-container .source { display: block !important; font-size: var(--source-size) !important; font-weight: bold !important; color: #333 !important; margin-bottom: 10px !important; padding: 0 35px !important; }\n\n#trainer-container .input-wrapper { \n  position: relative !important; \n  width: var(--input-width) !important; \n  margin: 0 auto !important; \n}\n\/* BLINKEN (GELB) *\/\n@keyframes input-mic-blink-intense {\n  0% { background-color: #ffffff; border-color: #ccc; }\n  50% { background-color: #ffeb3b; border-color: #fbc02d; box-shadow: 0 0 12px rgba(251, 192, 45, 0.6); }\n  100% { background-color: #ffffff; border-color: #ccc; }\n}\n@keyframes mic-icon-blink-intense {\n  0% { transform: scale(1); color: #333; opacity: 0.4; }\n  50% { transform: scale(1.3); color: #d32f2f; opacity: 1; }\n  100% { transform: scale(1); color: #333; opacity: 0.4; }\n}\n#trainer-container textarea.recording-active { \n  animation: input-mic-blink-intense 0.7s infinite ease-in-out !important; \n  border: 1px solid #fbc02d !important;\n}\n#trainer-container .mic-in-input.recording-active { \n  animation: mic-icon-blink-intense 0.7s infinite ease-in-out !important; \n  opacity: 1 !important;\n}\n\n#trainer-container textarea {\n  display: block !important; \n  width: 100% !important; \n  min-height: 44px !important; \n  height: 44px; \n  font-size: var(--input-size) !important;\n  padding: 10px 45px 10px 10px !important; \n  border-radius: 8px !important; \n  border: 1px solid #ccc !important;\n  resize: none !important; \n  text-align: center !important; \n  box-sizing: border-box !important; \n  font-family: inherit !important; \n  background: white !important;\n  overflow-y: hidden !important; \n  line-height: 1.4 !important;\n}\n#trainer-container .mic-in-input {\n  position: absolute !important; \n  right: 1px !important; \n  top: 1px !important; \n  background: none !important; \n  border: none !important; \n  cursor: pointer !important; \n  font-size: 18px !important; \n  opacity: 0.4 !important;\n\n}\n#trainer-container .save-btn, #trainer-container .lang-indicator {\n  position: absolute !important; top: 0px !important; background: none !important; border: none !important; cursor: pointer !important;\n}\n#trainer-container .save-btn { left: 0px !important; font-size: 18px !important; color: #ccc !important; }\n#trainer-container .save-btn.saved { color: #fbc02d !important; }\n#trainer-container .lang-indicator { right: 0px !important; font-size: 15px !important; }\n\n#trainer-container .feedback-wrapper { \n  display: block !important; \n  margin-top: 5px !important; \n  position: relative !important; \n}\n#trainer-container .feedback { display: block !important; font-size: var(--feedback-size) !important; line-height: 1.4em !important; }\n#trainer-container .solution-text { display: block !important; font-size: var(--alt-size) !important; color: #666 !important; font-style: italic !important; margin-top: 1px !important; border-top: 1px dashed #eee !important; line-height: 1.2em !important; padding-top: 2px !important; }\n\n#trainer-container .sentence-controls { display: flex !important; justify-content: center !important; align-items: flex-end !important; gap: 14px !important; margin-top: 10px !important; }\n#trainer-container .btn-icon {\n  all: unset !important; display: inline-flex !important; justify-content: center !important; align-items: center !important;\n  border-radius: 12px !important; border: 1px solid #eee !important; background: white !important;\n  cursor: pointer !important; width: 50px !important; height: 50px !important; font-size: 22px !important;\n  box-shadow: 0 2px 4px rgba(0,0,0,0.08) !important;\n}\n#trainer-container .btn-label { font-size: 10px !important; color: #777 !important; margin-top: 4px !important; text-transform: uppercase !important; }\n\n#trainer-container .correct { background: var(--correct) !important; padding: 2px 5px !important; border-radius: 5px !important; }\n#trainer-container .tip { background: var(--tip) !important; padding: 2px 5px !important; border-radius: 5px !important; }\n#trainer-container .missing { background: var(--missing) !important; padding: 2px 5px !important; border-radius: 5px !important; }\n\/* MOBILE ANPASSUNGEN *\/\n@media (max-width: 800px) {\n  #trainer-container.trainer-app { \n    flex-direction: column !important; \n    display: block !important;\n  }\n\n  #trainer-container .sidebar {\n    margin-bottom: 20px !important;\n    width: auto !important;\n  }\n\n  #trainer-container textarea {\n    font-size: 16px !important; \n    min-height: 30px !important;\n  }\n\n  #trainer-container .source { font-size: 16px !important; }\n  #trainer-container .feedback { font-size: 16px !important; }\n  #trainer-container .solution-text { font-size: 14px !important; }\n\n  #trainer-container .topic-list { display: none !important; } \n  #trainer-container #topicDropdown { display: block !important; }  \n}\n\n\/* Modal Animation (GEH\u00d6RT AUCH INS CSS, ABER AUSSERHALB DER MEDIA QUERY) *\/\n@keyframes modalFadeIn {\n  from { opacity: 0; transform: translateY(-20px); }\n  to { opacity: 1; transform: translateY(0); }\n}\n<\/style>\n<!-- Modal Pop-up -->\n<div id=\"trainer-modal-overlay\" style=\"display:none; position:fixed; z-index:9999; left:0; top:0; width:100%; height:100%; background:rgba(0,0,0,0.7); backdrop-filter:blur(4px); align-items:center; justify-content:center;\">\n    <div style=\"background:white; padding:30px; border-radius:20px; max-width:500px; width:90%; position:relative; box-shadow:0 15px 35px rgba(0,0,0,0.4); animation: modalFadeIn 0.3s ease;\">\n        <span id=\"close-modal\" style=\"position:absolute; top:15px; right:20px; font-size:28px; cursor:pointer; color:#bbb;\">&times;<\/span>\n        <h3 id=\"modal-title\" style=\"margin-top:0; color:#6EC1E4; font-size:22px; border-bottom:1px solid #eee; padding-bottom:10px;\"><\/h3>\n        <div id=\"modal-body\" style=\"font-size:16px; line-height:1.6; color:#444; margin-top:15px; max-height:60vh; overflow-y:auto;\"><\/div>\n    <\/div>\n<\/div>\n\n<div class=\"sidebar\">\n  <div class=\"level-buttons\" id=\"levelBtnGroup\">\n    <button class=\"level-btn\" data-level=\"B1\">B1<\/button>\n  <\/div>\n  <h3 class=\"ui-topic-title\">Grammar Topic<\/h3>\n  <select id=\"topicDropdown\"><\/select>\n  <ul class=\"topic-list\" id=\"topicList\"><\/ul>\n<\/div>\n\n<div class=\"main-content\">\n  <div id=\"sentenceArea\">\n    <div class=\"sentence-block ui-loading\">Loading...<\/div>\n  <\/div>\n<\/div>\n<script>\n(function(container){\n    const SHOW_SIDEBAR = true; \n    const MANUAL_LEVEL = \"B1\";             \n    const MANUAL_TOPIC_ID = \"\"; \n    \n    if (!SHOW_SIDEBAR) {\n        container.classList.add('no-sidebar');\n    } else {\n        container.classList.remove('no-sidebar');\n    }\n    const UI_LANGUAGES = {\n        en: {\n            topicTitle: \"Grammar Topic\", loading: \"Loading data...\", placeholder: \"translation...\",\n            saveHint: \"Save Sentence\", langHint: \"Change Language\", micHint: \"Speech to Text\",\n            btnHint: \"Hint\", btnListen: \"Listen\", btnCheck: \"Check\", btnNew: \"New\",\n            altLabel: \"Alternative: \", noData: \"No data found.\", allTopics: \"-- All Topics --\"\n        },\n        es: {\n            topicTitle: \"Tema de gram\u00e1tica\", loading: \"Cargando datos...\", placeholder: \"traducci\u00f3n...\",\n            saveHint: \"Guardar frase\", langHint: \"Cambiar idioma\", micHint: \"Reconocimiento de voz\",\n            btnHint: \"Pista\", btnListen: \"Escucha\", btnCheck: \"Valida\", btnNew: \"Nuevo\",\n            altLabel: \"Alternativa: \", noData: \"No hay datos.\", allTopics: \"-- Todos los temas --\"\n        }\n    };\nconst WEB_APP_URL = 'https:\/\/script.google.com\/macros\/s\/AKfycbw3rL1RlpTHfr9jVwDpsFE79_8uNoXLkpyd8Qa-lGxWFbkvjPo25lNum2hOyLMIUEzVTw\/exec'; \nconst SHEET_ID = '1pGfdVy3yDtzS56FIxiP4KsHHYuKUqPWVG18ULv9wxi4';\nconst URL_S = `https:\/\/docs.google.com\/spreadsheets\/d\/${SHEET_ID}\/export?format=csv&gid=0`;\nconst URL_T = `https:\/\/docs.google.com\/spreadsheets\/d\/${SHEET_ID}\/export?format=csv&gid=368365373`;\n\n    let allData = [];\n    let pool = [];\n    let state_topics = []; \/\/ Speichert die Themenliste aus Tabelle 2\n    let currentLevel = MANUAL_LEVEL;\n    let currentTopicId = MANUAL_TOPIC_ID;\n    let currentLang = \"en\";\n    const initialBtn = container.querySelector(`.level-btn[data-level=\"${MANUAL_LEVEL}\"]`);\n    if(initialBtn) initialBtn.classList.add('active');\n    function getLevenshteinDist(a, b) {\n        const matrix = [];\n        for (let i = 0; i <= b.length; i++) matrix[i] = [i];\n        for (let j = 0; j <= a.length; j++) matrix[0][j] = j;\n        for (let i = 1; i <= b.length; i++) {\n            for (let j = 1; j <= a.length; j++) {\n                if (b.charAt(i - 1) === a.charAt(j - 1)) matrix[i][j] = matrix[i - 1][j - 1];\n                else matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, Math.min(matrix[i][j - 1] + 1, matrix[i - 1][j] + 1));\n            }\n        }\n        return matrix[b.length][a.length];\n    }\n    const normalize = (t) => t ? t.toLowerCase().trim().replace(\/[.,!?]\/g, '').replace(\/\u00e4\/g,'ae').replace(\/\u00f6\/g,'oe').replace(\/\u00fc\/g,'ue').replace(\/\u00df\/g,'ss').replace(\/\\s+\/g, ' ') : \"\";\n\n    function speakText(textToSpeak) {\n        if (!textToSpeak) return;\n        speechSynthesis.cancel(); \n        const u = new SpeechSynthesisUtterance(textToSpeak);\n        const voices = speechSynthesis.getVoices();\n        const preferredVoice = voices.find(v => (v.lang === 'de-DE' && v.name.includes('Google')) || (v.lang === 'de-DE' && v.name.includes('Stefan')) || (v.lang === 'de-DE' && v.name.includes('Hedda')));\n        if (preferredVoice) u.voice = preferredVoice;\n        u.lang = \"de-DE\";\n        u.pitch = 1.0; \n        u.rate = 0.95; \n        speechSynthesis.speak(u);\n    }\n    window.speechSynthesis.onvoiceschanged = () => speechSynthesis.getVoices();\n\nasync function logToGoogle(data) {\n    \/\/ Falls du ein Login-System hast, werden hier Name\/ID geholt, sonst \"Gast\"\n    const userInfo = window.wpTrainerUserInfo || { id: 'Gast', name: 'Gast' };\n    data.user_id = userInfo.id;\n    data.user_name = userInfo.name;\n    \n    const WEB_APP_URL = 'https:\/\/script.google.com\/macros\/s\/AKfycbz7ViqxDaSi1l2M0o4rsPHqS09qmgosQy34eIJmBhfd1hs2SBbRAuc3nrJC4D4KfBoPPQ\/exec'; \n\n    try {\n        await fetch(WEB_APP_URL, {\n            method: 'POST',\n            mode: 'no-cors', \/\/ Wichtig f\u00fcr Google Scripts\n            cache: 'no-cache',\n            headers: { 'Content-Type': 'application\/json' },\n            body: JSON.stringify(data)\n        });\n    } catch (e) { \n        console.error(\"Logging Fehler:\", e); \n    }\n}\n\nasync function loadData() {\n    try {\n        const [csvS, csvT] = await Promise.all([\n            fetch(URL_S).then(r => r.text()),\n            fetch(URL_T).then(r => r.text())\n        ]);\n        const rowsS = csvS.split(\/\\r?\\n\/).slice(1);\nallData = rowsS.map(row => {\n    \/\/ Teilt die CSV-Zeile korrekt auf, auch wenn Kommas in Anf\u00fchrungszeichen stehen\n    const cols = row.split(\/,(?=(?:(?:[^\"]*\"){2})*[^\"]*$)\/);\n    const clean = (v) => v ? v.trim().replace(\/^\"|\"$\/g, '').replace(\/\"\"\/g, '\"') : \"\";\n    \n    return {\n        id: clean(cols[0]),       \/\/ Spalte A: Satz-ID\n        level: clean(cols[1]),    \/\/ Spalte B: Level\n        topicId: clean(cols[2]),  \/\/ Spalte C: Topic\n        de: clean(cols[4]),       \/\/ Spalte E: Deutsch\n        en: clean(cols[5]),       \/\/ Spalte F: Englisch\n        es: clean(cols[6]),       \/\/ Spalte G: Spanisch\n        alt: clean(cols[7])       \/\/ Spalte H: Alternativen\n    };\n}).filter(r => r.de);\n        const rowsT = csvT.split(\/\\r?\\n\/).slice(1);\n        state_topics = rowsT.map(row => {\n            const cols = row.split(\/,(?=(?:(?:[^\"]*\"){2})*[^\"]*$)\/);\n            const clean = (v) => v ? v.trim().replace(\/^\"|\"$\/g, '').replace(\/\"\"\/g, '\"') : \"\";\n            return { \n                level: clean(cols[0]), \n                id: clean(cols[1]), \n                name: clean(cols[2]), \n                nameEn: clean(cols[3]), \n\t\t\t\tnameEs: clean(cols[4]),  \/\/ Spanisch Name\n        infoDe: clean(cols[5]),  \/\/ Spalte F: Info Deutsch\n        infoEn: clean(cols[6]),  \/\/ Spalte G: Info Englisch\n        infoEs: clean(cols[7]),  \/\/ Spalte H: Info Spanisch\n        status: clean(cols[8])   \/\/ Spalte I: Status (2 = Gesperrt)\n            };\n        });\n        updateInterfaceStrings();\n        if (SHOW_SIDEBAR) renderTopics();\n        updateUI();\n    } catch (e) { console.error(\"Fehler beim Laden:\", e); }\n}\n    function updateInterfaceStrings() {\n        container.querySelector('.ui-topic-title').textContent = UI_LANGUAGES[currentLang].topicTitle;\n    }\nfunction renderTopics() {\n    const list = container.querySelector('#topicList');\n    const dropdown = container.querySelector('#topicDropdown');\n    if(!list || !dropdown) return;\n\n    \/\/ 1. Desktop-Liste leeren\n    list.innerHTML = \"\";\n    \n    \/\/ 2. Mobiles Dropdown zur\u00fccksetzen (Startwert: Alle Themen)\n    dropdown.innerHTML = `<option value=\"\">${UI_LANGUAGES[currentLang].allTopics}<\/option>`;\n\n    const filtered = state_topics.filter(t => t.level === currentLevel);\n\nfiltered.forEach(t => {\n        const topicName = (currentLang === 'en' ? t.nameEn : t.nameEs) || t.name;\n        \n        const isLocked = (t.status === \"2\"); \/\/ To Lock: \"false;\": To open:\"(t.status === \"2\")\"\n        \n        \/\/ --- NEU: Sprache f\u00fcr den Tooltip festlegen ---\n        const lockedHint = currentLang === 'en' ? \"Members Only\" : \"Solo miembros\";\n\n        const li = document.createElement('li');\n        li.className = \"topic-item\" + (currentTopicId === t.id ? \" active\" : \"\") + (isLocked ? \" locked\" : \"\");\n        \n        \/\/ --- NEU: Tooltip nur setzen, wenn gesperrt ---\n        if (isLocked) {\n            li.setAttribute('data-tooltip', lockedHint);\n        }\n\n        const icon = isLocked ? \"\ud83d\udd12\" : \"\ud83d\udd0d\";\n        \n        \/\/ Hier bleibt dein restlicher Code gleich...\n        let activeInfo = t.infoDe;\n        if (currentLang === 'en') activeInfo = t.infoEn || t.infoDe;\n        if (currentLang === 'es') activeInfo = t.infoEs || t.infoDe;\n\n        li.innerHTML = `\n            <div class=\"topic-text-wrapper\">\n                <b style=\"min-width:25px; flex-shrink:0; display:inline-block;\">${t.id}:<\/b>\n                <span style=\"margin-left:5px; white-space: normal; word-break: break-word;\">${topicName}<\/span>\n            <\/div>\n            <button class=\"topic-info-btn\" title=\"Info\">${icon}<\/button>`;\n        \n        li.onclick = (e) => {\n            if (isLocked) return;\n            currentTopicId = t.id; \n            renderTopics(); \n            updateUI();\n        };\n\n\/\/ Die Klick-Funktion f\u00fcr den Info-Button (Lupe\/Schloss)\n        li.querySelector('.topic-info-btn').onclick = (e) => {\n            e.stopPropagation();\n            \n            \/\/ NEU: Wenn gesperrt, brich hier einfach ab -> Klick macht nichts\n            if (isLocked) return; \n\n            \/\/ Wenn nicht gesperrt, \u00f6ffne das Modal wie gewohnt\n            openModal(topicName, activeInfo || \"Keine Informationen verf\u00fcgbar.\");\n        };\n        list.appendChild(li);\n\n        \/\/ --- OPTIONAL: Auch im mobilen Dropdown anzeigen ---\n        const opt = document.createElement('option');\n        opt.value = t.id;\n        opt.textContent = `${t.id}: ${topicName}${isLocked ? ' (' + lockedHint + ')' : ''}`;\n        opt.disabled = isLocked;\n        if (currentTopicId === t.id) opt.selected = true;\n        dropdown.appendChild(opt);\n    });\n}\n    function updateUI() {\n        let filteredPool = allData.filter(d => d.level === currentLevel);\n        if (currentTopicId) filteredPool = filteredPool.filter(d => d.topicId === currentTopicId);\n        pool = filteredPool;\n        if (pool.length === 0) {\n            container.querySelector('#sentenceArea').innerHTML = `<div class=\"sentence-block\">No data found.<\/div>`;\n            return;\n        }\n        shuffle(pool);\n        const area = container.querySelector('#sentenceArea');\n        area.innerHTML = \"\";\n        const limit = Math.min(3, pool.length);\n        for(let i=0; i<limit; i++) { area.appendChild(createSentenceBlock(pool.pop())); }\n    }\n    function createSentenceBlock(initialS) {\n        const block = document.createElement(\"div\");\n        block.className = \"sentence-block\";\n        block.dataset.sentence = JSON.stringify(initialS);\n        block.dataset.tipLevel = 0;\n        const isB = isBookmarked(initialS);\n        block.innerHTML = `\n            <button class=\"save-btn ${isB ? 'saved' : ''}\" data-tooltip=\"${UI_LANGUAGES[currentLang].saveHint}\">${isB ? '\u2b50' : '\u2606'}<\/button>\n            <button class=\"lang-indicator\" data-tooltip=\"${UI_LANGUAGES[currentLang].langHint}\">${currentLang === 'en' ? '\ud83c\uddfa\ud83c\uddf8' : '\ud83c\uddea\ud83c\uddf8'}<\/button>\n            <div class=\"source\">${initialS[currentLang]}<\/div>\n            <div class=\"input-wrapper\">\n                <textarea placeholder=\"${UI_LANGUAGES[currentLang].placeholder}\"><\/textarea>\n                <button class=\"mic-in-input\" data-tooltip=\"${UI_LANGUAGES[currentLang].micHint}\">\ud83c\udfa4<\/button>\n            <\/div>\n            <div class=\"feedback-wrapper\"><div class=\"feedback\"><\/div><\/div>\n            <div class=\"sentence-controls\"><\/div>\n        `;\n        const input = block.querySelector(\"textarea\");\n        const micBtn = block.querySelector(\".mic-in-input\");\n        const fb = block.querySelector(\".feedback\");\n        const fbWrapper = block.querySelector(\".feedback-wrapper\");\n        const saveBtn = block.querySelector(\".save-btn\");\n        const langInd = block.querySelector(\".lang-indicator\");\n        const ctrlArea = block.querySelector(\".sentence-controls\");\nsaveBtn.onclick = () => {\n    const s = JSON.parse(block.dataset.sentence);\n    let bookmarks = JSON.parse(localStorage.getItem('trainer_bookmarks') || '[]');\n    const idx = bookmarks.findIndex(b => b.de === s.de);\n    let actionType = \"\";\n\n    if(idx > -1) {\n        bookmarks.splice(idx, 1);\n        saveBtn.classList.remove('saved');\n        saveBtn.textContent = '\u2606';\n        actionType = \"Bookmark entfernt\";\n    } else {\n        bookmarks.push(s);\n        saveBtn.classList.add('saved');\n        saveBtn.textContent = '\u2b50';\n        actionType = \"Bookmark gesetzt\";\n    }\n    localStorage.setItem('trainer_bookmarks', JSON.stringify(bookmarks));\n\n    \/\/ KORREKTUR: Logge die Bookmark-Aktion\n    logToGoogle({\n        sentence_id: s.id,\n        level: currentLevel, \n        topic: s.topicId || \"Mix\", \n        german: s.de, \n        translation: s[currentLang],\n        action: actionType, \n        status: \"-\",\n        user_input: \"\" \n    });\n};\n        langInd.onclick = () => {\n            currentLang = (currentLang === 'en' ? 'es' : 'en');\n            updateInterfaceStrings();\n            if (SHOW_SIDEBAR) renderTopics();\n            \n            container.querySelectorAll('.sentence-block').forEach(b => {\n                if(!b.dataset.sentence) return;\n                const data = JSON.parse(b.dataset.sentence);\n                b.querySelector('.source').textContent = data[currentLang];\n                b.querySelector('.lang-indicator').textContent = (currentLang === 'en' ? '\ud83c\uddfa\ud83c\uddf8' : '\ud83c\uddea\ud83c\uddf8');\n                b.querySelector('.lang-indicator').dataset.tooltip = UI_LANGUAGES[currentLang].langHint;\n                b.querySelector('.save-btn').dataset.tooltip = UI_LANGUAGES[currentLang].saveHint;\n                b.querySelector('.mic-in-input').dataset.tooltip = UI_LANGUAGES[currentLang].micHint;\n                \n                const txt = b.querySelector('textarea');\n                if(txt) txt.placeholder = UI_LANGUAGES[currentLang].placeholder;\n\n                const labels = b.querySelectorAll('.btn-label');\n                if (labels.length >= 4) {\n                    labels[0].textContent = UI_LANGUAGES[currentLang].btnHint;\n                    labels[1].textContent = UI_LANGUAGES[currentLang].btnListen;\n                    labels[2].textContent = UI_LANGUAGES[currentLang].btnCheck;\n                    labels[3].textContent = UI_LANGUAGES[currentLang].btnNew;\n                }\n                const altDiv = b.querySelector(\".solution-text\");\n                if(altDiv) altDiv.textContent = UI_LANGUAGES[currentLang].altLabel + data.alt;\n            });\n        };\n        function autoResize() {\n            input.style.height = '0px'; \n            input.style.height = Math.max(44, input.scrollHeight) + 'px';\n        }\n        input.addEventListener('input', autoResize);\n        const check = () => {\n            const s = JSON.parse(block.dataset.sentence);\n            fb.innerHTML = \"\";\n           const topicInfo = s.topicId \n    ? `Grammer Topic ${s.topicId} (${s.level})` \n    : `no specific grammar topic (${s.level})`;\n            fbWrapper.setAttribute('data-tooltip', topicInfo);\n\n            const targetWords = s.de.split(\/\\s+\/);\n            const userWords = normalize(input.value).split(\/\\s+\/);\n            targetWords.forEach((word, idx) => {\n                const span = document.createElement(\"span\");\n                span.textContent = word + \" \";\n                const normTarget = normalize(word);\n                const normUser = userWords[idx] ? normalize(userWords[idx]) : \"\";\n                if (normUser === normTarget) { span.className = \"correct\"; }\n                else {\n                    const dist = normUser ? getLevenshteinDist(normUser, normTarget) : 99;\n                    if (userWords.includes(normTarget) || (dist > 0 && dist <= 2)) { span.className = \"tip\"; }\n                    else { span.className = \"missing\"; }\n                }\n                fb.appendChild(span);\n            });\n            if(s.alt) {\n                const altDiv = document.createElement(\"div\");\n                altDiv.className = \"solution-text\";\n                altDiv.textContent = UI_LANGUAGES[currentLang].altLabel + s.alt;\n                fb.appendChild(altDiv);\n            }\nlogToGoogle({\n        sentence_id: s.id,\n        level: currentLevel, \n        topic: s.topicId || \"Mix\", \n        german: s.de, \n        translation: s[currentLang],\n        action: \"Check\", \n        status: (normalize(input.value) === normalize(s.de)) ? \"Richtig\" : \"Falsch\",\n        user_input: input.value \n    });\n};\n        if('webkitSpeechRecognition' in window) {\n            const rec = new webkitSpeechRecognition();\n            rec.continuous = true; \n            rec.interimResults = true; \n            rec.lang = 'de-DE';\n            rec.maxAlternatives = 1; \n\n            let isListening = false;\n            let autoStopTimer = null;\n\n            const resetTimer = () => {\n                if (autoStopTimer) clearTimeout(autoStopTimer);\n                autoStopTimer = setTimeout(() => { \n                    if (isListening) {\n                        console.log(\"Stille erkannt, Aufnahme beendet.\");\n                        rec.stop(); \n                    }\n                }, 5000);\n            };\n            rec.onstart = () => { \n                isListening = true;\n                input.classList.add(\"recording-active\"); \n                micBtn.classList.add(\"recording-active\"); \n                resetTimer();\n            };\n            rec.onerror = (event) => {\n                console.error(\"Spracherkennungsfehler:\", event.error);\n                rec.stop();\n            };\n            rec.onend = () => { \n                isListening = false;\n                input.classList.remove(\"recording-active\"); \n                micBtn.classList.remove(\"recording-active\"); \n                if (autoStopTimer) clearTimeout(autoStopTimer);\n                \n                if (input.value) {\n                    let text = input.value.trim();\n                    if (text.length > 0) {\n                        input.value = text.charAt(0).toUpperCase() + text.slice(1);\n                    }\n                }\n            };\n            rec.onresult = (e) => { \n                let currentTranscript = '';\n                for (let i = 0; i < e.results.length; ++i) {\n                    let word = e.results[i][0].transcript;\n                    if (i > 0 && !word.startsWith(' ')) {\n                        currentTranscript += ' ' + word;\n                    } else {\n                        currentTranscript += word;\n                    }\n                }\n                input.value = currentTranscript.replace(\/\\s\\s+\/g, ' '); \n                autoResize(); \n                resetTimer();\n            };\n            micBtn.onclick = (e) => { \n                e.preventDefault();\n                if (isListening) { \n                    rec.stop(); \n                } else { \n                    input.value = \"\"; \n                    if (window.speechSynthesis.speaking) window.speechSynthesis.cancel();\n                    rec.start(); \n                } \n            };\n        }\n        const createBtn = (icon, labelKey, fn) => {\n            const div = document.createElement(\"div\");\n            div.className = \"control-item\";\n            div.innerHTML = `<button class=\"btn-icon\">${icon}<\/button><div class=\"btn-label\">${UI_LANGUAGES[currentLang][labelKey]}<\/div>`;\n            div.querySelector(\"button\").onclick = fn;\n            return div;\n        };\n        ctrlArea.appendChild(createBtn(\"\ud83d\udca1\", \"btnHint\", () => {\n            const s = JSON.parse(block.dataset.sentence);\n            let tipL = (parseInt(block.dataset.tipLevel) || 0) + 1;\n            block.dataset.tipLevel = tipL;\n            fb.innerHTML = s.de.split(\" \").map(w => `<span class=\"tip\">${w.slice(0, tipL)}${tipL < w.length ? '...' : ''} <\/span>`).join(\"\");\n        }));\n   ctrlArea.appendChild(createBtn(\"\ud83d\udd0a\", \"btnListen\", () => {\n    const s = JSON.parse(block.dataset.sentence);\n    speakText(s.de);\n}));\n        ctrlArea.appendChild(createBtn(\"\u2705\", \"btnCheck\", check));\n        ctrlArea.appendChild(createBtn(\"\ud83d\udd04\", \"btnNew\", () => {\n            if(pool.length === 0) { updateUI(); return; }\n            const next = pool.pop();\n            block.dataset.sentence = JSON.stringify(next);\n            block.dataset.tipLevel = 0;\n            block.querySelector(\".source\").textContent = next[currentLang];\n            input.value = \"\"; input.style.height = '44px'; fb.innerHTML = \"\";\n            fbWrapper.removeAttribute('data-tooltip'); \n            const isBNow = isBookmarked(next);\n            saveBtn.textContent = isBNow ? '\u2b50' : '\u2606';\n            saveBtn.className = \"save-btn\" + (isBNow ? \" saved\" : \"\");\n            saveBtn.dataset.tooltip = UI_LANGUAGES[currentLang].saveHint;\n        }));\n        input.onkeydown = (e) => { if(e.key === \"Enter\") { e.preventDefault(); check(); }};\n        return block;\n    }\n    function isBookmarked(s) { return (JSON.parse(localStorage.getItem('trainer_bookmarks') || '[]')).some(b => b.de === s.de); }\n    function shuffle(a){ for(let i=a.length-1;i>0;i--){const j=Math.floor(Math.random()*(i+1));[a[i],a[j]]=[a[j],a[i]];}}\n    container.querySelectorAll('.level-btn').forEach(btn => {\n        btn.onclick = (e) => {\n            currentLevel = e.target.dataset.level;\n            container.querySelectorAll('.level-btn').forEach(b => b.classList.remove('active'));\n            e.target.classList.add('active');\n            currentTopicId = null; renderTopics(); updateUI();\n        };\n    });\n    container.querySelector('#topicDropdown').onchange = (e) => {\n        currentTopicId = e.target.value || null;\n        renderTopics(); updateUI();\n    };\n    function openModal(title, text) {\n        const overlay = container.querySelector('#trainer-modal-overlay');\n        const mTitle = container.querySelector('#modal-title');\n        const mBody = container.querySelector('#modal-body');\n        \n        if (overlay && mTitle && mBody) {\n            mTitle.innerText = title;\n            mBody.innerHTML = text.replace(\/\\n\/g, '<br>'); \n            overlay.style.display = 'flex';\n        }\n    }\n    const closeBtn = container.querySelector('#close-modal');\n    if (closeBtn) {\n        closeBtn.onclick = () => {\n            container.querySelector('#trainer-modal-overlay').style.display = 'none';\n        };\n    }\n    const overlayElement = container.querySelector('#trainer-modal-overlay');\n    if (overlayElement) {\n        overlayElement.onclick = (e) => {\n            if (e.target.id === 'trainer-modal-overlay') {\n                overlayElement.style.display = 'none';\n            }\n        };\n    }\n    loadData();\n})(document.currentScript.parentElement);\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>B1 Grammar in Context &times; \u00a0Grammar in Context Learn German grammar by translating sentences which include the selected topic. Select source language (\/) Select grammar topic Enter the German translation Press\u00a0\u00a0Check or Enter to verify. Use\u00a0\u00a0for help,\u00a0\u00a0to listen,\u00a0\u00a0to speak. Use\u00a0\u00a0to save sentences. Use\u00a0\u00a0for a new sentence. \u00a0shows grammer topic rules Tips: The alternative sentence under [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-497","page","type-page","status-publish","hentry"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.5 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Grammar in Context - sofapiano<\/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:\/\/sofapiano.com\/es\/b1-grammar-in-context\/\" \/>\n<meta property=\"og:locale\" content=\"es_ES\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Grammar in Context - sofapiano\" \/>\n<meta property=\"og:description\" content=\"B1 Grammar in Context &times; \u00a0Grammar in Context Learn German grammar by translating sentences which include the selected topic. Select source language (\/) Select grammar topic Enter the German translation Press\u00a0\u00a0Check or Enter to verify. Use\u00a0\u00a0for help,\u00a0\u00a0to listen,\u00a0\u00a0to speak. Use\u00a0\u00a0to save sentences. Use\u00a0\u00a0for a new sentence. \u00a0shows grammer topic rules Tips: The alternative sentence under [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/sofapiano.com\/es\/b1-grammar-in-context\/\" \/>\n<meta property=\"og:site_name\" content=\"sofapiano\" \/>\n<meta property=\"article:modified_time\" content=\"2026-06-26T14:48:46+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/s.w.org\/images\/core\/emoji\/17.0.2\/svg\/1f4cb.svg\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Tiempo de lectura\" \/>\n\t<meta name=\"twitter:data1\" content=\"6 minutos\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/sofapiano.com\\\/b1-grammar-in-context\\\/\",\"url\":\"https:\\\/\\\/sofapiano.com\\\/b1-grammar-in-context\\\/\",\"name\":\"Grammar in Context - sofapiano\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/sofapiano.com\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/sofapiano.com\\\/b1-grammar-in-context\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/sofapiano.com\\\/b1-grammar-in-context\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/s.w.org\\\/images\\\/core\\\/emoji\\\/17.0.2\\\/svg\\\/1f4cb.svg\",\"datePublished\":\"2026-03-10T20:58:45+00:00\",\"dateModified\":\"2026-06-26T14:48:46+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/sofapiano.com\\\/b1-grammar-in-context\\\/#breadcrumb\"},\"inLanguage\":\"es\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/sofapiano.com\\\/b1-grammar-in-context\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"es\",\"@id\":\"https:\\\/\\\/sofapiano.com\\\/b1-grammar-in-context\\\/#primaryimage\",\"url\":\"https:\\\/\\\/s.w.org\\\/images\\\/core\\\/emoji\\\/17.0.2\\\/svg\\\/1f4cb.svg\",\"contentUrl\":\"https:\\\/\\\/s.w.org\\\/images\\\/core\\\/emoji\\\/17.0.2\\\/svg\\\/1f4cb.svg\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/sofapiano.com\\\/b1-grammar-in-context\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Start\",\"item\":\"https:\\\/\\\/sofapiano.com\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Grammar in Context\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/sofapiano.com\\\/#website\",\"url\":\"https:\\\/\\\/sofapiano.com\\\/\",\"name\":\"sofapiano\",\"description\":\"Learn German through translation\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/sofapiano.com\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"es\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Grammar in Context - sofapiano","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:\/\/sofapiano.com\/es\/b1-grammar-in-context\/","og_locale":"es_ES","og_type":"article","og_title":"Grammar in Context - sofapiano","og_description":"B1 Grammar in Context &times; \u00a0Grammar in Context Learn German grammar by translating sentences which include the selected topic. Select source language (\/) Select grammar topic Enter the German translation Press\u00a0\u00a0Check or Enter to verify. Use\u00a0\u00a0for help,\u00a0\u00a0to listen,\u00a0\u00a0to speak. Use\u00a0\u00a0to save sentences. Use\u00a0\u00a0for a new sentence. \u00a0shows grammer topic rules Tips: The alternative sentence under [&hellip;]","og_url":"https:\/\/sofapiano.com\/es\/b1-grammar-in-context\/","og_site_name":"sofapiano","article_modified_time":"2026-06-26T14:48:46+00:00","og_image":[{"url":"https:\/\/s.w.org\/images\/core\/emoji\/17.0.2\/svg\/1f4cb.svg","type":"","width":"","height":""}],"twitter_card":"summary_large_image","twitter_misc":{"Tiempo de lectura":"6 minutos"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/sofapiano.com\/b1-grammar-in-context\/","url":"https:\/\/sofapiano.com\/b1-grammar-in-context\/","name":"Grammar in Context - sofapiano","isPartOf":{"@id":"https:\/\/sofapiano.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/sofapiano.com\/b1-grammar-in-context\/#primaryimage"},"image":{"@id":"https:\/\/sofapiano.com\/b1-grammar-in-context\/#primaryimage"},"thumbnailUrl":"https:\/\/s.w.org\/images\/core\/emoji\/17.0.2\/svg\/1f4cb.svg","datePublished":"2026-03-10T20:58:45+00:00","dateModified":"2026-06-26T14:48:46+00:00","breadcrumb":{"@id":"https:\/\/sofapiano.com\/b1-grammar-in-context\/#breadcrumb"},"inLanguage":"es","potentialAction":[{"@type":"ReadAction","target":["https:\/\/sofapiano.com\/b1-grammar-in-context\/"]}]},{"@type":"ImageObject","inLanguage":"es","@id":"https:\/\/sofapiano.com\/b1-grammar-in-context\/#primaryimage","url":"https:\/\/s.w.org\/images\/core\/emoji\/17.0.2\/svg\/1f4cb.svg","contentUrl":"https:\/\/s.w.org\/images\/core\/emoji\/17.0.2\/svg\/1f4cb.svg"},{"@type":"BreadcrumbList","@id":"https:\/\/sofapiano.com\/b1-grammar-in-context\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Start","item":"https:\/\/sofapiano.com\/"},{"@type":"ListItem","position":2,"name":"Grammar in Context"}]},{"@type":"WebSite","@id":"https:\/\/sofapiano.com\/#website","url":"https:\/\/sofapiano.com\/","name":"sofapiano","description":"Aprende alem\u00e1n a trav\u00e9s de la traducci\u00f3n","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/sofapiano.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"es"}]}},"_links":{"self":[{"href":"https:\/\/sofapiano.com\/es\/wp-json\/wp\/v2\/pages\/497","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/sofapiano.com\/es\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/sofapiano.com\/es\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/sofapiano.com\/es\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/sofapiano.com\/es\/wp-json\/wp\/v2\/comments?post=497"}],"version-history":[{"count":6,"href":"https:\/\/sofapiano.com\/es\/wp-json\/wp\/v2\/pages\/497\/revisions"}],"predecessor-version":[{"id":16427,"href":"https:\/\/sofapiano.com\/es\/wp-json\/wp\/v2\/pages\/497\/revisions\/16427"}],"wp:attachment":[{"href":"https:\/\/sofapiano.com\/es\/wp-json\/wp\/v2\/media?parent=497"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}