* 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
This commit is contained in:
		@@ -9,6 +9,7 @@ const config: QuartzConfig = {
 | 
				
			|||||||
    analytics: {
 | 
					    analytics: {
 | 
				
			||||||
      provider: "plausible",
 | 
					      provider: "plausible",
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    locale: "en-US",
 | 
				
			||||||
    baseUrl: "quartz.jzhao.xyz",
 | 
					    baseUrl: "quartz.jzhao.xyz",
 | 
				
			||||||
    ignorePatterns: ["private", "templates", ".obsidian"],
 | 
					    ignorePatterns: ["private", "templates", ".obsidian"],
 | 
				
			||||||
    defaultDateType: "created",
 | 
					    defaultDateType: "created",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -37,8 +37,8 @@ export interface GlobalConfiguration {
 | 
				
			|||||||
  baseUrl?: string
 | 
					  baseUrl?: string
 | 
				
			||||||
  theme: Theme
 | 
					  theme: Theme
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * The locale to use for date formatting. Default to "en-US"
 | 
					 | 
				
			||||||
   * Allow to translate the date in the language of your choice.
 | 
					   * 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 the IETF language tag format (https://en.wikipedia.org/wiki/IETF_language_tag)
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  locale?: string
 | 
					  locale?: string
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,14 +1,15 @@
 | 
				
			|||||||
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
 | 
					import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
 | 
				
			||||||
import style from "./styles/backlinks.scss"
 | 
					import style from "./styles/backlinks.scss"
 | 
				
			||||||
import { resolveRelative, simplifySlug } from "../util/path"
 | 
					import { resolveRelative, simplifySlug } from "../util/path"
 | 
				
			||||||
 | 
					import { i18n } from "../i18n/i18next"
 | 
				
			||||||
import { classNames } from "../util/lang"
 | 
					import { classNames } from "../util/lang"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function Backlinks({ fileData, allFiles, displayClass }: QuartzComponentProps) {
 | 
					function Backlinks({ fileData, allFiles, displayClass, cfg }: QuartzComponentProps) {
 | 
				
			||||||
  const slug = simplifySlug(fileData.slug!)
 | 
					  const slug = simplifySlug(fileData.slug!)
 | 
				
			||||||
  const backlinkFiles = allFiles.filter((file) => file.links?.includes(slug))
 | 
					  const backlinkFiles = allFiles.filter((file) => file.links?.includes(slug))
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <div class={classNames(displayClass, "backlinks")}>
 | 
					    <div class={classNames(displayClass, "backlinks")}>
 | 
				
			||||||
      <h3>Backlinks</h3>
 | 
					      <h3>{i18n(cfg.locale, "backlinks.backlinks")}</h3>
 | 
				
			||||||
      <ul class="overflow">
 | 
					      <ul class="overflow">
 | 
				
			||||||
        {backlinkFiles.length > 0 ? (
 | 
					        {backlinkFiles.length > 0 ? (
 | 
				
			||||||
          backlinkFiles.map((f) => (
 | 
					          backlinkFiles.map((f) => (
 | 
				
			||||||
@@ -19,7 +20,7 @@ function Backlinks({ fileData, allFiles, displayClass }: QuartzComponentProps) {
 | 
				
			|||||||
            </li>
 | 
					            </li>
 | 
				
			||||||
          ))
 | 
					          ))
 | 
				
			||||||
        ) : (
 | 
					        ) : (
 | 
				
			||||||
          <li>No backlinks found</li>
 | 
					          <li>{i18n(cfg.locale, "backlinks.noBlacklinksFound")}</li>
 | 
				
			||||||
        )}
 | 
					        )}
 | 
				
			||||||
      </ul>
 | 
					      </ul>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,9 +4,10 @@
 | 
				
			|||||||
import darkmodeScript from "./scripts/darkmode.inline"
 | 
					import darkmodeScript from "./scripts/darkmode.inline"
 | 
				
			||||||
import styles from "./styles/darkmode.scss"
 | 
					import styles from "./styles/darkmode.scss"
 | 
				
			||||||
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
 | 
					import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
 | 
				
			||||||
 | 
					import { i18n } from "../i18n/i18next"
 | 
				
			||||||
import { classNames } from "../util/lang"
 | 
					import { classNames } from "../util/lang"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function Darkmode({ displayClass }: QuartzComponentProps) {
 | 
					function Darkmode({ displayClass, cfg }: QuartzComponentProps) {
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <div class={classNames(displayClass, "darkmode")}>
 | 
					    <div class={classNames(displayClass, "darkmode")}>
 | 
				
			||||||
      <input class="toggle" id="darkmode-toggle" type="checkbox" tabIndex={-1} />
 | 
					      <input class="toggle" id="darkmode-toggle" type="checkbox" tabIndex={-1} />
 | 
				
			||||||
@@ -22,7 +23,7 @@ function Darkmode({ displayClass }: QuartzComponentProps) {
 | 
				
			|||||||
          style="enable-background:new 0 0 35 35"
 | 
					          style="enable-background:new 0 0 35 35"
 | 
				
			||||||
          xmlSpace="preserve"
 | 
					          xmlSpace="preserve"
 | 
				
			||||||
        >
 | 
					        >
 | 
				
			||||||
          <title>Light mode</title>
 | 
					          <title>{i18n(cfg.locale, "darkmode.lightMode")}</title>
 | 
				
			||||||
          <path d="M6,17.5C6,16.672,5.328,16,4.5,16h-3C0.672,16,0,16.672,0,17.5    S0.672,19,1.5,19h3C5.328,19,6,18.328,6,17.5z M7.5,26c-0.414,0-0.789,0.168-1.061,0.439l-2,2C4.168,28.711,4,29.086,4,29.5    C4,30.328,4.671,31,5.5,31c0.414,0,0.789-0.168,1.06-0.44l2-2C8.832,28.289,9,27.914,9,27.5C9,26.672,8.329,26,7.5,26z M17.5,6    C18.329,6,19,5.328,19,4.5v-3C19,0.672,18.329,0,17.5,0S16,0.672,16,1.5v3C16,5.328,16.671,6,17.5,6z M27.5,9    c0.414,0,0.789-0.168,1.06-0.439l2-2C30.832,6.289,31,5.914,31,5.5C31,4.672,30.329,4,29.5,4c-0.414,0-0.789,0.168-1.061,0.44    l-2,2C26.168,6.711,26,7.086,26,7.5C26,8.328,26.671,9,27.5,9z M6.439,8.561C6.711,8.832,7.086,9,7.5,9C8.328,9,9,8.328,9,7.5    c0-0.414-0.168-0.789-0.439-1.061l-2-2C6.289,4.168,5.914,4,5.5,4C4.672,4,4,4.672,4,5.5c0,0.414,0.168,0.789,0.439,1.06    L6.439,8.561z M33.5,16h-3c-0.828,0-1.5,0.672-1.5,1.5s0.672,1.5,1.5,1.5h3c0.828,0,1.5-0.672,1.5-1.5S34.328,16,33.5,16z     M28.561,26.439C28.289,26.168,27.914,26,27.5,26c-0.828,0-1.5,0.672-1.5,1.5c0,0.414,0.168,0.789,0.439,1.06l2,2    C28.711,30.832,29.086,31,29.5,31c0.828,0,1.5-0.672,1.5-1.5c0-0.414-0.168-0.789-0.439-1.061L28.561,26.439z M17.5,29    c-0.829,0-1.5,0.672-1.5,1.5v3c0,0.828,0.671,1.5,1.5,1.5s1.5-0.672,1.5-1.5v-3C19,29.672,18.329,29,17.5,29z M17.5,7    C11.71,7,7,11.71,7,17.5S11.71,28,17.5,28S28,23.29,28,17.5S23.29,7,17.5,7z M17.5,25c-4.136,0-7.5-3.364-7.5-7.5    c0-4.136,3.364-7.5,7.5-7.5c4.136,0,7.5,3.364,7.5,7.5C25,21.636,21.636,25,17.5,25z"></path>
 | 
					          <path d="M6,17.5C6,16.672,5.328,16,4.5,16h-3C0.672,16,0,16.672,0,17.5    S0.672,19,1.5,19h3C5.328,19,6,18.328,6,17.5z M7.5,26c-0.414,0-0.789,0.168-1.061,0.439l-2,2C4.168,28.711,4,29.086,4,29.5    C4,30.328,4.671,31,5.5,31c0.414,0,0.789-0.168,1.06-0.44l2-2C8.832,28.289,9,27.914,9,27.5C9,26.672,8.329,26,7.5,26z M17.5,6    C18.329,6,19,5.328,19,4.5v-3C19,0.672,18.329,0,17.5,0S16,0.672,16,1.5v3C16,5.328,16.671,6,17.5,6z M27.5,9    c0.414,0,0.789-0.168,1.06-0.439l2-2C30.832,6.289,31,5.914,31,5.5C31,4.672,30.329,4,29.5,4c-0.414,0-0.789,0.168-1.061,0.44    l-2,2C26.168,6.711,26,7.086,26,7.5C26,8.328,26.671,9,27.5,9z M6.439,8.561C6.711,8.832,7.086,9,7.5,9C8.328,9,9,8.328,9,7.5    c0-0.414-0.168-0.789-0.439-1.061l-2-2C6.289,4.168,5.914,4,5.5,4C4.672,4,4,4.672,4,5.5c0,0.414,0.168,0.789,0.439,1.06    L6.439,8.561z M33.5,16h-3c-0.828,0-1.5,0.672-1.5,1.5s0.672,1.5,1.5,1.5h3c0.828,0,1.5-0.672,1.5-1.5S34.328,16,33.5,16z     M28.561,26.439C28.289,26.168,27.914,26,27.5,26c-0.828,0-1.5,0.672-1.5,1.5c0,0.414,0.168,0.789,0.439,1.06l2,2    C28.711,30.832,29.086,31,29.5,31c0.828,0,1.5-0.672,1.5-1.5c0-0.414-0.168-0.789-0.439-1.061L28.561,26.439z M17.5,29    c-0.829,0-1.5,0.672-1.5,1.5v3c0,0.828,0.671,1.5,1.5,1.5s1.5-0.672,1.5-1.5v-3C19,29.672,18.329,29,17.5,29z M17.5,7    C11.71,7,7,11.71,7,17.5S11.71,28,17.5,28S28,23.29,28,17.5S23.29,7,17.5,7z M17.5,25c-4.136,0-7.5-3.364-7.5-7.5    c0-4.136,3.364-7.5,7.5-7.5c4.136,0,7.5,3.364,7.5,7.5C25,21.636,21.636,25,17.5,25z"></path>
 | 
				
			||||||
        </svg>
 | 
					        </svg>
 | 
				
			||||||
      </label>
 | 
					      </label>
 | 
				
			||||||
@@ -38,7 +39,7 @@ function Darkmode({ displayClass }: QuartzComponentProps) {
 | 
				
			|||||||
          style="enable-background:new 0 0 100 100"
 | 
					          style="enable-background:new 0 0 100 100"
 | 
				
			||||||
          xmlSpace="preserve"
 | 
					          xmlSpace="preserve"
 | 
				
			||||||
        >
 | 
					        >
 | 
				
			||||||
          <title>Dark mode</title>
 | 
					          <title>{i18n(cfg.locale, "darkmode.lightMode")}</title>
 | 
				
			||||||
          <path d="M96.76,66.458c-0.853-0.852-2.15-1.064-3.23-0.534c-6.063,2.991-12.858,4.571-19.655,4.571  C62.022,70.495,50.88,65.88,42.5,57.5C29.043,44.043,25.658,23.536,34.076,6.47c0.532-1.08,0.318-2.379-0.534-3.23  c-0.851-0.852-2.15-1.064-3.23-0.534c-4.918,2.427-9.375,5.619-13.246,9.491c-9.447,9.447-14.65,22.008-14.65,35.369  c0,13.36,5.203,25.921,14.65,35.368s22.008,14.65,35.368,14.65c13.361,0,25.921-5.203,35.369-14.65  c3.872-3.871,7.064-8.328,9.491-13.246C97.826,68.608,97.611,67.309,96.76,66.458z"></path>
 | 
					          <path d="M96.76,66.458c-0.853-0.852-2.15-1.064-3.23-0.534c-6.063,2.991-12.858,4.571-19.655,4.571  C62.022,70.495,50.88,65.88,42.5,57.5C29.043,44.043,25.658,23.536,34.076,6.47c0.532-1.08,0.318-2.379-0.534-3.23  c-0.851-0.852-2.15-1.064-3.23-0.534c-4.918,2.427-9.375,5.619-13.246,9.491c-9.447,9.447-14.65,22.008-14.65,35.369  c0,13.36,5.203,25.921,14.65,35.368s22.008,14.65,35.368,14.65c13.361,0,25.921-5.203,35.369-14.65  c3.872-3.871,7.064-8.328,9.491-13.246C97.826,68.608,97.611,67.309,96.76,66.458z"></path>
 | 
				
			||||||
        </svg>
 | 
					        </svg>
 | 
				
			||||||
      </label>
 | 
					      </label>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,20 +1,22 @@
 | 
				
			|||||||
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
 | 
					import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
 | 
				
			||||||
import style from "./styles/footer.scss"
 | 
					import style from "./styles/footer.scss"
 | 
				
			||||||
import { version } from "../../package.json"
 | 
					import { version } from "../../package.json"
 | 
				
			||||||
 | 
					import { i18n } from "../i18n/i18next"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface Options {
 | 
					interface Options {
 | 
				
			||||||
  links: Record<string, string>
 | 
					  links: Record<string, string>
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default ((opts?: Options) => {
 | 
					export default ((opts?: Options) => {
 | 
				
			||||||
  function Footer({ displayClass }: QuartzComponentProps) {
 | 
					  function Footer({ displayClass, cfg }: QuartzComponentProps) {
 | 
				
			||||||
    const year = new Date().getFullYear()
 | 
					    const year = new Date().getFullYear()
 | 
				
			||||||
    const links = opts?.links ?? []
 | 
					    const links = opts?.links ?? []
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
      <footer class={`${displayClass ?? ""}`}>
 | 
					      <footer class={`${displayClass ?? ""}`}>
 | 
				
			||||||
        <hr />
 | 
					        <hr />
 | 
				
			||||||
        <p>
 | 
					        <p>
 | 
				
			||||||
          Created with <a href="https://quartz.jzhao.xyz/">Quartz v{version}</a>, © {year}
 | 
					          {i18n(cfg.locale, "footer.createdWith")}{" "}
 | 
				
			||||||
 | 
					          <a href="https://quartz.jzhao.xyz/">Quartz v{version}</a>, © {year}
 | 
				
			||||||
        </p>
 | 
					        </p>
 | 
				
			||||||
        <ul>
 | 
					        <ul>
 | 
				
			||||||
          {Object.entries(links).map(([text, link]) => (
 | 
					          {Object.entries(links).map(([text, link]) => (
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,7 @@ import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
 | 
				
			|||||||
// @ts-ignore
 | 
					// @ts-ignore
 | 
				
			||||||
import script from "./scripts/graph.inline"
 | 
					import script from "./scripts/graph.inline"
 | 
				
			||||||
import style from "./styles/graph.scss"
 | 
					import style from "./styles/graph.scss"
 | 
				
			||||||
 | 
					import { i18n } from "../i18n/i18next"
 | 
				
			||||||
import { classNames } from "../util/lang"
 | 
					import { classNames } from "../util/lang"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface D3Config {
 | 
					export interface D3Config {
 | 
				
			||||||
@@ -53,12 +54,12 @@ const defaultOptions: GraphOptions = {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default ((opts?: GraphOptions) => {
 | 
					export default ((opts?: GraphOptions) => {
 | 
				
			||||||
  function Graph({ displayClass }: QuartzComponentProps) {
 | 
					  function Graph({ displayClass, cfg }: QuartzComponentProps) {
 | 
				
			||||||
    const localGraph = { ...defaultOptions.localGraph, ...opts?.localGraph }
 | 
					    const localGraph = { ...defaultOptions.localGraph, ...opts?.localGraph }
 | 
				
			||||||
    const globalGraph = { ...defaultOptions.globalGraph, ...opts?.globalGraph }
 | 
					    const globalGraph = { ...defaultOptions.globalGraph, ...opts?.globalGraph }
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
      <div class={classNames(displayClass, "graph")}>
 | 
					      <div class={classNames(displayClass, "graph")}>
 | 
				
			||||||
        <h3>Graph View</h3>
 | 
					        <h3>{i18n(cfg.locale, "graph.graphView")}</h3>
 | 
				
			||||||
        <div class="graph-outer">
 | 
					        <div class="graph-outer">
 | 
				
			||||||
          <div id="graph-container" data-cfg={JSON.stringify(localGraph)}></div>
 | 
					          <div id="graph-container" data-cfg={JSON.stringify(localGraph)}></div>
 | 
				
			||||||
          <svg
 | 
					          <svg
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,11 +1,13 @@
 | 
				
			|||||||
 | 
					import { i18n } from "../i18n/i18next"
 | 
				
			||||||
import { FullSlug, _stripSlashes, joinSegments, pathToRoot } from "../util/path"
 | 
					import { FullSlug, _stripSlashes, joinSegments, pathToRoot } from "../util/path"
 | 
				
			||||||
import { JSResourceToScriptElement } from "../util/resources"
 | 
					import { JSResourceToScriptElement } from "../util/resources"
 | 
				
			||||||
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
 | 
					import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default (() => {
 | 
					export default (() => {
 | 
				
			||||||
  function Head({ cfg, fileData, externalResources }: QuartzComponentProps) {
 | 
					  function Head({ cfg, fileData, externalResources }: QuartzComponentProps) {
 | 
				
			||||||
    const title = fileData.frontmatter?.title ?? "Untitled"
 | 
					    const title = fileData.frontmatter?.title ?? i18n(cfg.locale, "head.untitled")
 | 
				
			||||||
    const description = fileData.description?.trim() ?? "No description provided"
 | 
					    const description =
 | 
				
			||||||
 | 
					      fileData.description?.trim() ?? i18n(cfg.locale, "head.noDescriptionProvided")
 | 
				
			||||||
    const { css, js } = externalResources
 | 
					    const { css, js } = externalResources
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const url = new URL(`https://${cfg.baseUrl ?? "example.com"}`)
 | 
					    const url = new URL(`https://${cfg.baseUrl ?? "example.com"}`)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,6 +5,7 @@ import { byDateAndAlphabetical } from "./PageList"
 | 
				
			|||||||
import style from "./styles/recentNotes.scss"
 | 
					import style from "./styles/recentNotes.scss"
 | 
				
			||||||
import { Date, getDate } from "./Date"
 | 
					import { Date, getDate } from "./Date"
 | 
				
			||||||
import { GlobalConfiguration } from "../cfg"
 | 
					import { GlobalConfiguration } from "../cfg"
 | 
				
			||||||
 | 
					import { i18n } from "../i18n/i18next"
 | 
				
			||||||
import { classNames } from "../util/lang"
 | 
					import { classNames } from "../util/lang"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface Options {
 | 
					interface Options {
 | 
				
			||||||
@@ -70,7 +71,13 @@ export default ((userOpts?: Partial<Options>) => {
 | 
				
			|||||||
        </ul>
 | 
					        </ul>
 | 
				
			||||||
        {opts.linkToMore && remaining > 0 && (
 | 
					        {opts.linkToMore && remaining > 0 && (
 | 
				
			||||||
          <p>
 | 
					          <p>
 | 
				
			||||||
            <a href={resolveRelative(fileData.slug!, opts.linkToMore)}>See {remaining} more →</a>
 | 
					            <a href={resolveRelative(fileData.slug!, opts.linkToMore)}>
 | 
				
			||||||
 | 
					              {" "}
 | 
				
			||||||
 | 
					              {i18n(cfg.locale, "recentNotes.seeRemainingMore", {
 | 
				
			||||||
 | 
					                remaining: remaining.toString(),
 | 
				
			||||||
 | 
					              })}{" "}
 | 
				
			||||||
 | 
					              →
 | 
				
			||||||
 | 
					            </a>
 | 
				
			||||||
          </p>
 | 
					          </p>
 | 
				
			||||||
        )}
 | 
					        )}
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,6 +3,7 @@ import style from "./styles/search.scss"
 | 
				
			|||||||
// @ts-ignore
 | 
					// @ts-ignore
 | 
				
			||||||
import script from "./scripts/search.inline"
 | 
					import script from "./scripts/search.inline"
 | 
				
			||||||
import { classNames } from "../util/lang"
 | 
					import { classNames } from "../util/lang"
 | 
				
			||||||
 | 
					import { i18n } from "../i18n/i18next"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface SearchOptions {
 | 
					export interface SearchOptions {
 | 
				
			||||||
  enablePreview: boolean
 | 
					  enablePreview: boolean
 | 
				
			||||||
@@ -13,13 +14,13 @@ const defaultOptions: SearchOptions = {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default ((userOpts?: Partial<SearchOptions>) => {
 | 
					export default ((userOpts?: Partial<SearchOptions>) => {
 | 
				
			||||||
  function Search({ displayClass }: QuartzComponentProps) {
 | 
					  function Search({ displayClass, cfg }: QuartzComponentProps) {
 | 
				
			||||||
    const opts = { ...defaultOptions, ...userOpts }
 | 
					    const opts = { ...defaultOptions, ...userOpts }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
      <div class={classNames(displayClass, "search")}>
 | 
					      <div class={classNames(displayClass, "search")}>
 | 
				
			||||||
        <div id="search-icon">
 | 
					        <div id="search-icon">
 | 
				
			||||||
          <p>Search</p>
 | 
					          <p>{i18n(cfg.locale, "search")}</p>
 | 
				
			||||||
          <div></div>
 | 
					          <div></div>
 | 
				
			||||||
          <svg
 | 
					          <svg
 | 
				
			||||||
            tabIndex={0}
 | 
					            tabIndex={0}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,6 +5,7 @@ import { classNames } from "../util/lang"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// @ts-ignore
 | 
					// @ts-ignore
 | 
				
			||||||
import script from "./scripts/toc.inline"
 | 
					import script from "./scripts/toc.inline"
 | 
				
			||||||
 | 
					import { i18n } from "../i18n/i18next"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface Options {
 | 
					interface Options {
 | 
				
			||||||
  layout: "modern" | "legacy"
 | 
					  layout: "modern" | "legacy"
 | 
				
			||||||
@@ -14,7 +15,7 @@ const defaultOptions: Options = {
 | 
				
			|||||||
  layout: "modern",
 | 
					  layout: "modern",
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function TableOfContents({ fileData, displayClass }: QuartzComponentProps) {
 | 
					function TableOfContents({ fileData, displayClass, cfg }: QuartzComponentProps) {
 | 
				
			||||||
  if (!fileData.toc) {
 | 
					  if (!fileData.toc) {
 | 
				
			||||||
    return null
 | 
					    return null
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -22,7 +23,7 @@ function TableOfContents({ fileData, displayClass }: QuartzComponentProps) {
 | 
				
			|||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <div class={classNames(displayClass, "toc")}>
 | 
					    <div class={classNames(displayClass, "toc")}>
 | 
				
			||||||
      <button type="button" id="toc" class={fileData.collapseToc ? "collapsed" : ""}>
 | 
					      <button type="button" id="toc" class={fileData.collapseToc ? "collapsed" : ""}>
 | 
				
			||||||
        <h3>Table of Contents</h3>
 | 
					        <h3>{i18n(cfg.locale, "tableOfContent")}</h3>
 | 
				
			||||||
        <svg
 | 
					        <svg
 | 
				
			||||||
          xmlns="http://www.w3.org/2000/svg"
 | 
					          xmlns="http://www.w3.org/2000/svg"
 | 
				
			||||||
          width="24"
 | 
					          width="24"
 | 
				
			||||||
@@ -55,15 +56,14 @@ function TableOfContents({ fileData, displayClass }: QuartzComponentProps) {
 | 
				
			|||||||
TableOfContents.css = modernStyle
 | 
					TableOfContents.css = modernStyle
 | 
				
			||||||
TableOfContents.afterDOMLoaded = script
 | 
					TableOfContents.afterDOMLoaded = script
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function LegacyTableOfContents({ fileData }: QuartzComponentProps) {
 | 
					function LegacyTableOfContents({ fileData, cfg }: QuartzComponentProps) {
 | 
				
			||||||
  if (!fileData.toc) {
 | 
					  if (!fileData.toc) {
 | 
				
			||||||
    return null
 | 
					    return null
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <details id="toc" open={!fileData.collapseToc}>
 | 
					    <details id="toc" open={!fileData.collapseToc}>
 | 
				
			||||||
      <summary>
 | 
					      <summary>
 | 
				
			||||||
        <h3>Table of Contents</h3>
 | 
					        <h3>{i18n(cfg.locale, "tableOfContent")}</h3>
 | 
				
			||||||
      </summary>
 | 
					      </summary>
 | 
				
			||||||
      <ul>
 | 
					      <ul>
 | 
				
			||||||
        {fileData.toc.map((tocEntry) => (
 | 
					        {fileData.toc.map((tocEntry) => (
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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 (
 | 
					  return (
 | 
				
			||||||
    <article class="popover-hint">
 | 
					    <article class="popover-hint">
 | 
				
			||||||
      <h1>404</h1>
 | 
					      <h1>404</h1>
 | 
				
			||||||
      <p>Either this page is private or doesn't exist.</p>
 | 
					      <p>{i18n(cfg.locale, "404")}</p>
 | 
				
			||||||
    </article>
 | 
					    </article>
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,6 +7,7 @@ import { _stripSlashes, simplifySlug } from "../../util/path"
 | 
				
			|||||||
import { Root } from "hast"
 | 
					import { Root } from "hast"
 | 
				
			||||||
import { pluralize } from "../../util/lang"
 | 
					import { pluralize } from "../../util/lang"
 | 
				
			||||||
import { htmlToJsx } from "../../util/jsx"
 | 
					import { htmlToJsx } from "../../util/jsx"
 | 
				
			||||||
 | 
					import { i18n } from "../../i18n/i18next"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface FolderContentOptions {
 | 
					interface FolderContentOptions {
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
@@ -23,7 +24,7 @@ export default ((opts?: Partial<FolderContentOptions>) => {
 | 
				
			|||||||
  const options: FolderContentOptions = { ...defaultOptions, ...opts }
 | 
					  const options: FolderContentOptions = { ...defaultOptions, ...opts }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  function FolderContent(props: QuartzComponentProps) {
 | 
					  function FolderContent(props: QuartzComponentProps) {
 | 
				
			||||||
    const { tree, fileData, allFiles } = props
 | 
					    const { tree, fileData, allFiles, cfg } = props
 | 
				
			||||||
    const folderSlug = _stripSlashes(simplifySlug(fileData.slug!))
 | 
					    const folderSlug = _stripSlashes(simplifySlug(fileData.slug!))
 | 
				
			||||||
    const allPagesInFolder = allFiles.filter((file) => {
 | 
					    const allPagesInFolder = allFiles.filter((file) => {
 | 
				
			||||||
      const fileSlug = _stripSlashes(simplifySlug(file.slug!))
 | 
					      const fileSlug = _stripSlashes(simplifySlug(file.slug!))
 | 
				
			||||||
@@ -52,7 +53,10 @@ export default ((opts?: Partial<FolderContentOptions>) => {
 | 
				
			|||||||
        </article>
 | 
					        </article>
 | 
				
			||||||
        <div class="page-listing">
 | 
					        <div class="page-listing">
 | 
				
			||||||
          {options.showFolderCount && (
 | 
					          {options.showFolderCount && (
 | 
				
			||||||
            <p>{pluralize(allPagesInFolder.length, "item")} under this folder.</p>
 | 
					            <p>
 | 
				
			||||||
 | 
					              {pluralize(allPagesInFolder.length, i18n(cfg.locale, "common.item"))}{" "}
 | 
				
			||||||
 | 
					              {i18n(cfg.locale, "folderContent.underThisFolder")}.
 | 
				
			||||||
 | 
					            </p>
 | 
				
			||||||
          )}
 | 
					          )}
 | 
				
			||||||
          <div>
 | 
					          <div>
 | 
				
			||||||
            <PageList {...listProps} />
 | 
					            <PageList {...listProps} />
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,10 +6,11 @@ import { QuartzPluginData } from "../../plugins/vfile"
 | 
				
			|||||||
import { Root } from "hast"
 | 
					import { Root } from "hast"
 | 
				
			||||||
import { pluralize } from "../../util/lang"
 | 
					import { pluralize } from "../../util/lang"
 | 
				
			||||||
import { htmlToJsx } from "../../util/jsx"
 | 
					import { htmlToJsx } from "../../util/jsx"
 | 
				
			||||||
 | 
					import { i18n } from "../../i18n/i18next"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const numPages = 10
 | 
					const numPages = 10
 | 
				
			||||||
function TagContent(props: QuartzComponentProps) {
 | 
					function TagContent(props: QuartzComponentProps) {
 | 
				
			||||||
  const { tree, fileData, allFiles } = props
 | 
					  const { tree, fileData, allFiles, cfg } = props
 | 
				
			||||||
  const slug = fileData.slug
 | 
					  const slug = fileData.slug
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (!(slug?.startsWith("tags/") || slug === "tags")) {
 | 
					  if (!(slug?.startsWith("tags/") || slug === "tags")) {
 | 
				
			||||||
@@ -43,7 +44,10 @@ function TagContent(props: QuartzComponentProps) {
 | 
				
			|||||||
        <article>
 | 
					        <article>
 | 
				
			||||||
          <p>{content}</p>
 | 
					          <p>{content}</p>
 | 
				
			||||||
        </article>
 | 
					        </article>
 | 
				
			||||||
        <p>Found {tags.length} total tags.</p>
 | 
					        <p>
 | 
				
			||||||
 | 
					          {i18n(cfg.locale, "tagContent.found")} {tags.length}{" "}
 | 
				
			||||||
 | 
					          {i18n(cfg.locale, "tagContent.totalTags")}.
 | 
				
			||||||
 | 
					        </p>
 | 
				
			||||||
        <div>
 | 
					        <div>
 | 
				
			||||||
          {tags.map((tag) => {
 | 
					          {tags.map((tag) => {
 | 
				
			||||||
            const pages = tagItemMap.get(tag)!
 | 
					            const pages = tagItemMap.get(tag)!
 | 
				
			||||||
@@ -64,8 +68,10 @@ function TagContent(props: QuartzComponentProps) {
 | 
				
			|||||||
                {content && <p>{content}</p>}
 | 
					                {content && <p>{content}</p>}
 | 
				
			||||||
                <div class="page-listing">
 | 
					                <div class="page-listing">
 | 
				
			||||||
                  <p>
 | 
					                  <p>
 | 
				
			||||||
                    {pluralize(pages.length, "item")} with this tag.{" "}
 | 
					                    {pluralize(pages.length, i18n(cfg.locale, "common.item"))}{" "}
 | 
				
			||||||
                    {pages.length > numPages && `Showing first ${numPages}.`}
 | 
					                    {i18n(cfg.locale, "tagContent.withThisTag")}.{" "}
 | 
				
			||||||
 | 
					                    {pages.length > numPages &&
 | 
				
			||||||
 | 
					                      `${i18n(cfg.locale, "tagContent.showingFirst")} ${numPages}.`}
 | 
				
			||||||
                  </p>
 | 
					                  </p>
 | 
				
			||||||
                  <PageList limit={numPages} {...listProps} />
 | 
					                  <PageList limit={numPages} {...listProps} />
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
@@ -86,7 +92,10 @@ function TagContent(props: QuartzComponentProps) {
 | 
				
			|||||||
      <div class={classes}>
 | 
					      <div class={classes}>
 | 
				
			||||||
        <article>{content}</article>
 | 
					        <article>{content}</article>
 | 
				
			||||||
        <div class="page-listing">
 | 
					        <div class="page-listing">
 | 
				
			||||||
          <p>{pluralize(pages.length, "item")} with this tag.</p>
 | 
					          <p>
 | 
				
			||||||
 | 
					            {pluralize(pages.length, i18n(cfg.locale, "common.item"))}{" "}
 | 
				
			||||||
 | 
					            {i18n(cfg.locale, "tagContent.withThisTag")}.
 | 
				
			||||||
 | 
					          </p>
 | 
				
			||||||
          <div>
 | 
					          <div>
 | 
				
			||||||
            <PageList {...listProps} />
 | 
					            <PageList {...listProps} />
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -306,7 +306,13 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => {
 | 
				
			|||||||
    itemTile.classList.add("result-card")
 | 
					    itemTile.classList.add("result-card")
 | 
				
			||||||
    itemTile.id = slug
 | 
					    itemTile.id = slug
 | 
				
			||||||
    itemTile.href = resolveUrl(slug).toString()
 | 
					    itemTile.href = resolveUrl(slug).toString()
 | 
				
			||||||
    itemTile.innerHTML = `<h3>${title}</h3>${htmlTags}<p class="preview">${content}</p>`
 | 
					    itemTile.innerHTML = `<h3>${title}</h3>${htmlTags}${
 | 
				
			||||||
 | 
					      enablePreview && window.innerWidth > 600 ? "" : `<p>${content}</p>`
 | 
				
			||||||
 | 
					    }`
 | 
				
			||||||
 | 
					    itemTile.addEventListener("click", (event) => {
 | 
				
			||||||
 | 
					      if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) return
 | 
				
			||||||
 | 
					      hideSearch()
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const handler = (event: MouseEvent) => {
 | 
					    const handler = (event: MouseEvent) => {
 | 
				
			||||||
      if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) return
 | 
					      if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) return
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										37
									
								
								quartz/i18n/i18next.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								quartz/i18n/i18next.ts
									
									
									
									
									
										Normal file
									
								
							@@ -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<string, unknown> =
 | 
				
			||||||
 | 
					      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()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										37
									
								
								quartz/i18n/locales/en.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								quartz/i18n/locales/en.json
									
									
									
									
									
										Normal file
									
								
							@@ -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"
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										38
									
								
								quartz/i18n/locales/fr.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								quartz/i18n/locales/fr.json
									
									
									
									
									
										Normal file
									
								
							@@ -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é"
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user