Bố cục
Các tệp bố cục được tạo ra từ các hàm TypeScript. Bạn có thể định nghĩa nhiều thuộc tính như bạn muốn miễn là bạn trả về một chuỗi. Sau đó, để mỗi trang trong dự án của bạn truyền vào dữ liệu liên quan đến hàm này.
Chỉ sử dụng các tệp TypeScript giúp giảm số lượng ngôn ngữ trong dự án này. Bạn không cần phải học thêm một ngôn ngữ mẫu khác. Để làm cho việc tương tác với HTML trong TypeScript trở nên dễ dàng hơn, tôi đã sử dụng nhận xét `/* HTML */` để cho phép các chuỗi nhiều dòng. Điều này được nhận dạng bởi 2 tiện ích mở rộng mà tôi thấy hữu ích.
- esbenp.prettier-vscode
- tobermory.es6-string-html
TypeScript - src/Layout.ts
import { LanguageOption } from "./GlobalSitesCore/LanguageOption";
import i18next from "./GlobalSitesCore/i18n";
import { languageSettings } from "./GlobalSitesCore/languages";
import { urlBuilder } from "./GlobalSitesCore/urlBuilder";
interface LayoutProps {
content: string;
lang: string;
description: string;
title: string;
languageOptions: LanguageOption[];
}
export function Layout(props: LayoutProps): string {
const baseUrl = "https://www.globalsites.ai";
var currentLanguageOption = props.languageOptions.find(
(option) => option.code == props.lang
);
var defaultLanguageOption = props.languageOptions.find(
(option) => option.code == languageSettings.defaultLanguage
);
return /* HTML */ `
<!DOCTYPE html>
<html lang="${props.lang}">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="${props.description}" />
<meta property="og:title" content="${props.title}" />
<meta property="og:description" content="${props.description}" />
<meta property="og:url" content="${currentLanguageOption?.url}" />
<meta property="og:type" content="website" />
<meta property="og:site_name" content="Global Sites" />
<meta property="og:locale" content="${props.lang}" />
<meta property="og:image" content="${baseUrl}/ogImage.jpg" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@benwmaddox" />
<meta name="twitter:creator" content="@benwmaddox" />
<meta name="twitter:title" content="${props.title}" />
<meta name="twitter:description" content="${props.description}" />
<meta name="twitter:image" content="${baseUrl}/ogImage.jpg" />
<link
rel="sitemap"
type="application/xml"
title="Sitemap"
href="/sitemap.xml"
/>
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/dark.min.css"
/>
<link rel="stylesheet" href="/styles.css" />
<link rel="icon" type="image/svg+xml" href="/icon.svg" />
<title>
${props.title ? props.title + " - " : ""}${i18next.t(`Global Sites`)}
</title>
<style>
body {
height: 100vh;
}
</style>
${props.languageOptions
.map((option) => {
return option.code == languageSettings.defaultLanguage
? `<link rel="alternate" href="${baseUrl}${option.url}" hreflang="${option.code}"/>
<link rel="alternate" href="${baseUrl}${option.url}" hreflang="x-default"/>`
: `<link rel="alternate" href="${baseUrl}${option.url}" hreflang="${option.code}"/>`;
})
.join("
")}
<script>
function switchLanguage() {
var selectedLanguageUrl =
document.getElementById("language-select").value;
window.location.href = selectedLanguageUrl;
}
</script>
</head>
<body>
<header>
<div class="flex">
<div class="logo">
<a
href="${props.lang === "en" ? "/" : `/${props.lang}/`}"
style="display:inline-block;"
>
<img src="/logo.svg" alt="Logo"
/></a>
</div>
<div class="language-switcher">
<select id="language-select" onchange="switchLanguage()">
${[...props.languageOptions]
.sort((a, b) =>
a.code == props.lang ? 1 : a.code.localeCompare(b.code)
)
.map((option) => {
return `<option value="${option.url}" ${
option.code === props.lang ? "selected" : ""
}>${option.name}</option>`;
})
.join("")}
</select>
</div>
<nav>
<a href="/${urlBuilder(undefined, undefined)}"
>${i18next.t("Home")}</a
>
|
<a href="/${urlBuilder(undefined, "documentation")}"
>${i18next.t("Documentation")}</a
>
|
<a href="/${urlBuilder(undefined, "contact-us")}"
>${i18next.t("Contact Us")}</a
>
|
<a href="/${urlBuilder(undefined, "privacy-policy")}"
>${i18next.t("Privacy Policy")}</a
>
|
<a href="/${urlBuilder(undefined, "faq")}">${i18next.t("FAQ")}</a>
</nav>
</div>
</header>
<main>
<div class="container">${props.content}</div>
</main>
<footer>
<div class="footer-content">
<p>
<a href="/${urlBuilder(undefined, "contact-us")}"
>${i18next.t("Contact Us")}</a
>
|
<a href="/${urlBuilder(undefined, "privacy-policy")}"
>${i18next.t("Privacy Policy")}</a
>
|
<a href="/${urlBuilder(undefined, "faq")}">${i18next.t("FAQ")}</a>
© ${new Date().getFullYear()} ${baseUrl}
</p>
<div>
${[...props.languageOptions]
.map((option) => {
return `<a href="${option.url}" >${option.name}</a>`;
})
.join(" | ")}
</div>
</div>
</footer>
</body>
</html>
`;
}