diff --git a/pages/_document.tsx b/pages/_document.tsx
new file mode 100644
index 0000000..b2fff8b
--- /dev/null
+++ b/pages/_document.tsx
@@ -0,0 +1,13 @@
+import { Html, Head, Main, NextScript } from "next/document";
+
+export default function Document() {
+ return (
+
+
+
+
+
+
+ );
+}
diff --git a/scripts/seo-build.test.mjs b/scripts/seo-build.test.mjs
index 67f95c7..99f4bc5 100644
--- a/scripts/seo-build.test.mjs
+++ b/scripts/seo-build.test.mjs
@@ -18,6 +18,7 @@ function links(html) {
for (const route of routes) {
test(`built metadata: ${route}`, () => {
const html = read(`.next/server/pages${route === "/" ? "/index" : route}.html`);
+ assert.match(html, /]*\slang="en"(?:\s|>)/, "English pages must declare their document language");
const tags = links(html);
const { canonicalUrl, chineseUrl } = getPageUrls(route);
assert.deepEqual(tags.filter((link) => link.rel === "canonical").map((link) => link.href), [canonicalUrl]);
diff --git a/scripts/seo.test.mjs b/scripts/seo.test.mjs
index d9b52b0..35e5a59 100644
--- a/scripts/seo.test.mjs
+++ b/scripts/seo.test.mjs
@@ -8,6 +8,10 @@ import { getPageUrls, hasChineseTranslation, normalizePath } from "../lib/seo.mj
const root = new URL("../", import.meta.url);
const read = (path) => readFileSync(new URL(path, root), "utf8");
+test("English document declares its language before client rendering", () => {
+ assert.match(read("pages/_document.tsx"), //);
+});
+
test("canonical paths exclude queries, fragments, and trailing slashes", () => {
for (const path of [undefined, "", "/", "/?utm_source=test#top"]) {
assert.equal(normalizePath(path), "/");