{"id":8670,"date":"2026-09-19T09:19:26","date_gmt":"2026-09-19T09:19:26","guid":{"rendered":"https:\/\/3solutions.pt\/?page_id=8670"},"modified":"2026-09-19T09:28:32","modified_gmt":"2026-09-19T09:28:32","slug":"english-trainer","status":"publish","type":"page","link":"https:\/\/3solutions.pt\/en\/english-trainer\/","title":{"rendered":"English Trainer"},"content":{"rendered":"    <div id=\"et_app_612750\" class=\"et_app\" data-et-version=\"2\">\r\n      <style>\r\n        #et_app_612750{\r\n          --et-navy:#102a43;\r\n          --et-blue:#2463eb;\r\n          --et-bg:#f4f7fb;\r\n          --et-white:#fff;\r\n          --et-ok:#137a4b;\r\n          --et-bad:#b42318;\r\n          --et-muted:#62748a;\r\n          font-family:system-ui,-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif;\r\n          max-width:760px;\r\n          margin:24px auto;\r\n          padding:16px;\r\n          color:var(--et-navy);\r\n          line-height:1.45;\r\n          box-sizing:border-box\r\n        }\r\n\r\n        #et_app_612750 *{box-sizing:border-box}\r\n\r\n        #et_app_612750 button,\r\n        #et_app_612750 input{\r\n          font:inherit\r\n        }\r\n\r\n        #et_app_612750 .et_header{\r\n          display:flex;\r\n          align-items:center;\r\n          justify-content:space-between;\r\n          gap:16px;\r\n          margin-bottom:16px\r\n        }\r\n\r\n        #et_app_612750 .et_title{\r\n          font-size:clamp(22px,6vw,34px);\r\n          font-weight:800;\r\n          letter-spacing:-.04em;\r\n          margin:0\r\n        }\r\n\r\n        #et_app_612750 .et_points{\r\n          white-space:nowrap;\r\n          font-weight:800;\r\n          background:#e8efff;\r\n          padding:8px 12px;\r\n          border-radius:999px\r\n        }\r\n\r\n        #et_app_612750 .et_tabs{\r\n          display:grid;\r\n          grid-template-columns:1fr 1fr;\r\n          gap:8px;\r\n          margin-bottom:16px\r\n        }\r\n\r\n        #et_app_612750 .et_tab{\r\n          border:1px solid #c8d4e3;\r\n          background:var(--et-white);\r\n          color:var(--et-navy);\r\n          padding:12px 8px;\r\n          border-radius:12px;\r\n          font-weight:800;\r\n          cursor:pointer\r\n        }\r\n\r\n        #et_app_612750 .et_tab[aria-selected=\"true\"]{\r\n          background:var(--et-navy);\r\n          color:#fff;\r\n          border-color:var(--et-navy)\r\n        }\r\n\r\n        #et_app_612750 .et_card{\r\n          background:var(--et-white);\r\n          border:1px solid #d9e2ec;\r\n          border-radius:20px;\r\n          padding:clamp(22px,6vw,42px);\r\n          box-shadow:0 12px 32px rgba(16,42,67,.08);\r\n          text-align:center;\r\n          min-height:380px;\r\n          display:flex;\r\n          flex-direction:column;\r\n          justify-content:center\r\n        }\r\n\r\n        #et_app_612750 .et_prompt{\r\n          font-size:clamp(28px,8vw,48px);\r\n          line-height:1.1;\r\n          font-weight:850;\r\n          letter-spacing:-.035em;\r\n          margin:0 0 8px;\r\n          overflow-wrap:anywhere\r\n        }\r\n\r\n        #et_app_612750 .et_translation{\r\n          font-size:clamp(17px,4.5vw,22px);\r\n          font-weight:750;\r\n          color:var(--et-muted);\r\n          margin:0 0 18px\r\n        }\r\n\r\n        #et_app_612750 .et_instruction{\r\n          text-transform:uppercase;\r\n          letter-spacing:.12em;\r\n          color:var(--et-muted);\r\n          font-weight:800;\r\n          font-size:13px;\r\n          margin:0 0 24px\r\n        }\r\n\r\n        #et_app_612750 .et_speak{\r\n          border:0;\r\n          background:transparent;\r\n          cursor:pointer;\r\n          font-size:22px;\r\n          padding:6px;\r\n          margin-left:4px;\r\n          vertical-align:middle\r\n        }\r\n\r\n        #et_app_612750 .et_fields{\r\n          display:grid;\r\n          gap:10px;\r\n          margin:0 auto 14px;\r\n          width:min(100%,480px)\r\n        }\r\n\r\n        #et_app_612750 .et_field label{\r\n          display:block;\r\n          text-align:left;\r\n          font-size:13px;\r\n          font-weight:750;\r\n          margin-bottom:5px;\r\n          color:var(--et-muted)\r\n        }\r\n\r\n        #et_app_612750 .et_input{\r\n          width:100%;\r\n          min-height:52px;\r\n          border:2px solid #b8c6d8;\r\n          border-radius:12px;\r\n          padding:10px 14px;\r\n          background:#fff;\r\n          color:#102a43\r\n        }\r\n\r\n        #et_app_612750 .et_input:focus{\r\n          outline:3px solid rgba(36,99,235,.16);\r\n          border-color:var(--et-blue)\r\n        }\r\n\r\n        #et_app_612750 .et_actions{\r\n          display:flex;\r\n          flex-wrap:wrap;\r\n          justify-content:center;\r\n          gap:10px\r\n        }\r\n\r\n        #et_app_612750 .et_btn{\r\n          border:0;\r\n          border-radius:12px;\r\n          min-height:48px;\r\n          padding:11px 18px;\r\n          font-weight:800;\r\n          cursor:pointer\r\n        }\r\n\r\n        #et_app_612750 .et_primary{\r\n          background:var(--et-blue);\r\n          color:#fff\r\n        }\r\n\r\n        #et_app_612750 .et_secondary{\r\n          background:#e9eef5;\r\n          color:var(--et-navy)\r\n        }\r\n\r\n        #et_app_612750 .et_danger{\r\n          background:#fff0ee;\r\n          color:var(--et-bad);\r\n          border:1px solid #f2c0ba\r\n        }\r\n\r\n        #et_app_612750 .et_mic{\r\n          font-size:17px;\r\n          min-width:190px\r\n        }\r\n\r\n        #et_app_612750 .et_btn:disabled{\r\n          opacity:.5;\r\n          cursor:not-allowed\r\n        }\r\n\r\n        #et_app_612750 .et_feedback{\r\n          font-size:clamp(24px,7vw,38px);\r\n          font-weight:900;\r\n          margin:18px 0 10px\r\n        }\r\n\r\n        #et_app_612750 .et_feedback.et_ok{\r\n          color:var(--et-ok)\r\n        }\r\n\r\n        #et_app_612750 .et_feedback.et_bad{\r\n          color:var(--et-bad)\r\n        }\r\n\r\n        #et_app_612750 .et_full_answer{\r\n          width:min(100%,560px);\r\n          margin:8px auto 20px;\r\n          padding:18px;\r\n          background:var(--et-bg);\r\n          border-radius:14px\r\n        }\r\n\r\n        #et_app_612750 .et_full_answer_en{\r\n          font-size:22px;\r\n          font-weight:900;\r\n          margin-bottom:4px\r\n        }\r\n\r\n        #et_app_612750 .et_full_answer_pt{\r\n          font-size:17px;\r\n          color:var(--et-muted);\r\n          font-weight:700\r\n        }\r\n\r\n        #et_app_612750 .et_forms{\r\n          margin-top:12px;\r\n          font-size:18px;\r\n          font-weight:850\r\n        }\r\n\r\n        #et_app_612750 .et_heard{\r\n          background:#f1f5f9;\r\n          border-radius:10px;\r\n          padding:10px;\r\n          margin:10px auto 14px;\r\n          width:min(100%,480px)\r\n        }\r\n\r\n        #et_app_612750 .et_notice{\r\n          font-size:14px;\r\n          color:var(--et-muted);\r\n          margin:8px auto 14px;\r\n          width:min(100%,500px)\r\n        }\r\n\r\n        #et_app_612750 .et_progress{\r\n          margin-top:16px;\r\n          border:1px solid #d9e2ec;\r\n          border-radius:16px;\r\n          background:#fff;\r\n          overflow:hidden\r\n        }\r\n\r\n        #et_app_612750 .et_progress summary{\r\n          cursor:pointer;\r\n          padding:15px 18px;\r\n          font-weight:850\r\n        }\r\n\r\n        #et_app_612750 .et_progress_body{\r\n          padding:0 18px 18px\r\n        }\r\n\r\n        #et_app_612750 .et_stats{\r\n          display:grid;\r\n          grid-template-columns:repeat(2,1fr);\r\n          gap:8px;\r\n          margin-bottom:14px\r\n        }\r\n\r\n        #et_app_612750 .et_stat{\r\n          background:var(--et-bg);\r\n          border-radius:10px;\r\n          padding:10px\r\n        }\r\n\r\n        #et_app_612750 .et_stat b{\r\n          display:block;\r\n          font-size:20px\r\n        }\r\n\r\n        #et_app_612750 .et_practice{\r\n          margin:8px 0 16px;\r\n          padding-left:20px\r\n        }\r\n\r\n        #et_app_612750 .et_reset{\r\n          background:transparent;\r\n          color:var(--et-bad);\r\n          border:1px solid #e6aaa3\r\n        }\r\n\r\n        @media(min-width:640px){\r\n          #et_app_612750{padding:24px}\r\n          #et_app_612750 .et_stats{grid-template-columns:repeat(6,1fr)}\r\n          #et_app_612750 .et_card{min-height:430px}\r\n        }\r\n      <\/style>\r\n\r\n      <header class=\"et_header\">\r\n        <h1 class=\"et_title\">ENGLISH TRAINER<\/h1>\r\n        <div class=\"et_points\" aria-live=\"polite\">\r\n          PONTOS: <span data-et=\"points\">0<\/span>\r\n        <\/div>\r\n      <\/header>\r\n\r\n      <div class=\"et_tabs\" role=\"tablist\" aria-label=\"M\u00f3dulos\">\r\n        <button class=\"et_tab\" role=\"tab\" aria-selected=\"true\" data-module=\"phrasal\">\r\n          PHRASAL VERBS\r\n        <\/button>\r\n        <button class=\"et_tab\" role=\"tab\" aria-selected=\"false\" data-module=\"verbs\">\r\n          VERBS\r\n        <\/button>\r\n      <\/div>\r\n\r\n      <main class=\"et_card\" data-et=\"card\" aria-live=\"polite\"><\/main>\r\n\r\n      <details class=\"et_progress\">\r\n        <summary>PROGRESSO<\/summary>\r\n        <div class=\"et_progress_body\">\r\n          <div class=\"et_stats\" data-et=\"stats\"><\/div>\r\n          <strong>Preciso de praticar<\/strong>\r\n          <ul class=\"et_practice\" data-et=\"practice\"><\/ul>\r\n          <button class=\"et_btn et_reset\" data-action=\"reset\">Repor progresso<\/button>\r\n        <\/div>\r\n      <\/details>\r\n    <\/div>\r\n\r\n    <script>\r\n    (() => {\r\n      const root = document.getElementById(\"et_app_612750\");\r\n\r\n      if (!root || root.dataset.ready) return;\r\n      root.dataset.ready = '1';\r\n\r\n      const STORAGE = 'et_english_trainer_v2';\r\n\r\n      const phrasal = [\r\n        ['catch up','p\u00f4r-se em dia',['p\u00f4r-se em dia','por-se em dia','alcan\u00e7ar']],\r\n        ['drop back','ficar para tr\u00e1s',['ficar para tr\u00e1s']],\r\n        ['settle down','acalmar-se \/ estabelecer-se',['acalmar-se','estabelecer-se']],\r\n        ['mess around','brincar \/ perder tempo',['brincar','andar a perder tempo','perder tempo']],\r\n        ['put together','montar',['montar','juntar']],\r\n        ['take apart','desmontar',['desmontar']],\r\n        ['put up','colocar \/ erguer',['colocar','erguer']],\r\n        ['take down','retirar \/ desmontar',['retirar','desmontar']],\r\n        ['speed up','acelerar',['acelerar']],\r\n        ['slow down','abrandar',['abrandar','desacelerar']],\r\n        ['mess up','estragar \/ fazer asneira',['estragar','fazer asneira']],\r\n        ['tidy up','arrumar',['arrumar']],\r\n        ['sit down','sentar-se',['sentar-se','sentar']],\r\n        ['stand up','levantar-se',['levantar-se','levantar']],\r\n        ['dress up','vestir-se de forma elegante',['vestir-se de forma elegante','vestir-se elegantemente']],\r\n        ['dress down','vestir-se de forma informal',['vestir-se de forma informal','vestir-se informalmente']],\r\n        ['get on','entrar \/ subir num transporte',['entrar num transporte','subir num transporte','entrar','subir']],\r\n        ['get off','sair \/ descer de um transporte',['sair de um transporte','descer de um transporte','sair','descer']],\r\n        ['take up','come\u00e7ar uma atividade',['come\u00e7ar uma atividade','iniciar uma atividade','ocupar']],\r\n        ['let down','desiludir',['desiludir','desapontar']],\r\n        ['stick on','colar \/ p\u00f4r',['colar','p\u00f4r','por']],\r\n        ['cut off','cortar \/ interromper',['cortar','interromper']],\r\n        ['get ahead','avan\u00e7ar \/ ficar \u00e0 frente',['avan\u00e7ar','ficar \u00e0 frente']],\r\n        ['fall behind','ficar para tr\u00e1s \/ atrasar-se',['ficar para tr\u00e1s','atrasar-se']],\r\n        ['hand out','distribuir',['distribuir']],\r\n        ['take in','absorver \/ compreender',['absorver','compreender']],\r\n        ['look into','investigar \/ averiguar',['investigar','analisar','averiguar']],\r\n        ['look after','cuidar de',['cuidar de','tomar conta de']],\r\n        ['look for','procurar',['procurar']],\r\n        ['look forward to','aguardar com expectativa',['aguardar com expectativa','esperar com expectativa','estar ansioso por']],\r\n        ['figure out','perceber \/ descobrir',['perceber','descobrir','entender']],\r\n        ['end up','acabar por',['acabar por']],\r\n        ['turn out','acabar por se revelar',['acabar por se revelar','resultar','revelar-se']],\r\n        ['carry on','continuar',['continuar','prosseguir']],\r\n        ['manage to','conseguir',['conseguir']],\r\n        ['turn up','aparecer',['aparecer','chegar']],\r\n        ['stand out','destacar-se',['destacar-se','sobressair']],\r\n        ['cut down on','reduzir',['reduzir','diminuir']],\r\n        ['put off','adiar',['adiar','postergar']],\r\n        ['hold back','conter \/ reter',['conter','reter']],\r\n        ['drift apart','afastar-se gradualmente',['afastar-se gradualmente','afastarem-se','distanciarem-se','distanciar-se']],\r\n        ['cheer up','animar',['animar','animar-se']],\r\n        ['drag on','arrastar-se',['arrastar-se','prolongar-se']],\r\n        ['find out','descobrir',['descobrir']],\r\n        ['get through','ultrapassar \/ conseguir passar por',['ultrapassar','conseguir passar por','passar por']],\r\n        ['cover up','encobrir',['encobrir','ocultar']],\r\n        ['step in','intervir',['intervir']]\r\n      ].map((x,i) => ({\r\n        id:'p_' + i,\r\n        en:x[0],\r\n        pt:x[1],\r\n        ptAnswers:x[2]\r\n      }));\r\n\r\n      \/*\r\n       * VERBS\r\n       * Sempre mostramos:\r\n       * TO VERB \u2014 significado PT\r\n       *\r\n       * O utilizador tem de recuperar:\r\n       * Past Simple + Past Participle\r\n       *\/\r\n      const verbs = [\r\n        ['be','ser \/ estar','was \/ were','been'],\r\n        ['bear','suportar \/ aguentar','bore','borne'],\r\n        ['become','tornar-se','became','become'],\r\n        ['begin','come\u00e7ar','began','begun'],\r\n        ['break','partir \/ quebrar','broke','broken'],\r\n        ['bring','trazer','brought','brought'],\r\n        ['buy','comprar','bought','bought'],\r\n        ['catch','apanhar','caught','caught'],\r\n        ['come','vir','came','come'],\r\n        ['do','fazer','did','done'],\r\n        ['drink','beber','drank','drunk'],\r\n        ['drive','conduzir','drove','driven'],\r\n        ['eat','comer','ate','eaten'],\r\n        ['fall','cair','fell','fallen'],\r\n        ['feel','sentir','felt','felt'],\r\n        ['find','encontrar','found','found'],\r\n        ['forget','esquecer','forgot','forgotten'],\r\n        ['get','obter \/ conseguir','got','got'],\r\n        ['give','dar','gave','given'],\r\n        ['go','ir','went','gone'],\r\n        ['hear','ouvir','heard','heard'],\r\n        ['keep','manter \/ guardar','kept','kept'],\r\n        ['know','saber \/ conhecer','knew','known'],\r\n        ['leave','deixar \/ partir','left','left'],\r\n        ['lie','estar deitado \/ deitar-se','lay','lain'],\r\n        ['make','fazer \/ criar','made','made'],\r\n        ['meet','encontrar-se \/ conhecer','met','met'],\r\n        ['pay','pagar','paid','paid'],\r\n        ['read','ler','read','read'],\r\n        ['ride','andar \/ montar','rode','ridden'],\r\n        ['ring','tocar \/ telefonar','rang','rung'],\r\n        ['run','correr','ran','run'],\r\n        ['say','dizer','said','said'],\r\n        ['see','ver','saw','seen'],\r\n        ['sit','sentar-se','sat','sat'],\r\n        ['sleep','dormir','slept','slept'],\r\n        ['speak','falar','spoke','spoken'],\r\n        ['stand','estar de p\u00e9','stood','stood'],\r\n        ['steal','roubar','stole','stolen'],\r\n        ['take','levar \/ tomar','took','taken'],\r\n        ['tell','dizer \/ contar','told','told'],\r\n        ['think','pensar','thought','thought'],\r\n        ['throw','atirar','threw','thrown'],\r\n        ['understand','compreender','understood','understood'],\r\n        ['wake','acordar','woke','woken'],\r\n        ['wear','vestir \/ usar','wore','worn'],\r\n        ['write','escrever','wrote','written'],\r\n        ['sink','afundar','sank','sunk'],\r\n        ['mislead','enganar \/ induzir em erro','misled','misled'],\r\n        ['let','deixar \/ permitir','let','let'],\r\n        ['quit','desistir \/ abandonar','quit','quit'],\r\n        ['lie','mentir','lied','lied']\r\n      ].map((x,i) => ({\r\n        id:'v_' + i,\r\n        en:x[0],\r\n        pt:x[1],\r\n        past:x[2],\r\n        participle:x[3]\r\n      }));\r\n\r\n      let state = load();\r\n      let module = 'phrasal';\r\n      let current = null;\r\n      let last = {phrasal:null, verbs:null};\r\n      let locked = false;\r\n      let recognition = null;\r\n\r\n      function empty() {\r\n        return {\r\n          points:0,\r\n          total:0,\r\n          correct:0,\r\n          errors:0,\r\n          dontKnow:0,\r\n          items:{},\r\n          recent:[]\r\n        };\r\n      }\r\n\r\n      function load() {\r\n        try {\r\n          return Object.assign(\r\n            empty(),\r\n            JSON.parse(localStorage.getItem(STORAGE) || '{}')\r\n          );\r\n        } catch(e) {\r\n          return empty();\r\n        }\r\n      }\r\n\r\n      function save() {\r\n        try {\r\n          localStorage.setItem(STORAGE, JSON.stringify(state));\r\n        } catch(e) {}\r\n        updateProgress();\r\n      }\r\n\r\n      function norm(v) {\r\n        return String(v || '')\r\n          .toLocaleLowerCase('pt-PT')\r\n          .normalize('NFD')\r\n          .replace(\/[\\u0300-\\u036f]\/g,'')\r\n          .replace(\/[.!?,;:]+$\/g,'')\r\n          .replace(\/[\\\/_-]+\/g,' ')\r\n          .replace(\/\\s+\/g,' ')\r\n          .trim();\r\n      }\r\n\r\n      function same(v, answers) {\r\n        const n = norm(v);\r\n        return answers.some(a => norm(a) === n);\r\n      }\r\n\r\n      function esc(v) {\r\n        return String(v).replace(\/[&<>\"']\/g, c => ({\r\n          '&':'&amp;',\r\n          '<':'&lt;',\r\n          '>':'&gt;',\r\n          '\"':'&quot;',\r\n          \"'\":'&#039;'\r\n        }[c]));\r\n      }\r\n\r\n      function itemStat(id) {\r\n        if (!state.items[id]) {\r\n          state.items[id] = {\r\n            seen:0,\r\n            correct:0,\r\n            errors:0,\r\n            dontKnow:0,\r\n            streak:0,\r\n            needsReview:false\r\n          };\r\n        }\r\n        return state.items[id];\r\n      }\r\n\r\n      function allItems() {\r\n        return module === 'phrasal' ? phrasal : verbs;\r\n      }\r\n\r\n      \/*\r\n       * Itens que continuam a precisar de revis\u00e3o.\r\n       * Tr\u00eas acertos consecutivos retiram o item da revis\u00e3o intensiva.\r\n       *\/\r\n      function reviewItems() {\r\n        return allItems().filter(item => {\r\n          const s = itemStat(item.id);\r\n          return s.needsReview || s.streak < 3 && (s.errors > 0 || s.dontKnow > 0);\r\n        });\r\n      }\r\n\r\n      \/*\r\n       * Regra adaptativa:\r\n       *\r\n       * At\u00e9 3 erros pendentes:\r\n       * 2 revis\u00f5es em cada bloco de 5.\r\n       *\r\n       * Mais de 3:\r\n       * 4 revis\u00f5es em cada 7.\r\n       *\r\n       * Mais de 7:\r\n       * 5 revis\u00f5es em cada 7.\r\n       *\r\n       * Mais de 12:\r\n       * 6 revis\u00f5es em cada 8.\r\n       *\r\n       * N\u00e3o h\u00e1 n\u00edveis.\r\n       *\/\r\n      function reviewPlan() {\r\n        const count = reviewItems().length;\r\n\r\n        if (count > 12) return {size:8, reviews:6};\r\n        if (count > 7)  return {size:7, reviews:5};\r\n        if (count > 3)  return {size:7, reviews:4};\r\n\r\n        return {size:5, reviews:2};\r\n      }\r\n\r\n      \/*\r\n       * Decide se esta posi\u00e7\u00e3o do bloco deve ser revis\u00e3o.\r\n       * As posi\u00e7\u00f5es s\u00e3o distribu\u00eddas pelo bloco para n\u00e3o aparecerem\r\n       * todas seguidas.\r\n       *\/\r\n      function shouldReview() {\r\n        const reviews = reviewItems();\r\n        if (!reviews.length) return false;\r\n\r\n        const plan = reviewPlan();\r\n        const position = state.total % plan.size;\r\n\r\n        const slots = [];\r\n\r\n        for (let i=0; i<plan.reviews; i++) {\r\n          slots.push(\r\n            Math.floor(((i + 1) * plan.size) \/ (plan.reviews + 1))\r\n          );\r\n        }\r\n\r\n        return slots.includes(position);\r\n      }\r\n\r\n      function chooseRandom(list, key) {\r\n        if (!list.length) return null;\r\n\r\n        let pool = list.filter(x => x.id !== last[key]);\r\n\r\n        if (!pool.length) pool = list;\r\n\r\n        const chosen = pool[Math.floor(Math.random() * pool.length)];\r\n\r\n        last[key] = chosen.id;\r\n\r\n        return chosen;\r\n      }\r\n\r\n      function chooseQuestionItem() {\r\n        const key = module === 'phrasal' ? 'phrasal' : 'verbs';\r\n\r\n        if (shouldReview()) {\r\n          const review = reviewItems();\r\n          const chosen = chooseRandom(review, key);\r\n          if (chosen) return chosen;\r\n        }\r\n\r\n        return chooseRandom(allItems(), key);\r\n      }\r\n\r\n      function audioButton(text) {\r\n        return '<button type=\"button\" class=\"et_speak\" data-speak=\"' +\r\n          esc(text) +\r\n          '\" aria-label=\"Ouvir pron\u00fancia inglesa\">\ud83d\udd0a<\/button>';\r\n      }\r\n\r\n      function buildQuestion() {\r\n        locked = false;\r\n\r\n        if (recognition) {\r\n          try { recognition.abort(); } catch(e) {}\r\n          recognition = null;\r\n        }\r\n\r\n        const item = chooseQuestionItem();\r\n\r\n        if (!item) return;\r\n\r\n        \/*\r\n         * PHRASAL:\r\n         * dire\u00e7\u00e3o aleat\u00f3ria.\r\n         *\r\n         * PT -> EN: write OU speak\r\n         * EN -> PT: SEMPRE write\r\n         *\/\r\n        if (module === 'phrasal') {\r\n          const toEnglish = Math.random() < .5;\r\n          const mode = toEnglish\r\n            ? (Math.random() < .5 ? 'write' : 'speak')\r\n            : 'write';\r\n\r\n          current = {\r\n            module:'phrasal',\r\n            item,\r\n            toEnglish,\r\n            mode,\r\n            prompt:toEnglish ? item.pt : item.en,\r\n            answers:toEnglish ? [item.en] : item.ptAnswers,\r\n            canonical:toEnglish ? item.en : item.pt\r\n          };\r\n        }\r\n\r\n        \/*\r\n         * VERBS:\r\n         * mostra sempre TO VERB + significado PT.\r\n         * pede sempre Past Simple + Past Participle.\r\n         * write\/speak aleat\u00f3rio.\r\n         *\/\r\n        else {\r\n          const mode = Math.random() < .5 ? 'write' : 'speak';\r\n\r\n          current = {\r\n            module:'verbs',\r\n            item,\r\n            mode,\r\n            answers:[\r\n              acceptedVerbForm(item.past),\r\n              acceptedVerbForm(item.participle)\r\n            ]\r\n          };\r\n        }\r\n\r\n        state.total++;\r\n        itemStat(item.id).seen++;\r\n\r\n        save();\r\n        render();\r\n      }\r\n\r\n      function acceptedVerbForm(v) {\r\n        if (norm(v) === 'was were') {\r\n          return ['was \/ were','was were','was\/were'];\r\n        }\r\n\r\n        return [v];\r\n      }\r\n\r\n      function render() {\r\n        const card = root.querySelector('[data-et=\"card\"]');\r\n\r\n        let body = '';\r\n\r\n        if (module === 'phrasal') {\r\n          const englishShown = !current.toEnglish;\r\n\r\n          body += '<h2 class=\"et_prompt\">' +\r\n            esc(current.prompt) +\r\n            (englishShown ? audioButton(current.prompt) : '') +\r\n            '<\/h2>';\r\n\r\n          body += '<p class=\"et_instruction\">' +\r\n            (current.mode === 'speak' ? 'DIZ' : 'ESCREVE') +\r\n            (current.toEnglish ? ' EM INGL\u00caS' : ' EM PORTUGU\u00caS') +\r\n            '<\/p>';\r\n        }\r\n\r\n        else {\r\n          body += '<h2 class=\"et_prompt\">TO ' +\r\n            esc(current.item.en.toUpperCase()) +\r\n            audioButton(current.item.en) +\r\n            '<\/h2>';\r\n\r\n          body += '<div class=\"et_translation\">' +\r\n            esc(current.item.pt) +\r\n            '<\/div>';\r\n\r\n          body += '<p class=\"et_instruction\">' +\r\n            (current.mode === 'speak' ? 'DIZ' : 'ESCREVE') +\r\n            ': PAST SIMPLE + PAST PARTICIPLE<\/p>';\r\n        }\r\n\r\n        if (current.mode === 'write') {\r\n          body += fieldsHtml(false);\r\n\r\n          body +=\r\n            '<div class=\"et_actions\">' +\r\n              '<button class=\"et_btn et_primary\" data-action=\"confirm\">CONFIRMAR<\/button>' +\r\n              '<button class=\"et_btn et_secondary\" data-action=\"dontknow\">N\u00c3O SEI<\/button>' +\r\n            '<\/div>';\r\n        }\r\n\r\n        else {\r\n          body +=\r\n            '<div class=\"et_actions\">' +\r\n              '<button class=\"et_btn et_primary et_mic\" data-action=\"mic\">\ud83c\udf99\ufe0f RESPONDER<\/button>' +\r\n              '<button class=\"et_btn et_secondary\" data-action=\"dontknow\">N\u00c3O SEI<\/button>' +\r\n            '<\/div>' +\r\n            '<p class=\"et_notice\">A transcri\u00e7\u00e3o n\u00e3o retira pontos automaticamente.<\/p>';\r\n        }\r\n\r\n        card.innerHTML = body;\r\n\r\n        const first = card.querySelector('.et_input');\r\n        if (first) first.focus();\r\n\r\n        updateProgress();\r\n      }\r\n\r\n      function fieldsHtml(required) {\r\n        if (module === 'phrasal') {\r\n          return `\r\n            <div class=\"et_fields\">\r\n              <div class=\"et_field\">\r\n                <label>A tua resposta<\/label>\r\n                <input\r\n                  class=\"et_input\"\r\n                  autocomplete=\"off\"\r\n                  autocapitalize=\"none\"\r\n                  data-answer=\"0\"\r\n                  ${required ? 'placeholder=\"Escreve a resposta correta\"' : ''}\r\n                >\r\n              <\/div>\r\n            <\/div>\r\n          `;\r\n        }\r\n\r\n        return `\r\n          <div class=\"et_fields\">\r\n            <div class=\"et_field\">\r\n              <label>Past Simple<\/label>\r\n              <input\r\n                class=\"et_input\"\r\n                autocomplete=\"off\"\r\n                autocapitalize=\"none\"\r\n                data-answer=\"0\"\r\n                ${required ? 'placeholder=\"Past Simple\"' : ''}\r\n              >\r\n            <\/div>\r\n\r\n            <div class=\"et_field\">\r\n              <label>Past Participle<\/label>\r\n              <input\r\n                class=\"et_input\"\r\n                autocomplete=\"off\"\r\n                autocapitalize=\"none\"\r\n                data-answer=\"1\"\r\n                ${required ? 'placeholder=\"Past Participle\"' : ''}\r\n              >\r\n            <\/div>\r\n          <\/div>\r\n        `;\r\n      }\r\n\r\n      function writtenValues() {\r\n        return [...root.querySelectorAll('[data-answer]')]\r\n          .map(x => x.value);\r\n      }\r\n\r\n      function isCorrect(vals) {\r\n        if (module === 'phrasal') {\r\n          return same(vals[0], current.answers);\r\n        }\r\n\r\n        return current.answers.every(\r\n          (answers, i) => same(vals[i], answers)\r\n        );\r\n      }\r\n\r\n      \/*\r\n       * Mostra SEMPRE o conjunto completo depois da avalia\u00e7\u00e3o.\r\n       *\/\r\n      function completeAnswerHtml() {\r\n        if (module === 'phrasal') {\r\n          return `\r\n            <div class=\"et_full_answer\">\r\n              <div class=\"et_full_answer_en\">\r\n                ${esc(current.item.en.toUpperCase())}\r\n                ${audioButton(current.item.en)}\r\n              <\/div>\r\n              <div class=\"et_full_answer_pt\">\r\n                ${esc(current.item.pt)}\r\n              <\/div>\r\n            <\/div>\r\n          `;\r\n        }\r\n\r\n        return `\r\n          <div class=\"et_full_answer\">\r\n            <div class=\"et_full_answer_en\">\r\n              TO ${esc(current.item.en.toUpperCase())}\r\n              ${audioButton(current.item.en)}\r\n            <\/div>\r\n\r\n            <div class=\"et_full_answer_pt\">\r\n              ${esc(current.item.pt)}\r\n            <\/div>\r\n\r\n            <div class=\"et_forms\">\r\n              ${esc(current.item.past)}\r\n              &nbsp;\u2192&nbsp;\r\n              ${esc(current.item.participle)}\r\n            <\/div>\r\n          <\/div>\r\n        `;\r\n      }\r\n\r\n      function score(kind) {\r\n        const s = itemStat(current.item.id);\r\n\r\n        if (kind === 'correct') {\r\n          state.points++;\r\n          state.correct++;\r\n          s.correct++;\r\n          s.streak++;\r\n\r\n          if (s.streak >= 3) {\r\n            s.needsReview = false;\r\n          }\r\n        }\r\n\r\n        else {\r\n          state.points--;\r\n          state.errors++;\r\n          s.errors++;\r\n          s.streak = 0;\r\n          s.needsReview = true;\r\n\r\n          if (kind === 'dontknow') {\r\n            state.dontKnow++;\r\n            s.dontKnow++;\r\n          }\r\n        }\r\n\r\n        save();\r\n      }\r\n\r\n      function finish(ok, kind) {\r\n        if (locked) return;\r\n\r\n        locked = true;\r\n\r\n        score(ok ? 'correct' : (kind || 'error'));\r\n\r\n        const card = root.querySelector('[data-et=\"card\"]');\r\n\r\n        card.innerHTML =\r\n          '<div class=\"et_feedback ' +\r\n            (ok ? 'et_ok' : 'et_bad') +\r\n          '\">' +\r\n            (ok ? '\u2713 CERTO +1' : '\u2717 ERRADO \u22121') +\r\n          '<\/div>' +\r\n\r\n          completeAnswerHtml() +\r\n\r\n          '<div class=\"et_actions\">' +\r\n            '<button class=\"et_btn et_primary\" data-action=\"next\">PR\u00d3XIMA<\/button>' +\r\n          '<\/div>';\r\n      }\r\n\r\n      function dontKnow() {\r\n        if (locked) return;\r\n\r\n        locked = true;\r\n\r\n        score('dontknow');\r\n\r\n        const card = root.querySelector('[data-et=\"card\"]');\r\n\r\n        card.innerHTML =\r\n          '<div class=\"et_feedback et_bad\">N\u00c3O SEI \u22121<\/div>' +\r\n\r\n          completeAnswerHtml() +\r\n\r\n          '<p class=\"et_notice\">' +\r\n            'Agora escreve corretamente a resposta antes de continuar.' +\r\n          '<\/p>' +\r\n\r\n          fieldsHtml(true) +\r\n\r\n          '<div class=\"et_actions\">' +\r\n            '<button class=\"et_btn et_primary\" data-action=\"learned\">VALIDAR<\/button>' +\r\n          '<\/div>';\r\n\r\n        const first = card.querySelector('.et_input');\r\n        if (first) first.focus();\r\n      }\r\n\r\n      function validateLearned() {\r\n        const vals = writtenValues();\r\n\r\n        if (isCorrect(vals)) {\r\n          root.querySelector('[data-et=\"card\"]').innerHTML =\r\n            '<div class=\"et_feedback et_ok\">\u2713 RESPOSTA MEMORIZADA<\/div>' +\r\n            completeAnswerHtml() +\r\n            '<div class=\"et_actions\">' +\r\n              '<button class=\"et_btn et_primary\" data-action=\"next\">PR\u00d3XIMA<\/button>' +\r\n            '<\/div>';\r\n        }\r\n\r\n        else {\r\n          const notice = root.querySelector('.et_notice');\r\n\r\n          if (notice) {\r\n            notice.textContent =\r\n              'Ainda n\u00e3o est\u00e1 correto. Observa a resposta acima e tenta novamente.';\r\n            notice.style.color = '#b42318';\r\n          }\r\n        }\r\n      }\r\n\r\n      \/*\r\n       * VOICE\r\n       * S\u00f3 PT->EN nos phrasal verbs.\r\n       * Nos verbs, resposta em ingl\u00eas.\r\n       *\/\r\n      function startVoice() {\r\n        const SR =\r\n          window.SpeechRecognition ||\r\n          window.webkitSpeechRecognition;\r\n\r\n        if (!SR) {\r\n          voiceFallback(\r\n            'O reconhecimento de voz n\u00e3o \u00e9 suportado neste browser. Responde por escrita.'\r\n          );\r\n          return;\r\n        }\r\n\r\n        recognition = new SR();\r\n        recognition.lang = 'en-GB';\r\n        recognition.interimResults = false;\r\n        recognition.maxAlternatives = 5;\r\n\r\n        const button = root.querySelector('[data-action=\"mic\"]');\r\n\r\n        if (button) {\r\n          button.disabled = true;\r\n          button.textContent = 'A OUVIR\u2026';\r\n        }\r\n\r\n        recognition.onresult = e => {\r\n          const heard = [...e.results[0]].map(x => x.transcript);\r\n          showHeard(heard);\r\n        };\r\n\r\n        recognition.onerror = () => {\r\n          voiceFallback(\r\n            'N\u00e3o foi poss\u00edvel ouvir. Podes tentar novamente ou responder por escrita.'\r\n          );\r\n        };\r\n\r\n        recognition.onend = () => {\r\n          const b = root.querySelector('[data-action=\"mic\"]');\r\n\r\n          if (b) {\r\n            b.disabled = false;\r\n            b.textContent = '\ud83c\udf99\ufe0f RESPONDER';\r\n          }\r\n        };\r\n\r\n        try {\r\n          recognition.start();\r\n        } catch(e) {\r\n          voiceFallback(\r\n            'N\u00e3o foi poss\u00edvel iniciar o microfone. Responde por escrita.'\r\n          );\r\n        }\r\n      }\r\n\r\n      function voiceMatches(text) {\r\n        if (module === 'phrasal') {\r\n          return same(text, current.answers);\r\n        }\r\n\r\n        \/*\r\n         * Para verbos esperamos as duas formas faladas.\r\n         * Ex.: \"rang rung\"\r\n         *\/\r\n        const heard = norm(text);\r\n\r\n        const past = norm(current.item.past);\r\n        const participle = norm(current.item.participle);\r\n\r\n        if (heard === norm(past + ' ' + participle)) {\r\n          return true;\r\n        }\r\n\r\n        \/*\r\n         * BE: toler\u00e2ncia para \"was were been\".\r\n         *\/\r\n        if (\r\n          current.item.en === 'be' &&\r\n          (\r\n            heard === 'was were been' ||\r\n            heard === 'were been' ||\r\n            heard === 'was been'\r\n          )\r\n        ) {\r\n          return true;\r\n        }\r\n\r\n        return false;\r\n      }\r\n\r\n      function showHeard(alternatives) {\r\n        const heard = alternatives[0] || '';\r\n\r\n        if (alternatives.some(voiceMatches)) {\r\n          finish(true, 'correct');\r\n          return;\r\n        }\r\n\r\n        const card = root.querySelector('[data-et=\"card\"]');\r\n\r\n        let title = '';\r\n\r\n        if (module === 'phrasal') {\r\n          title =\r\n            '<h2 class=\"et_prompt\">' +\r\n              esc(current.prompt) +\r\n            '<\/h2>';\r\n        } else {\r\n          title =\r\n            '<h2 class=\"et_prompt\">TO ' +\r\n              esc(current.item.en.toUpperCase()) +\r\n            '<\/h2>' +\r\n            '<div class=\"et_translation\">' +\r\n              esc(current.item.pt) +\r\n            '<\/div>';\r\n        }\r\n\r\n        card.innerHTML =\r\n          title +\r\n\r\n          '<div class=\"et_heard\">' +\r\n            '<strong>Ouvi:<\/strong> ' +\r\n            esc(heard) +\r\n          '<\/div>' +\r\n\r\n          '<p class=\"et_notice\">' +\r\n            'A transcri\u00e7\u00e3o pode estar errada. S\u00f3 confirma o erro se foste tu que erraste.' +\r\n          '<\/p>' +\r\n\r\n          '<div class=\"et_actions\">' +\r\n            '<button class=\"et_btn et_primary\" data-action=\"mic\">TENTAR NOVAMENTE<\/button>' +\r\n            '<button class=\"et_btn et_danger\" data-action=\"voicewrong\">EU ERREI<\/button>' +\r\n          '<\/div>';\r\n      }\r\n\r\n      function voiceFallback(message) {\r\n        const card = root.querySelector('[data-et=\"card\"]');\r\n\r\n        let top = '';\r\n\r\n        if (module === 'phrasal') {\r\n          top =\r\n            '<h2 class=\"et_prompt\">' +\r\n              esc(current.prompt) +\r\n            '<\/h2>';\r\n        } else {\r\n          top =\r\n            '<h2 class=\"et_prompt\">TO ' +\r\n              esc(current.item.en.toUpperCase()) +\r\n            '<\/h2>' +\r\n            '<div class=\"et_translation\">' +\r\n              esc(current.item.pt) +\r\n            '<\/div>';\r\n        }\r\n\r\n        card.innerHTML =\r\n          top +\r\n          '<p class=\"et_notice\">' + esc(message) + '<\/p>' +\r\n          fieldsHtml(false) +\r\n          '<div class=\"et_actions\">' +\r\n            '<button class=\"et_btn et_primary\" data-action=\"confirm\">CONFIRMAR<\/button>' +\r\n            '<button class=\"et_btn et_secondary\" data-action=\"dontknow\">N\u00c3O SEI<\/button>' +\r\n          '<\/div>';\r\n\r\n        const first = card.querySelector('.et_input');\r\n        if (first) first.focus();\r\n      }\r\n\r\n      function speak(text) {\r\n        if (!('speechSynthesis' in window)) return;\r\n\r\n        const utterance = new SpeechSynthesisUtterance(text);\r\n\r\n        const voices = speechSynthesis.getVoices();\r\n\r\n        const voice =\r\n          voices.find(x => x.lang === 'en-GB') ||\r\n          voices.find(x => x.lang && x.lang.startsWith('en'));\r\n\r\n        if (voice) utterance.voice = voice;\r\n\r\n        utterance.lang = voice ? voice.lang : 'en-GB';\r\n\r\n        speechSynthesis.cancel();\r\n        speechSynthesis.speak(utterance);\r\n      }\r\n\r\n      function updateProgress() {\r\n        root.querySelector('[data-et=\"points\"]').textContent = state.points;\r\n\r\n        const attempts = state.correct + state.errors;\r\n\r\n        const rate = attempts\r\n          ? Math.round((state.correct \/ attempts) * 100)\r\n          : 0;\r\n\r\n        const pending = [\r\n          ...phrasal,\r\n          ...verbs\r\n        ].filter(item => {\r\n          const s = state.items[item.id];\r\n          return s && s.needsReview;\r\n        }).length;\r\n\r\n        const stats = [\r\n          ['Pontua\u00e7\u00e3o',state.points],\r\n          ['Perguntas',state.total],\r\n          ['Acertos',state.correct],\r\n          ['Erros',state.errors],\r\n          ['N\u00e3o sei',state.dontKnow],\r\n          ['Revis\u00e3o',pending],\r\n          ['Taxa',rate + '%']\r\n        ];\r\n\r\n        root.querySelector('[data-et=\"stats\"]').innerHTML =\r\n          stats.map(x =>\r\n            '<div class=\"et_stat\">' +\r\n              '<span>' + esc(x[0]) + '<\/span>' +\r\n              '<b>' + esc(x[1]) + '<\/b>' +\r\n            '<\/div>'\r\n          ).join('');\r\n\r\n        const all = [...phrasal,...verbs]\r\n          .map(item => ({\r\n            item,\r\n            stat:state.items[item.id]\r\n          }))\r\n          .filter(o =>\r\n            o.stat &&\r\n            (o.stat.errors || o.stat.dontKnow)\r\n          )\r\n          .sort((a,b) =>\r\n            (\r\n              b.stat.errors +\r\n              b.stat.dontKnow * 2\r\n            ) -\r\n            (\r\n              a.stat.errors +\r\n              a.stat.dontKnow * 2\r\n            )\r\n          )\r\n          .slice(0,8);\r\n\r\n        root.querySelector('[data-et=\"practice\"]').innerHTML =\r\n          all.length\r\n            ? all.map(o => {\r\n                const label = o.item.en;\r\n\r\n                return '<li>' +\r\n                  esc(label) +\r\n                  ' \u2014 ' +\r\n                  o.stat.errors +\r\n                  ' erro(s), ' +\r\n                  o.stat.dontKnow +\r\n                  ' \u201cn\u00e3o sei\u201d' +\r\n                '<\/li>';\r\n              }).join('')\r\n            : '<li>Ainda sem dados suficientes.<\/li>';\r\n      }\r\n\r\n      root.addEventListener('click', e => {\r\n        const tab = e.target.closest('[data-module]');\r\n\r\n        if (tab) {\r\n          module = tab.dataset.module;\r\n\r\n          root.querySelectorAll('[data-module]')\r\n            .forEach(x =>\r\n              x.setAttribute(\r\n                'aria-selected',\r\n                String(x === tab)\r\n              )\r\n            );\r\n\r\n          buildQuestion();\r\n          return;\r\n        }\r\n\r\n        const speakButton = e.target.closest('[data-speak]');\r\n\r\n        if (speakButton) {\r\n          speak(speakButton.dataset.speak);\r\n          return;\r\n        }\r\n\r\n        const action = e.target.closest('[data-action]');\r\n\r\n        if (!action) return;\r\n\r\n        const act = action.dataset.action;\r\n\r\n        if (act === 'confirm') {\r\n          const vals = writtenValues();\r\n\r\n          if (vals.some(v => !norm(v))) return;\r\n\r\n          const ok = isCorrect(vals);\r\n\r\n          finish(\r\n            ok,\r\n            ok ? 'correct' : 'error'\r\n          );\r\n        }\r\n\r\n        else if (act === 'dontknow') {\r\n          dontKnow();\r\n        }\r\n\r\n        else if (act === 'next') {\r\n          buildQuestion();\r\n        }\r\n\r\n        else if (act === 'learned') {\r\n          validateLearned();\r\n        }\r\n\r\n        else if (act === 'mic') {\r\n          startVoice();\r\n        }\r\n\r\n        else if (act === 'voicewrong') {\r\n          finish(false,'error');\r\n        }\r\n\r\n        else if (act === 'reset') {\r\n          if (\r\n            confirm(\r\n              'Repor todo o progresso guardado neste dispositivo?'\r\n            )\r\n          ) {\r\n            localStorage.removeItem(STORAGE);\r\n            state = empty();\r\n            last = {\r\n              phrasal:null,\r\n              verbs:null\r\n            };\r\n            buildQuestion();\r\n          }\r\n        }\r\n      });\r\n\r\n      root.addEventListener('keydown', e => {\r\n        if (\r\n          e.key === 'Enter' &&\r\n          e.target.matches('.et_input')\r\n        ) {\r\n          const button = root.querySelector(\r\n            '[data-action=\"confirm\"],[data-action=\"learned\"]'\r\n          );\r\n\r\n          if (button) button.click();\r\n        }\r\n      });\r\n\r\n      buildQuestion();\r\n    })();\r\n    <\/script>\r\n    \n","protected":false},"excerpt":{"rendered":"","protected":false},"author":2,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"set","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"class_list":["post-8670","page","type-page","status-publish","hentry"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.5 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>English Trainer - 3 Solutions | WE SPEAK RETAIL - Consultoria e Forma\u00e7\u00e3o retalho<\/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:\/\/3solutions.pt\/en\/english-trainer\/\" \/>\n<meta property=\"og:locale\" content=\"en_GB\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"English Trainer - 3 Solutions | WE SPEAK RETAIL - Consultoria e Forma\u00e7\u00e3o retalho\" \/>\n<meta property=\"og:url\" content=\"https:\/\/3solutions.pt\/en\/english-trainer\/\" \/>\n<meta property=\"og:site_name\" content=\"3 Solutions | WE SPEAK RETAIL - Consultoria e Forma\u00e7\u00e3o retalho\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/share\/17VAXrDnwy\/\" \/>\n<meta property=\"article:modified_time\" content=\"2026-09-19T09:28:32+00:00\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/3solutions.pt\\\/english-trainer\\\/\",\"url\":\"https:\\\/\\\/3solutions.pt\\\/english-trainer\\\/\",\"name\":\"English Trainer - 3 Solutions | WE SPEAK RETAIL - Consultoria e Forma\u00e7\u00e3o retalho\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/3solutions.pt\\\/#website\"},\"datePublished\":\"2026-09-19T09:19:26+00:00\",\"dateModified\":\"2026-09-19T09:28:32+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/3solutions.pt\\\/english-trainer\\\/#breadcrumb\"},\"inLanguage\":\"en-GB\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/3solutions.pt\\\/english-trainer\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/3solutions.pt\\\/english-trainer\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"In\u00edcio\",\"item\":\"https:\\\/\\\/3solutions.pt\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"English Trainer\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/3solutions.pt\\\/#website\",\"url\":\"https:\\\/\\\/3solutions.pt\\\/\",\"name\":\"3Solutions Consultoria em Marketing e Gest\u00e3o\",\"description\":\"Consultoria e Marketing Digital\",\"publisher\":{\"@id\":\"https:\\\/\\\/3solutions.pt\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/3solutions.pt\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-GB\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/3solutions.pt\\\/#organization\",\"name\":\"3Solutions Consultoria em Marketing e Gest\u00e3o\",\"url\":\"https:\\\/\\\/3solutions.pt\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-GB\",\"@id\":\"https:\\\/\\\/3solutions.pt\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/3solutions.pt\\\/wp-content\\\/uploads\\\/2026\\\/05\\\/cropped-Prancheta-1-copia-scaled-1.png\",\"contentUrl\":\"https:\\\/\\\/3solutions.pt\\\/wp-content\\\/uploads\\\/2026\\\/05\\\/cropped-Prancheta-1-copia-scaled-1.png\",\"width\":1505,\"height\":785,\"caption\":\"3Solutions Consultoria em Marketing e Gest\u00e3o\"},\"image\":{\"@id\":\"https:\\\/\\\/3solutions.pt\\\/#\\\/schema\\\/logo\\\/image\\\/\"},\"sameAs\":[\"https:\\\/\\\/www.facebook.com\\\/share\\\/17VAXrDnwy\\\/\",\"https:\\\/\\\/www.instagram.com\\\/wespeakretail?igsh=d2Y0a2RzY29nN2Fv\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"English Trainer - 3 Solutions | WE SPEAK RETAIL - Consultoria e Forma\u00e7\u00e3o retalho","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:\/\/3solutions.pt\/en\/english-trainer\/","og_locale":"en_GB","og_type":"article","og_title":"English Trainer - 3 Solutions | WE SPEAK RETAIL - Consultoria e Forma\u00e7\u00e3o retalho","og_url":"https:\/\/3solutions.pt\/en\/english-trainer\/","og_site_name":"3 Solutions | WE SPEAK RETAIL - Consultoria e Forma\u00e7\u00e3o retalho","article_publisher":"https:\/\/www.facebook.com\/share\/17VAXrDnwy\/","article_modified_time":"2026-09-19T09:28:32+00:00","twitter_card":"summary_large_image","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/3solutions.pt\/english-trainer\/","url":"https:\/\/3solutions.pt\/english-trainer\/","name":"English Trainer - 3 Solutions | WE SPEAK RETAIL - Consultoria e Forma\u00e7\u00e3o retalho","isPartOf":{"@id":"https:\/\/3solutions.pt\/#website"},"datePublished":"2026-09-19T09:19:26+00:00","dateModified":"2026-09-19T09:28:32+00:00","breadcrumb":{"@id":"https:\/\/3solutions.pt\/english-trainer\/#breadcrumb"},"inLanguage":"en-GB","potentialAction":[{"@type":"ReadAction","target":["https:\/\/3solutions.pt\/english-trainer\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/3solutions.pt\/english-trainer\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"In\u00edcio","item":"https:\/\/3solutions.pt\/"},{"@type":"ListItem","position":2,"name":"English Trainer"}]},{"@type":"WebSite","@id":"https:\/\/3solutions.pt\/#website","url":"https:\/\/3solutions.pt\/","name":"3Solutions Consultoria em Marketing e Gest\u00e3o","description":"Consultoria e Marketing Digital","publisher":{"@id":"https:\/\/3solutions.pt\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/3solutions.pt\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-GB"},{"@type":"Organization","@id":"https:\/\/3solutions.pt\/#organization","name":"3Solutions Consultoria em Marketing e Gest\u00e3o","url":"https:\/\/3solutions.pt\/","logo":{"@type":"ImageObject","inLanguage":"en-GB","@id":"https:\/\/3solutions.pt\/#\/schema\/logo\/image\/","url":"https:\/\/3solutions.pt\/wp-content\/uploads\/2026\/05\/cropped-Prancheta-1-copia-scaled-1.png","contentUrl":"https:\/\/3solutions.pt\/wp-content\/uploads\/2026\/05\/cropped-Prancheta-1-copia-scaled-1.png","width":1505,"height":785,"caption":"3Solutions Consultoria em Marketing e Gest\u00e3o"},"image":{"@id":"https:\/\/3solutions.pt\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/share\/17VAXrDnwy\/","https:\/\/www.instagram.com\/wespeakretail?igsh=d2Y0a2RzY29nN2Fv"]}]}},"_links":{"self":[{"href":"https:\/\/3solutions.pt\/en\/wp-json\/wp\/v2\/pages\/8670","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/3solutions.pt\/en\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/3solutions.pt\/en\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/3solutions.pt\/en\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/3solutions.pt\/en\/wp-json\/wp\/v2\/comments?post=8670"}],"version-history":[{"count":1,"href":"https:\/\/3solutions.pt\/en\/wp-json\/wp\/v2\/pages\/8670\/revisions"}],"predecessor-version":[{"id":8671,"href":"https:\/\/3solutions.pt\/en\/wp-json\/wp\/v2\/pages\/8670\/revisions\/8671"}],"wp:attachment":[{"href":"https:\/\/3solutions.pt\/en\/wp-json\/wp\/v2\/media?parent=8670"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}