diff --git a/docs/configuration.md b/docs/configuration.md index 33d5a5744..b0c850623 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -34,6 +34,7 @@ This part of the configuration concerns anything that can affect the whole site. - `ignorePatterns`: a list of [glob]() patterns that Quartz should ignore and not search through when looking for files inside the `content` folder. See [[private pages]] for more details. - `defaultDateType`: whether to use created, modified, or published as the default date to display on pages and page listings. - `theme`: configure how the site looks. + - `cdnCaching`: Whether to use Google CDN to cache the fonts (generally will be faster). Disable this if you want Quartz to be self-contained. Default to `true` - `typography`: what fonts to use. Any font available on [Google Fonts](https://fonts.google.com/) works here. - `header`: Font to use for headers - `code`: Font for inline and block quotes. diff --git a/quartz.config.ts b/quartz.config.ts index 2f2f25071..9a1d0c6c0 100644 --- a/quartz.config.ts +++ b/quartz.config.ts @@ -14,6 +14,7 @@ const config: QuartzConfig = { ignorePatterns: ["private", "templates"], defaultDateType: "modified", theme: { + cdnCaching: true, typography: { header: "Bitter", // Schibsted Grotesk body: "Poppins", // Source Sans Pro, Poppins diff --git a/quartz/components/Head.tsx b/quartz/components/Head.tsx index b94909cc1..dae81c731 100644 --- a/quartz/components/Head.tsx +++ b/quartz/components/Head.tsx @@ -30,8 +30,12 @@ export default (() => { - - + {cfg.theme.cdnCaching && ( + <> + + + + )} {css.map((href) => ( ))} diff --git a/quartz/components/renderPage.tsx b/quartz/components/renderPage.tsx index 9666f0171..810fbf438 100644 --- a/quartz/components/renderPage.tsx +++ b/quartz/components/renderPage.tsx @@ -209,8 +209,7 @@ export function renderPage( ) - const lang = componentData.frontmatter?.lang ?? cfg.locale.split("-")[0] - + const lang = componentData.frontmatter?.lang ?? cfg.locale?.split("-")[0] ?? "en" const doc = ( diff --git a/quartz/i18n/index.ts b/quartz/i18n/index.ts index d24d5282a..7fd978e6d 100644 --- a/quartz/i18n/index.ts +++ b/quartz/i18n/index.ts @@ -6,6 +6,7 @@ import de from "./locales/de-DE" import nl from "./locales/nl-NL" import ro from "./locales/ro-RO" import es from "./locales/es-ES" +import uk from "./locales/uk-UA" export const TRANSLATIONS = { "en-US": en, @@ -14,8 +15,11 @@ export const TRANSLATIONS = { "de-DE": de, "nl-NL": nl, "ro-RO": ro, + "ro-MD": ro, "es-ES": es, + "uk-UA": uk, } as const -export const i18n = (locale: ValidLocale): Translation => TRANSLATIONS[locale ?? "en-US"] +export const defaultTranslation = "en-US" +export const i18n = (locale: ValidLocale): Translation => TRANSLATIONS[locale ?? defaultTranslation] export type ValidLocale = keyof typeof TRANSLATIONS diff --git a/quartz/i18n/locales/uk-UA.ts b/quartz/i18n/locales/uk-UA.ts new file mode 100644 index 000000000..bf664b6f8 --- /dev/null +++ b/quartz/i18n/locales/uk-UA.ts @@ -0,0 +1,65 @@ +import { Translation } from "./definition" + +export default { + propertyDefaults: { + title: "Без назви", + description: "Опис не надано", + }, + components: { + backlinks: { + title: "Зворотні посилання", + noBacklinksFound: "Зворотних посилань не знайдено", + }, + themeToggle: { + lightMode: "Світлий режим", + darkMode: "Темний режим", + }, + explorer: { + title: "Провідник", + }, + footer: { + createdWith: "Створено за допомогою", + }, + graph: { + title: "Вигляд графа", + }, + recentNotes: { + title: "Останні нотатки", + seeRemainingMore: ({ remaining }) => `Переглянути ще ${remaining} →`, + }, + transcludes: { + transcludeOf: ({ targetSlug }) => `Видобуто з ${targetSlug}`, + linkToOriginal: "Посилання на оригінал", + }, + search: { + title: "Пошук", + searchBarPlaceholder: "Шукати щось", + }, + tableOfContents: { + title: "Зміст", + }, + }, + pages: { + rss: { + recentNotes: "Останні нотатки", + lastFewNotes: ({ count }) => `Останні нотатки: ${count}`, + }, + error: { + title: "Не знайдено", + notFound: "Ця сторінка або приватна, або не існує.", + }, + folderContent: { + folder: "Папка", + itemsUnderFolder: ({ count }) => + count === 1 ? "У цій папці 1 елемент" : `Елементів у цій папці: ${count}.`, + }, + tagContent: { + tag: "Тег", + tagIndex: "Індекс тегу", + itemsUnderTag: ({ count }) => + count === 1 ? "1 елемент з цим тегом" : `Елементів з цим тегом: ${count}.`, + showingFirst: ({ count }) => `Показ перших ${count} тегів.`, + totalTags: ({ count }) => `Всього знайдено тегів: ${count}.`, + }, + }, +} as const satisfies Translation diff --git a/quartz/plugins/emitters/componentResources.ts b/quartz/plugins/emitters/componentResources.ts index 5eb9718a9..4033bdf7b 100644 --- a/quartz/plugins/emitters/componentResources.ts +++ b/quartz/plugins/emitters/componentResources.ts @@ -1,4 +1,4 @@ -import { FilePath, FullSlug } from "../../util/path" +import { FilePath, FullSlug, joinSegments } from "../../util/path" import { QuartzEmitterPlugin } from "../types" // @ts-ignore @@ -119,7 +119,7 @@ function addGlobalPageResources( } else if (cfg.analytics?.provider === "umami") { componentResources.afterDOMLoaded.push(` const umamiScript = document.createElement("script") - umamiScript.src = cfg.analytics.host ?? "https://analytics.umami.is/script.js" + umamiScript.src = ${cfg.analytics.host} ?? "https://analytics.umami.is/script.js" umamiScript.setAttribute("data-website-id", "${cfg.analytics.websiteId}") umamiScript.async = true @@ -172,27 +172,72 @@ export const ComponentResources: QuartzEmitterPlugin = (opts?: Partial< return [] }, async emit(ctx, _content, resources): Promise { + const promises: Promise[] = [] + const cfg = ctx.cfg.configuration // component specific scripts and styles const componentResources = getComponentResources(ctx) // important that this goes *after* component scripts // as the "nav" event gets triggered here and we should make sure // that everyone else had the chance to register a listener for it - if (fontOrigin === "googleFonts") { - resources.css.push(googleFontHref(ctx.cfg.configuration.theme)) - } else if (fontOrigin === "local") { + let googleFontsStyleSheet = "" + if (fontOrigin === "local") { // let the user do it themselves in css + } else if (fontOrigin === "googleFonts") { + if (cfg.theme.cdnCaching) { + resources.css.push(googleFontHref(cfg.theme)) + } else { + let match + + const fontSourceRegex = /url\((https:\/\/fonts.gstatic.com\/s\/[^)]+\.(woff2|ttf))\)/g + + googleFontsStyleSheet = await ( + await fetch(googleFontHref(ctx.cfg.configuration.theme)) + ).text() + + while ((match = fontSourceRegex.exec(googleFontsStyleSheet)) !== null) { + // match[0] is the `url(path)`, match[1] is the `path` + const url = match[1] + // the static name of this file. + const [filename, ext] = url.split("/").pop()!.split(".") + + googleFontsStyleSheet = googleFontsStyleSheet.replace(url, `/fonts/${filename}.ttf`) + + promises.push( + fetch(url) + .then((res) => { + if (!res.ok) { + throw new Error(`Failed to fetch font`) + } + return res.arrayBuffer() + }) + .then((buf) => + write({ + ctx, + slug: joinSegments("fonts", filename) as FullSlug, + ext: `.${ext}`, + content: Buffer.from(buf), + }), + ), + ) + } + } } addGlobalPageResources(ctx, resources, componentResources) - const stylesheet = joinStyles(ctx.cfg.configuration.theme, ...componentResources.css, styles) + const stylesheet = joinStyles( + ctx.cfg.configuration.theme, + ...componentResources.css, + googleFontsStyleSheet, + styles, + ) const [prescript, postscript] = await Promise.all([ joinScripts(componentResources.beforeDOMLoaded), joinScripts(componentResources.afterDOMLoaded), ]) - const fps = await Promise.all([ + promises.push( write({ ctx, slug: "index" as FullSlug, @@ -223,8 +268,9 @@ export const ComponentResources: QuartzEmitterPlugin = (opts?: Partial< ext: ".js", content: postscript, }), - ]) - return fps + ) + + return await Promise.all(promises) }, } } diff --git a/quartz/plugins/emitters/helpers.ts b/quartz/plugins/emitters/helpers.ts index ef1d1c35c..523151c2c 100644 --- a/quartz/plugins/emitters/helpers.ts +++ b/quartz/plugins/emitters/helpers.ts @@ -7,7 +7,7 @@ type WriteOptions = { ctx: BuildCtx slug: FullSlug ext: `.${string}` | "" - content: string + content: string | Buffer } export const write = async ({ ctx, slug, ext, content }: WriteOptions): Promise => { diff --git a/quartz/util/theme.ts b/quartz/util/theme.ts index 47951c4f4..bd0da5fb0 100644 --- a/quartz/util/theme.ts +++ b/quartz/util/theme.ts @@ -15,6 +15,7 @@ export interface Theme { body: string code: string } + cdnCaching: boolean colors: { lightMode: ColorScheme darkMode: ColorScheme