Tata Letak
Berkas tata letak dibuat dari fungsi TypeScript. Anda dapat mendefinisikan sebanyak mungkin properti yang Anda suka selama Anda mengembalikan sebuah string. Kemudian, setiap halaman dalam proyek Anda dapat mengirimkan data relevan ke fungsi ini.
Menggunakan file TypeScript saja menjaga jumlah bahasa dalam proyek ini tetap rendah. Anda tidak perlu belajar bahasa templating yang lain lagi. Untuk memudahkan berinteraksi dengan HTML dalam TypeScript, saya telah menggunakan komentar `/* HTML */` untuk memungkinkan string multiline. Itu dikenali oleh 2 ekstensi yang saya anggap berguna.
- 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>
`;
}