parent
dff4b06313
commit
36e4cc41a9
@ -278,7 +278,7 @@ export const ContentPage: QuartzEmitterPlugin = () => {
|
|||||||
allFiles,
|
allFiles,
|
||||||
}
|
}
|
||||||
|
|
||||||
const content = renderPage(slug, componentData, opts, externalResources)
|
const content = renderPage(cfg, slug, componentData, opts, externalResources)
|
||||||
const fp = await emit({
|
const fp = await emit({
|
||||||
content,
|
content,
|
||||||
slug: file.data.slug!,
|
slug: file.data.slug!,
|
||||||
|
@ -27,6 +27,7 @@ This part of the configuration concerns anything that can affect the whole site.
|
|||||||
- `null`: don't use analytics;
|
- `null`: don't use analytics;
|
||||||
- `{ provider: 'plausible' }`: use [Plausible](https://plausible.io/), a privacy-friendly alternative to Google Analytics; or
|
- `{ provider: 'plausible' }`: use [Plausible](https://plausible.io/), a privacy-friendly alternative to Google Analytics; or
|
||||||
- `{ provider: 'google', tagId: <your-google-tag> }`: use Google Analytics
|
- `{ provider: 'google', tagId: <your-google-tag> }`: 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.
|
- `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`
|
- 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.
|
- 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.
|
||||||
|
18
docs/features/i18n.md
Normal file
18
docs/features/i18n.md
Normal file
@ -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`.
|
@ -31,7 +31,7 @@ If you prefer instructions in a video format you can try following Nicole van de
|
|||||||
|
|
||||||
## 🔧 Features
|
## 🔧 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
|
- Hot-reload for both configuration and content
|
||||||
- Simple JSX layouts and [[creating components|page components]]
|
- Simple JSX layouts and [[creating components|page components]]
|
||||||
- [[SPA Routing|Ridiculously fast page loads]] and tiny bundle sizes
|
- [[SPA Routing|Ridiculously fast page loads]] and tiny bundle sizes
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { ValidDateType } from "./components/Date"
|
import { ValidDateType } from "./components/Date"
|
||||||
import { QuartzComponent } from "./components/types"
|
import { QuartzComponent } from "./components/types"
|
||||||
|
import { ValidLocale } from "./i18n"
|
||||||
import { PluginTypes } from "./plugins/types"
|
import { PluginTypes } from "./plugins/types"
|
||||||
import { Theme } from "./util/theme"
|
import { Theme } from "./util/theme"
|
||||||
|
|
||||||
@ -39,9 +40,12 @@ export interface GlobalConfiguration {
|
|||||||
/**
|
/**
|
||||||
* 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)
|
* 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 {
|
export interface QuartzConfig {
|
||||||
|
@ -9,6 +9,7 @@ function ArticleTitle({ fileData, displayClass }: QuartzComponentProps) {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ArticleTitle.css = `
|
ArticleTitle.css = `
|
||||||
.article-title {
|
.article-title {
|
||||||
margin: 2rem 0 0 0;
|
margin: 2rem 0 0 0;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
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 { i18n } from "../i18n"
|
||||||
import { classNames } from "../util/lang"
|
import { classNames } from "../util/lang"
|
||||||
|
|
||||||
function Backlinks({ fileData, allFiles, displayClass, cfg }: QuartzComponentProps) {
|
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))
|
const backlinkFiles = allFiles.filter((file) => file.links?.includes(slug))
|
||||||
return (
|
return (
|
||||||
<div class={classNames(displayClass, "backlinks")}>
|
<div class={classNames(displayClass, "backlinks")}>
|
||||||
<h3>{i18n(cfg.locale, "backlinks.backlinks")}</h3>
|
<h3>{i18n(cfg.locale).components.backlinks.title}</h3>
|
||||||
<ul class="overflow">
|
<ul class="overflow">
|
||||||
{backlinkFiles.length > 0 ? (
|
{backlinkFiles.length > 0 ? (
|
||||||
backlinkFiles.map((f) => (
|
backlinkFiles.map((f) => (
|
||||||
@ -20,7 +20,7 @@ function Backlinks({ fileData, allFiles, displayClass, cfg }: QuartzComponentPro
|
|||||||
</li>
|
</li>
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
<li>{i18n(cfg.locale, "backlinks.noBacklinksFound")}</li>
|
<li>{i18n(cfg.locale).components.backlinks.noBacklinksFound}</li>
|
||||||
)}
|
)}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
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 { i18n } from "../i18n"
|
||||||
import { classNames } from "../util/lang"
|
import { classNames } from "../util/lang"
|
||||||
|
|
||||||
function Darkmode({ displayClass, cfg }: QuartzComponentProps) {
|
function Darkmode({ displayClass, cfg }: QuartzComponentProps) {
|
||||||
@ -23,7 +23,7 @@ function Darkmode({ displayClass, cfg }: QuartzComponentProps) {
|
|||||||
style="enable-background:new 0 0 35 35"
|
style="enable-background:new 0 0 35 35"
|
||||||
xmlSpace="preserve"
|
xmlSpace="preserve"
|
||||||
>
|
>
|
||||||
<title>{i18n(cfg.locale, "darkmode.lightMode")}</title>
|
<title>{i18n(cfg.locale).components.themeToggle.darkMode}</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>
|
||||||
@ -39,7 +39,7 @@ function Darkmode({ displayClass, cfg }: QuartzComponentProps) {
|
|||||||
style="enable-background:new 0 0 100 100"
|
style="enable-background:new 0 0 100 100"
|
||||||
xmlSpace="preserve"
|
xmlSpace="preserve"
|
||||||
>
|
>
|
||||||
<title>{i18n(cfg.locale, "darkmode.lightMode")}</title>
|
<title>{i18n(cfg.locale).components.themeToggle.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,9 +1,10 @@
|
|||||||
import { GlobalConfiguration } from "../cfg"
|
import { GlobalConfiguration } from "../cfg"
|
||||||
|
import { ValidLocale } from "../i18n"
|
||||||
import { QuartzPluginData } from "../plugins/vfile"
|
import { QuartzPluginData } from "../plugins/vfile"
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
date: Date
|
date: Date
|
||||||
locale?: string
|
locale?: ValidLocale
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ValidDateType = keyof Required<QuartzPluginData>["dates"]
|
export type ValidDateType = keyof Required<QuartzPluginData>["dates"]
|
||||||
@ -17,7 +18,7 @@ export function getDate(cfg: GlobalConfiguration, data: QuartzPluginData): Date
|
|||||||
return data.dates?.[cfg.defaultDateType]
|
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, {
|
return d.toLocaleDateString(locale, {
|
||||||
year: "numeric",
|
year: "numeric",
|
||||||
month: "short",
|
month: "short",
|
||||||
|
@ -6,10 +6,10 @@ import script from "./scripts/explorer.inline"
|
|||||||
import { ExplorerNode, FileNode, Options } from "./ExplorerNode"
|
import { ExplorerNode, FileNode, Options } from "./ExplorerNode"
|
||||||
import { QuartzPluginData } from "../plugins/vfile"
|
import { QuartzPluginData } from "../plugins/vfile"
|
||||||
import { classNames } from "../util/lang"
|
import { classNames } from "../util/lang"
|
||||||
|
import { i18n } from "../i18n"
|
||||||
|
|
||||||
// Options interface defined in `ExplorerNode` to avoid circular dependency
|
// Options interface defined in `ExplorerNode` to avoid circular dependency
|
||||||
const defaultOptions = {
|
const defaultOptions = {
|
||||||
title: "Explorer",
|
|
||||||
folderClickBehavior: "collapse",
|
folderClickBehavior: "collapse",
|
||||||
folderDefaultState: "collapsed",
|
folderDefaultState: "collapsed",
|
||||||
useSavedState: true,
|
useSavedState: true,
|
||||||
@ -75,7 +75,7 @@ export default ((userOpts?: Partial<Options>) => {
|
|||||||
jsonTree = JSON.stringify(folders)
|
jsonTree = JSON.stringify(folders)
|
||||||
}
|
}
|
||||||
|
|
||||||
function Explorer({ allFiles, displayClass, fileData }: QuartzComponentProps) {
|
function Explorer({ cfg, allFiles, displayClass, fileData }: QuartzComponentProps) {
|
||||||
constructFileTree(allFiles)
|
constructFileTree(allFiles)
|
||||||
return (
|
return (
|
||||||
<div class={classNames(displayClass, "explorer")}>
|
<div class={classNames(displayClass, "explorer")}>
|
||||||
@ -87,7 +87,7 @@ export default ((userOpts?: Partial<Options>) => {
|
|||||||
data-savestate={opts.useSavedState}
|
data-savestate={opts.useSavedState}
|
||||||
data-tree={jsonTree}
|
data-tree={jsonTree}
|
||||||
>
|
>
|
||||||
<h1>{opts.title}</h1>
|
<h1>{opts.title ?? i18n(cfg.locale).components.explorer.title}</h1>
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
width="14"
|
width="14"
|
||||||
|
@ -12,7 +12,7 @@ import {
|
|||||||
type OrderEntries = "sort" | "filter" | "map"
|
type OrderEntries = "sort" | "filter" | "map"
|
||||||
|
|
||||||
export interface Options {
|
export interface Options {
|
||||||
title: string
|
title?: string
|
||||||
folderDefaultState: "collapsed" | "open"
|
folderDefaultState: "collapsed" | "open"
|
||||||
folderClickBehavior: "collapse" | "link"
|
folderClickBehavior: "collapse" | "link"
|
||||||
useSavedState: boolean
|
useSavedState: boolean
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
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"
|
import { i18n } from "../i18n"
|
||||||
|
|
||||||
interface Options {
|
interface Options {
|
||||||
links: Record<string, string>
|
links: Record<string, string>
|
||||||
@ -15,8 +15,8 @@ export default ((opts?: Options) => {
|
|||||||
<footer class={`${displayClass ?? ""}`}>
|
<footer class={`${displayClass ?? ""}`}>
|
||||||
<hr />
|
<hr />
|
||||||
<p>
|
<p>
|
||||||
{i18n(cfg.locale, "footer.createdWith")}{" "}
|
{i18n(cfg.locale).components.footer.createdWith}{" "}
|
||||||
<a href="https://quartz.jzhao.xyz/">Quartz v{version}</a>, © {year}
|
<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,7 +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 { i18n } from "../i18n"
|
||||||
import { classNames } from "../util/lang"
|
import { classNames } from "../util/lang"
|
||||||
|
|
||||||
export interface D3Config {
|
export interface D3Config {
|
||||||
@ -59,7 +59,7 @@ export default ((opts?: GraphOptions) => {
|
|||||||
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>{i18n(cfg.locale, "graph.graphView")}</h3>
|
<h3>{i18n(cfg.locale).components.graph.title}</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,13 +1,13 @@
|
|||||||
import { i18n } from "../i18n/i18next"
|
import { i18n } from "../i18n"
|
||||||
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 ?? i18n(cfg.locale, "head.untitled")
|
const title = fileData.frontmatter?.title ?? i18n(cfg.locale).propertyDefaults.title
|
||||||
const description =
|
const description =
|
||||||
fileData.description?.trim() ?? i18n(cfg.locale, "head.noDescriptionProvided")
|
fileData.description?.trim() ?? i18n(cfg.locale).propertyDefaults.description
|
||||||
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"}`)
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import { pathToRoot } from "../util/path"
|
import { pathToRoot } from "../util/path"
|
||||||
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
|
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
|
||||||
import { classNames } from "../util/lang"
|
import { classNames } from "../util/lang"
|
||||||
|
import { i18n } from "../i18n"
|
||||||
|
|
||||||
function PageTitle({ fileData, cfg, displayClass }: QuartzComponentProps) {
|
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!)
|
const baseDir = pathToRoot(fileData.slug!)
|
||||||
return (
|
return (
|
||||||
<h1 class={classNames(displayClass, "page-title")}>
|
<h1 class={classNames(displayClass, "page-title")}>
|
||||||
|
@ -5,11 +5,11 @@ 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 { i18n } from "../i18n"
|
||||||
import { classNames } from "../util/lang"
|
import { classNames } from "../util/lang"
|
||||||
|
|
||||||
interface Options {
|
interface Options {
|
||||||
title: string
|
title?: string
|
||||||
limit: number
|
limit: number
|
||||||
linkToMore: SimpleSlug | false
|
linkToMore: SimpleSlug | false
|
||||||
filter: (f: QuartzPluginData) => boolean
|
filter: (f: QuartzPluginData) => boolean
|
||||||
@ -17,7 +17,6 @@ interface Options {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const defaultOptions = (cfg: GlobalConfiguration): Options => ({
|
const defaultOptions = (cfg: GlobalConfiguration): Options => ({
|
||||||
title: "Recent Notes",
|
|
||||||
limit: 3,
|
limit: 3,
|
||||||
linkToMore: false,
|
linkToMore: false,
|
||||||
filter: () => true,
|
filter: () => true,
|
||||||
@ -31,10 +30,10 @@ export default ((userOpts?: Partial<Options>) => {
|
|||||||
const remaining = Math.max(0, pages.length - opts.limit)
|
const remaining = Math.max(0, pages.length - opts.limit)
|
||||||
return (
|
return (
|
||||||
<div class={classNames(displayClass, "recent-notes")}>
|
<div class={classNames(displayClass, "recent-notes")}>
|
||||||
<h3>{opts.title}</h3>
|
<h3>{opts.title ?? i18n(cfg.locale).components.recentNotes.title}</h3>
|
||||||
<ul class="recent-ul">
|
<ul class="recent-ul">
|
||||||
{pages.slice(0, opts.limit).map((page) => {
|
{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 ?? []
|
const tags = page.frontmatter?.tags ?? []
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -72,11 +71,7 @@ export default ((userOpts?: Partial<Options>) => {
|
|||||||
{opts.linkToMore && remaining > 0 && (
|
{opts.linkToMore && remaining > 0 && (
|
||||||
<p>
|
<p>
|
||||||
<a href={resolveRelative(fileData.slug!, opts.linkToMore)}>
|
<a href={resolveRelative(fileData.slug!, opts.linkToMore)}>
|
||||||
{" "}
|
{i18n(cfg.locale).components.recentNotes.seeRemainingMore({ remaining })}
|
||||||
{i18n(cfg.locale, "recentNotes.seeRemainingMore", {
|
|
||||||
remaining: remaining.toString(),
|
|
||||||
})}{" "}
|
|
||||||
→
|
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
@ -3,7 +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"
|
import { i18n } from "../i18n"
|
||||||
|
|
||||||
export interface SearchOptions {
|
export interface SearchOptions {
|
||||||
enablePreview: boolean
|
enablePreview: boolean
|
||||||
@ -16,11 +16,11 @@ const defaultOptions: SearchOptions = {
|
|||||||
export default ((userOpts?: Partial<SearchOptions>) => {
|
export default ((userOpts?: Partial<SearchOptions>) => {
|
||||||
function Search({ displayClass, cfg }: QuartzComponentProps) {
|
function Search({ displayClass, cfg }: QuartzComponentProps) {
|
||||||
const opts = { ...defaultOptions, ...userOpts }
|
const opts = { ...defaultOptions, ...userOpts }
|
||||||
|
const searchPlaceholder = i18n(cfg.locale).components.search.searchBarPlaceholder
|
||||||
return (
|
return (
|
||||||
<div class={classNames(displayClass, "search")}>
|
<div class={classNames(displayClass, "search")}>
|
||||||
<div id="search-icon">
|
<div id="search-icon">
|
||||||
<p>{i18n(cfg.locale, "search")}</p>
|
<p>{i18n(cfg.locale).components.search.title}</p>
|
||||||
<div></div>
|
<div></div>
|
||||||
<svg
|
<svg
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
@ -44,8 +44,8 @@ export default ((userOpts?: Partial<SearchOptions>) => {
|
|||||||
id="search-bar"
|
id="search-bar"
|
||||||
name="search"
|
name="search"
|
||||||
type="text"
|
type="text"
|
||||||
aria-label="Search for something"
|
aria-label={searchPlaceholder}
|
||||||
placeholder="Search for something"
|
placeholder={searchPlaceholder}
|
||||||
/>
|
/>
|
||||||
<div id="search-layout" data-preview={opts.enablePreview}></div>
|
<div id="search-layout" data-preview={opts.enablePreview}></div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -5,7 +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"
|
import { i18n } from "../i18n"
|
||||||
|
|
||||||
interface Options {
|
interface Options {
|
||||||
layout: "modern" | "legacy"
|
layout: "modern" | "legacy"
|
||||||
@ -23,7 +23,7 @@ function TableOfContents({ fileData, displayClass, cfg }: 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>{i18n(cfg.locale, "tableOfContent")}</h3>
|
<h3>{i18n(cfg.locale).components.tableOfContents.title}</h3>
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
width="24"
|
width="24"
|
||||||
@ -63,7 +63,7 @@ function LegacyTableOfContents({ fileData, cfg }: QuartzComponentProps) {
|
|||||||
return (
|
return (
|
||||||
<details id="toc" open={!fileData.collapseToc}>
|
<details id="toc" open={!fileData.collapseToc}>
|
||||||
<summary>
|
<summary>
|
||||||
<h3>{i18n(cfg.locale, "tableOfContent")}</h3>
|
<h3>{i18n(cfg.locale).components.tableOfContents.title}</h3>
|
||||||
</summary>
|
</summary>
|
||||||
<ul>
|
<ul>
|
||||||
{fileData.toc.map((tocEntry) => (
|
{fileData.toc.map((tocEntry) => (
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { i18n } from "../../i18n/i18next"
|
import { i18n } from "../../i18n"
|
||||||
import { QuartzComponentConstructor, QuartzComponentProps } from "../types"
|
import { QuartzComponentConstructor, QuartzComponentProps } from "../types"
|
||||||
|
|
||||||
function NotFound({ cfg }: QuartzComponentProps) {
|
function NotFound({ cfg }: QuartzComponentProps) {
|
||||||
return (
|
return (
|
||||||
<article class="popover-hint">
|
<article class="popover-hint">
|
||||||
<h1>404</h1>
|
<h1>404</h1>
|
||||||
<p>{i18n(cfg.locale, "404")}</p>
|
<p>{i18n(cfg.locale).pages.error.notFound}</p>
|
||||||
</article>
|
</article>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -5,9 +5,8 @@ import style from "../styles/listPage.scss"
|
|||||||
import { PageList } from "../PageList"
|
import { PageList } from "../PageList"
|
||||||
import { _stripSlashes, simplifySlug } from "../../util/path"
|
import { _stripSlashes, simplifySlug } from "../../util/path"
|
||||||
import { Root } from "hast"
|
import { Root } from "hast"
|
||||||
import { pluralize } from "../../util/lang"
|
|
||||||
import { htmlToJsx } from "../../util/jsx"
|
import { htmlToJsx } from "../../util/jsx"
|
||||||
import { i18n } from "../../i18n/i18next"
|
import { i18n } from "../../i18n"
|
||||||
|
|
||||||
interface FolderContentOptions {
|
interface FolderContentOptions {
|
||||||
/**
|
/**
|
||||||
@ -54,8 +53,9 @@ export default ((opts?: Partial<FolderContentOptions>) => {
|
|||||||
<div class="page-listing">
|
<div class="page-listing">
|
||||||
{options.showFolderCount && (
|
{options.showFolderCount && (
|
||||||
<p>
|
<p>
|
||||||
{pluralize(allPagesInFolder.length, i18n(cfg.locale, "common.item"))}{" "}
|
{i18n(cfg.locale).pages.folderContent.itemsUnderFolder({
|
||||||
{i18n(cfg.locale, "folderContent.underThisFolder")}.
|
count: allPagesInFolder.length,
|
||||||
|
})}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
<div>
|
<div>
|
||||||
|
@ -4,9 +4,8 @@ import { PageList } from "../PageList"
|
|||||||
import { FullSlug, getAllSegmentPrefixes, simplifySlug } from "../../util/path"
|
import { FullSlug, getAllSegmentPrefixes, simplifySlug } from "../../util/path"
|
||||||
import { QuartzPluginData } from "../../plugins/vfile"
|
import { QuartzPluginData } from "../../plugins/vfile"
|
||||||
import { Root } from "hast"
|
import { Root } from "hast"
|
||||||
import { pluralize } from "../../util/lang"
|
|
||||||
import { htmlToJsx } from "../../util/jsx"
|
import { htmlToJsx } from "../../util/jsx"
|
||||||
import { i18n } from "../../i18n/i18next"
|
import { i18n } from "../../i18n"
|
||||||
|
|
||||||
const numPages = 10
|
const numPages = 10
|
||||||
function TagContent(props: QuartzComponentProps) {
|
function TagContent(props: QuartzComponentProps) {
|
||||||
@ -44,10 +43,7 @@ function TagContent(props: QuartzComponentProps) {
|
|||||||
<article>
|
<article>
|
||||||
<p>{content}</p>
|
<p>{content}</p>
|
||||||
</article>
|
</article>
|
||||||
<p>
|
<p>{i18n(cfg.locale).pages.tagContent.totalTags({ count: tags.length })}</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)!
|
||||||
@ -68,10 +64,12 @@ 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, i18n(cfg.locale, "common.item"))}{" "}
|
{i18n(cfg.locale).pages.tagContent.itemsUnderTag({ count: pages.length })}
|
||||||
{i18n(cfg.locale, "tagContent.withThisTag")}.{" "}
|
{pages.length > numPages && (
|
||||||
{pages.length > numPages &&
|
<span>
|
||||||
`${i18n(cfg.locale, "tagContent.showingFirst")} ${numPages}.`}
|
{i18n(cfg.locale).pages.tagContent.showingFirst({ count: numPages })}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</p>
|
</p>
|
||||||
<PageList limit={numPages} {...listProps} />
|
<PageList limit={numPages} {...listProps} />
|
||||||
</div>
|
</div>
|
||||||
@ -92,10 +90,7 @@ 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>
|
<p>{i18n(cfg.locale).pages.tagContent.itemsUnderTag({ count: pages.length })}</p>
|
||||||
{pluralize(pages.length, i18n(cfg.locale, "common.item"))}{" "}
|
|
||||||
{i18n(cfg.locale, "tagContent.withThisTag")}.
|
|
||||||
</p>
|
|
||||||
<div>
|
<div>
|
||||||
<PageList {...listProps} />
|
<PageList {...listProps} />
|
||||||
</div>
|
</div>
|
||||||
|
@ -7,6 +7,8 @@ import { FullSlug, RelativeURL, joinSegments, normalizeHastElement } from "../ut
|
|||||||
import { visit } from "unist-util-visit"
|
import { visit } from "unist-util-visit"
|
||||||
import { Root, Element, ElementContent } from "hast"
|
import { Root, Element, ElementContent } from "hast"
|
||||||
import { QuartzPluginData } from "../plugins/vfile"
|
import { QuartzPluginData } from "../plugins/vfile"
|
||||||
|
import { GlobalConfiguration } from "../cfg"
|
||||||
|
import { i18n } from "../i18n"
|
||||||
|
|
||||||
interface RenderComponents {
|
interface RenderComponents {
|
||||||
head: QuartzComponent
|
head: QuartzComponent
|
||||||
@ -63,6 +65,7 @@ function getOrComputeFileIndex(allFiles: QuartzPluginData[]): Map<FullSlug, Quar
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function renderPage(
|
export function renderPage(
|
||||||
|
cfg: GlobalConfiguration,
|
||||||
slug: FullSlug,
|
slug: FullSlug,
|
||||||
componentData: QuartzComponentProps,
|
componentData: QuartzComponentProps,
|
||||||
components: RenderComponents,
|
components: RenderComponents,
|
||||||
@ -136,7 +139,9 @@ export function renderPage(
|
|||||||
type: "element",
|
type: "element",
|
||||||
tagName: "a",
|
tagName: "a",
|
||||||
properties: { href: inner.properties?.href, class: ["internal"] },
|
properties: { href: inner.properties?.href, class: ["internal"] },
|
||||||
children: [{ type: "text", value: `Link to original` }],
|
children: [
|
||||||
|
{ type: "text", value: i18n(cfg.locale).components.transcludes.linkToOriginal },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
} else if (page.htmlAst) {
|
} else if (page.htmlAst) {
|
||||||
@ -147,7 +152,14 @@ export function renderPage(
|
|||||||
tagName: "h1",
|
tagName: "h1",
|
||||||
properties: {},
|
properties: {},
|
||||||
children: [
|
children: [
|
||||||
{ type: "text", value: page.frontmatter?.title ?? `Transclude of ${page.slug}` },
|
{
|
||||||
|
type: "text",
|
||||||
|
value:
|
||||||
|
page.frontmatter?.title ??
|
||||||
|
i18n(cfg.locale).components.transcludes.transcludeOf({
|
||||||
|
targetSlug: page.slug!,
|
||||||
|
}),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
...(page.htmlAst.children as ElementContent[]).map((child) =>
|
...(page.htmlAst.children as ElementContent[]).map((child) =>
|
||||||
@ -157,7 +169,9 @@ export function renderPage(
|
|||||||
type: "element",
|
type: "element",
|
||||||
tagName: "a",
|
tagName: "a",
|
||||||
properties: { href: inner.properties?.href, class: ["internal"] },
|
properties: { href: inner.properties?.href, class: ["internal"] },
|
||||||
children: [{ type: "text", value: `Link to original` }],
|
children: [
|
||||||
|
{ type: "text", value: i18n(cfg.locale).components.transcludes.linkToOriginal },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,37 +0,0 @@
|
|||||||
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()
|
|
||||||
}
|
|
11
quartz/i18n/index.ts
Normal file
11
quartz/i18n/index.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { Translation } from "./locales/definition"
|
||||||
|
import en from "./locales/en-US"
|
||||||
|
import fr from "./locales/fr-FR"
|
||||||
|
|
||||||
|
export const TRANSLATIONS = {
|
||||||
|
"en-US": en,
|
||||||
|
"fr-FR": fr,
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export const i18n = (locale: ValidLocale): Translation => TRANSLATIONS[locale]
|
||||||
|
export type ValidLocale = keyof typeof TRANSLATIONS
|
63
quartz/i18n/locales/definition.ts
Normal file
63
quartz/i18n/locales/definition.ts
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import { FullSlug } from "../../util/path"
|
||||||
|
|
||||||
|
export interface Translation {
|
||||||
|
propertyDefaults: {
|
||||||
|
title: string
|
||||||
|
description: string
|
||||||
|
}
|
||||||
|
components: {
|
||||||
|
backlinks: {
|
||||||
|
title: string
|
||||||
|
noBacklinksFound: string
|
||||||
|
}
|
||||||
|
themeToggle: {
|
||||||
|
lightMode: string
|
||||||
|
darkMode: string
|
||||||
|
}
|
||||||
|
explorer: {
|
||||||
|
title: string
|
||||||
|
}
|
||||||
|
footer: {
|
||||||
|
createdWith: string
|
||||||
|
}
|
||||||
|
graph: {
|
||||||
|
title: string
|
||||||
|
}
|
||||||
|
recentNotes: {
|
||||||
|
title: string
|
||||||
|
seeRemainingMore: (variables: { remaining: number }) => string
|
||||||
|
}
|
||||||
|
transcludes: {
|
||||||
|
transcludeOf: (variables: { targetSlug: FullSlug }) => string
|
||||||
|
linkToOriginal: string
|
||||||
|
}
|
||||||
|
search: {
|
||||||
|
title: string
|
||||||
|
searchBarPlaceholder: string
|
||||||
|
}
|
||||||
|
tableOfContents: {
|
||||||
|
title: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pages: {
|
||||||
|
rss: {
|
||||||
|
recentNotes: string
|
||||||
|
lastFewNotes: (variables: { count: number }) => string
|
||||||
|
}
|
||||||
|
error: {
|
||||||
|
title: string
|
||||||
|
notFound: string
|
||||||
|
}
|
||||||
|
folderContent: {
|
||||||
|
folder: string
|
||||||
|
itemsUnderFolder: (variables: { count: number }) => string
|
||||||
|
}
|
||||||
|
tagContent: {
|
||||||
|
tag: string
|
||||||
|
tagIndex: string
|
||||||
|
itemsUnderTag: (variables: { count: number }) => string
|
||||||
|
showingFirst: (variables: { count: number }) => string
|
||||||
|
totalTags: (variables: { count: number }) => string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
65
quartz/i18n/locales/en-US.ts
Normal file
65
quartz/i18n/locales/en-US.ts
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import { Translation } from "./definition"
|
||||||
|
|
||||||
|
export default {
|
||||||
|
propertyDefaults: {
|
||||||
|
title: "Untitled",
|
||||||
|
description: "No description provided",
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
backlinks: {
|
||||||
|
title: "Backlinks",
|
||||||
|
noBacklinksFound: "No backlinks found",
|
||||||
|
},
|
||||||
|
themeToggle: {
|
||||||
|
lightMode: "Light mode",
|
||||||
|
darkMode: "Dark mode",
|
||||||
|
},
|
||||||
|
explorer: {
|
||||||
|
title: "Explorer",
|
||||||
|
},
|
||||||
|
footer: {
|
||||||
|
createdWith: "Created with",
|
||||||
|
},
|
||||||
|
graph: {
|
||||||
|
title: "Graph View",
|
||||||
|
},
|
||||||
|
recentNotes: {
|
||||||
|
title: "Recent Notes",
|
||||||
|
seeRemainingMore: ({ remaining }) => `See ${remaining} more →`,
|
||||||
|
},
|
||||||
|
transcludes: {
|
||||||
|
transcludeOf: ({ targetSlug }) => `Transclude of ${targetSlug}`,
|
||||||
|
linkToOriginal: "Link to original",
|
||||||
|
},
|
||||||
|
search: {
|
||||||
|
title: "Search",
|
||||||
|
searchBarPlaceholder: "Search for something",
|
||||||
|
},
|
||||||
|
tableOfContents: {
|
||||||
|
title: "Table of Contents",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pages: {
|
||||||
|
rss: {
|
||||||
|
recentNotes: "Recent notes",
|
||||||
|
lastFewNotes: ({ count }) => `Last ${count} notes`,
|
||||||
|
},
|
||||||
|
error: {
|
||||||
|
title: "Not Found",
|
||||||
|
notFound: "Either this page is private or doesn't exist.",
|
||||||
|
},
|
||||||
|
folderContent: {
|
||||||
|
folder: "Folder",
|
||||||
|
itemsUnderFolder: ({ count }) =>
|
||||||
|
count === 1 ? "1 item under this folder" : `${count} items under this folder.`,
|
||||||
|
},
|
||||||
|
tagContent: {
|
||||||
|
tag: "Tag",
|
||||||
|
tagIndex: "Tag Index",
|
||||||
|
itemsUnderTag: ({ count }) =>
|
||||||
|
count === 1 ? "1 item with this tag" : `${count} items with this tag.`,
|
||||||
|
showingFirst: ({ count }) => `Showing first ${count} tags.`,
|
||||||
|
totalTags: ({ count }) => `Found ${count} total tags.`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const satisfies Translation
|
@ -1,37 +0,0 @@
|
|||||||
{
|
|
||||||
"404": "Either this page is private or doesn't exist.",
|
|
||||||
"backlinks": {
|
|
||||||
"backlinks": "Backlinks",
|
|
||||||
"noBacklinksFound": "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"
|
|
||||||
}
|
|
||||||
}
|
|
65
quartz/i18n/locales/fr-FR.ts
Normal file
65
quartz/i18n/locales/fr-FR.ts
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import { Translation } from "./definition"
|
||||||
|
|
||||||
|
export default {
|
||||||
|
propertyDefaults: {
|
||||||
|
title: "Sans titre",
|
||||||
|
description: "Aucune description fournie",
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
backlinks: {
|
||||||
|
title: "Liens retour",
|
||||||
|
noBacklinksFound: "Aucun lien retour trouvé",
|
||||||
|
},
|
||||||
|
themeToggle: {
|
||||||
|
lightMode: "Mode clair",
|
||||||
|
darkMode: "Mode sombre",
|
||||||
|
},
|
||||||
|
explorer: {
|
||||||
|
title: "Explorateur",
|
||||||
|
},
|
||||||
|
footer: {
|
||||||
|
createdWith: "Créé avec",
|
||||||
|
},
|
||||||
|
graph: {
|
||||||
|
title: "Vue Graphique",
|
||||||
|
},
|
||||||
|
recentNotes: {
|
||||||
|
title: "Notes Récentes",
|
||||||
|
seeRemainingMore: ({ remaining }) => `Voir ${remaining} de plus →`,
|
||||||
|
},
|
||||||
|
transcludes: {
|
||||||
|
transcludeOf: ({ targetSlug }) => `Transclusion de ${targetSlug}`,
|
||||||
|
linkToOriginal: "Lien vers l'original",
|
||||||
|
},
|
||||||
|
search: {
|
||||||
|
title: "Recherche",
|
||||||
|
searchBarPlaceholder: "Rechercher quelque chose",
|
||||||
|
},
|
||||||
|
tableOfContents: {
|
||||||
|
title: "Table des Matières",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pages: {
|
||||||
|
rss: {
|
||||||
|
recentNotes: "Notes récentes",
|
||||||
|
lastFewNotes: ({ count }) => `Les dernières ${count} notes`,
|
||||||
|
},
|
||||||
|
error: {
|
||||||
|
title: "Pas trouvé",
|
||||||
|
notFound: "Cette page est soit privée, soit elle n'existe pas.",
|
||||||
|
},
|
||||||
|
folderContent: {
|
||||||
|
folder: "Dossier",
|
||||||
|
itemsUnderFolder: ({ count }) =>
|
||||||
|
count === 1 ? "1 élément sous ce dossier" : `${count} éléments sous ce dossier.`,
|
||||||
|
},
|
||||||
|
tagContent: {
|
||||||
|
tag: "Étiquette",
|
||||||
|
tagIndex: "Index des étiquettes",
|
||||||
|
itemsUnderTag: ({ count }) =>
|
||||||
|
count === 1 ? "1 élément avec cette étiquette" : `${count} éléments avec cette étiquette.`,
|
||||||
|
showingFirst: ({ count }) => `Affichage des premières ${count} étiquettes.`,
|
||||||
|
totalTags: ({ count }) => `Trouvé ${count} étiquettes au total.`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const satisfies Translation
|
@ -1,38 +0,0 @@
|
|||||||
{
|
|
||||||
"404": "Soit cette page est privée, soit elle n'existe pas.",
|
|
||||||
"backlinks": {
|
|
||||||
"backlinks": "Rétroliens",
|
|
||||||
"noBacklinksFound": "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é"
|
|
||||||
}
|
|
||||||
}
|
|
@ -8,6 +8,7 @@ import { sharedPageComponents } from "../../../quartz.layout"
|
|||||||
import { NotFound } from "../../components"
|
import { NotFound } from "../../components"
|
||||||
import { defaultProcessedContent } from "../vfile"
|
import { defaultProcessedContent } from "../vfile"
|
||||||
import { write } from "./helpers"
|
import { write } from "./helpers"
|
||||||
|
import { i18n } from "../../i18n"
|
||||||
|
|
||||||
export const NotFoundPage: QuartzEmitterPlugin = () => {
|
export const NotFoundPage: QuartzEmitterPlugin = () => {
|
||||||
const opts: FullPageLayout = {
|
const opts: FullPageLayout = {
|
||||||
@ -33,11 +34,12 @@ export const NotFoundPage: QuartzEmitterPlugin = () => {
|
|||||||
const url = new URL(`https://${cfg.baseUrl ?? "example.com"}`)
|
const url = new URL(`https://${cfg.baseUrl ?? "example.com"}`)
|
||||||
const path = url.pathname as FullSlug
|
const path = url.pathname as FullSlug
|
||||||
const externalResources = pageResources(path, resources)
|
const externalResources = pageResources(path, resources)
|
||||||
|
const notFound = i18n(cfg.locale).pages.error.title
|
||||||
const [tree, vfile] = defaultProcessedContent({
|
const [tree, vfile] = defaultProcessedContent({
|
||||||
slug,
|
slug,
|
||||||
text: "Not Found",
|
text: notFound,
|
||||||
description: "Not Found",
|
description: notFound,
|
||||||
frontmatter: { title: "Not Found", tags: [] },
|
frontmatter: { title: notFound, tags: [] },
|
||||||
})
|
})
|
||||||
const componentData: QuartzComponentProps = {
|
const componentData: QuartzComponentProps = {
|
||||||
fileData: vfile.data,
|
fileData: vfile.data,
|
||||||
@ -51,7 +53,7 @@ export const NotFoundPage: QuartzEmitterPlugin = () => {
|
|||||||
return [
|
return [
|
||||||
await write({
|
await write({
|
||||||
ctx,
|
ctx,
|
||||||
content: renderPage(slug, componentData, opts, externalResources),
|
content: renderPage(cfg, slug, componentData, opts, externalResources),
|
||||||
slug,
|
slug,
|
||||||
ext: ".html",
|
ext: ".html",
|
||||||
}),
|
}),
|
||||||
|
@ -6,6 +6,7 @@ import { FilePath, FullSlug, SimpleSlug, joinSegments, simplifySlug } from "../.
|
|||||||
import { QuartzEmitterPlugin } from "../types"
|
import { QuartzEmitterPlugin } from "../types"
|
||||||
import { toHtml } from "hast-util-to-html"
|
import { toHtml } from "hast-util-to-html"
|
||||||
import { write } from "./helpers"
|
import { write } from "./helpers"
|
||||||
|
import { i18n } from "../../i18n"
|
||||||
|
|
||||||
export type ContentIndex = Map<FullSlug, ContentDetails>
|
export type ContentIndex = Map<FullSlug, ContentDetails>
|
||||||
export type ContentDetails = {
|
export type ContentDetails = {
|
||||||
@ -38,7 +39,7 @@ function generateSiteMap(cfg: GlobalConfiguration, idx: ContentIndex): string {
|
|||||||
const base = cfg.baseUrl ?? ""
|
const base = cfg.baseUrl ?? ""
|
||||||
const createURLEntry = (slug: SimpleSlug, content: ContentDetails): string => `<url>
|
const createURLEntry = (slug: SimpleSlug, content: ContentDetails): string => `<url>
|
||||||
<loc>https://${joinSegments(base, encodeURI(slug))}</loc>
|
<loc>https://${joinSegments(base, encodeURI(slug))}</loc>
|
||||||
<lastmod>${content.date?.toISOString()}</lastmod>
|
${content.date && `<lastmod>${content.date.toISOString()}</lastmod>`}
|
||||||
</url>`
|
</url>`
|
||||||
const urls = Array.from(idx)
|
const urls = Array.from(idx)
|
||||||
.map(([slug, content]) => createURLEntry(simplifySlug(slug), content))
|
.map(([slug, content]) => createURLEntry(simplifySlug(slug), content))
|
||||||
@ -78,7 +79,7 @@ function generateRSSFeed(cfg: GlobalConfiguration, idx: ContentIndex, limit?: nu
|
|||||||
<channel>
|
<channel>
|
||||||
<title>${escapeHTML(cfg.pageTitle)}</title>
|
<title>${escapeHTML(cfg.pageTitle)}</title>
|
||||||
<link>https://${base}</link>
|
<link>https://${base}</link>
|
||||||
<description>${!!limit ? `Last ${limit} notes` : "Recent notes"} on ${escapeHTML(
|
<description>${!!limit ? i18n(cfg.locale).pages.rss.lastFewNotes({ count: limit }) : i18n(cfg.locale).pages.rss.recentNotes} on ${escapeHTML(
|
||||||
cfg.pageTitle,
|
cfg.pageTitle,
|
||||||
)}</description>
|
)}</description>
|
||||||
<generator>Quartz -- quartz.jzhao.xyz</generator>
|
<generator>Quartz -- quartz.jzhao.xyz</generator>
|
||||||
|
@ -49,7 +49,7 @@ export const ContentPage: QuartzEmitterPlugin<Partial<FullPageLayout>> = (userOp
|
|||||||
allFiles,
|
allFiles,
|
||||||
}
|
}
|
||||||
|
|
||||||
const content = renderPage(slug, componentData, opts, externalResources)
|
const content = renderPage(cfg, slug, componentData, opts, externalResources)
|
||||||
const fp = await write({
|
const fp = await write({
|
||||||
ctx,
|
ctx,
|
||||||
content,
|
content,
|
||||||
|
@ -18,6 +18,7 @@ import {
|
|||||||
import { defaultListPageLayout, sharedPageComponents } from "../../../quartz.layout"
|
import { defaultListPageLayout, sharedPageComponents } from "../../../quartz.layout"
|
||||||
import { FolderContent } from "../../components"
|
import { FolderContent } from "../../components"
|
||||||
import { write } from "./helpers"
|
import { write } from "./helpers"
|
||||||
|
import { i18n } from "../../i18n"
|
||||||
|
|
||||||
export const FolderPage: QuartzEmitterPlugin<Partial<FullPageLayout>> = (userOpts) => {
|
export const FolderPage: QuartzEmitterPlugin<Partial<FullPageLayout>> = (userOpts) => {
|
||||||
const opts: FullPageLayout = {
|
const opts: FullPageLayout = {
|
||||||
@ -57,7 +58,10 @@ export const FolderPage: QuartzEmitterPlugin<Partial<FullPageLayout>> = (userOpt
|
|||||||
folder,
|
folder,
|
||||||
defaultProcessedContent({
|
defaultProcessedContent({
|
||||||
slug: joinSegments(folder, "index") as FullSlug,
|
slug: joinSegments(folder, "index") as FullSlug,
|
||||||
frontmatter: { title: `Folder: ${folder}`, tags: [] },
|
frontmatter: {
|
||||||
|
title: `${i18n(cfg.locale).pages.folderContent.folder}: ${folder}`,
|
||||||
|
tags: [],
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
]),
|
]),
|
||||||
)
|
)
|
||||||
@ -82,7 +86,7 @@ export const FolderPage: QuartzEmitterPlugin<Partial<FullPageLayout>> = (userOpt
|
|||||||
allFiles,
|
allFiles,
|
||||||
}
|
}
|
||||||
|
|
||||||
const content = renderPage(slug, componentData, opts, externalResources)
|
const content = renderPage(cfg, slug, componentData, opts, externalResources)
|
||||||
const fp = await write({
|
const fp = await write({
|
||||||
ctx,
|
ctx,
|
||||||
content,
|
content,
|
||||||
|
@ -15,6 +15,7 @@ import {
|
|||||||
import { defaultListPageLayout, sharedPageComponents } from "../../../quartz.layout"
|
import { defaultListPageLayout, sharedPageComponents } from "../../../quartz.layout"
|
||||||
import { TagContent } from "../../components"
|
import { TagContent } from "../../components"
|
||||||
import { write } from "./helpers"
|
import { write } from "./helpers"
|
||||||
|
import { i18n } from "../../i18n"
|
||||||
|
|
||||||
export const TagPage: QuartzEmitterPlugin<Partial<FullPageLayout>> = (userOpts) => {
|
export const TagPage: QuartzEmitterPlugin<Partial<FullPageLayout>> = (userOpts) => {
|
||||||
const opts: FullPageLayout = {
|
const opts: FullPageLayout = {
|
||||||
@ -47,7 +48,10 @@ export const TagPage: QuartzEmitterPlugin<Partial<FullPageLayout>> = (userOpts)
|
|||||||
|
|
||||||
const tagDescriptions: Record<string, ProcessedContent> = Object.fromEntries(
|
const tagDescriptions: Record<string, ProcessedContent> = Object.fromEntries(
|
||||||
[...tags].map((tag) => {
|
[...tags].map((tag) => {
|
||||||
const title = tag === "index" ? "Tag Index" : `Tag: #${tag}`
|
const title =
|
||||||
|
tag === "index"
|
||||||
|
? i18n(cfg.locale).pages.tagContent.tagIndex
|
||||||
|
: `${i18n(cfg.locale).pages.tagContent.tag}: #${tag}`
|
||||||
return [
|
return [
|
||||||
tag,
|
tag,
|
||||||
defaultProcessedContent({
|
defaultProcessedContent({
|
||||||
@ -81,7 +85,7 @@ export const TagPage: QuartzEmitterPlugin<Partial<FullPageLayout>> = (userOpts)
|
|||||||
allFiles,
|
allFiles,
|
||||||
}
|
}
|
||||||
|
|
||||||
const content = renderPage(slug, componentData, opts, externalResources)
|
const content = renderPage(cfg, slug, componentData, opts, externalResources)
|
||||||
const fp = await write({
|
const fp = await write({
|
||||||
ctx,
|
ctx,
|
||||||
content,
|
content,
|
||||||
|
@ -5,6 +5,7 @@ import yaml from "js-yaml"
|
|||||||
import toml from "toml"
|
import toml from "toml"
|
||||||
import { slugTag } from "../../util/path"
|
import { slugTag } from "../../util/path"
|
||||||
import { QuartzPluginData } from "../vfile"
|
import { QuartzPluginData } from "../vfile"
|
||||||
|
import { i18n } from "../../i18n"
|
||||||
|
|
||||||
export interface Options {
|
export interface Options {
|
||||||
delims: string | string[]
|
delims: string | string[]
|
||||||
@ -43,7 +44,7 @@ export const FrontMatter: QuartzTransformerPlugin<Partial<Options> | undefined>
|
|||||||
const opts = { ...defaultOptions, ...userOpts }
|
const opts = { ...defaultOptions, ...userOpts }
|
||||||
return {
|
return {
|
||||||
name: "FrontMatter",
|
name: "FrontMatter",
|
||||||
markdownPlugins() {
|
markdownPlugins({ cfg }) {
|
||||||
return [
|
return [
|
||||||
[remarkFrontmatter, ["yaml", "toml"]],
|
[remarkFrontmatter, ["yaml", "toml"]],
|
||||||
() => {
|
() => {
|
||||||
@ -59,7 +60,7 @@ export const FrontMatter: QuartzTransformerPlugin<Partial<Options> | undefined>
|
|||||||
if (data.title) {
|
if (data.title) {
|
||||||
data.title = data.title.toString()
|
data.title = data.title.toString()
|
||||||
} else if (data.title === null || data.title === undefined) {
|
} else if (data.title === null || data.title === undefined) {
|
||||||
data.title = file.stem ?? "Untitled"
|
data.title = file.stem ?? i18n(cfg.configuration.locale).propertyDefaults.title
|
||||||
}
|
}
|
||||||
|
|
||||||
const tags = coerceToArray(coalesceAliases(data, ["tags", "tag"]))
|
const tags = coerceToArray(coalesceAliases(data, ["tags", "tag"]))
|
||||||
|
@ -3,7 +3,6 @@ import { Root } from "mdast"
|
|||||||
import { visit } from "unist-util-visit"
|
import { visit } from "unist-util-visit"
|
||||||
import { toString } from "mdast-util-to-string"
|
import { toString } from "mdast-util-to-string"
|
||||||
import Slugger from "github-slugger"
|
import Slugger from "github-slugger"
|
||||||
import { wikilinkRegex } from "./ofm"
|
|
||||||
|
|
||||||
export interface Options {
|
export interface Options {
|
||||||
maxDepth: 1 | 2 | 3 | 4 | 5 | 6
|
maxDepth: 1 | 2 | 3 | 4 | 5 | 6
|
||||||
@ -25,7 +24,6 @@ interface TocEntry {
|
|||||||
slug: string // this is just the anchor (#some-slug), not the canonical slug
|
slug: string // this is just the anchor (#some-slug), not the canonical slug
|
||||||
}
|
}
|
||||||
|
|
||||||
const regexMdLinks = new RegExp(/\[([^\[]+)\](\(.*\))/, "g")
|
|
||||||
const slugAnchor = new Slugger()
|
const slugAnchor = new Slugger()
|
||||||
export const TableOfContents: QuartzTransformerPlugin<Partial<Options> | undefined> = (
|
export const TableOfContents: QuartzTransformerPlugin<Partial<Options> | undefined> = (
|
||||||
userOpts,
|
userOpts,
|
||||||
@ -44,16 +42,7 @@ export const TableOfContents: QuartzTransformerPlugin<Partial<Options> | undefin
|
|||||||
let highestDepth: number = opts.maxDepth
|
let highestDepth: number = opts.maxDepth
|
||||||
visit(tree, "heading", (node) => {
|
visit(tree, "heading", (node) => {
|
||||||
if (node.depth <= opts.maxDepth) {
|
if (node.depth <= opts.maxDepth) {
|
||||||
let text = toString(node)
|
const text = toString(node)
|
||||||
|
|
||||||
// strip link formatting from toc entries
|
|
||||||
text = text.replace(wikilinkRegex, (_, rawFp, __, rawAlias) => {
|
|
||||||
const fp = rawFp?.trim() ?? ""
|
|
||||||
const alias = rawAlias?.slice(1).trim()
|
|
||||||
return alias ?? fp
|
|
||||||
})
|
|
||||||
text = text.replace(regexMdLinks, "$1")
|
|
||||||
|
|
||||||
highestDepth = Math.min(highestDepth, node.depth)
|
highestDepth = Math.min(highestDepth, node.depth)
|
||||||
toc.push({
|
toc.push({
|
||||||
depth: node.depth,
|
depth: node.depth,
|
||||||
|
@ -1,11 +1,3 @@
|
|||||||
export function pluralize(count: number, s: string): string {
|
|
||||||
if (count === 1) {
|
|
||||||
return `1 ${s}`
|
|
||||||
} else {
|
|
||||||
return `${count} ${s}s`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function capitalize(s: string): string {
|
export function capitalize(s: string): string {
|
||||||
return s.substring(0, 1).toUpperCase() + s.substring(1)
|
return s.substring(0, 1).toUpperCase() + s.substring(1)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user