レイアウト

レイアウトファイルはTypeScript関数から作成されます。 好きなだけプロパティを定義できますが、必ず文字列を返す必要があります。次に、プロジェクト内の各ページがこの関数に関連するデータを渡すようにします。

TypeScriptファイルを使用するだけで、このプロジェクトの言語の数を減らすことができます。これ以上のテンプレート言語を学ぶ必要はありません。 TypeScriptでHTMLとのインタラクションを簡単にするために、複数行の文字列を可能にする`/* HTML */`コメントを使用しました。これは、私が便利だと感じる2つの拡張機能によって認識されます。

  • 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>
              &copy; ${new Date().getFullYear()} ${baseUrl}
            </p>
            <div>
              ${[...props.languageOptions]
                .map((option) => {
                  return `<a href="${option.url}" >${option.name}</a>`;
                })
                .join(" | ")}
            </div>
          </div>
        </footer>
      </body>
    </html>
  `;
}