From 0a3379a8530f365e2bd85e8ea20a1dfc8126c39c Mon Sep 17 00:00:00 2001 From: Jacky Zhao Date: Fri, 2 Feb 2024 10:10:25 -0800 Subject: [PATCH 01/86] fix(search): null checks and focus fixes --- quartz/components/scripts/search.inline.ts | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/quartz/components/scripts/search.inline.ts b/quartz/components/scripts/search.inline.ts index ec55f96b5..1ecf62fa4 100644 --- a/quartz/components/scripts/search.inline.ts +++ b/quartz/components/scripts/search.inline.ts @@ -224,12 +224,11 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => { if (currentHover) { currentHover.classList.remove("focus") - currentHover.blur() } // If search is active, then we will render the first result and display accordingly if (!container?.classList.contains("active")) return - else if (e.key === "Enter") { + if (e.key === "Enter") { // If result has focus, navigate to that one, otherwise pick first result if (results?.contains(document.activeElement)) { const active = document.activeElement as HTMLInputElement @@ -252,7 +251,7 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => { const prevResult = currentResult?.previousElementSibling as HTMLInputElement | null currentResult?.classList.remove("focus") prevResult?.focus() - currentHover = prevResult + if (prevResult) currentHover = prevResult await displayPreview(prevResult) } } else if (e.key === "ArrowDown" || e.key === "Tab") { @@ -266,18 +265,8 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => { const secondResult = firstResult?.nextElementSibling as HTMLInputElement | null firstResult?.classList.remove("focus") secondResult?.focus() - currentHover = secondResult + if (secondResult) currentHover = secondResult await displayPreview(secondResult) - } else { - // If an element in results-container already has focus, focus next one - const active = currentHover - ? currentHover - : (document.activeElement as HTMLInputElement | null) - active?.classList.remove("focus") - const nextResult = active?.nextElementSibling as HTMLInputElement | null - nextResult?.focus() - currentHover = nextResult - await displayPreview(nextResult) } } } From 260498a96b90ed44c120f4234238b6813272ec47 Mon Sep 17 00:00:00 2001 From: Jacky Zhao Date: Fri, 2 Feb 2024 10:23:24 -0800 Subject: [PATCH 02/86] fix(style): prevent callout icon from shrinking on long titles (closes #792) --- quartz/styles/callouts.scss | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/quartz/styles/callouts.scss b/quartz/styles/callouts.scss index d4f7069a0..7fa52c506 100644 --- a/quartz/styles/callouts.scss +++ b/quartz/styles/callouts.scss @@ -120,7 +120,7 @@ .callout-title { display: flex; - align-items: center; + align-items: flex-start; gap: 5px; padding: 1rem 0; color: var(--color); @@ -131,8 +131,6 @@ transition: transform 0.15s ease; opacity: 0.8; cursor: pointer; - width: var(--icon-size); - height: var(--icon-size); --callout-icon: var(--callout-icon-fold); } @@ -145,6 +143,7 @@ & .fold-callout-icon { width: var(--icon-size); height: var(--icon-size); + flex: 0 0 var(--icon-size); // icon support background-size: var(--icon-size) var(--icon-size); @@ -154,6 +153,7 @@ mask-size: var(--icon-size) var(--icon-size); mask-position: center; mask-repeat: no-repeat; + padding: 0.2rem 0; } .callout-title-inner { From a2c46f442d26fe33c9b4cc00ddb9b805363edd20 Mon Sep 17 00:00:00 2001 From: Jacky Zhao Date: Fri, 2 Feb 2024 10:44:19 -0800 Subject: [PATCH 03/86] fix(search): dont rely on mouse to manipulate focus --- quartz/components/scripts/search.inline.ts | 36 +++------------------- quartz/components/styles/search.scss | 1 + 2 files changed, 6 insertions(+), 31 deletions(-) diff --git a/quartz/components/scripts/search.inline.ts b/quartz/components/scripts/search.inline.ts index 1ecf62fa4..b9b55bec6 100644 --- a/quartz/components/scripts/search.inline.ts +++ b/quartz/components/scripts/search.inline.ts @@ -310,38 +310,12 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => { itemTile.href = resolveUrl(slug).toString() itemTile.innerHTML = `

${title}

${htmlTags}

${content}

` - async function onMouseEnter(ev: MouseEvent) { - if (!ev.target) return - currentHover?.classList.remove("focus") - currentHover?.blur() - const target = ev.target as HTMLInputElement - currentHover = target - currentHover.classList.add("focus") - await displayPreview(target) + const handler = (event: MouseEvent) => { + if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) return + hideSearch() } - - async function onMouseLeave(ev: MouseEvent) { - if (!ev.target) return - const target = ev.target as HTMLElement - target.classList.remove("focus") - } - - const events = [ - ["mouseenter", onMouseEnter], - ["mouseleave", onMouseLeave], - [ - "click", - (event: MouseEvent) => { - if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) return - hideSearch() - }, - ], - ] as const - - events.forEach(([event, handler]) => { - itemTile.addEventListener(event, handler) - window.addCleanup(() => itemTile.removeEventListener(event, handler)) - }) + itemTile.addEventListener("click", handler) + window.addCleanup(() => itemTile.removeEventListener("click", handler)) return itemTile } diff --git a/quartz/components/styles/search.scss b/quartz/components/styles/search.scss index 24a72848f..9cc85dfcc 100644 --- a/quartz/components/styles/search.scss +++ b/quartz/components/styles/search.scss @@ -179,6 +179,7 @@ outline: none; font-weight: inherit; + &:hover, &:focus, &.focus { background: var(--lightgray); From 9ff1fdd280f4b4c554f1bddfa51689fcb1576558 Mon Sep 17 00:00:00 2001 From: Jacky Zhao Date: Fri, 2 Feb 2024 10:52:51 -0800 Subject: [PATCH 04/86] fix(search): oops restore ability to preview on hover lol --- quartz/components/scripts/search.inline.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/quartz/components/scripts/search.inline.ts b/quartz/components/scripts/search.inline.ts index b9b55bec6..d707cfd66 100644 --- a/quartz/components/scripts/search.inline.ts +++ b/quartz/components/scripts/search.inline.ts @@ -314,6 +314,15 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => { if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) return hideSearch() } + + async function onMouseEnter(ev: MouseEvent) { + if (!ev.target) return + const target = ev.target as HTMLInputElement + await displayPreview(target) + } + + itemTile.addEventListener("mouseenter", onMouseEnter) + window.addCleanup(() => itemTile.removeEventListener("mouseenter", onMouseEnter)) itemTile.addEventListener("click", handler) window.addCleanup(() => itemTile.removeEventListener("click", handler)) From 742b8832569e338848476fa415b858ce57b99e1b Mon Sep 17 00:00:00 2001 From: Jacky Zhao Date: Fri, 2 Feb 2024 12:18:02 -0800 Subject: [PATCH 05/86] fix(search): flex basis and card highlighting --- quartz/components/scripts/search.inline.ts | 2 - quartz/components/styles/search.scss | 53 +++++++++++----------- quartz/styles/base.scss | 2 +- 3 files changed, 28 insertions(+), 29 deletions(-) diff --git a/quartz/components/scripts/search.inline.ts b/quartz/components/scripts/search.inline.ts index d707cfd66..59942ebf5 100644 --- a/quartz/components/scripts/search.inline.ts +++ b/quartz/components/scripts/search.inline.ts @@ -163,13 +163,11 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => { let previewInner: HTMLDivElement | undefined = undefined const results = document.createElement("div") results.id = "results-container" - results.style.flexBasis = enablePreview ? "min(30%, 450px)" : "100%" appendLayout(results) if (enablePreview) { preview = document.createElement("div") preview.id = "preview-container" - preview.style.flexBasis = "100%" appendLayout(preview) } diff --git a/quartz/components/styles/search.scss b/quartz/components/styles/search.scss index 9cc85dfcc..3ad774ed3 100644 --- a/quartz/components/styles/search.scss +++ b/quartz/components/styles/search.scss @@ -61,7 +61,7 @@ & > * { width: 100%; - border-radius: 5px; + border-radius: 7px; background: var(--light); box-shadow: 0 14px 50px rgba(27, 33, 48, 0.12), @@ -86,69 +86,70 @@ display: none; flex-direction: row; border: 1px solid var(--lightgray); + flex: 0 0 100%; + box-sizing: border-box; &.display-results { display: flex; } + &[data-preview] > #results-container { + flex: 0 0 min(30%, 450px); + } + @media all and (min-width: $tabletBreakpoint) { &[data-preview] { & .result-card > p.preview { display: none; } + + & > div { + &:first-child { + border-right: 1px solid var(--lightgray); + border-top-right-radius: unset; + border-bottom-right-radius: unset; + } + + &:last-child { + border-top-left-radius: unset; + border-bottom-left-radius: unset; + } + } } } & > div { - // vh - #search-space.margin-top height: calc(75vh - 12vh); - background: none; - - &:first-child { - border-top-left-radius: 5px; - border-bottom-left-radius: 5px; - border-right: 1px solid var(--lightgray); - } - - &:last-child { - border-top-right-radius: 5px; - border-bottom-right-radius: 5px; - } + border-radius: 5px; } @media all and (max-width: $tabletBreakpoint) { - display: block !important; - - & > *:not(#results-container) { + & > #preview-container { display: none !important; } - & > #results-container { + &[data-preview] > #results-container { width: 100%; height: auto; + flex: 0 0 100%; } } & .highlight { - background: color-mix(in srgb, var(--tertiary) 60%, transparent); + background: color-mix(in srgb, var(--tertiary) 60%, rgba(255, 255, 255, 0)); border-radius: 5px; scroll-margin-top: 2rem; } & > #preview-container { display: block; - box-sizing: border-box; overflow: hidden; - box-sizing: border-box; font-family: inherit; color: var(--dark); line-height: 1.5em; font-weight: $normalWeight; - background: var(--light); - border-top-right-radius: 5px; - border-bottom-right-radius: 5px; overflow-y: auto; - padding: 1rem; + padding: 0 2rem; & .preview-inner { margin: 0 auto; @@ -160,6 +161,7 @@ overflow-y: auto; & .result-card { + overflow: hidden; padding: 1em; cursor: pointer; transition: background 0.2s ease; @@ -175,7 +177,6 @@ margin: 0; text-transform: none; text-align: left; - background: var(--light); outline: none; font-weight: inherit; diff --git a/quartz/styles/base.scss b/quartz/styles/base.scss index a4fde0639..33d6267f8 100644 --- a/quartz/styles/base.scss +++ b/quartz/styles/base.scss @@ -26,7 +26,7 @@ section { } ::selection { - background: color-mix(in srgb, var(--tertiary) 60%, transparent); + background: color-mix(in srgb, var(--tertiary) 60%, rgba(255, 255, 255, 0)); color: var(--darkgray); } From 3fb3930df8d3bc61bbf6ac69360a1b5949270cca Mon Sep 17 00:00:00 2001 From: Jacky Zhao Date: Sat, 3 Feb 2024 19:44:24 -0800 Subject: [PATCH 06/86] fix: calculate heading after latex (closes #719) --- quartz.config.ts | 2 +- quartz/plugins/transformers/toc.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/quartz.config.ts b/quartz.config.ts index 1f7399a88..d4fc5d38a 100644 --- a/quartz.config.ts +++ b/quartz.config.ts @@ -45,7 +45,6 @@ const config: QuartzConfig = { plugins: { transformers: [ Plugin.FrontMatter(), - Plugin.TableOfContents(), Plugin.CreatedModifiedDate({ // you can add 'git' here for last modified from Git // if you do rely on git for dates, ensure defaultDateType is 'modified' @@ -55,6 +54,7 @@ const config: QuartzConfig = { Plugin.SyntaxHighlighting(), Plugin.ObsidianFlavoredMarkdown({ enableInHtmlEmbed: false }), Plugin.GitHubFlavoredMarkdown(), + Plugin.TableOfContents(), Plugin.CrawlLinks({ markdownLinkResolution: "shortest" }), Plugin.Description(), ], diff --git a/quartz/plugins/transformers/toc.ts b/quartz/plugins/transformers/toc.ts index d0781ec20..e831f9452 100644 --- a/quartz/plugins/transformers/toc.ts +++ b/quartz/plugins/transformers/toc.ts @@ -26,6 +26,7 @@ interface TocEntry { } const regexMdLinks = new RegExp(/\[([^\[]+)\](\(.*\))/, "g") +const slugAnchor = new Slugger() export const TableOfContents: QuartzTransformerPlugin | undefined> = ( userOpts, ) => { @@ -38,7 +39,7 @@ export const TableOfContents: QuartzTransformerPlugin | undefin return async (tree: Root, file) => { const display = file.data.frontmatter?.enableToc ?? opts.showByDefault if (display) { - const slugAnchor = new Slugger() + slugAnchor.reset() const toc: TocEntry[] = [] let highestDepth: number = opts.maxDepth visit(tree, "heading", (node) => { From dbbc672c67aa5ac0a915d22af5cf44c4e7011aae Mon Sep 17 00:00:00 2001 From: Mara-Li Date: Sun, 4 Feb 2024 04:55:24 +0100 Subject: [PATCH 07/86] feat: Adding support for i18n (closes #462) (#738) * fix: alt error mix with height/width More granular detection of alt and resize in image * fix: format * feat: init i18n * feat: add translation * style: prettier for test * fix: build-up the locale to fusion with dateLocale * style: run prettier * remove cursed file * refactor: remove i18n library and use locale way instead * format with prettier * forgot to remove test * prevent merging error * format * format * fix: allow string for locale - Check during translation if valid / existing locale - Allow to use "en" and "en-US" for example - Add fallback directly in the function - Add default key in the function - Add docstring to cfg.ts * forgot item translation * remove unused locale variable * forgot to remove fr-FR testing * format --- quartz.config.ts | 1 + quartz/cfg.ts | 2 +- quartz/components/Backlinks.tsx | 7 ++-- quartz/components/Darkmode.tsx | 7 ++-- quartz/components/Footer.tsx | 6 ++-- quartz/components/Graph.tsx | 5 +-- quartz/components/Head.tsx | 6 ++-- quartz/components/RecentNotes.tsx | 9 ++++- quartz/components/Search.tsx | 5 +-- quartz/components/TableOfContents.tsx | 10 +++--- quartz/components/pages/404.tsx | 7 ++-- quartz/components/pages/FolderContent.tsx | 8 +++-- quartz/components/pages/TagContent.tsx | 19 ++++++++--- quartz/components/scripts/search.inline.ts | 8 ++++- quartz/i18n/i18next.ts | 37 +++++++++++++++++++++ quartz/i18n/locales/en.json | 37 +++++++++++++++++++++ quartz/i18n/locales/fr.json | 38 ++++++++++++++++++++++ 17 files changed, 180 insertions(+), 32 deletions(-) create mode 100644 quartz/i18n/i18next.ts create mode 100644 quartz/i18n/locales/en.json create mode 100644 quartz/i18n/locales/fr.json diff --git a/quartz.config.ts b/quartz.config.ts index d4fc5d38a..4921a1185 100644 --- a/quartz.config.ts +++ b/quartz.config.ts @@ -9,6 +9,7 @@ const config: QuartzConfig = { analytics: { provider: "plausible", }, + locale: "en-US", baseUrl: "quartz.jzhao.xyz", ignorePatterns: ["private", "templates", ".obsidian"], defaultDateType: "created", diff --git a/quartz/cfg.ts b/quartz/cfg.ts index a7f79e3b8..e7ae783f8 100644 --- a/quartz/cfg.ts +++ b/quartz/cfg.ts @@ -37,8 +37,8 @@ export interface GlobalConfiguration { baseUrl?: string theme: Theme /** - * The locale to use for date formatting. Default to "en-US" * Allow to translate the date in the language of your choice. + * Also used for UI translation (default: en-US) * Need to be formated following the IETF language tag format (https://en.wikipedia.org/wiki/IETF_language_tag) */ locale?: string diff --git a/quartz/components/Backlinks.tsx b/quartz/components/Backlinks.tsx index d5bdc0b95..1688db62d 100644 --- a/quartz/components/Backlinks.tsx +++ b/quartz/components/Backlinks.tsx @@ -1,14 +1,15 @@ import { QuartzComponentConstructor, QuartzComponentProps } from "./types" import style from "./styles/backlinks.scss" import { resolveRelative, simplifySlug } from "../util/path" +import { i18n } from "../i18n/i18next" import { classNames } from "../util/lang" -function Backlinks({ fileData, allFiles, displayClass }: QuartzComponentProps) { +function Backlinks({ fileData, allFiles, displayClass, cfg }: QuartzComponentProps) { const slug = simplifySlug(fileData.slug!) const backlinkFiles = allFiles.filter((file) => file.links?.includes(slug)) return (
-

Backlinks

+

{i18n(cfg.locale, "backlinks.backlinks")}

    {backlinkFiles.length > 0 ? ( backlinkFiles.map((f) => ( @@ -19,7 +20,7 @@ function Backlinks({ fileData, allFiles, displayClass }: QuartzComponentProps) { )) ) : ( -
  • No backlinks found
  • +
  • {i18n(cfg.locale, "backlinks.noBlacklinksFound")}
  • )}
diff --git a/quartz/components/Darkmode.tsx b/quartz/components/Darkmode.tsx index 6d10bb99e..056e684d3 100644 --- a/quartz/components/Darkmode.tsx +++ b/quartz/components/Darkmode.tsx @@ -4,9 +4,10 @@ import darkmodeScript from "./scripts/darkmode.inline" import styles from "./styles/darkmode.scss" import { QuartzComponentConstructor, QuartzComponentProps } from "./types" +import { i18n } from "../i18n/i18next" import { classNames } from "../util/lang" -function Darkmode({ displayClass }: QuartzComponentProps) { +function Darkmode({ displayClass, cfg }: QuartzComponentProps) { return (
@@ -22,7 +23,7 @@ function Darkmode({ displayClass }: QuartzComponentProps) { style="enable-background:new 0 0 35 35" xmlSpace="preserve" > - Light mode + {i18n(cfg.locale, "darkmode.lightMode")} @@ -38,7 +39,7 @@ function Darkmode({ displayClass }: QuartzComponentProps) { style="enable-background:new 0 0 100 100" xmlSpace="preserve" > - Dark mode + {i18n(cfg.locale, "darkmode.lightMode")} diff --git a/quartz/components/Footer.tsx b/quartz/components/Footer.tsx index 54440cffd..40faef9c6 100644 --- a/quartz/components/Footer.tsx +++ b/quartz/components/Footer.tsx @@ -1,20 +1,22 @@ import { QuartzComponentConstructor, QuartzComponentProps } from "./types" import style from "./styles/footer.scss" import { version } from "../../package.json" +import { i18n } from "../i18n/i18next" interface Options { links: Record } export default ((opts?: Options) => { - function Footer({ displayClass }: QuartzComponentProps) { + function Footer({ displayClass, cfg }: QuartzComponentProps) { const year = new Date().getFullYear() const links = opts?.links ?? [] return (

- Created with Quartz v{version}, © {year} + {i18n(cfg.locale, "footer.createdWith")}{" "} + Quartz v{version}, © {year}

    {Object.entries(links).map(([text, link]) => ( diff --git a/quartz/components/Graph.tsx b/quartz/components/Graph.tsx index 756a46bb7..f728c5e5d 100644 --- a/quartz/components/Graph.tsx +++ b/quartz/components/Graph.tsx @@ -2,6 +2,7 @@ import { QuartzComponentConstructor, QuartzComponentProps } from "./types" // @ts-ignore import script from "./scripts/graph.inline" import style from "./styles/graph.scss" +import { i18n } from "../i18n/i18next" import { classNames } from "../util/lang" export interface D3Config { @@ -53,12 +54,12 @@ const defaultOptions: GraphOptions = { } export default ((opts?: GraphOptions) => { - function Graph({ displayClass }: QuartzComponentProps) { + function Graph({ displayClass, cfg }: QuartzComponentProps) { const localGraph = { ...defaultOptions.localGraph, ...opts?.localGraph } const globalGraph = { ...defaultOptions.globalGraph, ...opts?.globalGraph } return (
    -

    Graph View

    +

    {i18n(cfg.locale, "graph.graphView")}

    { function Head({ cfg, fileData, externalResources }: QuartzComponentProps) { - const title = fileData.frontmatter?.title ?? "Untitled" - const description = fileData.description?.trim() ?? "No description provided" + const title = fileData.frontmatter?.title ?? i18n(cfg.locale, "head.untitled") + const description = + fileData.description?.trim() ?? i18n(cfg.locale, "head.noDescriptionProvided") const { css, js } = externalResources const url = new URL(`https://${cfg.baseUrl ?? "example.com"}`) diff --git a/quartz/components/RecentNotes.tsx b/quartz/components/RecentNotes.tsx index 81b354d77..240ef98f3 100644 --- a/quartz/components/RecentNotes.tsx +++ b/quartz/components/RecentNotes.tsx @@ -5,6 +5,7 @@ import { byDateAndAlphabetical } from "./PageList" import style from "./styles/recentNotes.scss" import { Date, getDate } from "./Date" import { GlobalConfiguration } from "../cfg" +import { i18n } from "../i18n/i18next" import { classNames } from "../util/lang" interface Options { @@ -70,7 +71,13 @@ export default ((userOpts?: Partial) => {
{opts.linkToMore && remaining > 0 && (

- See {remaining} more → + + {" "} + {i18n(cfg.locale, "recentNotes.seeRemainingMore", { + remaining: remaining.toString(), + })}{" "} + → +

)}
diff --git a/quartz/components/Search.tsx b/quartz/components/Search.tsx index 239bc033b..b73ce0bfc 100644 --- a/quartz/components/Search.tsx +++ b/quartz/components/Search.tsx @@ -3,6 +3,7 @@ import style from "./styles/search.scss" // @ts-ignore import script from "./scripts/search.inline" import { classNames } from "../util/lang" +import { i18n } from "../i18n/i18next" export interface SearchOptions { enablePreview: boolean @@ -13,13 +14,13 @@ const defaultOptions: SearchOptions = { } export default ((userOpts?: Partial) => { - function Search({ displayClass }: QuartzComponentProps) { + function Search({ displayClass, cfg }: QuartzComponentProps) { const opts = { ...defaultOptions, ...userOpts } return (
-

Search

+

{i18n(cfg.locale, "search")}

Table of Contents

+

{i18n(cfg.locale, "tableOfContent")}

-

Table of Contents

+

{i18n(cfg.locale, "tableOfContent")}

    {fileData.toc.map((tocEntry) => ( diff --git a/quartz/components/pages/404.tsx b/quartz/components/pages/404.tsx index c276f568d..56adbf981 100644 --- a/quartz/components/pages/404.tsx +++ b/quartz/components/pages/404.tsx @@ -1,10 +1,11 @@ -import { QuartzComponentConstructor } from "../types" +import { i18n } from "../../i18n/i18next" +import { QuartzComponentConstructor, QuartzComponentProps } from "../types" -function NotFound() { +function NotFound({ cfg }: QuartzComponentProps) { return (

    404

    -

    Either this page is private or doesn't exist.

    +

    {i18n(cfg.locale, "404")}

    ) } diff --git a/quartz/components/pages/FolderContent.tsx b/quartz/components/pages/FolderContent.tsx index 47fb02f1b..02938e32c 100644 --- a/quartz/components/pages/FolderContent.tsx +++ b/quartz/components/pages/FolderContent.tsx @@ -7,6 +7,7 @@ import { _stripSlashes, simplifySlug } from "../../util/path" import { Root } from "hast" import { pluralize } from "../../util/lang" import { htmlToJsx } from "../../util/jsx" +import { i18n } from "../../i18n/i18next" interface FolderContentOptions { /** @@ -23,7 +24,7 @@ export default ((opts?: Partial) => { const options: FolderContentOptions = { ...defaultOptions, ...opts } function FolderContent(props: QuartzComponentProps) { - const { tree, fileData, allFiles } = props + const { tree, fileData, allFiles, cfg } = props const folderSlug = _stripSlashes(simplifySlug(fileData.slug!)) const allPagesInFolder = allFiles.filter((file) => { const fileSlug = _stripSlashes(simplifySlug(file.slug!)) @@ -52,7 +53,10 @@ export default ((opts?: Partial) => {
    {options.showFolderCount && ( -

    {pluralize(allPagesInFolder.length, "item")} under this folder.

    +

    + {pluralize(allPagesInFolder.length, i18n(cfg.locale, "common.item"))}{" "} + {i18n(cfg.locale, "folderContent.underThisFolder")}. +

    )}
    diff --git a/quartz/components/pages/TagContent.tsx b/quartz/components/pages/TagContent.tsx index ec30c5ff9..57a6c3216 100644 --- a/quartz/components/pages/TagContent.tsx +++ b/quartz/components/pages/TagContent.tsx @@ -6,10 +6,11 @@ import { QuartzPluginData } from "../../plugins/vfile" import { Root } from "hast" import { pluralize } from "../../util/lang" import { htmlToJsx } from "../../util/jsx" +import { i18n } from "../../i18n/i18next" const numPages = 10 function TagContent(props: QuartzComponentProps) { - const { tree, fileData, allFiles } = props + const { tree, fileData, allFiles, cfg } = props const slug = fileData.slug if (!(slug?.startsWith("tags/") || slug === "tags")) { @@ -43,7 +44,10 @@ function TagContent(props: QuartzComponentProps) {

    {content}

    -

    Found {tags.length} total tags.

    +

    + {i18n(cfg.locale, "tagContent.found")} {tags.length}{" "} + {i18n(cfg.locale, "tagContent.totalTags")}. +

    {tags.map((tag) => { const pages = tagItemMap.get(tag)! @@ -64,8 +68,10 @@ function TagContent(props: QuartzComponentProps) { {content &&

    {content}

    }

    - {pluralize(pages.length, "item")} with this tag.{" "} - {pages.length > numPages && `Showing first ${numPages}.`} + {pluralize(pages.length, i18n(cfg.locale, "common.item"))}{" "} + {i18n(cfg.locale, "tagContent.withThisTag")}.{" "} + {pages.length > numPages && + `${i18n(cfg.locale, "tagContent.showingFirst")} ${numPages}.`}

    @@ -86,7 +92,10 @@ function TagContent(props: QuartzComponentProps) {
    {content}
    -

    {pluralize(pages.length, "item")} with this tag.

    +

    + {pluralize(pages.length, i18n(cfg.locale, "common.item"))}{" "} + {i18n(cfg.locale, "tagContent.withThisTag")}. +

    diff --git a/quartz/components/scripts/search.inline.ts b/quartz/components/scripts/search.inline.ts index 59942ebf5..a75f4ff46 100644 --- a/quartz/components/scripts/search.inline.ts +++ b/quartz/components/scripts/search.inline.ts @@ -306,7 +306,13 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => { itemTile.classList.add("result-card") itemTile.id = slug itemTile.href = resolveUrl(slug).toString() - itemTile.innerHTML = `

    ${title}

    ${htmlTags}

    ${content}

    ` + itemTile.innerHTML = `

    ${title}

    ${htmlTags}${ + enablePreview && window.innerWidth > 600 ? "" : `

    ${content}

    ` + }` + itemTile.addEventListener("click", (event) => { + if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) return + hideSearch() + }) const handler = (event: MouseEvent) => { if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) return diff --git a/quartz/i18n/i18next.ts b/quartz/i18n/i18next.ts new file mode 100644 index 000000000..39c446132 --- /dev/null +++ b/quartz/i18n/i18next.ts @@ -0,0 +1,37 @@ +import en from "./locales/en.json" +import fr from "./locales/fr.json" + +const TRANSLATION = { + "en-US": en, + "fr-FR": fr, +} as const + +type TranslationOptions = { + [key: string]: string +} + +export const i18n = (lang = "en-US", key: string, options?: TranslationOptions) => { + const locale = + Object.keys(TRANSLATION).find( + (key) => + key.toLowerCase() === lang.toLowerCase() || key.toLowerCase().includes(lang.toLowerCase()), + ) ?? "en-US" + const getTranslation = (key: string) => { + const keys = key.split(".") + let translationString: string | Record = + TRANSLATION[locale as keyof typeof TRANSLATION] + keys.forEach((key) => { + // @ts-ignore + translationString = translationString[key] + }) + return translationString + } + if (options) { + let translationString = getTranslation(key).toString() + Object.keys(options).forEach((key) => { + translationString = translationString.replace(`{{${key}}}`, options[key]) + }) + return translationString + } + return getTranslation(key).toString() +} diff --git a/quartz/i18n/locales/en.json b/quartz/i18n/locales/en.json new file mode 100644 index 000000000..28b6dff2d --- /dev/null +++ b/quartz/i18n/locales/en.json @@ -0,0 +1,37 @@ +{ + "404": "Either this page is private or doesn't exist.", + "backlinks": { + "backlinks": "Backlinks", + "noBlacklinksFound": "No backlinks found" + }, + "common": { + "item": "item" + }, + "darkmode": { + "lightMode": "Light mode" + }, + "folderContent": { + "underThisFolder": "under this folder" + }, + "footer": { + "createdWith": "Created with" + }, + "graph": { + "graphView": "Graph View" + }, + "head": { + "noDescriptionProvided": "No description provided", + "untitled": "Untitled" + }, + "recentNotes": { + "seeRemainingMore": "See {{remaining}} more" + }, + "search": "Search", + "tableOfContent": "Table of Contents", + "tagContent": { + "showingFirst": "Showing first", + "totalTags": "total tags", + "withThisTag": "with this tag", + "found": "Found" + } +} diff --git a/quartz/i18n/locales/fr.json b/quartz/i18n/locales/fr.json new file mode 100644 index 000000000..97f8f31bc --- /dev/null +++ b/quartz/i18n/locales/fr.json @@ -0,0 +1,38 @@ +{ + "404": "Soit cette page est privée, soit elle n'existe pas.", + "backlinks": { + "backlinks": "Rétroliens", + "noBlacklinksFound": "Aucun rétrolien trouvé" + }, + "common": { + "item": "fichier" + }, + "darkmode": { + "darkmode": "Thème sombre", + "lightMode": "Thème clair" + }, + "folderContent": { + "underThisFolder": "dans ce dossier" + }, + "footer": { + "createdWith": "Créé avec" + }, + "graph": { + "graphView": "Vue Graphique" + }, + "head": { + "noDescriptionProvided": "Aucune description n'a été fournie", + "untitled": "Sans titre" + }, + "recentNotes": { + "seeRemainingMore": "Voir {{remaining}} plus" + }, + "search": "Rechercher", + "tableOfContent": "Table des Matières", + "tagContent": { + "showingFirst": "Afficher en premier", + "totalTags": "tags totaux", + "withThisTag": "avec ce tag", + "found": "Trouvé" + } +} From 5b90fbd0d0ac93a6ef5921f7693d36574224531c Mon Sep 17 00:00:00 2001 From: Aaron Pham <29749331+aarnphm@users.noreply.github.com> Date: Sun, 4 Feb 2024 00:51:55 -0500 Subject: [PATCH 08/86] feat(ofm): parsing all type of arrow (#797) * feat(ofm): parsing all type of arrow Signed-off-by: Aaron <29749331+aarnphm@users.noreply.github.com> * fix: use html value instead of decimal Signed-off-by: Aaron <29749331+aarnphm@users.noreply.github.com> * fix: skip parsing arrow if it is not a valid supported mapping Signed-off-by: Aaron <29749331+aarnphm@users.noreply.github.com> --------- Signed-off-by: Aaron <29749331+aarnphm@users.noreply.github.com> --- quartz/plugins/transformers/ofm.ts | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/quartz/plugins/transformers/ofm.ts b/quartz/plugins/transformers/ofm.ts index 44df3fa9e..18ff6b438 100644 --- a/quartz/plugins/transformers/ofm.ts +++ b/quartz/plugins/transformers/ofm.ts @@ -74,6 +74,17 @@ const calloutMapping = { cite: "quote", } as const +const arrowMapping: Record = { + "->": "→", + "-->": "⇒", + "=>": "⇒", + "==>": "⇒", + "<-": "←", + "<--": "⇐", + "<=": "⇐", + "<==": "⇐", +} + function canonicalizeCallout(calloutName: string): keyof typeof calloutMapping { const normalizedCallout = calloutName.toLowerCase() as keyof typeof calloutMapping // if callout is not recognized, make it a custom one @@ -82,7 +93,7 @@ function canonicalizeCallout(calloutName: string): keyof typeof calloutMapping { export const externalLinkRegex = /^https?:\/\//i -export const arrowRegex = new RegExp(/-{1,2}>/, "g") +export const arrowRegex = new RegExp(/(-{1,2}>|={1,2}>|<-{1,2}|<={1,2})/, "g") // !? -> optional embedding // \[\[ -> open brace @@ -271,10 +282,12 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin if (opts.parseArrows) { replacements.push([ arrowRegex, - (_value: string, ..._capture: string[]) => { + (value: string, ..._capture: string[]) => { + const maybeArrow = arrowMapping[value] + if (maybeArrow === undefined) return SKIP return { type: "html", - value: ``, + value: `${maybeArrow}`, } }, ]) From dff4b063135297aaa2f0605fd3267a874baaa90d Mon Sep 17 00:00:00 2001 From: Mats Fangohr <83273529+MatsFangohr@users.noreply.github.com> Date: Sun, 4 Feb 2024 15:48:31 +0100 Subject: [PATCH 09/86] fix(i18n): backlinks naming in mapping (#800) --- quartz/components/Backlinks.tsx | 2 +- quartz/i18n/locales/en.json | 2 +- quartz/i18n/locales/fr.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/quartz/components/Backlinks.tsx b/quartz/components/Backlinks.tsx index 1688db62d..458e48b24 100644 --- a/quartz/components/Backlinks.tsx +++ b/quartz/components/Backlinks.tsx @@ -20,7 +20,7 @@ function Backlinks({ fileData, allFiles, displayClass, cfg }: QuartzComponentPro )) ) : ( -
  • {i18n(cfg.locale, "backlinks.noBlacklinksFound")}
  • +
  • {i18n(cfg.locale, "backlinks.noBacklinksFound")}
  • )}
diff --git a/quartz/i18n/locales/en.json b/quartz/i18n/locales/en.json index 28b6dff2d..fc3cebbb6 100644 --- a/quartz/i18n/locales/en.json +++ b/quartz/i18n/locales/en.json @@ -2,7 +2,7 @@ "404": "Either this page is private or doesn't exist.", "backlinks": { "backlinks": "Backlinks", - "noBlacklinksFound": "No backlinks found" + "noBacklinksFound": "No backlinks found" }, "common": { "item": "item" diff --git a/quartz/i18n/locales/fr.json b/quartz/i18n/locales/fr.json index 97f8f31bc..8b526b542 100644 --- a/quartz/i18n/locales/fr.json +++ b/quartz/i18n/locales/fr.json @@ -2,7 +2,7 @@ "404": "Soit cette page est privée, soit elle n'existe pas.", "backlinks": { "backlinks": "Rétroliens", - "noBlacklinksFound": "Aucun rétrolien trouvé" + "noBacklinksFound": "Aucun rétrolien trouvé" }, "common": { "item": "fichier" From 36e4cc41a9e74faddabfd22878ea13b6c504209c Mon Sep 17 00:00:00 2001 From: Jacky Zhao Date: Sun, 4 Feb 2024 20:57:10 -0800 Subject: [PATCH 10/86] chore(i18n): refactor and cleanup (#805) * checkpoint * finish * docs --- docs/advanced/making plugins.md | 2 +- docs/configuration.md | 1 + docs/features/i18n.md | 18 ++++++ docs/index.md | 2 +- quartz/cfg.ts | 8 ++- quartz/components/ArticleTitle.tsx | 1 + quartz/components/Backlinks.tsx | 6 +- quartz/components/Darkmode.tsx | 6 +- quartz/components/Date.tsx | 5 +- quartz/components/Explorer.tsx | 6 +- quartz/components/ExplorerNode.tsx | 2 +- quartz/components/Footer.tsx | 6 +- quartz/components/Graph.tsx | 4 +- quartz/components/Head.tsx | 6 +- quartz/components/PageTitle.tsx | 3 +- quartz/components/RecentNotes.tsx | 15 ++--- quartz/components/Search.tsx | 10 ++-- quartz/components/TableOfContents.tsx | 6 +- quartz/components/pages/404.tsx | 4 +- quartz/components/pages/FolderContent.tsx | 8 +-- quartz/components/pages/TagContent.tsx | 23 +++----- quartz/components/renderPage.tsx | 20 ++++++- quartz/i18n/i18next.ts | 37 ------------ quartz/i18n/index.ts | 11 ++++ quartz/i18n/locales/definition.ts | 63 +++++++++++++++++++++ quartz/i18n/locales/en-US.ts | 65 ++++++++++++++++++++++ quartz/i18n/locales/en.json | 37 ------------ quartz/i18n/locales/fr-FR.ts | 65 ++++++++++++++++++++++ quartz/i18n/locales/fr.json | 38 ------------- quartz/plugins/emitters/404.tsx | 10 ++-- quartz/plugins/emitters/contentIndex.ts | 5 +- quartz/plugins/emitters/contentPage.tsx | 2 +- quartz/plugins/emitters/folderPage.tsx | 8 ++- quartz/plugins/emitters/tagPage.tsx | 8 ++- quartz/plugins/transformers/frontmatter.ts | 5 +- quartz/plugins/transformers/toc.ts | 13 +---- quartz/util/lang.ts | 8 --- 37 files changed, 326 insertions(+), 211 deletions(-) create mode 100644 docs/features/i18n.md delete mode 100644 quartz/i18n/i18next.ts create mode 100644 quartz/i18n/index.ts create mode 100644 quartz/i18n/locales/definition.ts create mode 100644 quartz/i18n/locales/en-US.ts delete mode 100644 quartz/i18n/locales/en.json create mode 100644 quartz/i18n/locales/fr-FR.ts delete mode 100644 quartz/i18n/locales/fr.json diff --git a/docs/advanced/making plugins.md b/docs/advanced/making plugins.md index 65209a2ca..565f5bdba 100644 --- a/docs/advanced/making plugins.md +++ b/docs/advanced/making plugins.md @@ -278,7 +278,7 @@ export const ContentPage: QuartzEmitterPlugin = () => { allFiles, } - const content = renderPage(slug, componentData, opts, externalResources) + const content = renderPage(cfg, slug, componentData, opts, externalResources) const fp = await emit({ content, slug: file.data.slug!, diff --git a/docs/configuration.md b/docs/configuration.md index 047f6ca6b..33d5a5744 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -27,6 +27,7 @@ This part of the configuration concerns anything that can affect the whole site. - `null`: don't use analytics; - `{ provider: 'plausible' }`: use [Plausible](https://plausible.io/), a privacy-friendly alternative to Google Analytics; or - `{ provider: 'google', tagId: }`: use Google Analytics +- `locale`: used for [[i18n]] and date formatting - `baseUrl`: this is used for sitemaps and RSS feeds that require an absolute URL to know where the canonical 'home' of your site lives. This is normally the deployed URL of your site (e.g. `quartz.jzhao.xyz` for this site). Do not include the protocol (i.e. `https://`) or any leading or trailing slashes. - This should also include the subpath if you are [[hosting]] on GitHub pages without a custom domain. For example, if my repository is `jackyzha0/quartz`, GitHub pages would deploy to `https://jackyzha0.github.io/quartz` and the `baseUrl` would be `jackyzha0.github.io/quartz` - Note that Quartz 4 will avoid using this as much as possible and use relative URLs whenever it can to make sure your site works no matter _where_ you end up actually deploying it. diff --git a/docs/features/i18n.md b/docs/features/i18n.md new file mode 100644 index 000000000..57547ddad --- /dev/null +++ b/docs/features/i18n.md @@ -0,0 +1,18 @@ +--- +title: Internationalization +--- + +Internationalization allows users to translate text in the Quartz interface into various supported languages without needing to make extensive code changes. This can be changed via the `locale` [[configuration]] field in `quartz.config.ts`. + +The locale field generally follows a certain format: `{language}-{REGION}` + +- `{language}` is usually a [2-letter lowercase language code](https://en.wikipedia.org/wiki/List_of_ISO_639_language_codes). +- `{REGION}` is usually a [2-letter uppercase region code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) + +> [!tip] Interested in contributing? +> We [gladly welcome translation PRs](https://github.com/jackyzha0/quartz/tree/v4/quartz/i18n/locales)! To contribute a translation, do the following things: +> +> 1. In the `quartz/i18n/locales` folder, copy the `en-US.ts` file. +> 2. Rename it to `{language}-{REGION}.ts` so it matches a locale of the format shown above. +> 3. Fill in the translations! +> 4. Add the entry under `TRANSLATIONS` in `quartz/i18n/index.ts`. diff --git a/docs/index.md b/docs/index.md index cbf8719d1..f25b6e244 100644 --- a/docs/index.md +++ b/docs/index.md @@ -31,7 +31,7 @@ If you prefer instructions in a video format you can try following Nicole van de ## 🔧 Features -- [[Obsidian compatibility]], [[full-text search]], [[graph view]], note transclusion, [[wikilinks]], [[backlinks]], [[Latex]], [[syntax highlighting]], [[popover previews]], [[Docker Support]], and [many more](./features) right out of the box +- [[Obsidian compatibility]], [[full-text search]], [[graph view]], note transclusion, [[wikilinks]], [[backlinks]], [[Latex]], [[syntax highlighting]], [[popover previews]], [[Docker Support]], [[i18n|internationalization]] and [many more](./features) right out of the box - Hot-reload for both configuration and content - Simple JSX layouts and [[creating components|page components]] - [[SPA Routing|Ridiculously fast page loads]] and tiny bundle sizes diff --git a/quartz/cfg.ts b/quartz/cfg.ts index e7ae783f8..a477db057 100644 --- a/quartz/cfg.ts +++ b/quartz/cfg.ts @@ -1,5 +1,6 @@ import { ValidDateType } from "./components/Date" import { QuartzComponent } from "./components/types" +import { ValidLocale } from "./i18n" import { PluginTypes } from "./plugins/types" import { Theme } from "./util/theme" @@ -39,9 +40,12 @@ export interface GlobalConfiguration { /** * Allow to translate the date in the language of your choice. * Also used for UI translation (default: en-US) - * Need to be formated following the IETF language tag format (https://en.wikipedia.org/wiki/IETF_language_tag) + * Need to be formated following BCP 47: https://en.wikipedia.org/wiki/IETF_language_tag + * The first part is the language (en) and the second part is the script/region (US) + * Language Codes: https://en.wikipedia.org/wiki/List_of_ISO_639_language_codes + * Region Codes: https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 */ - locale?: string + locale: ValidLocale } export interface QuartzConfig { diff --git a/quartz/components/ArticleTitle.tsx b/quartz/components/ArticleTitle.tsx index 2484c946a..7768de6cb 100644 --- a/quartz/components/ArticleTitle.tsx +++ b/quartz/components/ArticleTitle.tsx @@ -9,6 +9,7 @@ function ArticleTitle({ fileData, displayClass }: QuartzComponentProps) { return null } } + ArticleTitle.css = ` .article-title { margin: 2rem 0 0 0; diff --git a/quartz/components/Backlinks.tsx b/quartz/components/Backlinks.tsx index 458e48b24..573c1c391 100644 --- a/quartz/components/Backlinks.tsx +++ b/quartz/components/Backlinks.tsx @@ -1,7 +1,7 @@ import { QuartzComponentConstructor, QuartzComponentProps } from "./types" import style from "./styles/backlinks.scss" import { resolveRelative, simplifySlug } from "../util/path" -import { i18n } from "../i18n/i18next" +import { i18n } from "../i18n" import { classNames } from "../util/lang" function Backlinks({ fileData, allFiles, displayClass, cfg }: QuartzComponentProps) { @@ -9,7 +9,7 @@ function Backlinks({ fileData, allFiles, displayClass, cfg }: QuartzComponentPro const backlinkFiles = allFiles.filter((file) => file.links?.includes(slug)) return (
-

{i18n(cfg.locale, "backlinks.backlinks")}

+

{i18n(cfg.locale).components.backlinks.title}

    {backlinkFiles.length > 0 ? ( backlinkFiles.map((f) => ( @@ -20,7 +20,7 @@ function Backlinks({ fileData, allFiles, displayClass, cfg }: QuartzComponentPro )) ) : ( -
  • {i18n(cfg.locale, "backlinks.noBacklinksFound")}
  • +
  • {i18n(cfg.locale).components.backlinks.noBacklinksFound}
  • )}
diff --git a/quartz/components/Darkmode.tsx b/quartz/components/Darkmode.tsx index 056e684d3..62d3c2382 100644 --- a/quartz/components/Darkmode.tsx +++ b/quartz/components/Darkmode.tsx @@ -4,7 +4,7 @@ import darkmodeScript from "./scripts/darkmode.inline" import styles from "./styles/darkmode.scss" import { QuartzComponentConstructor, QuartzComponentProps } from "./types" -import { i18n } from "../i18n/i18next" +import { i18n } from "../i18n" import { classNames } from "../util/lang" function Darkmode({ displayClass, cfg }: QuartzComponentProps) { @@ -23,7 +23,7 @@ function Darkmode({ displayClass, cfg }: QuartzComponentProps) { style="enable-background:new 0 0 35 35" xmlSpace="preserve" > - {i18n(cfg.locale, "darkmode.lightMode")} + {i18n(cfg.locale).components.themeToggle.darkMode} @@ -39,7 +39,7 @@ function Darkmode({ displayClass, cfg }: QuartzComponentProps) { style="enable-background:new 0 0 100 100" xmlSpace="preserve" > - {i18n(cfg.locale, "darkmode.lightMode")} + {i18n(cfg.locale).components.themeToggle.lightMode} diff --git a/quartz/components/Date.tsx b/quartz/components/Date.tsx index 6feac178c..26b59647c 100644 --- a/quartz/components/Date.tsx +++ b/quartz/components/Date.tsx @@ -1,9 +1,10 @@ import { GlobalConfiguration } from "../cfg" +import { ValidLocale } from "../i18n" import { QuartzPluginData } from "../plugins/vfile" interface Props { date: Date - locale?: string + locale?: ValidLocale } export type ValidDateType = keyof Required["dates"] @@ -17,7 +18,7 @@ export function getDate(cfg: GlobalConfiguration, data: QuartzPluginData): Date return data.dates?.[cfg.defaultDateType] } -export function formatDate(d: Date, locale = "en-US"): string { +export function formatDate(d: Date, locale: ValidLocale = "en-US"): string { return d.toLocaleDateString(locale, { year: "numeric", month: "short", diff --git a/quartz/components/Explorer.tsx b/quartz/components/Explorer.tsx index e964c5a41..f7017342e 100644 --- a/quartz/components/Explorer.tsx +++ b/quartz/components/Explorer.tsx @@ -6,10 +6,10 @@ import script from "./scripts/explorer.inline" import { ExplorerNode, FileNode, Options } from "./ExplorerNode" import { QuartzPluginData } from "../plugins/vfile" import { classNames } from "../util/lang" +import { i18n } from "../i18n" // Options interface defined in `ExplorerNode` to avoid circular dependency const defaultOptions = { - title: "Explorer", folderClickBehavior: "collapse", folderDefaultState: "collapsed", useSavedState: true, @@ -75,7 +75,7 @@ export default ((userOpts?: Partial) => { jsonTree = JSON.stringify(folders) } - function Explorer({ allFiles, displayClass, fileData }: QuartzComponentProps) { + function Explorer({ cfg, allFiles, displayClass, fileData }: QuartzComponentProps) { constructFileTree(allFiles) return (
@@ -87,7 +87,7 @@ export default ((userOpts?: Partial) => { data-savestate={opts.useSavedState} data-tree={jsonTree} > -

{opts.title}

+

{opts.title ?? i18n(cfg.locale).components.explorer.title}

@@ -15,8 +15,8 @@ export default ((opts?: Options) => {

- {i18n(cfg.locale, "footer.createdWith")}{" "} - Quartz v{version}, © {year} + {i18n(cfg.locale).components.footer.createdWith}{" "} + Quartz v{version} © {year}

    {Object.entries(links).map(([text, link]) => ( diff --git a/quartz/components/Graph.tsx b/quartz/components/Graph.tsx index f728c5e5d..9fce9bd8f 100644 --- a/quartz/components/Graph.tsx +++ b/quartz/components/Graph.tsx @@ -2,7 +2,7 @@ import { QuartzComponentConstructor, QuartzComponentProps } from "./types" // @ts-ignore import script from "./scripts/graph.inline" import style from "./styles/graph.scss" -import { i18n } from "../i18n/i18next" +import { i18n } from "../i18n" import { classNames } from "../util/lang" export interface D3Config { @@ -59,7 +59,7 @@ export default ((opts?: GraphOptions) => { const globalGraph = { ...defaultOptions.globalGraph, ...opts?.globalGraph } return (
    -

    {i18n(cfg.locale, "graph.graphView")}

    +

    {i18n(cfg.locale).components.graph.title}

    { function Head({ cfg, fileData, externalResources }: QuartzComponentProps) { - const title = fileData.frontmatter?.title ?? i18n(cfg.locale, "head.untitled") + const title = fileData.frontmatter?.title ?? i18n(cfg.locale).propertyDefaults.title const description = - fileData.description?.trim() ?? i18n(cfg.locale, "head.noDescriptionProvided") + fileData.description?.trim() ?? i18n(cfg.locale).propertyDefaults.description const { css, js } = externalResources const url = new URL(`https://${cfg.baseUrl ?? "example.com"}`) diff --git a/quartz/components/PageTitle.tsx b/quartz/components/PageTitle.tsx index fb1660a7c..d12960264 100644 --- a/quartz/components/PageTitle.tsx +++ b/quartz/components/PageTitle.tsx @@ -1,9 +1,10 @@ import { pathToRoot } from "../util/path" import { QuartzComponentConstructor, QuartzComponentProps } from "./types" import { classNames } from "../util/lang" +import { i18n } from "../i18n" function PageTitle({ fileData, cfg, displayClass }: QuartzComponentProps) { - const title = cfg?.pageTitle ?? "Untitled Quartz" + const title = cfg?.pageTitle ?? i18n(cfg.locale).propertyDefaults.title const baseDir = pathToRoot(fileData.slug!) return (

    diff --git a/quartz/components/RecentNotes.tsx b/quartz/components/RecentNotes.tsx index 240ef98f3..f8f6de41f 100644 --- a/quartz/components/RecentNotes.tsx +++ b/quartz/components/RecentNotes.tsx @@ -5,11 +5,11 @@ import { byDateAndAlphabetical } from "./PageList" import style from "./styles/recentNotes.scss" import { Date, getDate } from "./Date" import { GlobalConfiguration } from "../cfg" -import { i18n } from "../i18n/i18next" +import { i18n } from "../i18n" import { classNames } from "../util/lang" interface Options { - title: string + title?: string limit: number linkToMore: SimpleSlug | false filter: (f: QuartzPluginData) => boolean @@ -17,7 +17,6 @@ interface Options { } const defaultOptions = (cfg: GlobalConfiguration): Options => ({ - title: "Recent Notes", limit: 3, linkToMore: false, filter: () => true, @@ -31,10 +30,10 @@ export default ((userOpts?: Partial) => { const remaining = Math.max(0, pages.length - opts.limit) return (
    -

    {opts.title}

    +

    {opts.title ?? i18n(cfg.locale).components.recentNotes.title}

      {pages.slice(0, opts.limit).map((page) => { - const title = page.frontmatter?.title + const title = page.frontmatter?.title ?? i18n(cfg.locale).propertyDefaults.title const tags = page.frontmatter?.tags ?? [] return ( @@ -72,11 +71,7 @@ export default ((userOpts?: Partial) => { {opts.linkToMore && remaining > 0 && (

      - {" "} - {i18n(cfg.locale, "recentNotes.seeRemainingMore", { - remaining: remaining.toString(), - })}{" "} - → + {i18n(cfg.locale).components.recentNotes.seeRemainingMore({ remaining })}

      )} diff --git a/quartz/components/Search.tsx b/quartz/components/Search.tsx index b73ce0bfc..a07dbc4fd 100644 --- a/quartz/components/Search.tsx +++ b/quartz/components/Search.tsx @@ -3,7 +3,7 @@ import style from "./styles/search.scss" // @ts-ignore import script from "./scripts/search.inline" import { classNames } from "../util/lang" -import { i18n } from "../i18n/i18next" +import { i18n } from "../i18n" export interface SearchOptions { enablePreview: boolean @@ -16,11 +16,11 @@ const defaultOptions: SearchOptions = { export default ((userOpts?: Partial) => { function Search({ displayClass, cfg }: QuartzComponentProps) { const opts = { ...defaultOptions, ...userOpts } - + const searchPlaceholder = i18n(cfg.locale).components.search.searchBarPlaceholder return (
      -

      {i18n(cfg.locale, "search")}

      +

      {i18n(cfg.locale).components.search.title}

      ) => { id="search-bar" name="search" type="text" - aria-label="Search for something" - placeholder="Search for something" + aria-label={searchPlaceholder} + placeholder={searchPlaceholder} />
      diff --git a/quartz/components/TableOfContents.tsx b/quartz/components/TableOfContents.tsx index 2e015076f..2abc74b53 100644 --- a/quartz/components/TableOfContents.tsx +++ b/quartz/components/TableOfContents.tsx @@ -5,7 +5,7 @@ import { classNames } from "../util/lang" // @ts-ignore import script from "./scripts/toc.inline" -import { i18n } from "../i18n/i18next" +import { i18n } from "../i18n" interface Options { layout: "modern" | "legacy" @@ -23,7 +23,7 @@ function TableOfContents({ fileData, displayClass, cfg }: QuartzComponentProps) return (