import { renderToString } from "react-dom/server"; import { HydrationData } from "../libs/types/hydration"; import { DEFAULT_LOCALE } from "../libs/constants/index"; import { ALWAYS_ALLOW_ROBOTS, BASE_URL, WEBFONT_URLS, GTAG_PATH, ASSET_MANIFEST, } from "./include"; import { getMetaDescription } from "./meta-description"; import appleIcon from "../client/public/apple-touch-icon.png?public"; import manifest from "../client/public/manifest.json?public"; import ogImage from "../client/public/mdn-social-share.png?public"; import printCSS from "./print.css?inline"; import themeJS from "./theme.js?inline"; // When there are multiple options for a given language, this gives the // preferred locale for that language (language => preferred locale). const PREFERRED_LOCALE = { pt: "pt-PT", zh: "zh-CN", }; // We should use the language tag (e.g. "zh-Hans") instead of the locale. // This is a map of locale => language tag. // See https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry const LANGUAGE_TAGS = Object.freeze({ "zh-CN": "zh-Hans", "zh-TW": "zh-Hant", }); function getHrefLang(locale: string, allLocales: Array) { // In most cases, just return the language code, removing the country // code if present (so, for example, 'en-US' becomes 'en'). const hreflang = locale.split("-")[0]; // Suppose the locale is one that is ambiguous, we need to fall back on a // a preferred one. For example, if the document is available in 'zh-CN' and // in 'zh-TW', we need to output something like this: // // // // But other bother if both ambigious locale-to-hreflang are present. const preferred = PREFERRED_LOCALE[hreflang]; if (preferred) { // e.g. `preferred===zh-CN` if hreflang was `zh` if (locale !== preferred) { // e.g. `locale===zh-TW` if (allLocales.includes(preferred)) { // If the more preferred one was there, use the locale + region format. return LANGUAGE_TAGS[locale] ?? locale; } } } return hreflang; } export default function render( renderApp, url: string, { doc = null, pageNotFound = false, hyData = null, pageTitle = null, pageDescription = "", possibleLocales = null, locale = null, noIndexing = false, onlyFollow = false, image = null, blogMeta = null, }: HydrationData = { url } ) { if (!locale) { locale = (doc && doc.locale) || DEFAULT_LOCALE; } const canonicalURL = `${BASE_URL}${url}`; let realPageTitle = pageTitle; let metaDescription = pageDescription; const hydrationData: HydrationData = { url }; const translations: JSX.Element[] = []; if (blogMeta) { hydrationData.blogMeta = blogMeta; } if (pageNotFound) { realPageTitle = `🤷🏽‍♀️ Page not found | ${realPageTitle || "MDN Web Docs"}`; hydrationData.pageNotFound = true; } else if (hyData) { hydrationData.hyData = hyData; } else if (doc) { // Use the doc's title instead realPageTitle = doc.pageTitle; metaDescription = getMetaDescription(doc); if (doc.summary) { pageDescription = doc.summary; } hydrationData.doc = doc; if (doc.other_translations) { // Note, we also always include "self" as a locale. That's why we concat // this doc's locale plus doc.other_translations. const thisLocale = { locale: doc.locale, title: doc.title, url: doc.mdn_url, }; const allTranslations = [...doc.other_translations, thisLocale]; const allLocales = allTranslations.map((t) => t.locale); for (const translation of allTranslations) { const translationURL = doc.mdn_url.replace( `/${doc.locale}/`, () => `/${translation.locale}/` ); // The locale used in `` needs to be the ISO-639-1 // code. For example, it's "en", not "en-US". And it's "sv" not "sv-SE". // See https://developers.google.com/search/docs/specialty/international/localized-versions#language-codes translations.push( ); } } } if (possibleLocales) { hydrationData.possibleLocales = possibleLocales; } // Open Graph protocol expects `language_TERRITORY` format. const ogLocale = locale.replace("-", "_"); const robotsContent = !ALWAYS_ALLOW_ROBOTS || (doc && doc.noIndexing) || noIndexing ? "noindex, nofollow" : onlyFollow ? "noindex, follow" : ""; return ( "" + renderToString( {realPageTitle || "MDN Web Docs"} {translations} {WEBFONT_URLS.map((url) => ( ))} {robotsContent && } {!pageNotFound && }