PageSourceSearch

https://grammarmama.com/learn/

html grammarmama.com collected 2026-09-24 14:44:07 UTC 17,049 bytes, 478 lines download raw bytes

1
2
3
4<!DOCTYPE html>
5<html lang="en" dir="ltr">
6<head>
7    <meta charset="UTF-8">
8    <meta name="viewport" content="width=device-width, initial-scale=1.0">
9    <title>Grammar Learning Paths A1–B2 | GrammarMama</title>
10    <meta name="description" content="Free, structured grammar learning paths by CEFR level. Browse every grammar topic you need from A1 to B2 — clear explanations and examples, no sign-up required.">
11    <meta property="og:type" content="website">
12    <meta property="og:url" content="https://grammarmama.com/learn/">
13    <meta property="og:title" content="Grammar Learning Paths A1–B2 | GrammarMama">
14    <meta property="og:description" content="Free, structured grammar learning paths by CEFR level. Browse every grammar topic you need from A1 to B2 — clear explanations and examples, no sign-up required.">
15    <link rel="canonical" href="https://grammarmama.com/learn/">
16    
17    <link rel="alternate" hreflang="en" href="https://grammarmama.com/learn/">
18    
19    <link rel="alternate" hreflang="de" href="https://grammarmama.com/de/learn/">
20    
21    <link rel="alternate" hreflang="ru" href="https://grammarmama.com/ru/learn/">
22    
23    <link rel="alternate" hreflang="fr" href="https://grammarmama.com/fr/learn/">
24    
25    <link rel="alternate" hreflang="es" href="https://grammarmama.com/es/learn/">
26    
27    <link rel="alternate" hreflang="it" href="https://grammarmama.com/it/learn/">
28    
29    <link rel="alternate" hreflang="pl" href="https://grammarmama.com/pl/learn/">
30    
31    <link rel="alternate" hreflang="zh" href="https://grammarmama.com/zh/learn/">
32    
33    <link rel="alternate" hreflang="pt" href="https://grammarmama.com/pt/learn/">
34    
35    <link rel="alternate" hreflang="ja" href="https://grammarmama.com/ja/learn/">
36    
37    <link rel="alternate" hreflang="x-default" href="https://grammarmama.com/learn/">
38    <link rel="icon" href="/favicon.ico">
39    <link rel="stylesheet" href="/styles.css">
40</head>
41<body>
42    
43
44<header class="site-header">
45    <nav class="site-nav">
46        <a href="/" class="site-nav-link">Check</a>
47        <a href="/learn/" class="site-nav-link">Learn</a>
48        <a href="/practice/" class="site-nav-link">Practice</a>
49        <a href="/vocabulary/" class="site-nav-link">Vocabulary</a>
50        <a href="/profile/" class="site-nav-link">Profile</a>
51    </nav>
52</header>
53
54    <main class="content-page">
55        
56        <div class="kb-layout">
57        
58
59<aside class="kb-aside">
60    <img src="/images/mascot.webp" alt="GrammarMama mascot" class="mascot-img" width="140" height="140">
61    <nav class="lang-sidebar" aria-label="Your native language:">
62        <div class="lang-sidebar-label">Your native language:</div>
63        <ul class="lang-pill-list">
64            
65            
66            
67            
68            <li>
69                
70                <span class="lang-pill active">English</span>
71                
72            </li>
73            
74            
75            
76            
77            <li>
78                
79                <a class="lang-pill" href="/de/learn/" hreflang="de">Deutsch</a>
80                
81            </li>
82            
83            
84            
85            
86            <li>
87                
88                <a class="lang-pill" href="/ru/learn/" hreflang="ru">Русский</a>
89                
90            </li>
91            
92            
93            
94            
95            <li>
96                
97                <a class="lang-pill" href="/fr/learn/" hreflang="fr">Français</a>
98                
99            </li>
100            
101            
102            
103            
104            <li>
105                
106                <a class="lang-pill" href="/es/learn/" hreflang="es">Español</a>
107                
108            </li>
109            
110            
111            
112            
113            <li>
114                
115                <a class="lang-pill" href="/it/learn/" hreflang="it">Italiano</a>
116                
117            </li>
118            
119            
120            
121            
122            <li>
123                
124                <a class="lang-pill" href="/pl/learn/" hreflang="pl">Polski</a>
125                
126            </li>
127            
128            
129            
130            
131            <li>
132                
133                <a class="lang-pill" href="/zh/learn/" hreflang="zh">中文</a>
134                
135            </li>
136            
137            
138            
139            
140            <li>
141                
142                <a class="lang-pill" href="/pt/learn/" hreflang="pt">Português</a>
143                
144            </li>
145            
146            
147            
148            
149            <li>
150                
151                <a class="lang-pill" href="/ja/learn/" hreflang="ja">日本語</a>
152                
153            </li>
154            
155        </ul>
156    </nav>
157</aside>
158
159        <div class="content-container">
160            <div class="blog-post-nav">
161                <a href="/" class="blog-post-cta">
161← Home</a>
162            </div>
163            <h1>Grammar Learning Paths</h1>
164            <p class="post-date">Every grammar topic you need, organised by language and level — from your first words to confident, complex sentences.</p>
165
166            <h2>Which language are you learning?</h2>
167            <div class="kb-lang-grid">
168                
169                
170                
171                <a class="kb-lang-card" href="/learn/english/">
172                    <span class="kb-lang-name">English</span>
173                    <span class="kb-lang-levels">A1–C2</span>
174                </a>
175                
176                
177                
178                
179                <a class="kb-lang-card" href="/learn/german/">
180                    <span class="kb-lang-name">German</span>
181                    <span class="kb-lang-levels">A1–C2</span>
182                </a>
183                
184                
185                
186                
187                <a class="kb-lang-card" href="/learn/russian/">
188                    <span class="kb-lang-name">Russian</span>
189                    <span class="kb-lang-levels">A1–C2</span>
190                </a>
191                
192                
193                
194                
195                <a class="kb-lang-card" href="/learn/french/">
196                    <span class="kb-lang-name">French</span>
197                    <span class="kb-lang-levels">A1–C2</span>
198                </a>
199                
200                
201                
202                
203                <a class="kb-lang-card" href="/learn/spanish/">
204                    <span class="kb-lang-name">Spanish</span>
205                    <span class="kb-lang-levels">A1–C2</span>
206                </a>
207                
208                
209                
210                
211                <a class="kb-lang-card" href="/learn/italian/">
212                    <span class="kb-lang-name">Italiano</span>
213                    <span class="kb-lang-levels">A1–C2</span>
214                </a>
215                
216                
217                
218                
219                <a class="kb-lang-card" href="/learn/norwegian/">
220                    <span class="kb-lang-name">Norwegian</span>
221                    <span class="kb-lang-levels">A1–C2</span>
222                </a>
223                
224                
225                
226                
227                <span class="kb-lang-card kb-lang-card--soon">
228                    <span class="kb-lang-name">Polski</span>
229                    <span class="kb-lang-levels">Coming soon</span>
230                </span>
231                
232                
233                
234                
235                <span class="kb-lang-card kb-lang-card--soon">
236                    <span class="kb-lang-name">中文</span>
237                    <span class="kb-lang-levels">Coming soon</span>
238                </span>
239                
240                
241                
242                
243                <span class="kb-lang-card kb-lang-card--soon">
244                    <span class="kb-lang-name">日本語</span>
245                    <span class="kb-lang-levels">Coming soon</span>
246                </span>
247                
248                
249                
250                
251                <span class="kb-lang-card kb-lang-card--soon">
252                    <span class="kb-lang-name">Português</span>
253                    <span class="kb-lang-levels">Coming soon</span>
254                </span>
255                
256                
257            </div>
258        </div>
259        </div>
260    </main>
261
262    
263
264
265
266<script>
267(function () {
268  var REDIRECTABLE = ['/', '/practice/', '/vocabulary/', '/profile/'];
269  if (REDIRECTABLE.indexOf(window.location.pathname) === -1) return;
270  // Same key/TTL as profile.njk's _TELEGRAM_INTENT_KEY — duplicated here (not shared) because
271  // this script and profile.njk's are two separate template scopes.
272  function _hasTelegramConnectIntent() {
273    try {
274      var t = parseInt(localStorage.getItem('gm_telegram_connect_intent'), 10);
275      return !!t && (Date.now() - t) < 30 * 60 * 1000;
276    } catch (e) { return false; }
277  }
278  window.addEventListener('load', function () {
279    if (!window.Clerk) return;
280    window.Clerk.load().then(function () {
281      if (!window.Clerk.user || !window.Clerk.session) return;
282      window.Clerk.session.getToken({ template: 'grammar-mama-template' })
283        .then(function (token) {
284          if (!token) return Promise.resolve(null);
285          return fetch('/api/user', { headers: { Authorization: 'Bearer ' + token } });
286        })
287        .then(function (resp) {
288          if (!resp || !resp.ok) return Promise.resolve(null);
289          return resp.json();
290        })
291        .then(function (user) {
292          if (!user) return;
293          // Clerk's post-login redirect does not reliably land back on /profile (it can land
294          // on this page's own path instead, e.g. home) — if there's a pending Telegram-connect
295          // intent (see profile.njk), finish the trip there instead of the normal locale redirect.
296          // profile.njk itself already handles the rest once it's the page loaded.
297          if (_hasTelegramConnectIntent() && !user.telegramLinked && window.location.pathname.indexOf('/profile/') === -1) {
298            var langPrefix = user.nativeLanguage && user.nativeLanguage !== 'en' ? '/' + user.nativeLanguage : '';
299            window.location.replace(langPrefix + '/profile/?connectTelegram=1');
300            return;
301          }
302          if (!user.nativeLanguage || user.nativeLanguage === 'en') return;
303          window.location.replace('/' + user.nativeLanguage + window.location.pathname);
304        })
305        .catch(function () {});
306    });
307  });
308})();
309</script>
309
310
311    <footer>
312        <a href="/">Home</a>
313        <a href="/learn/">Learn</a>
314        <a href="/blog.html">Blog</a>
315        <a href="/pricing.html">Pricing</a>
316        <a href="/terms.html">T&amp;C</a>
317        <a href="/privacy.html">Privacy</a>
318        <a href="/refund.html">Refund Policy</a>
319        <a href="/#contact">Contact</a>
320        <a href="/#feedback">Feedback</a>
321        <a href="#" id="gmCookieSettingsLink">Cookie settings</a>
322    </footer>
323
324    
325    <div class="cookie-banner" id="gmCookieBanner" role="dialog" aria-live="polite" aria-label="Cookie settings" hidden>
326      <div class="cookie-banner__box">
327        <p>We use optional analytics cookies to see how visitors use GrammarMama, so we can keep improving it. Essential cookies (like keeping you signed in) are always on. <a href="/privacy.html">Privacy policy</a></p>
328        <div class="cookie-banner__actions">
329          <button type="button" class="cookie-banner__reject" id="gmCookieReject">Decline</button>
330          <button type="button" class="cookie-banner__accept" id="gmCookieAccept">Accept</button>
331        </div>
332      </div>
333    </div>
334    
334<script>
335    (function () {
336      var KEY = 'gm_cookie_consent';
337      var banner = document.getElementById('gmCookieBanner');
338      var acceptBtn = document.getElementById('gmCookieAccept');
339      var rejectBtn = document.getElementById('gmCookieReject');
340      var settingsLink = document.getElementById('gmCookieSettingsLink');
341
342      function getConsent() {
343        try { return localStorage.getItem(KEY); } catch (e) { return null; }
344      }
345      function setConsent(v) {
346        try { localStorage.setItem(KEY, v); } catch (e) {}
347      }
348
349      window.GM_CONSENT = { granted: getConsent() === 'accepted', onChange: null };
350
351      function show() { if (banner) banner.hidden = false; }
352      function hide() { if (banner) banner.hidden = true; }
353
354      function decide(value) {
355        setConsent(value);
356        window.GM_CONSENT.granted = (value === 'accepted');
357        hide();
358        if (typeof window.GM_CONSENT.onChange === 'function') {
359          window.GM_CONSENT.onChange(window.GM_CONSENT.granted);
360        }
361      }
362
363      if (acceptBtn) acceptBtn.addEventListener('click', function () { decide('accepted'); });
364      if (rejectBtn) rejectBtn.addEventListener('click', function () { decide('rejected'); });
365      if (settingsLink) {
366        settingsLink.addEventListener('click', function (e) { e.preventDefault(); show(); });
367      }
368
369      if (getConsent() === null) show();
370
371      // Funnel event helper. Fans out one call to both GA4 (gtag) and Clarity.
372      // No-op until an analytics script is actually loaded (i.e. consent granted),
373      // so callers everywhere can fire events unconditionally. props become GA4
374      // event params and Clarity custom tags.
375      window.GM_TRACK = function (name, props) {
376        props = props || {};
377        try {
378          if (typeof window.gtag === 'function') window.gtag('event', name, props);
379          if (typeof window.clarity === 'function') {
380            window.clarity('event', name);
381            for (var k in props) {
382              if (Object.prototype.hasOwnProperty.call(props, k) && props[k] != null) {
383                window.clarity('set', k, String(props[k]));
384              }
385            }
386          }
387        } catch (e) {}
388      };
389
390      // Signup-completion detector. A signup CTA sets localStorage 'gm_signup_intent'
391      // before Clerk redirects away; on the page it lands back on, wait for Clerk to
392      // report a signed-in user, then fire one 'sign_up' event and clear the flag.
393      (function () {
394        var FLAG = 'gm_signup_intent';
395        var pending = null;
396        try { pending = localStorage.getItem(FLAG); } catch (e) {}
397        if (!pending) return;
398        var tries = 0;
399        var iv = setInterval(function () {
400          tries += 1;
401          if (window.Clerk && window.Clerk.user) {
402            clearInterval(iv);
403            try { localStorage.removeItem(FLAG); } catch (e) {}
404            if (window.GM_TRACK) window.GM_TRACK('sign_up', { source: pending });
405          } else if (tries > 40) {
406            clearInterval(iv);
407          }
408        }, 500);
409      })();
410    })();
411    </script>
411
412
413    
414    
415    
415<script>
416    (function () {
417      var GA_ID = "G-KMYM08QRY7";
418      var CLARITY_ID = "yditf2dtk5";
419      var _loaded = false;
420
421      function loadGa() {
422        if (!GA_ID) return;
423        var s = document.createElement('script');
424        s.async = true;
425        s.src = 'https://www.googletagmanager.com/gtag/js?id=' + GA_ID;
426        document.head.appendChild(s);
427        window.dataLayer = window.dataLayer || [];
428        window.gtag = function () { window.dataLayer.push(arguments); };
429        window.gtag('js', new Date());
430        window.gtag('config', GA_ID);
431      }
432
433      function loadClarity() {
434        if (!CLARITY_ID) return;
435        (function (c, l, a, r, i, t, y) {
436          c[a] = c[a] || function () { (c[a].q = c[a].q || []).push(arguments); };
437          t = l.createElement(r); t.async = 1; t.src = 'https://www.clarity.ms/tag/' + i;
438          y = l.getElementsByTagName(r)[0]; y.parentNode.insertBefore(t, y);
439        })(window, document, 'clarity', 'script', CLARITY_ID);
440      }
441
442      function setContext() {
443        var loc = document.documentElement.lang || 'en';
444        var pt = window.GM_PAGE_TYPE || '';
445        var authState = (window.Clerk && window.Clerk.user) ? 'signedIn' : 'anonymous';
446        try {
447          if (typeof window.gtag === 'function') {
448            window.gtag('set', 'user_properties', { locale: loc, page_type: pt, auth_state: authState });
449          }
450          if (typeof window.clarity === 'function') {
451            window.clarity('set', 'locale', loc);
452            if (pt) window.clarity('set', 'pageType', pt);
453            window.clarity('set', 'authState', authState);
454          }
455        } catch (e) {}
456      }
457
458      function loadAll() {
459        if (_loaded) return;
460        _loaded = true;
461        loadGa();
462        loadClarity();
463        setContext();
464        // GM_PAGE_TYPE / Clerk.user are often set by scripts that run after this one.
465        window.setTimeout(setContext, 1500);
466      }
467
468      if (window.GM_CONSENT && window.GM_CONSENT.granted) loadAll();
469      if (window.GM_CONSENT) {
470        window.GM_CONSENT.onChange = function (granted) { if (granted) loadAll(); };
471      }
472    })();
473    </script>
473
474    
475
476</body>
477</html>
478

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.