@escape-game-over/atlas 0.1.49 → 0.1.51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
package/src/astro/Document.astro
CHANGED
|
@@ -13,6 +13,9 @@
|
|
|
13
13
|
* A site with a lead form passes `rememberCampaign`, so the `utm_*` tags a
|
|
14
14
|
* visit landed with survive to the page the form is on; see `attribution.ts`.
|
|
15
15
|
*
|
|
16
|
+
* It sends a reader arriving from elsewhere to the page in the language they
|
|
17
|
+
* last chose; see `RememberLocale.astro`. `rememberLocale={false}` turns it off.
|
|
18
|
+
*
|
|
16
19
|
* `fonts={site.fonts}` writes the `@font-face` rules and preloads for the
|
|
17
20
|
* faces `siteFonts(site)` registered in the Astro config.
|
|
18
21
|
*
|
|
@@ -29,6 +32,7 @@ import type { Fonts } from "../fonts.ts";
|
|
|
29
32
|
import type { PageMeta } from "../site/index.ts";
|
|
30
33
|
import MetaTags from "./MetaTags.astro";
|
|
31
34
|
import RememberCampaign from "./RememberCampaign.astro";
|
|
35
|
+
import RememberLocale from "./RememberLocale.astro";
|
|
32
36
|
import ScrollRestore from "./ScrollRestore.astro";
|
|
33
37
|
|
|
34
38
|
interface Props {
|
|
@@ -41,6 +45,8 @@ interface Props {
|
|
|
41
45
|
readonly restoreScroll?: boolean;
|
|
42
46
|
/** Off unless stated: a site with no lead form has nothing to keep them for. */
|
|
43
47
|
readonly rememberCampaign?: boolean;
|
|
48
|
+
/** Defaults to `true`. */
|
|
49
|
+
readonly rememberLocale?: boolean;
|
|
44
50
|
}
|
|
45
51
|
|
|
46
52
|
const {
|
|
@@ -50,6 +56,7 @@ const {
|
|
|
50
56
|
body,
|
|
51
57
|
restoreScroll = true,
|
|
52
58
|
rememberCampaign = false,
|
|
59
|
+
rememberLocale = true,
|
|
53
60
|
} = Astro.props;
|
|
54
61
|
---
|
|
55
62
|
|
|
@@ -62,6 +69,7 @@ const {
|
|
|
62
69
|
<head>
|
|
63
70
|
<slot name="head-start" />
|
|
64
71
|
<MetaTags tags={meta.tags} />
|
|
72
|
+
{rememberLocale && <RememberLocale />}
|
|
65
73
|
{Object.entries(fonts).map(([key, font]) => (
|
|
66
74
|
<Font cssVariable={`--${key}`} preload={font.preload === true} />
|
|
67
75
|
))}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* Opens a page in the language its reader chose, however they reached it.
|
|
4
|
+
*
|
|
5
|
+
* The choice is the locale of the last page reached from inside the site, so
|
|
6
|
+
* using the language switcher makes it and a shared link does not. Arriving
|
|
7
|
+
* from anywhere else, a reader is sent to this page in their chosen locale when
|
|
8
|
+
* the head lists an alternate for it. Crawlers keep no storage, so they are
|
|
9
|
+
* never sent anywhere.
|
|
10
|
+
*
|
|
11
|
+
* Rendered by `Document` after the head tags, whose alternates it reads.
|
|
12
|
+
* Inline and blocking, so the wrong language is never painted.
|
|
13
|
+
*/
|
|
14
|
+
import { inlineScript } from "./inline-script.ts";
|
|
15
|
+
import source from "./remember-locale.ts?raw";
|
|
16
|
+
|
|
17
|
+
const code = await inlineScript(source);
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
<script
|
|
21
|
+
is:inline
|
|
22
|
+
set:html={code}
|
|
23
|
+
/>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// The body of `RememberLocale.astro`'s inline script. A classic script: no
|
|
2
|
+
// imports or exports, and the block keeps its names out of the global scope.
|
|
3
|
+
{
|
|
4
|
+
const KEY = "atlas-locale";
|
|
5
|
+
const current = document.documentElement.lang;
|
|
6
|
+
|
|
7
|
+
const remember = () => {
|
|
8
|
+
// Came from this site: the visitor is browsing, or just switched
|
|
9
|
+
// language. Either way, this locale is their choice.
|
|
10
|
+
if (document.referrer.startsWith(`${location.origin}/`)) {
|
|
11
|
+
localStorage.setItem(KEY, current);
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const chosen = localStorage.getItem(KEY);
|
|
16
|
+
if (chosen === null || chosen === current) return;
|
|
17
|
+
|
|
18
|
+
// No alternate means this page is not built in that locale.
|
|
19
|
+
const alternate = document.querySelector<HTMLLinkElement>(
|
|
20
|
+
`link[rel="alternate"][hreflang="${CSS.escape(chosen)}"]`
|
|
21
|
+
);
|
|
22
|
+
if (alternate === null) return;
|
|
23
|
+
|
|
24
|
+
// Path only: alternates carry the production origin, not a preview's.
|
|
25
|
+
location.replace(
|
|
26
|
+
new URL(alternate.href).pathname + location.search + location.hash
|
|
27
|
+
);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
remember();
|
|
32
|
+
} catch {
|
|
33
|
+
// Storage is blocked in some private windows.
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -36,6 +36,12 @@
|
|
|
36
36
|
|
|
37
37
|
restore(history.state);
|
|
38
38
|
|
|
39
|
+
// A page back from the back/forward cache runs no script, and with
|
|
40
|
+
// `manual` Safari shows it at the top.
|
|
41
|
+
addEventListener("pageshow", (event) => {
|
|
42
|
+
if (event.persisted) restore(history.state);
|
|
43
|
+
});
|
|
44
|
+
|
|
39
45
|
// Filters push entries on the same page; `manual` turned off restoring
|
|
40
46
|
// those too. A frame later, so the filtered list has re-rendered first.
|
|
41
47
|
addEventListener("popstate", (event) => {
|